mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
Merge branch 'master' into JDK-8367530-3
This commit is contained in:
commit
cfa2efafd4
@ -668,7 +668,7 @@ update.</p>
|
||||
(Note that this version is often presented as "MSVC 14.28", and reported
|
||||
by cl.exe as 19.28.) Older versions will not be accepted by
|
||||
<code>configure</code> and will not work. The maximum accepted version
|
||||
of Visual Studio is 2022.</p>
|
||||
of Visual Studio is 2026.</p>
|
||||
<p>If you have multiple versions of Visual Studio installed,
|
||||
<code>configure</code> will by default pick the latest. You can request
|
||||
a specific version to be used by setting
|
||||
|
||||
@ -468,7 +468,7 @@ available for this update.
|
||||
The minimum accepted version is Visual Studio 2019 version 16.8. (Note that
|
||||
this version is often presented as "MSVC 14.28", and reported by cl.exe as
|
||||
19.28.) Older versions will not be accepted by `configure` and will not work.
|
||||
The maximum accepted version of Visual Studio is 2022.
|
||||
The maximum accepted version of Visual Studio is 2026.
|
||||
|
||||
If you have multiple versions of Visual Studio installed, `configure` will by
|
||||
default pick the latest. You can request a specific version to be used by
|
||||
|
||||
@ -535,6 +535,8 @@ failure. This helps to reproduce intermittent test failures. Defaults to
|
||||
<h4 id="report">REPORT</h4>
|
||||
<p>Use this report style when reporting test results (sent to JTReg as
|
||||
<code>-report</code>). Defaults to <code>files</code>.</p>
|
||||
<h4 id="manual">MANUAL</h4>
|
||||
<p>Set to <code>true</code> to execute manual tests only.</p>
|
||||
<h3 id="gtest-keywords">Gtest keywords</h3>
|
||||
<h4 id="repeat">REPEAT</h4>
|
||||
<p>The number of times to repeat the tests
|
||||
|
||||
@ -512,6 +512,10 @@ helps to reproduce intermittent test failures. Defaults to 0.
|
||||
Use this report style when reporting test results (sent to JTReg as `-report`).
|
||||
Defaults to `files`.
|
||||
|
||||
#### MANUAL
|
||||
|
||||
Set to `true` to execute manual tests only.
|
||||
|
||||
### Gtest keywords
|
||||
|
||||
#### REPEAT
|
||||
|
||||
@ -206,7 +206,7 @@ $(eval $(call ParseKeywordVariable, JTREG, \
|
||||
SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \
|
||||
TEST_MODE ASSERT VERBOSE RETAIN TEST_THREAD_FACTORY JVMTI_STRESS_AGENT \
|
||||
MAX_MEM RUN_PROBLEM_LISTS RETRY_COUNT REPEAT_COUNT MAX_OUTPUT REPORT \
|
||||
AOT_JDK $(CUSTOM_JTREG_SINGLE_KEYWORDS), \
|
||||
AOT_JDK MANUAL $(CUSTOM_JTREG_SINGLE_KEYWORDS), \
|
||||
STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \
|
||||
EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS \
|
||||
$(CUSTOM_JTREG_STRING_KEYWORDS), \
|
||||
@ -911,7 +911,13 @@ define SetupRunJtregTestBody
|
||||
-vmoption:-Dtest.boot.jdk="$$(BOOT_JDK)" \
|
||||
-vmoption:-Djava.io.tmpdir="$$($1_TEST_TMP_DIR)"
|
||||
|
||||
$1_JTREG_BASIC_OPTIONS += -automatic -ignore:quiet
|
||||
$1_JTREG_BASIC_OPTIONS += -ignore:quiet
|
||||
|
||||
ifeq ($$(JTREG_MANUAL), true)
|
||||
$1_JTREG_BASIC_OPTIONS += -manual
|
||||
else
|
||||
$1_JTREG_BASIC_OPTIONS += -automatic
|
||||
endif
|
||||
|
||||
# Make it possible to specify the JIB_DATA_DIR for tests using the
|
||||
# JIB Artifact resolver
|
||||
@ -1151,6 +1157,7 @@ define SetupRunJtregTestBody
|
||||
$$(EXPR) $$($1_PASSED) + $$($1_FAILED) + $$($1_ERROR) + $$($1_SKIPPED))) \
|
||||
, \
|
||||
$$(eval $1_PASSED_AND_RUNTIME_SKIPPED := 0) \
|
||||
$$(eval $1_PASSED := 0) \
|
||||
$$(eval $1_RUNTIME_SKIPPED := 0) \
|
||||
$$(eval $1_SKIPPED := 0) \
|
||||
$$(eval $1_FAILED := 0) \
|
||||
|
||||
@ -282,10 +282,17 @@ AC_DEFUN([FLAGS_SETUP_OPTIMIZATION],
|
||||
C_O_FLAG_DEBUG_JVM="-O0"
|
||||
C_O_FLAG_NONE="-O0"
|
||||
|
||||
if test "x$TOOLCHAIN_TYPE" = xgcc; then
|
||||
C_O_FLAG_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing -fno-fat-lto-objects"
|
||||
else
|
||||
C_O_FLAG_LTO="-flto -fno-strict-aliasing"
|
||||
fi
|
||||
|
||||
if test "x$TOOLCHAIN_TYPE" = xclang && test "x$OPENJDK_TARGET_OS" = xaix; then
|
||||
C_O_FLAG_HIGHEST_JVM="${C_O_FLAG_HIGHEST_JVM} -finline-functions"
|
||||
C_O_FLAG_HIGHEST="${C_O_FLAG_HIGHEST} -finline-functions"
|
||||
C_O_FLAG_HI="${C_O_FLAG_HI} -finline-functions"
|
||||
C_O_FLAG_LTO="${C_O_FLAG_LTO} -ffat-lto-objects"
|
||||
fi
|
||||
|
||||
# -D_FORTIFY_SOURCE=2 hardening option needs optimization (at least -O1) enabled
|
||||
@ -317,6 +324,7 @@ AC_DEFUN([FLAGS_SETUP_OPTIMIZATION],
|
||||
C_O_FLAG_DEBUG_JVM=""
|
||||
C_O_FLAG_NONE="-Od"
|
||||
C_O_FLAG_SIZE="-O1"
|
||||
C_O_FLAG_LTO="-GL"
|
||||
fi
|
||||
|
||||
# Now copy to C++ flags
|
||||
@ -328,6 +336,7 @@ AC_DEFUN([FLAGS_SETUP_OPTIMIZATION],
|
||||
CXX_O_FLAG_DEBUG_JVM="$C_O_FLAG_DEBUG_JVM"
|
||||
CXX_O_FLAG_NONE="$C_O_FLAG_NONE"
|
||||
CXX_O_FLAG_SIZE="$C_O_FLAG_SIZE"
|
||||
CXX_O_FLAG_LTO="$C_O_FLAG_LTO"
|
||||
|
||||
# Adjust optimization flags according to debug level.
|
||||
case $DEBUG_LEVEL in
|
||||
@ -360,12 +369,15 @@ AC_DEFUN([FLAGS_SETUP_OPTIMIZATION],
|
||||
AC_SUBST(C_O_FLAG_NORM)
|
||||
AC_SUBST(C_O_FLAG_NONE)
|
||||
AC_SUBST(C_O_FLAG_SIZE)
|
||||
AC_SUBST(C_O_FLAG_LTO)
|
||||
|
||||
AC_SUBST(CXX_O_FLAG_HIGHEST_JVM)
|
||||
AC_SUBST(CXX_O_FLAG_HIGHEST)
|
||||
AC_SUBST(CXX_O_FLAG_HI)
|
||||
AC_SUBST(CXX_O_FLAG_NORM)
|
||||
AC_SUBST(CXX_O_FLAG_NONE)
|
||||
AC_SUBST(CXX_O_FLAG_SIZE)
|
||||
AC_SUBST(CXX_O_FLAG_LTO)
|
||||
])
|
||||
|
||||
AC_DEFUN([FLAGS_SETUP_CFLAGS],
|
||||
|
||||
@ -61,6 +61,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
|
||||
fi
|
||||
|
||||
BASIC_LDFLAGS_JVM_ONLY=""
|
||||
LDFLAGS_LTO="-flto=auto -fuse-linker-plugin -fno-strict-aliasing"
|
||||
|
||||
LDFLAGS_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r"
|
||||
|
||||
@ -68,6 +69,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_CXX_PARTIAL_LINKING="$MACHINE_FLAG -r"
|
||||
|
||||
if test "x$OPENJDK_TARGET_OS" = xlinux; then
|
||||
@ -87,6 +89,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
|
||||
BASIC_LDFLAGS="-opt:ref"
|
||||
BASIC_LDFLAGS_JDK_ONLY="-incremental:no"
|
||||
BASIC_LDFLAGS_JVM_ONLY="-opt:icf,8 -subsystem:windows"
|
||||
LDFLAGS_LTO="-LTCG:INCREMENTAL"
|
||||
fi
|
||||
|
||||
if (test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang) \
|
||||
@ -148,6 +151,7 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
|
||||
|
||||
# Export some intermediate variables for compatibility
|
||||
LDFLAGS_CXX_JDK="$DEBUGLEVEL_LDFLAGS_JDK_ONLY"
|
||||
AC_SUBST(LDFLAGS_LTO)
|
||||
AC_SUBST(LDFLAGS_CXX_JDK)
|
||||
AC_SUBST(LDFLAGS_CXX_PARTIAL_LINKING)
|
||||
])
|
||||
|
||||
@ -513,12 +513,14 @@ C_O_FLAG_HI := @C_O_FLAG_HI@
|
||||
C_O_FLAG_NORM := @C_O_FLAG_NORM@
|
||||
C_O_FLAG_NONE := @C_O_FLAG_NONE@
|
||||
C_O_FLAG_SIZE := @C_O_FLAG_SIZE@
|
||||
C_O_FLAG_LTO := @C_O_FLAG_LTO@
|
||||
CXX_O_FLAG_HIGHEST_JVM := @CXX_O_FLAG_HIGHEST_JVM@
|
||||
CXX_O_FLAG_HIGHEST := @CXX_O_FLAG_HIGHEST@
|
||||
CXX_O_FLAG_HI := @CXX_O_FLAG_HI@
|
||||
CXX_O_FLAG_NORM := @CXX_O_FLAG_NORM@
|
||||
CXX_O_FLAG_NONE := @CXX_O_FLAG_NONE@
|
||||
CXX_O_FLAG_SIZE := @CXX_O_FLAG_SIZE@
|
||||
CXX_O_FLAG_LTO := @CXX_O_FLAG_LTO@
|
||||
|
||||
GENDEPS_FLAGS := @GENDEPS_FLAGS@
|
||||
|
||||
@ -587,6 +589,9 @@ LDFLAGS_CXX_JDK := @LDFLAGS_CXX_JDK@
|
||||
# LDFLAGS specific to partial linking.
|
||||
LDFLAGS_CXX_PARTIAL_LINKING := @LDFLAGS_CXX_PARTIAL_LINKING@
|
||||
|
||||
# LDFLAGS specific to link time optimization
|
||||
LDFLAGS_LTO := @LDFLAGS_LTO@
|
||||
|
||||
# Sometimes a different linker is needed for c++ libs
|
||||
LDCXX := @LDCXX@
|
||||
# The flags for linking libstdc++ linker.
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
|
||||
################################################################################
|
||||
# The order of these defines the priority by which we try to find them.
|
||||
VALID_VS_VERSIONS="2022 2019"
|
||||
VALID_VS_VERSIONS="2022 2019 2026"
|
||||
|
||||
VS_DESCRIPTION_2019="Microsoft Visual Studio 2019"
|
||||
VS_VERSION_INTERNAL_2019=142
|
||||
@ -57,6 +57,21 @@ VS_SDK_PLATFORM_NAME_2022=
|
||||
VS_SUPPORTED_2022=true
|
||||
VS_TOOLSET_SUPPORTED_2022=true
|
||||
|
||||
VS_DESCRIPTION_2026="Microsoft Visual Studio 2026"
|
||||
VS_VERSION_INTERNAL_2026=145
|
||||
VS_MSVCR_2026=vcruntime140.dll
|
||||
VS_VCRUNTIME_1_2026=vcruntime140_1.dll
|
||||
VS_MSVCP_2026=msvcp140.dll
|
||||
VS_ENVVAR_2026="VS180COMNTOOLS"
|
||||
VS_USE_UCRT_2026="true"
|
||||
VS_VS_INSTALLDIR_2026="Microsoft Visual Studio/18"
|
||||
VS_EDITIONS_2026="BuildTools Community Professional Enterprise"
|
||||
VS_SDK_INSTALLDIR_2026=
|
||||
VS_VS_PLATFORM_NAME_2026="v145"
|
||||
VS_SDK_PLATFORM_NAME_2026=
|
||||
VS_SUPPORTED_2026=true
|
||||
VS_TOOLSET_SUPPORTED_2026=true
|
||||
|
||||
################################################################################
|
||||
|
||||
AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT],
|
||||
|
||||
@ -98,6 +98,7 @@ include native/Paths.gmk
|
||||
# SYSROOT_CFLAGS the compiler flags for using the specific sysroot
|
||||
# SYSROOT_LDFLAGS the linker flags for using the specific sysroot
|
||||
# OPTIMIZATION sets optimization level to NONE, LOW, HIGH, HIGHEST, HIGHEST_JVM, SIZE
|
||||
# LINK_TIME_OPTIMIZATION if set to true, enables link time optimization
|
||||
# DISABLED_WARNINGS_<toolchain> Disable the given warnings for the specified toolchain
|
||||
# DISABLED_WARNINGS_<toolchain>_<OS> Disable the given warnings for the specified
|
||||
# toolchain and target OS
|
||||
|
||||
@ -194,6 +194,11 @@ define SetupCompilerFlags
|
||||
$1_EXTRA_CXXFLAGS += $(CFLAGS_WARNINGS_ARE_ERRORS)
|
||||
endif
|
||||
|
||||
ifeq (true, $$($1_LINK_TIME_OPTIMIZATION))
|
||||
$1_EXTRA_CFLAGS += $(C_O_FLAG_LTO)
|
||||
$1_EXTRA_CXXFLAGS += $(CXX_O_FLAG_LTO)
|
||||
endif
|
||||
|
||||
ifeq (NONE, $$($1_OPTIMIZATION))
|
||||
$1_OPT_CFLAGS := $(C_O_FLAG_NONE)
|
||||
$1_OPT_CXXFLAGS := $(CXX_O_FLAG_NONE)
|
||||
@ -222,6 +227,10 @@ define SetupLinkerFlags
|
||||
# Pickup extra OPENJDK_TARGET_OS_TYPE, OPENJDK_TARGET_OS and TOOLCHAIN_TYPE
|
||||
# dependent variables for LDFLAGS and LIBS, and additionally the pair dependent
|
||||
# TOOLCHAIN_TYPE plus OPENJDK_TARGET_OS
|
||||
ifeq ($$($1_LINK_TIME_OPTIMIZATION), true)
|
||||
$1_EXTRA_LDFLAGS += $(LDFLAGS_LTO)
|
||||
endif
|
||||
|
||||
$1_EXTRA_LDFLAGS += $$($1_LDFLAGS_$(OPENJDK_TARGET_OS_TYPE)) $$($1_LDFLAGS_$(OPENJDK_TARGET_OS)) \
|
||||
$$($1_LDFLAGS_$(TOOLCHAIN_TYPE)) $$($1_LDFLAGS_$(TOOLCHAIN_TYPE)_$(OPENJDK_TARGET_OS))
|
||||
$1_EXTRA_LIBS += $$($1_LIBS_$(OPENJDK_TARGET_OS_TYPE)) $$($1_LIBS_$(OPENJDK_TARGET_OS)) \
|
||||
|
||||
@ -95,6 +95,7 @@ $(eval $(call SetupJdkLibrary, BUILD_GTEST_LIBJVM, \
|
||||
EXTRA_OBJECT_FILES := $(BUILD_LIBJVM_ALL_OBJS), \
|
||||
DEFAULT_CFLAGS := false, \
|
||||
CFLAGS := $(JVM_CFLAGS) \
|
||||
-DHOTSPOT_GTEST \
|
||||
-I$(GTEST_FRAMEWORK_SRC)/googletest/include \
|
||||
-I$(GTEST_FRAMEWORK_SRC)/googlemock/include \
|
||||
$(addprefix -I, $(GTEST_TEST_SRC)), \
|
||||
|
||||
@ -234,6 +234,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \
|
||||
LDFLAGS := $(JVM_LDFLAGS), \
|
||||
LIBS := $(JVM_LIBS), \
|
||||
OPTIMIZATION := $(JVM_OPTIMIZATION), \
|
||||
LINK_TIME_OPTIMIZATION := $(JVM_LTO), \
|
||||
OBJECT_DIR := $(JVM_OUTPUTDIR)/objs, \
|
||||
STRIPFLAGS := $(JVM_STRIPFLAGS), \
|
||||
EMBED_MANIFEST := true, \
|
||||
|
||||
@ -175,22 +175,12 @@ ifeq ($(call check-jvm-feature, link-time-opt), true)
|
||||
# Set JVM_OPTIMIZATION directly so other jvm-feature flags can override it
|
||||
# later on if desired
|
||||
JVM_OPTIMIZATION := HIGHEST_JVM
|
||||
ifeq ($(call isCompiler, gcc), true)
|
||||
JVM_CFLAGS_FEATURES += -flto=auto -fuse-linker-plugin -fno-strict-aliasing \
|
||||
-fno-fat-lto-objects
|
||||
JVM_LDFLAGS_FEATURES += $(CXX_O_FLAG_HIGHEST_JVM) -flto=auto \
|
||||
-fuse-linker-plugin -fno-strict-aliasing
|
||||
else ifeq ($(call isCompiler, clang), true)
|
||||
JVM_CFLAGS_FEATURES += -flto -fno-strict-aliasing
|
||||
ifeq ($(call isBuildOs, aix), true)
|
||||
JVM_CFLAGS_FEATURES += -ffat-lto-objects
|
||||
endif
|
||||
JVM_LDFLAGS_FEATURES += $(CXX_O_FLAG_HIGHEST_JVM) -flto -fno-strict-aliasing
|
||||
else ifeq ($(call isCompiler, microsoft), true)
|
||||
JVM_CFLAGS_FEATURES += -GL
|
||||
JVM_LDFLAGS_FEATURES += -LTCG:INCREMENTAL
|
||||
JVM_LTO := true
|
||||
ifneq ($(call isCompiler, microsoft), true)
|
||||
JVM_LDFLAGS_FEATURES += $(CXX_O_FLAG_HIGHEST_JVM)
|
||||
endif
|
||||
else
|
||||
JVM_LTO := false
|
||||
ifeq ($(call isCompiler, gcc), true)
|
||||
JVM_LDFLAGS_FEATURES += -O1
|
||||
endif
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -219,13 +219,13 @@ public final class SealedGraph implements Taglet {
|
||||
// This implies the module is always the same.
|
||||
private String relativeLink(TypeElement node) {
|
||||
var util = SealedGraph.this.docletEnvironment.getElementUtils();
|
||||
var rootPackage = util.getPackageOf(rootNode);
|
||||
var nodePackage = util.getPackageOf(node);
|
||||
var backNavigator = rootPackage.getQualifiedName().toString().chars()
|
||||
// Note: SVG files for nested types use the simple names of containing types as parent directories.
|
||||
// We therefore need to convert all dots in the qualified name to "../" below.
|
||||
var backNavigator = rootNode.getQualifiedName().toString().chars()
|
||||
.filter(c -> c == '.')
|
||||
.mapToObj(c -> "../")
|
||||
.collect(joining()) +
|
||||
"../";
|
||||
.collect(joining());
|
||||
var forwardNavigator = nodePackage.getQualifiedName().toString()
|
||||
.replace(".", "/");
|
||||
|
||||
|
||||
@ -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), \
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
//
|
||||
// Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
// Copyright (c) 2014, 2024, Red Hat, Inc. All rights reserved.
|
||||
// Copyright 2025 Arm Limited and/or its affiliates.
|
||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
//
|
||||
// This code is free software; you can redistribute it and/or modify it
|
||||
@ -1194,15 +1195,10 @@ class HandlerImpl {
|
||||
|
||||
public:
|
||||
|
||||
static int emit_exception_handler(C2_MacroAssembler *masm);
|
||||
static int emit_deopt_handler(C2_MacroAssembler* masm);
|
||||
|
||||
static uint size_exception_handler() {
|
||||
return MacroAssembler::far_codestub_branch_size();
|
||||
}
|
||||
|
||||
static uint size_deopt_handler() {
|
||||
// count one adr and one far branch instruction
|
||||
// count one branch instruction and one far call instruction sequence
|
||||
return NativeInstruction::instruction_size + MacroAssembler::far_codestub_branch_size();
|
||||
}
|
||||
};
|
||||
@ -2261,25 +2257,6 @@ uint MachUEPNode::size(PhaseRegAlloc* ra_) const
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// Emit exception handler code.
|
||||
int HandlerImpl::emit_exception_handler(C2_MacroAssembler* masm)
|
||||
{
|
||||
// mov rscratch1 #exception_blob_entry_point
|
||||
// br rscratch1
|
||||
// Note that the code buffer's insts_mark is always relative to insts.
|
||||
// That's why we must use the macroassembler to generate a handler.
|
||||
address base = __ start_a_stub(size_exception_handler());
|
||||
if (base == nullptr) {
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
return 0; // CodeBuffer::expand failed
|
||||
}
|
||||
int offset = __ offset();
|
||||
__ far_jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point()));
|
||||
assert(__ offset() - offset <= (int) size_exception_handler(), "overflow");
|
||||
__ end_a_stub();
|
||||
return offset;
|
||||
}
|
||||
|
||||
// Emit deopt handler code.
|
||||
int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm)
|
||||
{
|
||||
@ -2290,14 +2267,20 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm)
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
return 0; // CodeBuffer::expand failed
|
||||
}
|
||||
int offset = __ offset();
|
||||
|
||||
__ adr(lr, __ pc());
|
||||
__ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
int offset = __ offset();
|
||||
Label start;
|
||||
__ bind(start);
|
||||
__ far_call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
|
||||
int entry_offset = __ offset();
|
||||
__ b(start);
|
||||
|
||||
assert(__ offset() - offset == (int) size_deopt_handler(), "overflow");
|
||||
assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
__ end_a_stub();
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
// REQUIRED MATCHER CODE
|
||||
@ -3388,28 +3371,28 @@ encode %{
|
||||
// aarch64_enc_cmpxchg_acq is that we use load-acquire in the
|
||||
// CompareAndSwap sequence to serve as a barrier on acquiring a
|
||||
// lock.
|
||||
enc_class aarch64_enc_cmpxchg_acq(memory mem, iRegLNoSp oldval, iRegLNoSp newval) %{
|
||||
enc_class aarch64_enc_cmpxchg_acq(memory mem, iRegL oldval, iRegL newval) %{
|
||||
guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding");
|
||||
__ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register,
|
||||
Assembler::xword, /*acquire*/ true, /*release*/ true,
|
||||
/*weak*/ false, noreg);
|
||||
%}
|
||||
|
||||
enc_class aarch64_enc_cmpxchgw_acq(memory mem, iRegINoSp oldval, iRegINoSp newval) %{
|
||||
enc_class aarch64_enc_cmpxchgw_acq(memory mem, iRegI oldval, iRegI newval) %{
|
||||
guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding");
|
||||
__ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register,
|
||||
Assembler::word, /*acquire*/ true, /*release*/ true,
|
||||
/*weak*/ false, noreg);
|
||||
%}
|
||||
|
||||
enc_class aarch64_enc_cmpxchgs_acq(memory mem, iRegINoSp oldval, iRegINoSp newval) %{
|
||||
enc_class aarch64_enc_cmpxchgs_acq(memory mem, iRegI oldval, iRegI newval) %{
|
||||
guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding");
|
||||
__ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register,
|
||||
Assembler::halfword, /*acquire*/ true, /*release*/ true,
|
||||
/*weak*/ false, noreg);
|
||||
%}
|
||||
|
||||
enc_class aarch64_enc_cmpxchgb_acq(memory mem, iRegINoSp oldval, iRegINoSp newval) %{
|
||||
enc_class aarch64_enc_cmpxchgb_acq(memory mem, iRegI oldval, iRegI newval) %{
|
||||
guarantee($mem$$index == -1 && $mem$$disp == 0, "impossible encoding");
|
||||
__ cmpxchg($mem$$base$$Register, $oldval$$Register, $newval$$Register,
|
||||
Assembler::byte, /*acquire*/ true, /*release*/ true,
|
||||
@ -3417,7 +3400,7 @@ encode %{
|
||||
%}
|
||||
|
||||
// auxiliary used for CompareAndSwapX to set result register
|
||||
enc_class aarch64_enc_cset_eq(iRegINoSp res) %{
|
||||
enc_class aarch64_enc_cset_eq(iRegI res) %{
|
||||
Register res_reg = as_Register($res$$reg);
|
||||
__ cset(res_reg, Assembler::EQ);
|
||||
%}
|
||||
@ -8403,7 +8386,7 @@ instruct castVVMask(pRegGov dst)
|
||||
// XXX No flag versions for CompareAndSwap{I,L,P,N} because matcher
|
||||
// can't match them
|
||||
|
||||
instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{
|
||||
instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{
|
||||
|
||||
match(Set res (CompareAndSwapB mem (Binary oldval newval)));
|
||||
ins_cost(2 * VOLATILE_REF_COST);
|
||||
@ -8421,7 +8404,7 @@ instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoS
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{
|
||||
instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{
|
||||
|
||||
match(Set res (CompareAndSwapS mem (Binary oldval newval)));
|
||||
ins_cost(2 * VOLATILE_REF_COST);
|
||||
@ -8439,7 +8422,7 @@ instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoS
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{
|
||||
instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{
|
||||
|
||||
match(Set res (CompareAndSwapI mem (Binary oldval newval)));
|
||||
ins_cost(2 * VOLATILE_REF_COST);
|
||||
@ -8457,7 +8440,7 @@ instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoS
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegLNoSp oldval, iRegLNoSp newval, rFlagsReg cr) %{
|
||||
instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval, rFlagsReg cr) %{
|
||||
|
||||
match(Set res (CompareAndSwapL mem (Binary oldval newval)));
|
||||
ins_cost(2 * VOLATILE_REF_COST);
|
||||
@ -8494,7 +8477,7 @@ instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoSp newval, rFlagsReg cr) %{
|
||||
instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, rFlagsReg cr) %{
|
||||
|
||||
match(Set res (CompareAndSwapN mem (Binary oldval newval)));
|
||||
predicate(n->as_LoadStore()->barrier_data() == 0);
|
||||
@ -8515,7 +8498,7 @@ instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoS
|
||||
|
||||
// alternative CompareAndSwapX when we are eliding barriers
|
||||
|
||||
instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{
|
||||
instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{
|
||||
|
||||
predicate(needs_acquiring_load_exclusive(n));
|
||||
match(Set res (CompareAndSwapB mem (Binary oldval newval)));
|
||||
@ -8534,7 +8517,7 @@ instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegI
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{
|
||||
instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{
|
||||
|
||||
predicate(needs_acquiring_load_exclusive(n));
|
||||
match(Set res (CompareAndSwapS mem (Binary oldval newval)));
|
||||
@ -8553,7 +8536,7 @@ instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegI
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegINoSp newval, rFlagsReg cr) %{
|
||||
instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval, rFlagsReg cr) %{
|
||||
|
||||
predicate(needs_acquiring_load_exclusive(n));
|
||||
match(Set res (CompareAndSwapI mem (Binary oldval newval)));
|
||||
@ -8572,7 +8555,7 @@ instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegINoSp oldval, iRegI
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegLNoSp oldval, iRegLNoSp newval, rFlagsReg cr) %{
|
||||
instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval, rFlagsReg cr) %{
|
||||
|
||||
predicate(needs_acquiring_load_exclusive(n));
|
||||
match(Set res (CompareAndSwapL mem (Binary oldval newval)));
|
||||
@ -8610,7 +8593,7 @@ instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP new
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegNNoSp oldval, iRegNNoSp newval, rFlagsReg cr) %{
|
||||
instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, rFlagsReg cr) %{
|
||||
|
||||
predicate(needs_acquiring_load_exclusive(n) && n->as_LoadStore()->barrier_data() == 0);
|
||||
match(Set res (CompareAndSwapN mem (Binary oldval newval)));
|
||||
|
||||
@ -449,12 +449,20 @@ int LIR_Assembler::emit_deopt_handler() {
|
||||
|
||||
int offset = code_offset();
|
||||
|
||||
__ adr(lr, pc());
|
||||
__ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
Label start;
|
||||
__ bind(start);
|
||||
|
||||
__ far_call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
|
||||
int entry_offset = __ offset();
|
||||
__ b(start);
|
||||
|
||||
guarantee(code_offset() - offset <= deopt_handler_size(), "overflow");
|
||||
assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
__ end_a_stub();
|
||||
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
void LIR_Assembler::add_debug_info_for_branch(address adr, CodeEmitInfo* info) {
|
||||
|
||||
@ -71,7 +71,7 @@ friend class ArrayCopyStub;
|
||||
// CompiledDirectCall::to_trampoline_stub_size()
|
||||
_call_stub_size = 13 * NativeInstruction::instruction_size,
|
||||
_exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175),
|
||||
_deopt_handler_size = 7 * NativeInstruction::instruction_size
|
||||
_deopt_handler_size = 4 * NativeInstruction::instruction_size
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@ -879,7 +879,6 @@ void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
OrderAccess::fence();
|
||||
ICache::invalidate_word((address)patch_addr);
|
||||
}
|
||||
|
||||
|
||||
@ -394,12 +394,6 @@ void NativePostCallNop::make_deopt() {
|
||||
NativeDeoptInstruction::insert(addr_at(0));
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
static bool is_movk_to_zr(uint32_t insn) {
|
||||
return ((insn & 0xffe0001f) == 0xf280001f);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool NativePostCallNop::patch(int32_t oopmap_slot, int32_t cb_offset) {
|
||||
if (((oopmap_slot & 0xff) != oopmap_slot) || ((cb_offset & 0xffffff) != cb_offset)) {
|
||||
return false; // cannot encode
|
||||
|
||||
@ -526,14 +526,31 @@ inline NativeLdSt* NativeLdSt_at(address addr) {
|
||||
// can store an offset from the initial nop to the nmethod.
|
||||
|
||||
class NativePostCallNop: public NativeInstruction {
|
||||
private:
|
||||
static bool is_movk_to_zr(uint32_t insn) {
|
||||
return ((insn & 0xffe0001f) == 0xf280001f);
|
||||
}
|
||||
|
||||
public:
|
||||
enum AArch64_specific_constants {
|
||||
// The two parts should be checked separately to prevent out of bounds access in case
|
||||
// the return address points to the deopt handler stub code entry point which could be
|
||||
// at the end of page.
|
||||
first_check_size = instruction_size
|
||||
};
|
||||
|
||||
bool check() const {
|
||||
uint64_t insns = *(uint64_t*)addr_at(0);
|
||||
// Check for two instructions: nop; movk zr, xx
|
||||
// These instructions only ever appear together in a post-call
|
||||
// NOP, so it's unnecessary to check that the third instruction is
|
||||
// a MOVK as well.
|
||||
return (insns & 0xffe0001fffffffff) == 0xf280001fd503201f;
|
||||
// Check the first instruction is NOP.
|
||||
if (is_nop()) {
|
||||
uint32_t insn = *(uint32_t*)addr_at(first_check_size);
|
||||
// Check next instruction is MOVK zr, xx.
|
||||
// These instructions only ever appear together in a post-call
|
||||
// NOP, so it's unnecessary to check that the third instruction is
|
||||
// a MOVK as well.
|
||||
return is_movk_to_zr(insn);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const {
|
||||
|
||||
@ -260,8 +260,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() {
|
||||
|
||||
//------------------------------generate_exception_blob---------------------------
|
||||
// creates exception blob at the end
|
||||
// Using exception blob, this code is jumped from a compiled method.
|
||||
// (see emit_exception_handler in aarch64.ad file)
|
||||
//
|
||||
// Given an exception pc at a call we call into the runtime for the
|
||||
// handler in this method. This handler might merely restore state
|
||||
|
||||
@ -1375,7 +1375,6 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
|
||||
__ ldr(r10, Address(rmethod, Method::native_function_offset()));
|
||||
ExternalAddress unsatisfied(SharedRuntime::native_method_throw_unsatisfied_link_error_entry());
|
||||
__ lea(rscratch2, unsatisfied);
|
||||
__ ldr(rscratch2, rscratch2);
|
||||
__ cmp(r10, rscratch2);
|
||||
__ br(Assembler::NE, L);
|
||||
__ call_VM(noreg,
|
||||
|
||||
@ -105,14 +105,8 @@ class HandlerImpl {
|
||||
|
||||
public:
|
||||
|
||||
static int emit_exception_handler(C2_MacroAssembler *masm);
|
||||
static int emit_deopt_handler(C2_MacroAssembler* masm);
|
||||
|
||||
static uint size_exception_handler() {
|
||||
return ( 3 * 4 );
|
||||
}
|
||||
|
||||
|
||||
static uint size_deopt_handler() {
|
||||
return ( 9 * 4 );
|
||||
}
|
||||
@ -876,26 +870,6 @@ uint MachUEPNode::size(PhaseRegAlloc *ra_) const {
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// Emit exception handler code.
|
||||
int HandlerImpl::emit_exception_handler(C2_MacroAssembler* masm) {
|
||||
address base = __ start_a_stub(size_exception_handler());
|
||||
if (base == nullptr) {
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
return 0; // CodeBuffer::expand failed
|
||||
}
|
||||
|
||||
int offset = __ offset();
|
||||
|
||||
// OK to trash LR, because exception blob will kill it
|
||||
__ jump(OptoRuntime::exception_blob()->entry_point(), relocInfo::runtime_call_type, LR_tmp);
|
||||
|
||||
assert(__ offset() - offset <= (int) size_exception_handler(), "overflow");
|
||||
|
||||
__ end_a_stub();
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) {
|
||||
// Can't use any of the current frame's registers as we may have deopted
|
||||
// at a poll and everything can be live.
|
||||
@ -906,19 +880,28 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
int offset = __ offset();
|
||||
address deopt_pc = __ pc();
|
||||
|
||||
__ sub(SP, SP, wordSize); // make room for saved PC
|
||||
__ push(LR); // save LR that may be live when we get here
|
||||
__ mov_relative_address(LR, deopt_pc);
|
||||
__ str(LR, Address(SP, wordSize)); // save deopt PC
|
||||
__ pop(LR); // restore LR
|
||||
Label start;
|
||||
__ bind(start);
|
||||
|
||||
__ jump(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type, noreg);
|
||||
|
||||
int entry_offset = __ offset();
|
||||
address deopt_pc = __ pc();
|
||||
// Preserve R0 and reserve space for the address of the entry point
|
||||
__ push(RegisterSet(R0) | RegisterSet(R1));
|
||||
// Store the entry point address
|
||||
__ mov_relative_address(R0, deopt_pc);
|
||||
__ str(R0, Address(SP, wordSize));
|
||||
__ pop(R0); // restore R0
|
||||
__ b(start);
|
||||
|
||||
assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow");
|
||||
assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
|
||||
__ end_a_stub();
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
bool Matcher::match_rule_supported(int opcode) {
|
||||
|
||||
@ -272,14 +272,22 @@ int LIR_Assembler::emit_deopt_handler() {
|
||||
|
||||
int offset = code_offset();
|
||||
|
||||
__ mov_relative_address(LR, __ pc());
|
||||
__ push(LR); // stub expects LR to be saved
|
||||
Label start;
|
||||
__ bind(start);
|
||||
|
||||
__ jump(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type, noreg);
|
||||
|
||||
int entry_offset = __ offset();
|
||||
__ mov_relative_address(LR, __ pc());
|
||||
__ push(LR); // stub expects LR to be saved
|
||||
__ b(start);
|
||||
|
||||
assert(code_offset() - offset <= deopt_handler_size(), "overflow");
|
||||
assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
__ end_a_stub();
|
||||
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
enum {
|
||||
_call_stub_size = 16,
|
||||
_exception_handler_size = PRODUCT_ONLY(68) NOT_PRODUCT(68+60),
|
||||
_deopt_handler_size = 16
|
||||
_deopt_handler_size = 20
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@ -430,6 +430,13 @@ inline NativeCall* nativeCall_before(address return_address) {
|
||||
|
||||
class NativePostCallNop: public NativeInstruction {
|
||||
public:
|
||||
enum arm_specific_constants {
|
||||
// If the check is adjusted to read beyond size of the instruction sequence at the deopt
|
||||
// handler stub code entry point, it has to happen in two stages - to prevent out of bounds
|
||||
// access in case the return address points to the entry point which could be at
|
||||
// the end of page.
|
||||
first_check_size = instruction_size
|
||||
};
|
||||
bool check() const { return is_nop(); }
|
||||
bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const { return false; }
|
||||
bool patch(int32_t oopmap_slot, int32_t cb_offset) { return false; }
|
||||
|
||||
@ -182,8 +182,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() {
|
||||
|
||||
//------------------------------ generate_exception_blob ---------------------------
|
||||
// creates exception blob at the end
|
||||
// Using exception blob, this code is jumped from a compiled method.
|
||||
// (see emit_exception_handler in sparc.ad file)
|
||||
//
|
||||
// Given an exception pc at a call we call into the runtime for the
|
||||
// handler in this method. This handler might merely restore state
|
||||
|
||||
@ -264,12 +264,19 @@ int LIR_Assembler::emit_deopt_handler() {
|
||||
}
|
||||
|
||||
int offset = code_offset();
|
||||
Label start;
|
||||
|
||||
__ bind(start);
|
||||
__ bl64_patchable(SharedRuntime::deopt_blob()->unpack(), relocInfo::runtime_call_type);
|
||||
int entry_offset = __ offset();
|
||||
__ b(start);
|
||||
|
||||
guarantee(code_offset() - offset <= deopt_handler_size(), "overflow");
|
||||
assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
__ end_a_stub();
|
||||
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ enum {
|
||||
_static_call_stub_size = 4 * BytesPerInstWord + MacroAssembler::b64_patchable_size, // or smaller
|
||||
_call_stub_size = _static_call_stub_size + MacroAssembler::trampoline_stub_size, // or smaller
|
||||
_exception_handler_size = MacroAssembler::b64_patchable_size, // or smaller
|
||||
_deopt_handler_size = MacroAssembler::bl64_patchable_size
|
||||
_deopt_handler_size = MacroAssembler::bl64_patchable_size + BytesPerInstWord
|
||||
};
|
||||
|
||||
// '_static_call_stub_size' is only used on ppc (see LIR_Assembler::emit_static_call_stub()
|
||||
|
||||
@ -51,8 +51,6 @@ class NativeInstruction {
|
||||
friend class Relocation;
|
||||
|
||||
public:
|
||||
bool is_post_call_nop() const { return MacroAssembler::is_post_call_nop(long_at(0)); }
|
||||
|
||||
bool is_jump() const { return Assembler::is_b(long_at(0)); } // See NativeGeneralJump.
|
||||
|
||||
bool is_sigtrap_ic_miss_check() {
|
||||
@ -531,6 +529,14 @@ class NativePostCallNop: public NativeInstruction {
|
||||
};
|
||||
|
||||
public:
|
||||
enum ppc_specific_constants {
|
||||
// If the check is adjusted to read beyond size of the instruction at the deopt handler stub
|
||||
// code entry point, it has to happen in two stages - to prevent out of bounds access in case
|
||||
// the return address points to the entry point which could be at the end of page.
|
||||
first_check_size = BytesPerInstWord
|
||||
};
|
||||
|
||||
bool is_post_call_nop() const { return MacroAssembler::is_post_call_nop(long_at(0)); }
|
||||
bool check() const { return is_post_call_nop(); }
|
||||
bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const {
|
||||
uint32_t instr_bits = long_at(0);
|
||||
|
||||
@ -2088,17 +2088,11 @@ class HandlerImpl {
|
||||
|
||||
public:
|
||||
|
||||
static int emit_exception_handler(C2_MacroAssembler *masm);
|
||||
static int emit_deopt_handler(C2_MacroAssembler* masm);
|
||||
|
||||
static uint size_exception_handler() {
|
||||
// The exception_handler is a b64_patchable.
|
||||
return MacroAssembler::b64_patchable_size;
|
||||
}
|
||||
|
||||
static uint size_deopt_handler() {
|
||||
// The deopt_handler is a bl64_patchable.
|
||||
return MacroAssembler::bl64_patchable_size;
|
||||
return MacroAssembler::bl64_patchable_size + BytesPerInstWord;
|
||||
}
|
||||
|
||||
};
|
||||
@ -2114,22 +2108,6 @@ public:
|
||||
|
||||
source %{
|
||||
|
||||
int HandlerImpl::emit_exception_handler(C2_MacroAssembler *masm) {
|
||||
address base = __ start_a_stub(size_exception_handler());
|
||||
if (base == nullptr) {
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
return 0; // CodeBuffer::expand failed
|
||||
}
|
||||
|
||||
int offset = __ offset();
|
||||
__ b64_patchable((address)OptoRuntime::exception_blob()->content_begin(),
|
||||
relocInfo::runtime_call_type);
|
||||
assert(__ offset() - offset == (int)size_exception_handler(), "must be fixed size");
|
||||
__ end_a_stub();
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
// The deopt_handler is like the exception handler, but it calls to
|
||||
// the deoptimization blob instead of jumping to the exception blob.
|
||||
int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) {
|
||||
@ -2140,12 +2118,23 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
int offset = __ offset();
|
||||
|
||||
Label start;
|
||||
__ bind(start);
|
||||
|
||||
__ bl64_patchable((address)SharedRuntime::deopt_blob()->unpack(),
|
||||
relocInfo::runtime_call_type);
|
||||
|
||||
int entry_offset = __ offset();
|
||||
|
||||
__ b(start);
|
||||
|
||||
assert(__ offset() - offset == (int) size_deopt_handler(), "must be fixed size");
|
||||
assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
__ end_a_stub();
|
||||
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
@ -46,7 +46,6 @@
|
||||
|
||||
//------------------------------generate_exception_blob---------------------------
|
||||
// Creates exception blob at the end.
|
||||
// Using exception blob, this code is jumped from a compiled method.
|
||||
//
|
||||
// Given an exception pc at a call we call into the runtime for the
|
||||
// handler in this method. This handler might merely restore state
|
||||
|
||||
@ -83,7 +83,6 @@ class RegisterSaver {
|
||||
static OopMap* push_frame_reg_args_and_save_live_registers(MacroAssembler* masm,
|
||||
int* out_frame_size_in_bytes,
|
||||
bool generate_oop_map,
|
||||
int return_pc_adjustment,
|
||||
ReturnPCLocation return_pc_location,
|
||||
bool save_vectors = false);
|
||||
static void restore_live_registers_and_pop_frame(MacroAssembler* masm,
|
||||
@ -262,7 +261,6 @@ static const RegisterSaver::LiveRegType RegisterSaver_LiveVecRegs[] = {
|
||||
OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssembler* masm,
|
||||
int* out_frame_size_in_bytes,
|
||||
bool generate_oop_map,
|
||||
int return_pc_adjustment,
|
||||
ReturnPCLocation return_pc_location,
|
||||
bool save_vectors) {
|
||||
// Push an abi_reg_args-frame and store all registers which may be live.
|
||||
@ -271,7 +269,6 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble
|
||||
// propagated to the RegisterMap of the caller frame during
|
||||
// StackFrameStream construction (needed for deoptimization; see
|
||||
// compiledVFrame::create_stack_value).
|
||||
// If return_pc_adjustment != 0 adjust the return pc by return_pc_adjustment.
|
||||
// Updated return pc is returned in R31 (if not return_pc_is_pre_saved).
|
||||
|
||||
// calculate frame size
|
||||
@ -305,14 +302,11 @@ OopMap* RegisterSaver::push_frame_reg_args_and_save_live_registers(MacroAssemble
|
||||
// Do the save_LR by hand and adjust the return pc if requested.
|
||||
switch (return_pc_location) {
|
||||
case return_pc_is_lr: __ mflr(R31); break;
|
||||
case return_pc_is_pre_saved: assert(return_pc_adjustment == 0, "unsupported"); break;
|
||||
case return_pc_is_pre_saved: break;
|
||||
case return_pc_is_thread_saved_exception_pc: __ ld(R31, thread_(saved_exception_pc)); break;
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
if (return_pc_location != return_pc_is_pre_saved) {
|
||||
if (return_pc_adjustment != 0) {
|
||||
__ addi(R31, R31, return_pc_adjustment);
|
||||
}
|
||||
__ std(R31, frame_size_in_bytes + _abi0(lr), R1_SP);
|
||||
}
|
||||
|
||||
@ -2907,22 +2901,15 @@ void SharedRuntime::generate_deopt_blob() {
|
||||
// deopt_handler: call_deopt_stub
|
||||
// cur. return pc --> ...
|
||||
//
|
||||
// So currently SR_LR points behind the call in the deopt handler.
|
||||
// We adjust it such that it points to the start of the deopt handler.
|
||||
// The return_pc has been stored in the frame of the deoptee and
|
||||
// will replace the address of the deopt_handler in the call
|
||||
// to Deoptimization::fetch_unroll_info below.
|
||||
// We can't grab a free register here, because all registers may
|
||||
// contain live values, so let the RegisterSaver do the adjustment
|
||||
// of the return pc.
|
||||
const int return_pc_adjustment_no_exception = -MacroAssembler::bl64_patchable_size;
|
||||
|
||||
// Push the "unpack frame"
|
||||
// Save everything in sight.
|
||||
map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm,
|
||||
&first_frame_size_in_bytes,
|
||||
/*generate_oop_map=*/ true,
|
||||
return_pc_adjustment_no_exception,
|
||||
RegisterSaver::return_pc_is_lr);
|
||||
assert(map != nullptr, "OopMap must have been created");
|
||||
|
||||
@ -2957,7 +2944,6 @@ void SharedRuntime::generate_deopt_blob() {
|
||||
RegisterSaver::push_frame_reg_args_and_save_live_registers(masm,
|
||||
&first_frame_size_in_bytes,
|
||||
/*generate_oop_map=*/ false,
|
||||
/*return_pc_adjustment_exception=*/ 0,
|
||||
RegisterSaver::return_pc_is_pre_saved);
|
||||
|
||||
// Deopt during an exception. Save exec mode for unpack_frames.
|
||||
@ -2975,7 +2961,6 @@ void SharedRuntime::generate_deopt_blob() {
|
||||
RegisterSaver::push_frame_reg_args_and_save_live_registers(masm,
|
||||
&first_frame_size_in_bytes,
|
||||
/*generate_oop_map=*/ false,
|
||||
/*return_pc_adjustment_reexecute=*/ 0,
|
||||
RegisterSaver::return_pc_is_pre_saved);
|
||||
__ li(exec_mode_reg, Deoptimization::Unpack_reexecute);
|
||||
#endif
|
||||
@ -3266,7 +3251,6 @@ SafepointBlob* SharedRuntime::generate_handler_blob(StubId id, address call_ptr)
|
||||
map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm,
|
||||
&frame_size_in_bytes,
|
||||
/*generate_oop_map=*/ true,
|
||||
/*return_pc_adjustment=*/0,
|
||||
return_pc_location, save_vectors);
|
||||
|
||||
// The following is basically a call_VM. However, we need the precise
|
||||
@ -3367,7 +3351,6 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(StubId id, address destination
|
||||
map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm,
|
||||
&frame_size_in_bytes,
|
||||
/*generate_oop_map*/ true,
|
||||
/*return_pc_adjustment*/ 0,
|
||||
RegisterSaver::return_pc_is_lr);
|
||||
|
||||
// Use noreg as last_Java_pc, the return pc will be reconstructed
|
||||
|
||||
@ -377,12 +377,20 @@ int LIR_Assembler::emit_deopt_handler() {
|
||||
|
||||
int offset = code_offset();
|
||||
|
||||
__ auipc(ra, 0);
|
||||
__ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
Label start;
|
||||
__ bind(start);
|
||||
|
||||
__ far_call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
|
||||
int entry_offset = __ offset();
|
||||
__ j(start);
|
||||
|
||||
guarantee(code_offset() - offset <= deopt_handler_size(), "overflow");
|
||||
assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
__ end_a_stub();
|
||||
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
|
||||
|
||||
@ -72,7 +72,7 @@ private:
|
||||
// See emit_exception_handler for detail
|
||||
_exception_handler_size = DEBUG_ONLY(256) NOT_DEBUG(32), // or smaller
|
||||
// See emit_deopt_handler for detail
|
||||
// auipc (1) + far_jump (2)
|
||||
// far_call (2) + j (1)
|
||||
_deopt_handler_size = 1 * MacroAssembler::instruction_size +
|
||||
2 * MacroAssembler::instruction_size
|
||||
};
|
||||
|
||||
@ -311,12 +311,19 @@ inline bool NativeInstruction::is_jump_or_nop() {
|
||||
// can store an offset from the initial nop to the nmethod.
|
||||
class NativePostCallNop: public NativeInstruction {
|
||||
public:
|
||||
enum RISCV_specific_constants {
|
||||
// The two parts should be checked separately to prevent out of bounds access in
|
||||
// case the return address points to the deopt handler stub code entry point
|
||||
// which could be at the end of page.
|
||||
first_check_size = instruction_size
|
||||
};
|
||||
|
||||
bool check() const {
|
||||
// Check for two instructions: nop; lui zr, hi20
|
||||
// These instructions only ever appear together in a post-call
|
||||
// NOP, so it's unnecessary to check that the third instruction is
|
||||
// an addiw as well.
|
||||
return is_nop() && MacroAssembler::is_lui_to_zr_at(addr_at(4));
|
||||
return is_nop() && MacroAssembler::is_lui_to_zr_at(addr_at(first_check_size));
|
||||
}
|
||||
bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const;
|
||||
bool patch(int32_t oopmap_slot, int32_t cb_offset);
|
||||
|
||||
@ -1049,15 +1049,10 @@ class HandlerImpl {
|
||||
|
||||
public:
|
||||
|
||||
static int emit_exception_handler(C2_MacroAssembler *masm);
|
||||
static int emit_deopt_handler(C2_MacroAssembler* masm);
|
||||
|
||||
static uint size_exception_handler() {
|
||||
return MacroAssembler::far_branch_size();
|
||||
}
|
||||
|
||||
static uint size_deopt_handler() {
|
||||
// count auipc + far branch
|
||||
// count far call + j
|
||||
return NativeInstruction::instruction_size + MacroAssembler::far_branch_size();
|
||||
}
|
||||
};
|
||||
@ -1838,25 +1833,6 @@ uint MachUEPNode::size(PhaseRegAlloc* ra_) const
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// Emit exception handler code.
|
||||
int HandlerImpl::emit_exception_handler(C2_MacroAssembler* masm)
|
||||
{
|
||||
// auipc t1, #exception_blob_entry_point
|
||||
// jr (offset)t1
|
||||
// Note that the code buffer's insts_mark is always relative to insts.
|
||||
// That's why we must use the macroassembler to generate a handler.
|
||||
address base = __ start_a_stub(size_exception_handler());
|
||||
if (base == nullptr) {
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
return 0; // CodeBuffer::expand failed
|
||||
}
|
||||
int offset = __ offset();
|
||||
__ far_jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point()));
|
||||
assert(__ offset() - offset <= (int) size_exception_handler(), "overflow");
|
||||
__ end_a_stub();
|
||||
return offset;
|
||||
}
|
||||
|
||||
// Emit deopt handler code.
|
||||
int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm)
|
||||
{
|
||||
@ -1867,12 +1843,19 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm)
|
||||
}
|
||||
int offset = __ offset();
|
||||
|
||||
__ auipc(ra, 0);
|
||||
__ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
Label start;
|
||||
__ bind(start);
|
||||
|
||||
__ far_call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
|
||||
int entry_offset = __ offset();
|
||||
__ j(start);
|
||||
|
||||
assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow");
|
||||
assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
__ end_a_stub();
|
||||
return offset;
|
||||
return entry_offset;
|
||||
|
||||
}
|
||||
// REQUIRED MATCHER CODE
|
||||
|
||||
@ -249,8 +249,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() {
|
||||
|
||||
//------------------------------generate_exception_blob---------------------------
|
||||
// creates exception blob at the end
|
||||
// Using exception blob, this code is jumped from a compiled method.
|
||||
// (see emit_exception_handler in riscv.ad file)
|
||||
//
|
||||
// Given an exception pc at a call we call into the runtime for the
|
||||
// handler in this method. This handler might merely restore state
|
||||
|
||||
@ -89,11 +89,12 @@ class VM_Version : public Abstract_VM_Version {
|
||||
FLAG_SET_DEFAULT(flag, true); \
|
||||
} else { \
|
||||
FLAG_SET_DEFAULT(flag, false); \
|
||||
stringStream ss; \
|
||||
deps_string(ss, dep0, ##__VA_ARGS__); \
|
||||
warning("Cannot enable " #flag ", it's missing dependent extension(s) %s", ss.as_string(true)); \
|
||||
/* Sync CPU features with flags */ \
|
||||
disable_feature(); \
|
||||
stringStream ss; \
|
||||
ss.print("missing dependent extension(s): "); \
|
||||
deps_string(ss, dep0, ##__VA_ARGS__); \
|
||||
log_disabled(ss.as_string(true)); \
|
||||
} \
|
||||
} else { \
|
||||
/* Sync CPU features with flags */ \
|
||||
@ -101,11 +102,12 @@ class VM_Version : public Abstract_VM_Version {
|
||||
disable_feature(); \
|
||||
} else if (!deps_all_enabled(dep0, ##__VA_ARGS__)) { \
|
||||
FLAG_SET_DEFAULT(flag, false); \
|
||||
stringStream ss; \
|
||||
deps_string(ss, dep0, ##__VA_ARGS__); \
|
||||
warning("Cannot enable " #flag ", it's missing dependent extension(s) %s", ss.as_string(true)); \
|
||||
/* Sync CPU features with flags */ \
|
||||
disable_feature(); \
|
||||
stringStream ss; \
|
||||
ss.print("missing dependent extension(s): "); \
|
||||
deps_string(ss, dep0, ##__VA_ARGS__); \
|
||||
log_disabled(ss.as_string(true)); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
@ -136,6 +138,7 @@ class VM_Version : public Abstract_VM_Version {
|
||||
RVExtFeatures::current()->clear_feature(_cpu_feature_index);
|
||||
}
|
||||
void log_enabled();
|
||||
void log_disabled(const char* reason);
|
||||
|
||||
protected:
|
||||
bool deps_all_enabled(RVExtFeatureValue* dep0, ...) {
|
||||
|
||||
@ -272,14 +272,27 @@ int LIR_Assembler::emit_deopt_handler() {
|
||||
// Not enough space left for the handler.
|
||||
bailout("deopt handler overflow");
|
||||
return -1;
|
||||
} int offset = code_offset();
|
||||
}
|
||||
|
||||
int offset = code_offset();
|
||||
|
||||
Label start;
|
||||
__ bind(start);
|
||||
|
||||
// Size must be constant (see HandlerImpl::emit_deopt_handler).
|
||||
__ load_const(Z_R1_scratch, SharedRuntime::deopt_blob()->unpack());
|
||||
__ call(Z_R1_scratch);
|
||||
|
||||
int entry_offset = __ offset();
|
||||
|
||||
__ z_bru(start);
|
||||
|
||||
guarantee(code_offset() - offset <= deopt_handler_size(), "overflow");
|
||||
assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
__ end_a_stub();
|
||||
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
void LIR_Assembler::jobject2reg(jobject o, Register reg) {
|
||||
|
||||
@ -649,6 +649,13 @@ class NativeGeneralJump: public NativeInstruction {
|
||||
|
||||
class NativePostCallNop: public NativeInstruction {
|
||||
public:
|
||||
enum z_specific_constants {
|
||||
// Once the check is implemented, this has to specify number of bytes checked on the first
|
||||
// read. If the check would read beyond size of the instruction at the deopt handler stub
|
||||
// code entry point, then it has to happen in two stages - to prevent out of bounds access
|
||||
// in case the return address points to the entry point which could be at the end of page.
|
||||
first_check_size = 0 // check is unimplemented
|
||||
};
|
||||
bool check() const { Unimplemented(); return false; }
|
||||
bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const { return false; }
|
||||
bool patch(int32_t oopmap_slot, int32_t cb_offset) { Unimplemented(); return false; }
|
||||
|
||||
@ -43,8 +43,6 @@
|
||||
|
||||
//------------------------------generate_exception_blob---------------------------
|
||||
// creates exception blob at the end
|
||||
// Using exception blob, this code is jumped from a compiled method.
|
||||
// (see emit_exception_handler in s390.ad file)
|
||||
//
|
||||
// Given an exception pc at a call we call into the runtime for the
|
||||
// handler in this method. This handler might merely restore state
|
||||
|
||||
@ -1649,15 +1649,10 @@ source_hpp %{ // Header information of the source block.
|
||||
class HandlerImpl {
|
||||
public:
|
||||
|
||||
static int emit_exception_handler(C2_MacroAssembler *masm);
|
||||
static int emit_deopt_handler(C2_MacroAssembler* masm);
|
||||
|
||||
static uint size_exception_handler() {
|
||||
return NativeJump::max_instruction_size();
|
||||
}
|
||||
|
||||
static uint size_deopt_handler() {
|
||||
return NativeCall::max_instruction_size();
|
||||
return NativeCall::max_instruction_size() + MacroAssembler::jump_pcrelative_size();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1672,43 +1667,6 @@ public:
|
||||
|
||||
source %{
|
||||
|
||||
// This exception handler code snippet is placed after the method's
|
||||
// code. It is the return point if an exception occurred. it jumps to
|
||||
// the exception blob.
|
||||
//
|
||||
// If the method gets deoptimized, the method and this code snippet
|
||||
// get patched.
|
||||
//
|
||||
// 1) Trampoline code gets patched into the end of this exception
|
||||
// handler. the trampoline code jumps to the deoptimization blob.
|
||||
//
|
||||
// 2) The return address in the method's code will get patched such
|
||||
// that it jumps to the trampoline.
|
||||
//
|
||||
// 3) The handler will get patched such that it does not jump to the
|
||||
// exception blob, but to an entry in the deoptimization blob being
|
||||
// aware of the exception.
|
||||
int HandlerImpl::emit_exception_handler(C2_MacroAssembler *masm) {
|
||||
Register temp_reg = Z_R1;
|
||||
|
||||
address base = __ start_a_stub(size_exception_handler());
|
||||
if (base == nullptr) {
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
return 0; // CodeBuffer::expand failed
|
||||
}
|
||||
|
||||
int offset = __ offset();
|
||||
// Use unconditional pc-relative jump with 32-bit range here.
|
||||
__ load_const_optimized(temp_reg, (address)OptoRuntime::exception_blob()->content_begin());
|
||||
__ z_br(temp_reg);
|
||||
|
||||
assert(__ offset() - offset <= (int) size_exception_handler(), "overflow");
|
||||
|
||||
__ end_a_stub();
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
// Emit deopt handler code.
|
||||
int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) {
|
||||
address base = __ start_a_stub(size_deopt_handler());
|
||||
@ -1720,14 +1678,24 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) {
|
||||
|
||||
int offset = __ offset();
|
||||
|
||||
Label start;
|
||||
__ bind(start);
|
||||
|
||||
// Size_deopt_handler() must be exact on zarch, so for simplicity
|
||||
// we do not use load_const_opt here.
|
||||
__ load_const(Z_R1, SharedRuntime::deopt_blob()->unpack());
|
||||
__ call(Z_R1);
|
||||
|
||||
int entry_offset = __ offset();
|
||||
|
||||
__ z_bru(start);
|
||||
|
||||
assert(__ offset() - offset == (int) size_deopt_handler(), "must be fixed size");
|
||||
assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
|
||||
__ end_a_stub();
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
@ -2544,14 +2544,10 @@ void SharedRuntime::generate_deopt_blob() {
|
||||
// Normal entry (non-exception case)
|
||||
//
|
||||
// We have been called from the deopt handler of the deoptee.
|
||||
// Z_R14 points behind the call in the deopt handler. We adjust
|
||||
// it such that it points to the start of the deopt handler.
|
||||
// Z_R14 points to the entry point of the deopt handler.
|
||||
// The return_pc has been stored in the frame of the deoptee and
|
||||
// will replace the address of the deopt_handler in the call
|
||||
// to Deoptimization::fetch_unroll_info below.
|
||||
// The (int) cast is necessary, because -((unsigned int)14)
|
||||
// is an unsigned int.
|
||||
__ add2reg(Z_R14, -(int)NativeCall::max_instruction_size());
|
||||
|
||||
const Register exec_mode_reg = Z_tmp_1;
|
||||
|
||||
|
||||
@ -450,14 +450,22 @@ int LIR_Assembler::emit_deopt_handler() {
|
||||
}
|
||||
|
||||
int offset = code_offset();
|
||||
InternalAddress here(__ pc());
|
||||
|
||||
__ pushptr(here.addr(), rscratch1);
|
||||
__ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
Label start;
|
||||
__ bind(start);
|
||||
|
||||
__ call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
|
||||
int entry_offset = __ offset();
|
||||
|
||||
__ jmp(start);
|
||||
|
||||
guarantee(code_offset() - offset <= deopt_handler_size(), "overflow");
|
||||
assert(code_offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
__ end_a_stub();
|
||||
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
enum {
|
||||
_call_stub_size = 28,
|
||||
_exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175),
|
||||
_deopt_handler_size = 17
|
||||
_deopt_handler_size = 7
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@ -73,6 +73,7 @@ class NativeInstruction {
|
||||
s_char sbyte_at(int offset) const { return *(s_char*) addr_at(offset); }
|
||||
u_char ubyte_at(int offset) const { return *(u_char*) addr_at(offset); }
|
||||
|
||||
jshort short_at(int offset) const { return *(jshort*) addr_at(offset); }
|
||||
jint int_at(int offset) const { return *(jint*) addr_at(offset); }
|
||||
|
||||
intptr_t ptr_at(int offset) const { return *(intptr_t*) addr_at(offset); }
|
||||
@ -578,10 +579,15 @@ public:
|
||||
instruction_code = 0x0f,
|
||||
instruction_size = 8,
|
||||
instruction_offset = 0,
|
||||
displacement_offset = 4
|
||||
displacement_offset = 4,
|
||||
|
||||
// The two parts should be checked separately to prevent out of bounds access in case
|
||||
// the return address points to the deopt handler stub code entry point which could be
|
||||
// at the end of page.
|
||||
first_check_size = 2
|
||||
};
|
||||
|
||||
bool check() const { return int_at(0) == 0x841f0f; }
|
||||
bool check() const { return short_at(0) == 0x1f0f && short_at(first_check_size) == 0x0084; }
|
||||
bool decode(int32_t& oopmap_slot, int32_t& cb_offset) const {
|
||||
int32_t data = int_at(displacement_offset);
|
||||
if (data == 0) {
|
||||
|
||||
@ -242,8 +242,6 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() {
|
||||
|
||||
//------------------------------generate_exception_blob---------------------------
|
||||
// creates exception blob at the end
|
||||
// Using exception blob, this code is jumped from a compiled method.
|
||||
// (see emit_exception_handler in x86_64.ad file)
|
||||
//
|
||||
// Given an exception pc at a call we call into the runtime for the
|
||||
// handler in this method. This handler might merely restore state
|
||||
|
||||
@ -2767,21 +2767,11 @@ class HandlerImpl {
|
||||
|
||||
public:
|
||||
|
||||
static int emit_exception_handler(C2_MacroAssembler *masm);
|
||||
static int emit_deopt_handler(C2_MacroAssembler* masm);
|
||||
|
||||
static uint size_exception_handler() {
|
||||
// NativeCall instruction size is the same as NativeJump.
|
||||
// exception handler starts out as jump and can be patched to
|
||||
// a call be deoptimization. (4932387)
|
||||
// Note that this value is also credited (in output.cpp) to
|
||||
// the size of the code section.
|
||||
return NativeJump::instruction_size;
|
||||
}
|
||||
|
||||
static uint size_deopt_handler() {
|
||||
// three 5 byte instructions plus one move for unreachable address.
|
||||
return 15+3;
|
||||
// one call and one jmp.
|
||||
return 7;
|
||||
}
|
||||
};
|
||||
|
||||
@ -2873,24 +2863,6 @@ int MachNode::compute_padding(int current_offset) const {
|
||||
}
|
||||
}
|
||||
|
||||
// Emit exception handler code.
|
||||
// Stuff framesize into a register and call a VM stub routine.
|
||||
int HandlerImpl::emit_exception_handler(C2_MacroAssembler* masm) {
|
||||
|
||||
// Note that the code buffer's insts_mark is always relative to insts.
|
||||
// That's why we must use the macroassembler to generate a handler.
|
||||
address base = __ start_a_stub(size_exception_handler());
|
||||
if (base == nullptr) {
|
||||
ciEnv::current()->record_failure("CodeCache is full");
|
||||
return 0; // CodeBuffer::expand failed
|
||||
}
|
||||
int offset = __ offset();
|
||||
__ jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point()));
|
||||
assert(__ offset() - offset <= (int) size_exception_handler(), "overflow");
|
||||
__ end_a_stub();
|
||||
return offset;
|
||||
}
|
||||
|
||||
// Emit deopt handler code.
|
||||
int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) {
|
||||
|
||||
@ -2903,21 +2875,20 @@ int HandlerImpl::emit_deopt_handler(C2_MacroAssembler* masm) {
|
||||
}
|
||||
int offset = __ offset();
|
||||
|
||||
address the_pc = (address) __ pc();
|
||||
Label next;
|
||||
// push a "the_pc" on the stack without destroying any registers
|
||||
// as they all may be live.
|
||||
Label start;
|
||||
__ bind(start);
|
||||
|
||||
// push address of "next"
|
||||
__ call(next, relocInfo::none); // reloc none is fine since it is a disp32
|
||||
__ bind(next);
|
||||
// adjust it so it matches "the_pc"
|
||||
__ subptr(Address(rsp, 0), __ offset() - offset);
|
||||
__ call(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
|
||||
int entry_offset = __ offset();
|
||||
|
||||
__ jmp(start);
|
||||
|
||||
__ jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));
|
||||
assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow %d", (__ offset() - offset));
|
||||
assert(__ offset() - entry_offset >= NativePostCallNop::first_check_size,
|
||||
"out of bounds read in post-call NOP check");
|
||||
__ end_a_stub();
|
||||
return offset;
|
||||
return entry_offset;
|
||||
}
|
||||
|
||||
static Assembler::Width widthForType(BasicType bt) {
|
||||
|
||||
@ -1747,6 +1747,9 @@ size_t os::pd_pretouch_memory(void* first, void* last, size_t page_size) {
|
||||
return page_size;
|
||||
}
|
||||
|
||||
void os::numa_set_thread_affinity(Thread *thread, int node) {
|
||||
}
|
||||
|
||||
void os::numa_make_global(char *addr, size_t bytes) {
|
||||
}
|
||||
|
||||
|
||||
@ -1581,6 +1581,9 @@ size_t os::pd_pretouch_memory(void* first, void* last, size_t page_size) {
|
||||
return page_size;
|
||||
}
|
||||
|
||||
void os::numa_set_thread_affinity(Thread *thread, int node) {
|
||||
}
|
||||
|
||||
void os::numa_make_global(char *addr, size_t bytes) {
|
||||
}
|
||||
|
||||
|
||||
@ -209,14 +209,14 @@ class CgroupV1Subsystem: public CgroupSubsystem {
|
||||
|
||||
bool pids_max(uint64_t& result) override;
|
||||
bool pids_current(uint64_t& result) override;
|
||||
bool is_containerized();
|
||||
bool is_containerized() override;
|
||||
|
||||
const char * container_type() {
|
||||
const char * container_type() override {
|
||||
return "cgroupv1";
|
||||
}
|
||||
CachingCgroupController<CgroupMemoryController>* memory_controller() { return _memory; }
|
||||
CachingCgroupController<CgroupCpuController>* cpu_controller() { return _cpu; }
|
||||
CgroupCpuacctController* cpuacct_controller() { return _cpuacct; }
|
||||
CachingCgroupController<CgroupMemoryController>* memory_controller() override { return _memory; }
|
||||
CachingCgroupController<CgroupCpuController>* cpu_controller() override { return _cpu; }
|
||||
CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; }
|
||||
|
||||
private:
|
||||
/* controllers */
|
||||
|
||||
@ -1770,7 +1770,9 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
|
||||
{EM_LOONGARCH, EM_LOONGARCH, ELFCLASS64, ELFDATA2LSB, (char*)"LoongArch"},
|
||||
};
|
||||
|
||||
#if (defined AMD64)
|
||||
#if (defined IA32)
|
||||
static Elf32_Half running_arch_code=EM_386;
|
||||
#elif (defined AMD64) || (defined X32)
|
||||
static Elf32_Half running_arch_code=EM_X86_64;
|
||||
#elif (defined __sparc) && (defined _LP64)
|
||||
static Elf32_Half running_arch_code=EM_SPARCV9;
|
||||
@ -1804,7 +1806,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
|
||||
static Elf32_Half running_arch_code=EM_LOONGARCH;
|
||||
#else
|
||||
#error Method os::dll_load requires that one of following is defined:\
|
||||
AARCH64, ALPHA, ARM, AMD64, LOONGARCH64, M68K, MIPS, MIPSEL, PARISC, __powerpc__, __powerpc64__, RISCV, S390, SH, __sparc
|
||||
AARCH64, ALPHA, ARM, AMD64, IA32, LOONGARCH64, M68K, MIPS, MIPSEL, PARISC, __powerpc__, __powerpc64__, RISCV, S390, SH, __sparc
|
||||
#endif
|
||||
|
||||
// Identify compatibility class for VM's architecture and library's architecture
|
||||
@ -1866,6 +1868,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
|
||||
}
|
||||
|
||||
void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) {
|
||||
#ifndef IA32
|
||||
bool ieee_handling = IEEE_subnormal_handling_OK();
|
||||
if (!ieee_handling) {
|
||||
Events::log_dll_message(nullptr, "IEEE subnormal handling check failed before loading %s", filename);
|
||||
@ -1888,9 +1891,14 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) {
|
||||
// numerical "accuracy", but we need to protect Java semantics first
|
||||
// and foremost. See JDK-8295159.
|
||||
|
||||
// This workaround is ineffective on IA32 systems because the MXCSR
|
||||
// register (which controls flush-to-zero mode) is not stored in the
|
||||
// legacy fenv.
|
||||
|
||||
fenv_t default_fenv;
|
||||
int rtn = fegetenv(&default_fenv);
|
||||
assert(rtn == 0, "fegetenv must succeed");
|
||||
#endif // IA32
|
||||
|
||||
void* result;
|
||||
JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);)
|
||||
@ -1910,6 +1918,7 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) {
|
||||
} else {
|
||||
Events::log_dll_message(nullptr, "Loaded shared library %s", filename);
|
||||
log_info(os)("shared library load of %s was successful", filename);
|
||||
#ifndef IA32
|
||||
// Quickly test to make sure subnormals are correctly handled.
|
||||
if (! IEEE_subnormal_handling_OK()) {
|
||||
// We just dlopen()ed a library that mangled the floating-point flags.
|
||||
@ -1935,6 +1944,7 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) {
|
||||
assert(false, "fesetenv didn't work");
|
||||
}
|
||||
}
|
||||
#endif // IA32
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -2433,6 +2443,7 @@ void os::Linux::print_uptime_info(outputStream* st) {
|
||||
if (ret == 0) {
|
||||
os::print_dhm(st, "OS uptime:", (long) sinfo.uptime);
|
||||
}
|
||||
assert(ret == 0, "sysinfo failed: %s", os::strerror(errno));
|
||||
}
|
||||
|
||||
bool os::Linux::print_container_info(outputStream* st) {
|
||||
@ -2597,7 +2608,8 @@ void os::print_memory_info(outputStream* st) {
|
||||
|
||||
// values in struct sysinfo are "unsigned long"
|
||||
struct sysinfo si;
|
||||
sysinfo(&si);
|
||||
int ret = sysinfo(&si);
|
||||
assert(ret == 0, "sysinfo failed: %s", os::strerror(errno));
|
||||
physical_memory_size_type phys_mem = physical_memory();
|
||||
st->print(", physical " PHYS_MEM_TYPE_FORMAT "k",
|
||||
phys_mem >> 10);
|
||||
@ -2605,10 +2617,12 @@ void os::print_memory_info(outputStream* st) {
|
||||
(void)os::available_memory(avail_mem);
|
||||
st->print("(" PHYS_MEM_TYPE_FORMAT "k free)",
|
||||
avail_mem >> 10);
|
||||
st->print(", swap " UINT64_FORMAT "k",
|
||||
((jlong)si.totalswap * si.mem_unit) >> 10);
|
||||
st->print("(" UINT64_FORMAT "k free)",
|
||||
((jlong)si.freeswap * si.mem_unit) >> 10);
|
||||
if (ret == 0) {
|
||||
st->print(", swap " UINT64_FORMAT "k",
|
||||
((jlong)si.totalswap * si.mem_unit) >> 10);
|
||||
st->print("(" UINT64_FORMAT "k free)",
|
||||
((jlong)si.freeswap * si.mem_unit) >> 10);
|
||||
}
|
||||
st->cr();
|
||||
st->print("Page Sizes: ");
|
||||
_page_sizes.print_on(st);
|
||||
@ -2991,6 +3005,10 @@ size_t os::pd_pretouch_memory(void* first, void* last, size_t page_size) {
|
||||
return page_size;
|
||||
}
|
||||
|
||||
void os::numa_set_thread_affinity(Thread* thread, int node) {
|
||||
Linux::numa_set_thread_affinity(thread->osthread()->thread_id(), node);
|
||||
}
|
||||
|
||||
void os::numa_make_global(char *addr, size_t bytes) {
|
||||
Linux::numa_interleave_memory(addr, bytes);
|
||||
}
|
||||
@ -3173,6 +3191,8 @@ bool os::Linux::libnuma_init() {
|
||||
libnuma_dlsym(handle, "numa_set_bind_policy")));
|
||||
set_numa_bitmask_isbitset(CAST_TO_FN_PTR(numa_bitmask_isbitset_func_t,
|
||||
libnuma_dlsym(handle, "numa_bitmask_isbitset")));
|
||||
set_numa_bitmask_clearbit(CAST_TO_FN_PTR(numa_bitmask_clearbit_func_t,
|
||||
libnuma_dlsym(handle, "numa_bitmask_clearbit")));
|
||||
set_numa_bitmask_equal(CAST_TO_FN_PTR(numa_bitmask_equal_func_t,
|
||||
libnuma_dlsym(handle, "numa_bitmask_equal")));
|
||||
set_numa_distance(CAST_TO_FN_PTR(numa_distance_func_t,
|
||||
@ -3187,20 +3207,32 @@ bool os::Linux::libnuma_init() {
|
||||
libnuma_dlsym(handle, "numa_set_preferred")));
|
||||
set_numa_get_run_node_mask(CAST_TO_FN_PTR(numa_get_run_node_mask_func_t,
|
||||
libnuma_v2_dlsym(handle, "numa_get_run_node_mask")));
|
||||
set_numa_sched_setaffinity(CAST_TO_FN_PTR(numa_sched_setaffinity_func_t,
|
||||
libnuma_v2_dlsym(handle, "numa_sched_setaffinity")));
|
||||
set_numa_allocate_cpumask(CAST_TO_FN_PTR(numa_allocate_cpumask_func_t,
|
||||
libnuma_v2_dlsym(handle, "numa_allocate_cpumask")));
|
||||
|
||||
if (numa_available() != -1) {
|
||||
set_numa_all_nodes((unsigned long*)libnuma_dlsym(handle, "numa_all_nodes"));
|
||||
set_numa_all_nodes_ptr((struct bitmask **)libnuma_dlsym(handle, "numa_all_nodes_ptr"));
|
||||
set_numa_nodes_ptr((struct bitmask **)libnuma_dlsym(handle, "numa_nodes_ptr"));
|
||||
set_numa_all_cpus_ptr((struct bitmask **)libnuma_dlsym(handle, "numa_all_cpus_ptr"));
|
||||
set_numa_interleave_bitmask(_numa_get_interleave_mask());
|
||||
set_numa_membind_bitmask(_numa_get_membind());
|
||||
set_numa_cpunodebind_bitmask(_numa_get_run_node_mask());
|
||||
|
||||
// Create an index -> node mapping, since nodes are not always consecutive
|
||||
_nindex_to_node = new (mtInternal) GrowableArray<int>(0, mtInternal);
|
||||
rebuild_nindex_to_node_map();
|
||||
|
||||
// Create a cpu -> node mapping
|
||||
_cpu_to_node = new (mtInternal) GrowableArray<int>(0, mtInternal);
|
||||
rebuild_cpu_to_node_map();
|
||||
|
||||
// Create a node -> CPUs mapping
|
||||
_numa_affinity_masks = new (mtInternal) GrowableArray<struct bitmask*>(0, mtInternal);
|
||||
build_numa_affinity_masks();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -3236,6 +3268,42 @@ size_t os::Linux::default_guard_size(os::ThreadType thr_type) {
|
||||
return ((thr_type == java_thread || thr_type == compiler_thread) ? 0 : os::vm_page_size());
|
||||
}
|
||||
|
||||
void os::Linux::build_numa_affinity_masks() {
|
||||
// We only build the affinity masks if running libnuma v2 (_numa_node_to_cpus_v2
|
||||
// is available) and we have the affinity mask of the process when it started.
|
||||
if (_numa_node_to_cpus_v2 == nullptr || _numa_all_cpus_ptr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// It's important that we respect any user configuration by removing the
|
||||
// CPUs we're not allowed to run on from the affinity mask. For example,
|
||||
// if the user runs the JVM with "numactl -C 0-1,4-5" on a machine with
|
||||
// the following NUMA setup:
|
||||
// NUMA 0: CPUs 0-3, NUMA 1: CPUs 4-7
|
||||
// We expect to get the following affinity masks:
|
||||
// Affinity masks: idx 0 = (0, 1), idx 1 = (4, 5)
|
||||
|
||||
const int num_nodes = get_existing_num_nodes();
|
||||
const unsigned num_cpus = (unsigned)os::processor_count();
|
||||
|
||||
for (int i = 0; i < num_nodes; i++) {
|
||||
struct bitmask* affinity_mask = _numa_allocate_cpumask();
|
||||
|
||||
// Fill the affinity mask with all CPUs belonging to NUMA node i
|
||||
_numa_node_to_cpus_v2(i, affinity_mask);
|
||||
|
||||
// Clear the bits of all CPUs that the process is not allowed to
|
||||
// execute tasks on
|
||||
for (unsigned j = 0; j < num_cpus; j++) {
|
||||
if (!_numa_bitmask_isbitset(_numa_all_cpus_ptr, j)) {
|
||||
_numa_bitmask_clearbit(affinity_mask, j);
|
||||
}
|
||||
}
|
||||
|
||||
_numa_affinity_masks->push(affinity_mask);
|
||||
}
|
||||
}
|
||||
|
||||
void os::Linux::rebuild_nindex_to_node_map() {
|
||||
int highest_node_number = Linux::numa_max_node();
|
||||
|
||||
@ -3351,6 +3419,25 @@ int os::Linux::numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void os::Linux::numa_set_thread_affinity(pid_t tid, int node) {
|
||||
// We only set affinity if running libnuma v2 (_numa_sched_setaffinity
|
||||
// is available) and we have all affinity mask
|
||||
if (_numa_sched_setaffinity == nullptr ||
|
||||
_numa_all_cpus_ptr == nullptr ||
|
||||
_numa_affinity_masks->is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node == -1) {
|
||||
// If the node is -1, the affinity is reverted to the original affinity
|
||||
// of the thread when the VM was started
|
||||
_numa_sched_setaffinity(tid, _numa_all_cpus_ptr);
|
||||
} else {
|
||||
// Normal case, set the affinity to the corresponding affinity mask
|
||||
_numa_sched_setaffinity(tid, _numa_affinity_masks->at(node));
|
||||
}
|
||||
}
|
||||
|
||||
int os::Linux::get_node_by_cpu(int cpu_id) {
|
||||
if (cpu_to_node() != nullptr && cpu_id >= 0 && cpu_id < cpu_to_node()->length()) {
|
||||
return cpu_to_node()->at(cpu_id);
|
||||
@ -3360,6 +3447,7 @@ int os::Linux::get_node_by_cpu(int cpu_id) {
|
||||
|
||||
GrowableArray<int>* os::Linux::_cpu_to_node;
|
||||
GrowableArray<int>* os::Linux::_nindex_to_node;
|
||||
GrowableArray<struct bitmask*>* os::Linux::_numa_affinity_masks;
|
||||
os::Linux::sched_getcpu_func_t os::Linux::_sched_getcpu;
|
||||
os::Linux::numa_node_to_cpus_func_t os::Linux::_numa_node_to_cpus;
|
||||
os::Linux::numa_node_to_cpus_v2_func_t os::Linux::_numa_node_to_cpus_v2;
|
||||
@ -3371,17 +3459,21 @@ os::Linux::numa_interleave_memory_func_t os::Linux::_numa_interleave_memory;
|
||||
os::Linux::numa_interleave_memory_v2_func_t os::Linux::_numa_interleave_memory_v2;
|
||||
os::Linux::numa_set_bind_policy_func_t os::Linux::_numa_set_bind_policy;
|
||||
os::Linux::numa_bitmask_isbitset_func_t os::Linux::_numa_bitmask_isbitset;
|
||||
os::Linux::numa_bitmask_clearbit_func_t os::Linux::_numa_bitmask_clearbit;
|
||||
os::Linux::numa_bitmask_equal_func_t os::Linux::_numa_bitmask_equal;
|
||||
os::Linux::numa_distance_func_t os::Linux::_numa_distance;
|
||||
os::Linux::numa_get_membind_func_t os::Linux::_numa_get_membind;
|
||||
os::Linux::numa_get_interleave_mask_func_t os::Linux::_numa_get_interleave_mask;
|
||||
os::Linux::numa_get_run_node_mask_func_t os::Linux::_numa_get_run_node_mask;
|
||||
os::Linux::numa_sched_setaffinity_func_t os::Linux::_numa_sched_setaffinity;
|
||||
os::Linux::numa_allocate_cpumask_func_t os::Linux::_numa_allocate_cpumask;
|
||||
os::Linux::numa_move_pages_func_t os::Linux::_numa_move_pages;
|
||||
os::Linux::numa_set_preferred_func_t os::Linux::_numa_set_preferred;
|
||||
os::Linux::NumaAllocationPolicy os::Linux::_current_numa_policy;
|
||||
unsigned long* os::Linux::_numa_all_nodes;
|
||||
struct bitmask* os::Linux::_numa_all_nodes_ptr;
|
||||
struct bitmask* os::Linux::_numa_nodes_ptr;
|
||||
struct bitmask* os::Linux::_numa_all_cpus_ptr;
|
||||
struct bitmask* os::Linux::_numa_interleave_bitmask;
|
||||
struct bitmask* os::Linux::_numa_membind_bitmask;
|
||||
struct bitmask* os::Linux::_numa_cpunodebind_bitmask;
|
||||
|
||||
@ -45,6 +45,10 @@ class os::Linux {
|
||||
static GrowableArray<int>* _cpu_to_node;
|
||||
static GrowableArray<int>* _nindex_to_node;
|
||||
|
||||
static GrowableArray<struct bitmask*>* _numa_affinity_masks;
|
||||
|
||||
static void build_numa_affinity_masks();
|
||||
|
||||
protected:
|
||||
|
||||
static physical_memory_size_type _physical_memory;
|
||||
@ -230,8 +234,11 @@ class os::Linux {
|
||||
typedef void (*numa_set_preferred_func_t)(int node);
|
||||
typedef void (*numa_set_bind_policy_func_t)(int policy);
|
||||
typedef int (*numa_bitmask_isbitset_func_t)(struct bitmask *bmp, unsigned int n);
|
||||
typedef int (*numa_bitmask_clearbit_func_t)(struct bitmask *bmp, unsigned int n);
|
||||
typedef int (*numa_bitmask_equal_func_t)(struct bitmask *bmp1, struct bitmask *bmp2);
|
||||
typedef int (*numa_distance_func_t)(int node1, int node2);
|
||||
typedef int (*numa_sched_setaffinity_func_t)(pid_t pid, struct bitmask* mask);
|
||||
typedef struct bitmask* (*numa_allocate_cpumask_func_t)(void);
|
||||
|
||||
static sched_getcpu_func_t _sched_getcpu;
|
||||
static numa_node_to_cpus_func_t _numa_node_to_cpus;
|
||||
@ -244,6 +251,7 @@ class os::Linux {
|
||||
static numa_interleave_memory_v2_func_t _numa_interleave_memory_v2;
|
||||
static numa_set_bind_policy_func_t _numa_set_bind_policy;
|
||||
static numa_bitmask_isbitset_func_t _numa_bitmask_isbitset;
|
||||
static numa_bitmask_clearbit_func_t _numa_bitmask_clearbit;
|
||||
static numa_bitmask_equal_func_t _numa_bitmask_equal;
|
||||
static numa_distance_func_t _numa_distance;
|
||||
static numa_get_membind_func_t _numa_get_membind;
|
||||
@ -251,9 +259,12 @@ class os::Linux {
|
||||
static numa_get_interleave_mask_func_t _numa_get_interleave_mask;
|
||||
static numa_move_pages_func_t _numa_move_pages;
|
||||
static numa_set_preferred_func_t _numa_set_preferred;
|
||||
static numa_sched_setaffinity_func_t _numa_sched_setaffinity;
|
||||
static numa_allocate_cpumask_func_t _numa_allocate_cpumask;
|
||||
static unsigned long* _numa_all_nodes;
|
||||
static struct bitmask* _numa_all_nodes_ptr;
|
||||
static struct bitmask* _numa_nodes_ptr;
|
||||
static struct bitmask* _numa_all_cpus_ptr;
|
||||
static struct bitmask* _numa_interleave_bitmask;
|
||||
static struct bitmask* _numa_membind_bitmask;
|
||||
static struct bitmask* _numa_cpunodebind_bitmask;
|
||||
@ -269,6 +280,7 @@ class os::Linux {
|
||||
static void set_numa_interleave_memory_v2(numa_interleave_memory_v2_func_t func) { _numa_interleave_memory_v2 = func; }
|
||||
static void set_numa_set_bind_policy(numa_set_bind_policy_func_t func) { _numa_set_bind_policy = func; }
|
||||
static void set_numa_bitmask_isbitset(numa_bitmask_isbitset_func_t func) { _numa_bitmask_isbitset = func; }
|
||||
static void set_numa_bitmask_clearbit(numa_bitmask_clearbit_func_t func) { _numa_bitmask_clearbit = func; }
|
||||
static void set_numa_bitmask_equal(numa_bitmask_equal_func_t func) { _numa_bitmask_equal = func; }
|
||||
static void set_numa_distance(numa_distance_func_t func) { _numa_distance = func; }
|
||||
static void set_numa_get_membind(numa_get_membind_func_t func) { _numa_get_membind = func; }
|
||||
@ -279,9 +291,12 @@ class os::Linux {
|
||||
static void set_numa_all_nodes(unsigned long* ptr) { _numa_all_nodes = ptr; }
|
||||
static void set_numa_all_nodes_ptr(struct bitmask **ptr) { _numa_all_nodes_ptr = (ptr == nullptr ? nullptr : *ptr); }
|
||||
static void set_numa_nodes_ptr(struct bitmask **ptr) { _numa_nodes_ptr = (ptr == nullptr ? nullptr : *ptr); }
|
||||
static void set_numa_all_cpus_ptr(struct bitmask **ptr) { _numa_all_cpus_ptr = (ptr == nullptr ? nullptr : *ptr); }
|
||||
static void set_numa_interleave_bitmask(struct bitmask* ptr) { _numa_interleave_bitmask = ptr ; }
|
||||
static void set_numa_membind_bitmask(struct bitmask* ptr) { _numa_membind_bitmask = ptr ; }
|
||||
static void set_numa_cpunodebind_bitmask(struct bitmask* ptr) { _numa_cpunodebind_bitmask = ptr ; }
|
||||
static void set_numa_sched_setaffinity(numa_sched_setaffinity_func_t func) { _numa_sched_setaffinity = func; }
|
||||
static void set_numa_allocate_cpumask(numa_allocate_cpumask_func_t func) { _numa_allocate_cpumask = func; }
|
||||
static int sched_getcpu_syscall(void);
|
||||
|
||||
enum NumaAllocationPolicy{
|
||||
@ -292,6 +307,8 @@ class os::Linux {
|
||||
static NumaAllocationPolicy _current_numa_policy;
|
||||
|
||||
public:
|
||||
static void numa_set_thread_affinity(pid_t tid, int node);
|
||||
|
||||
static int sched_getcpu() { return _sched_getcpu != nullptr ? _sched_getcpu() : -1; }
|
||||
static int numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen);
|
||||
static int numa_max_node() { return _numa_max_node != nullptr ? _numa_max_node() : -1; }
|
||||
|
||||
@ -621,7 +621,7 @@ int JVM_HANDLE_XXX_SIGNAL(int sig, siginfo_t* info,
|
||||
if (cb != nullptr && cb->is_nmethod()) {
|
||||
nmethod* nm = cb->as_nmethod();
|
||||
assert(nm->insts_contains_inclusive(pc), "");
|
||||
address deopt = nm->deopt_handler_begin();
|
||||
address deopt = nm->deopt_handler_entry();
|
||||
assert(deopt != nullptr, "");
|
||||
|
||||
frame fr = os::fetch_frame_from_context(uc);
|
||||
|
||||
@ -2795,7 +2795,7 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) {
|
||||
if (cb != nullptr && cb->is_nmethod()) {
|
||||
nmethod* nm = cb->as_nmethod();
|
||||
frame fr = os::fetch_frame_from_context((void*)exceptionInfo->ContextRecord);
|
||||
address deopt = nm->deopt_handler_begin();
|
||||
address deopt = nm->deopt_handler_entry();
|
||||
assert(nm->insts_contains_inclusive(pc), "");
|
||||
nm->set_original_pc(&fr, pc);
|
||||
// Set pc to handler
|
||||
@ -3752,6 +3752,7 @@ size_t os::pd_pretouch_memory(void* first, void* last, size_t page_size) {
|
||||
return page_size;
|
||||
}
|
||||
|
||||
void os::numa_set_thread_affinity(Thread *thread, int node) { }
|
||||
void os::numa_make_global(char *addr, size_t bytes) { }
|
||||
void os::numa_make_local(char *addr, size_t bytes, int lgrp_hint) { }
|
||||
size_t os::numa_get_groups_num() { return MAX2(numa_node_list_holder.get_count(), 1); }
|
||||
|
||||
@ -104,11 +104,15 @@ uint32_t VM_Version::cpu_vector_length() {
|
||||
}
|
||||
|
||||
void VM_Version::RVExtFeatureValue::log_enabled() {
|
||||
log_debug(os, cpu)("Enabled RV64 feature \"%s\"", pretty());
|
||||
log_info(os, cpu)("Enabled RV64 feature \"%s\"", pretty());
|
||||
}
|
||||
|
||||
void VM_Version::RVExtFeatureValue::log_disabled(const char* reason) {
|
||||
log_info(os, cpu)("Disabled RV64 feature \"%s\" (%s)", pretty(), reason);
|
||||
}
|
||||
|
||||
void VM_Version::RVNonExtFeatureValue::log_enabled() {
|
||||
log_debug(os, cpu)("Enabled RV64 feature \"%s\" (%ld)", pretty(), value());
|
||||
log_info(os, cpu)("Enabled RV64 feature \"%s\" (%ld)", pretty(), value());
|
||||
}
|
||||
|
||||
void VM_Version::setup_cpu_available_features() {
|
||||
@ -193,7 +197,7 @@ void VM_Version::setup_cpu_available_features() {
|
||||
// via PR_RISCV_SCOPE_PER_THREAD, i.e. on VM attach/deattach.
|
||||
int ret = prctl(PR_RISCV_SET_ICACHE_FLUSH_CTX, PR_RISCV_CTX_SW_FENCEI_ON, PR_RISCV_SCOPE_PER_PROCESS);
|
||||
if (ret == 0) {
|
||||
log_debug(os, cpu)("UseCtxFencei (PR_RISCV_CTX_SW_FENCEI_ON) enabled.");
|
||||
log_info(os, cpu)("UseCtxFencei (PR_RISCV_CTX_SW_FENCEI_ON) enabled.");
|
||||
} else {
|
||||
FLAG_SET_ERGO(UseCtxFencei, false);
|
||||
log_info(os, cpu)("UseCtxFencei (PR_RISCV_CTX_SW_FENCEI_ON) disabled, unsupported by kernel.");
|
||||
|
||||
@ -39,7 +39,7 @@ class TypeArrayKlass;
|
||||
// It also decides what Klasses must be cached in aot-initialized state.
|
||||
//
|
||||
// ArchiveBuilder uses [1] as roots to scan for all MetaspaceObjs that need to be cached.
|
||||
// ArchiveHeapWriter uses [2] to create an image of the archived heap.
|
||||
// HeapShared uses [2] to create an image of the archived heap.
|
||||
//
|
||||
// [1] is stored in _all_cached_classes in aotArtifactFinder.cpp.
|
||||
// [2] is stored in HeapShared::archived_object_cache().
|
||||
|
||||
@ -449,7 +449,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_signature(ConstantPool* c
|
||||
}
|
||||
|
||||
bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) {
|
||||
int mt_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument_index(arg_i);
|
||||
int mt_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument(arg_i);
|
||||
if (!cp->tag_at(mt_index).is_method_type()) {
|
||||
// malformed class?
|
||||
return false;
|
||||
@ -465,7 +465,7 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodtype_arg(ConstantPo
|
||||
}
|
||||
|
||||
bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i) {
|
||||
int mh_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument_index(arg_i);
|
||||
int mh_index = cp->bsm_attribute_entry(bsms_attribute_index)->argument(arg_i);
|
||||
if (!cp->tag_at(mh_index).is_method_handle()) {
|
||||
// malformed class?
|
||||
return false;
|
||||
|
||||
@ -796,7 +796,7 @@ void AOTMapLogger::dumptime_log_mapped_heap_region(ArchiveMappedHeapInfo* heap_i
|
||||
address buffer_start = address(r.start()); // start of the current oop inside the buffer
|
||||
address buffer_end = address(r.end());
|
||||
|
||||
address requested_base = UseCompressedOops ? (address)CompressedOops::base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
|
||||
address requested_base = UseCompressedOops ? AOTMappedHeapWriter::narrow_oop_base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
|
||||
address requested_start = UseCompressedOops ? AOTMappedHeapWriter::buffered_addr_to_requested_addr(buffer_start) : requested_base;
|
||||
|
||||
log_region_range("heap", buffer_start, buffer_end, requested_start);
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
|
||||
GrowableArrayCHeap<u1, mtClassShared>* AOTMappedHeapWriter::_buffer = nullptr;
|
||||
|
||||
// The following are offsets from buffer_bottom()
|
||||
bool AOTMappedHeapWriter::_is_writing_deterministic_heap = false;
|
||||
size_t AOTMappedHeapWriter::_buffer_used;
|
||||
|
||||
// Heap root segments
|
||||
@ -74,7 +74,7 @@ AOTMappedHeapWriter::_buffer_offset_to_source_obj_table = nullptr;
|
||||
DumpedInternedStrings *AOTMappedHeapWriter::_dumped_interned_strings = nullptr;
|
||||
|
||||
typedef HashTable<
|
||||
size_t, // offset of a filler from ArchiveHeapWriter::buffer_bottom()
|
||||
size_t, // offset of a filler from AOTMappedHeapWriter::buffer_bottom()
|
||||
size_t, // size of this filler (in bytes)
|
||||
127, // prime number
|
||||
AnyObj::C_HEAP,
|
||||
@ -96,6 +96,45 @@ void AOTMappedHeapWriter::init() {
|
||||
_source_objs = new GrowableArrayCHeap<oop, mtClassShared>(10000);
|
||||
|
||||
guarantee(MIN_GC_REGION_ALIGNMENT <= G1HeapRegion::min_region_size_in_words() * HeapWordSize, "must be");
|
||||
|
||||
if (CDSConfig::old_cds_flags_used()) {
|
||||
// With the old CDS workflow, we can guatantee determninistic output: given
|
||||
// the same classlist file, we can generate the same static CDS archive.
|
||||
// To ensure determinism, we always use the same compressed oop encoding
|
||||
// (zero-based, no shift). See set_requested_address_range().
|
||||
_is_writing_deterministic_heap = true;
|
||||
} else {
|
||||
// Determninistic output is not supported by the new AOT workflow, so
|
||||
// we don't force the (zero-based, no shift) encoding. This way, it is more
|
||||
// likely that we can avoid oop relocation in the production run.
|
||||
_is_writing_deterministic_heap = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For AOTMappedHeapWriter::narrow_oop_{mode, base, shift}(), see comments
|
||||
// in AOTMappedHeapWriter::set_requested_address_range(),
|
||||
CompressedOops::Mode AOTMappedHeapWriter::narrow_oop_mode() {
|
||||
if (is_writing_deterministic_heap()) {
|
||||
return CompressedOops::UnscaledNarrowOop;
|
||||
} else {
|
||||
return CompressedOops::mode();
|
||||
}
|
||||
}
|
||||
|
||||
address AOTMappedHeapWriter::narrow_oop_base() {
|
||||
if (is_writing_deterministic_heap()) {
|
||||
return (address)0;
|
||||
} else {
|
||||
return CompressedOops::base();
|
||||
}
|
||||
}
|
||||
|
||||
int AOTMappedHeapWriter::narrow_oop_shift() {
|
||||
if (is_writing_deterministic_heap()) {
|
||||
return 0;
|
||||
} else {
|
||||
return CompressedOops::shift();
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +155,7 @@ void AOTMappedHeapWriter::write(GrowableArrayCHeap<oop, mtClassShared>* roots,
|
||||
assert(CDSConfig::is_dumping_heap(), "sanity");
|
||||
allocate_buffer();
|
||||
copy_source_objs_to_buffer(roots);
|
||||
set_requested_address(heap_info);
|
||||
set_requested_address_range(heap_info);
|
||||
relocate_embedded_oops(roots, heap_info);
|
||||
}
|
||||
|
||||
@ -536,14 +575,55 @@ size_t AOTMappedHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) {
|
||||
return buffered_obj_offset;
|
||||
}
|
||||
|
||||
void AOTMappedHeapWriter::set_requested_address(ArchiveMappedHeapInfo* info) {
|
||||
// Set the range [_requested_bottom, _requested_top), the requested address range of all
|
||||
// the archived heap objects in the production run.
|
||||
//
|
||||
// (1) UseCompressedOops == true && !is_writing_deterministic_heap()
|
||||
//
|
||||
// The archived objects are stored using the COOPS encoding of the assembly phase.
|
||||
// We pick a range within the heap used by the assembly phase.
|
||||
//
|
||||
// In the production run, if different COOPS encodings are used:
|
||||
// - The heap contents needs to be relocated.
|
||||
//
|
||||
// (2) UseCompressedOops == true && is_writing_deterministic_heap()
|
||||
//
|
||||
// We always use zero-based, zero-shift encoding. _requested_top is aligned to 0x10000000.
|
||||
//
|
||||
// (3) UseCompressedOops == false:
|
||||
//
|
||||
// In the production run, the heap range is usually picked (randomly) by the OS, so we
|
||||
// will almost always need to perform relocation, regardless of how we pick the requested
|
||||
// address range.
|
||||
//
|
||||
// So we just hard code it to NOCOOPS_REQUESTED_BASE.
|
||||
//
|
||||
void AOTMappedHeapWriter::set_requested_address_range(ArchiveMappedHeapInfo* info) {
|
||||
assert(!info->is_used(), "only set once");
|
||||
|
||||
size_t heap_region_byte_size = _buffer_used;
|
||||
assert(heap_region_byte_size > 0, "must archived at least one object!");
|
||||
|
||||
if (UseCompressedOops) {
|
||||
if (UseG1GC) {
|
||||
if (is_writing_deterministic_heap()) {
|
||||
// Pick a heap range so that requested addresses can be encoded with zero-base/no shift.
|
||||
// We align the requested bottom to at least 1 MB: if the production run uses G1 with a small
|
||||
// heap (e.g., -Xmx256m), it's likely that we can map the archived objects at the
|
||||
// requested location to avoid relocation.
|
||||
//
|
||||
// For other collectors or larger heaps, relocation is unavoidable, but is usually
|
||||
// quite cheap. If you really want to avoid relocation, use the AOT workflow instead.
|
||||
address heap_end = (address)0x100000000;
|
||||
size_t alignment = MAX2(MIN_GC_REGION_ALIGNMENT, 1024 * 1024);
|
||||
if (align_up(heap_region_byte_size, alignment) >= (size_t)heap_end) {
|
||||
log_error(aot, heap)("cached heap space is too large: %zu bytes", heap_region_byte_size);
|
||||
AOTMetaspace::unrecoverable_writing_error();
|
||||
}
|
||||
_requested_bottom = align_down(heap_end - heap_region_byte_size, alignment);
|
||||
} else if (UseG1GC) {
|
||||
// For G1, pick the range at the top of the current heap. If the exact same heap sizes
|
||||
// are used in the production run, it's likely that we can map the archived objects
|
||||
// at the requested location to avoid relocation.
|
||||
address heap_end = (address)G1CollectedHeap::heap()->reserved().end();
|
||||
log_info(aot, heap)("Heap end = %p", heap_end);
|
||||
_requested_bottom = align_down(heap_end - heap_region_byte_size, G1HeapRegion::GrainBytes);
|
||||
@ -612,7 +692,14 @@ oop AOTMappedHeapWriter::load_oop_from_buffer(narrowOop* buffered_addr) {
|
||||
|
||||
template <typename T> void AOTMappedHeapWriter::relocate_field_in_buffer(T* field_addr_in_buffer, oop source_referent, CHeapBitMap* oopmap) {
|
||||
oop request_referent = source_obj_to_requested_obj(source_referent);
|
||||
store_requested_oop_in_buffer<T>(field_addr_in_buffer, request_referent);
|
||||
if (UseCompressedOops && is_writing_deterministic_heap()) {
|
||||
// We use zero-based, 0-shift encoding, so the narrowOop is just the lower
|
||||
// 32 bits of request_referent
|
||||
intptr_t addr = cast_from_oop<intptr_t>(request_referent);
|
||||
*((narrowOop*)field_addr_in_buffer) = checked_cast<narrowOop>(addr);
|
||||
} else {
|
||||
store_requested_oop_in_buffer<T>(field_addr_in_buffer, request_referent);
|
||||
}
|
||||
if (request_referent != nullptr) {
|
||||
mark_oop_pointer<T>(field_addr_in_buffer, oopmap);
|
||||
}
|
||||
@ -918,9 +1005,9 @@ AOTMapLogger::OopDataIterator* AOTMappedHeapWriter::oop_iterator(ArchiveMappedHe
|
||||
address buffer_start = address(r.start());
|
||||
address buffer_end = address(r.end());
|
||||
|
||||
address requested_base = UseCompressedOops ? (address)CompressedOops::base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
|
||||
address requested_start = UseCompressedOops ? buffered_addr_to_requested_addr(buffer_start) : requested_base;
|
||||
int requested_shift = CompressedOops::shift();
|
||||
address requested_base = UseCompressedOops ? AOTMappedHeapWriter::narrow_oop_base() : (address)AOTMappedHeapWriter::NOCOOPS_REQUESTED_BASE;
|
||||
address requested_start = UseCompressedOops ? AOTMappedHeapWriter::buffered_addr_to_requested_addr(buffer_start) : requested_base;
|
||||
int requested_shift = AOTMappedHeapWriter::narrow_oop_shift();
|
||||
intptr_t buffer_to_requested_delta = requested_start - buffer_start;
|
||||
uint64_t buffer_start_narrow_oop = 0xdeadbeed;
|
||||
if (UseCompressedOops) {
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "cds/heapShared.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "oops/compressedOops.hpp"
|
||||
#include "oops/oopHandle.hpp"
|
||||
#include "utilities/bitMap.hpp"
|
||||
#include "utilities/exceptions.hpp"
|
||||
@ -71,7 +72,7 @@ class AOTMappedHeapWriter : AllStatic {
|
||||
// These are entered into HeapShared::archived_object_cache().
|
||||
//
|
||||
// - "buffered objects" are copies of the "source objects", and are stored in into
|
||||
// ArchiveHeapWriter::_buffer, which is a GrowableArray that sits outside of
|
||||
// AOTMappedHeapWriter::_buffer, which is a GrowableArray that sits outside of
|
||||
// the valid heap range. Therefore we avoid using the addresses of these copies
|
||||
// as oops. They are usually called "buffered_addr" in the code (of the type "address").
|
||||
//
|
||||
@ -81,26 +82,11 @@ class AOTMappedHeapWriter : AllStatic {
|
||||
// - Each archived object has a "requested address" -- at run time, if the object
|
||||
// can be mapped at this address, we can avoid relocation.
|
||||
//
|
||||
// The requested address is implemented differently depending on UseCompressedOops:
|
||||
// The requested address of an archived object is essentially its buffered_addr + delta,
|
||||
// where delta is (_requested_bottom - buffer_bottom());
|
||||
//
|
||||
// UseCompressedOops == true:
|
||||
// The archived objects are stored assuming that the runtime COOPS compression
|
||||
// scheme is exactly the same as in dump time (or else a more expensive runtime relocation
|
||||
// would be needed.)
|
||||
//
|
||||
// At dump time, we assume that the runtime heap range is exactly the same as
|
||||
// in dump time. The requested addresses of the archived objects are chosen such that
|
||||
// they would occupy the top end of a G1 heap (TBD when dumping is supported by other
|
||||
// collectors. See JDK-8298614).
|
||||
//
|
||||
// UseCompressedOops == false:
|
||||
// At runtime, the heap range is usually picked (randomly) by the OS, so we will almost always
|
||||
// need to perform relocation. Hence, the goal of the "requested address" is to ensure that
|
||||
// the contents of the archived objects are deterministic. I.e., the oop fields of archived
|
||||
// objects will always point to deterministic addresses.
|
||||
//
|
||||
// For G1, the archived heap is written such that the lowest archived object is placed
|
||||
// at NOCOOPS_REQUESTED_BASE. (TBD after JDK-8298614).
|
||||
// The requested addresses of all archived objects are within [_requested_bottom, _requested_top).
|
||||
// See AOTMappedHeapWriter::set_requested_address_range() for more info.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
@ -111,6 +97,15 @@ public:
|
||||
// Shenandoah heap region size can never be smaller than 256K.
|
||||
static constexpr int MIN_GC_REGION_ALIGNMENT = 256 * K;
|
||||
|
||||
// The heap contents are required to be deterministic when dumping "old" CDS archives, in order
|
||||
// to support reproducible lib/server/classes*.jsa when building the JDK.
|
||||
static bool is_writing_deterministic_heap() { return _is_writing_deterministic_heap; }
|
||||
|
||||
// The oop encoding used by the archived heap objects.
|
||||
static CompressedOops::Mode narrow_oop_mode();
|
||||
static address narrow_oop_base();
|
||||
static int narrow_oop_shift();
|
||||
|
||||
static const int INITIAL_TABLE_SIZE = 15889; // prime number
|
||||
static const int MAX_TABLE_SIZE = 1000000;
|
||||
|
||||
@ -121,6 +116,7 @@ private:
|
||||
int _field_offset;
|
||||
};
|
||||
|
||||
static bool _is_writing_deterministic_heap;
|
||||
static GrowableArrayCHeap<u1, mtClassShared>* _buffer;
|
||||
|
||||
// The number of bytes that have written into _buffer (may be smaller than _buffer->length()).
|
||||
@ -130,15 +126,15 @@ private:
|
||||
static HeapRootSegments _heap_root_segments;
|
||||
|
||||
// The address range of the requested location of the archived heap objects.
|
||||
static address _requested_bottom;
|
||||
static address _requested_top;
|
||||
static address _requested_bottom; // The requested address of the lowest archived heap object
|
||||
static address _requested_top; // The exclusive end of the highest archived heap object
|
||||
|
||||
static GrowableArrayCHeap<NativePointerInfo, mtClassShared>* _native_pointers;
|
||||
static GrowableArrayCHeap<oop, mtClassShared>* _source_objs;
|
||||
static DumpedInternedStrings *_dumped_interned_strings;
|
||||
|
||||
// We sort _source_objs_order to minimize the number of bits in ptrmap and oopmap.
|
||||
// See comments near the body of ArchiveHeapWriter::compare_objs_by_oop_fields().
|
||||
// See comments near the body of AOTMappedHeapWriter::compare_objs_by_oop_fields().
|
||||
// The objects will be written in the order of:
|
||||
//_source_objs->at(_source_objs_order->at(0)._index)
|
||||
// source_objs->at(_source_objs_order->at(1)._index)
|
||||
@ -200,7 +196,7 @@ private:
|
||||
static int filler_array_length(size_t fill_bytes);
|
||||
static HeapWord* init_filler_array_at_buffer_top(int array_length, size_t fill_bytes);
|
||||
|
||||
static void set_requested_address(ArchiveMappedHeapInfo* info);
|
||||
static void set_requested_address_range(ArchiveMappedHeapInfo* info);
|
||||
static void mark_native_pointers(oop orig_obj);
|
||||
static void relocate_embedded_oops(GrowableArrayCHeap<oop, mtClassShared>* roots, ArchiveMappedHeapInfo* info);
|
||||
static void compute_ptrmap(ArchiveMappedHeapInfo *info);
|
||||
|
||||
@ -114,6 +114,7 @@ intx AOTMetaspace::_relocation_delta;
|
||||
char* AOTMetaspace::_requested_base_address;
|
||||
Array<Method*>* AOTMetaspace::_archived_method_handle_intrinsics = nullptr;
|
||||
bool AOTMetaspace::_use_optimized_module_handling = true;
|
||||
FileMapInfo* AOTMetaspace::_output_mapinfo = nullptr;
|
||||
|
||||
// The CDS archive is divided into the following regions:
|
||||
// rw - read-write metadata
|
||||
@ -322,6 +323,24 @@ void AOTMetaspace::initialize_for_static_dump() {
|
||||
AOTMetaspace::unrecoverable_writing_error();
|
||||
}
|
||||
_symbol_region.init(&_symbol_rs, &_symbol_vs);
|
||||
if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
// We are in the AOT training run. User code is executed.
|
||||
//
|
||||
// On Windows, if the user code closes System.out and we open the AOT config file for output
|
||||
// only at VM exit, we might get back the same file HANDLE as stdout, and the AOT config
|
||||
// file may get corrupted by UL logs. By opening early, we ensure that the output
|
||||
// HANDLE is different than stdout so we can avoid such corruption.
|
||||
open_output_mapinfo();
|
||||
} else {
|
||||
// No need for the above as we won't execute any user code.
|
||||
}
|
||||
}
|
||||
|
||||
void AOTMetaspace::open_output_mapinfo() {
|
||||
const char* static_archive = CDSConfig::output_archive_path();
|
||||
assert(static_archive != nullptr, "sanity");
|
||||
_output_mapinfo = new FileMapInfo(static_archive, true);
|
||||
_output_mapinfo->open_as_output();
|
||||
}
|
||||
|
||||
// Called by universe_post_init()
|
||||
@ -655,15 +674,14 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
VM_PopulateDumpSharedSpace(StaticArchiveBuilder& b) :
|
||||
VM_Operation(), _mapped_heap_info(), _streamed_heap_info(), _map_info(nullptr), _builder(b) {}
|
||||
VM_PopulateDumpSharedSpace(StaticArchiveBuilder& b, FileMapInfo* map_info) :
|
||||
VM_Operation(), _mapped_heap_info(), _streamed_heap_info(), _map_info(map_info), _builder(b) {}
|
||||
|
||||
bool skip_operation() const { return false; }
|
||||
|
||||
VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
|
||||
ArchiveMappedHeapInfo* mapped_heap_info() { return &_mapped_heap_info; }
|
||||
ArchiveStreamedHeapInfo* streamed_heap_info() { return &_streamed_heap_info; }
|
||||
FileMapInfo* map_info() const { return _map_info; }
|
||||
void doit(); // outline because gdb sucks
|
||||
bool allow_nested_vm_operations() const { return true; }
|
||||
}; // class VM_PopulateDumpSharedSpace
|
||||
@ -795,12 +813,6 @@ void VM_PopulateDumpSharedSpace::doit() {
|
||||
CppVtables::zero_archived_vtables();
|
||||
|
||||
// Write the archive file
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
FileMapInfo::free_current_info(); // FIXME: should not free current info
|
||||
}
|
||||
const char* static_archive = CDSConfig::output_archive_path();
|
||||
assert(static_archive != nullptr, "sanity");
|
||||
_map_info = new FileMapInfo(static_archive, true);
|
||||
_map_info->populate_header(AOTMetaspace::core_region_alignment());
|
||||
_map_info->set_early_serialized_data(early_serialized_data);
|
||||
_map_info->set_serialized_data(serialized_data);
|
||||
@ -1138,7 +1150,14 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
|
||||
}
|
||||
#endif
|
||||
|
||||
VM_PopulateDumpSharedSpace op(builder);
|
||||
if (!CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
if (CDSConfig::is_dumping_final_static_archive()) {
|
||||
FileMapInfo::free_current_info(); // FIXME: should not free current info
|
||||
}
|
||||
open_output_mapinfo();
|
||||
}
|
||||
|
||||
VM_PopulateDumpSharedSpace op(builder, _output_mapinfo);
|
||||
VMThread::execute(&op);
|
||||
|
||||
if (AOTCodeCache::is_on_for_dump() && CDSConfig::is_dumping_final_static_archive()) {
|
||||
@ -1152,7 +1171,9 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
|
||||
CDSConfig::disable_dumping_aot_code();
|
||||
}
|
||||
|
||||
bool status = write_static_archive(&builder, op.map_info(), op.mapped_heap_info(), op.streamed_heap_info());
|
||||
bool status = write_static_archive(&builder, _output_mapinfo, op.mapped_heap_info(), op.streamed_heap_info());
|
||||
assert(!_output_mapinfo->is_open(), "Must be closed already");
|
||||
_output_mapinfo = nullptr;
|
||||
if (status && CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
tty->print_cr("%s AOTConfiguration recorded: %s",
|
||||
CDSConfig::has_temp_aot_config_file() ? "Temporary" : "", AOTConfiguration);
|
||||
@ -1173,11 +1194,10 @@ bool AOTMetaspace::write_static_archive(ArchiveBuilder* builder,
|
||||
// relocate the data so that it can be mapped to AOTMetaspace::requested_base_address()
|
||||
// without runtime relocation.
|
||||
builder->relocate_to_requested();
|
||||
|
||||
map_info->open_as_output();
|
||||
if (!map_info->is_open()) {
|
||||
return false;
|
||||
}
|
||||
map_info->prepare_for_writing();
|
||||
builder->write_archive(map_info, mapped_heap_info, streamed_heap_info);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -60,6 +60,7 @@ class AOTMetaspace : AllStatic {
|
||||
static char* _requested_base_address;
|
||||
static bool _use_optimized_module_handling;
|
||||
static Array<Method*>* _archived_method_handle_intrinsics;
|
||||
static FileMapInfo* _output_mapinfo;
|
||||
|
||||
public:
|
||||
enum {
|
||||
@ -185,6 +186,7 @@ public:
|
||||
private:
|
||||
static void read_extra_data(JavaThread* current, const char* filename) NOT_CDS_RETURN;
|
||||
static void fork_and_dump_final_static_archive(TRAPS);
|
||||
static void open_output_mapinfo();
|
||||
static bool write_static_archive(ArchiveBuilder* builder,
|
||||
FileMapInfo* map_info,
|
||||
ArchiveMappedHeapInfo* mapped_heap_info,
|
||||
|
||||
@ -353,6 +353,7 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data, AOTClassLocatio
|
||||
assert(dynamic_info != nullptr, "Sanity");
|
||||
|
||||
dynamic_info->open_as_output();
|
||||
dynamic_info->prepare_for_writing();
|
||||
ArchiveBuilder::write_archive(dynamic_info, nullptr, nullptr);
|
||||
|
||||
address base = _requested_dynamic_archive_bottom;
|
||||
|
||||
@ -216,12 +216,14 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment,
|
||||
_obj_alignment = ObjectAlignmentInBytes;
|
||||
_compact_strings = CompactStrings;
|
||||
_compact_headers = UseCompactObjectHeaders;
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
if (CDSConfig::is_dumping_heap()) {
|
||||
_object_streaming_mode = HeapShared::is_writing_streaming_mode();
|
||||
_narrow_oop_mode = CompressedOops::mode();
|
||||
_narrow_oop_base = CompressedOops::base();
|
||||
_narrow_oop_shift = CompressedOops::shift();
|
||||
_narrow_oop_mode = AOTMappedHeapWriter::narrow_oop_mode();
|
||||
_narrow_oop_base = AOTMappedHeapWriter::narrow_oop_base();
|
||||
_narrow_oop_shift = AOTMappedHeapWriter::narrow_oop_shift();
|
||||
}
|
||||
#endif
|
||||
_compressed_oops = UseCompressedOops;
|
||||
_compressed_class_ptrs = UseCompressedClassPointers;
|
||||
if (UseCompressedClassPointers) {
|
||||
@ -777,7 +779,9 @@ void FileMapInfo::open_as_output() {
|
||||
}
|
||||
_fd = fd;
|
||||
_file_open = true;
|
||||
}
|
||||
|
||||
void FileMapInfo::prepare_for_writing() {
|
||||
// Seek past the header. We will write the header after all regions are written
|
||||
// and their CRCs computed.
|
||||
size_t header_bytes = header()->header_size();
|
||||
@ -911,7 +915,7 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
|
||||
if (HeapShared::is_writing_mapping_mode()) {
|
||||
requested_base = (char*)AOTMappedHeapWriter::requested_address();
|
||||
if (UseCompressedOops) {
|
||||
mapping_offset = (size_t)((address)requested_base - CompressedOops::base());
|
||||
mapping_offset = (size_t)((address)requested_base - AOTMappedHeapWriter::narrow_oop_base());
|
||||
assert((mapping_offset >> CompressedOops::shift()) << CompressedOops::shift() == mapping_offset, "must be");
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -365,6 +365,7 @@ public:
|
||||
// File manipulation.
|
||||
bool open_as_input() NOT_CDS_RETURN_(false);
|
||||
void open_as_output();
|
||||
void prepare_for_writing();
|
||||
void write_header();
|
||||
void write_region(int region, char* base, size_t size,
|
||||
bool read_only, bool allow_exec);
|
||||
|
||||
@ -332,7 +332,7 @@ public:
|
||||
// Used by CDSHeapVerifier.
|
||||
OopHandle _orig_referrer;
|
||||
|
||||
// The location of this object inside ArchiveHeapWriter::_buffer
|
||||
// The location of this object inside {AOTMappedHeapWriter, AOTStreamedHeapWriter}::_buffer
|
||||
size_t _buffer_offset;
|
||||
|
||||
// One or more fields in this object are pointing to non-null oops.
|
||||
|
||||
@ -1057,7 +1057,9 @@ void ciEnv::register_method(ciMethod* target,
|
||||
}
|
||||
|
||||
assert(offsets->value(CodeOffsets::Deopt) != -1, "must have deopt entry");
|
||||
assert(offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry");
|
||||
|
||||
assert(compiler->type() == compiler_c2 ||
|
||||
offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry");
|
||||
|
||||
nm = nmethod::new_nmethod(method,
|
||||
compile_id(),
|
||||
|
||||
@ -605,7 +605,7 @@ bool ciInstanceKlass::is_leaf_type() {
|
||||
if (is_shared()) {
|
||||
return is_final(); // approximately correct
|
||||
} else {
|
||||
return !has_subklass() && (nof_implementors() == 0);
|
||||
return !has_subklass() && (!is_interface() || nof_implementors() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,6 +619,7 @@ bool ciInstanceKlass::is_leaf_type() {
|
||||
// This is OK, since any dependencies we decide to assert
|
||||
// will be checked later under the Compile_lock.
|
||||
ciInstanceKlass* ciInstanceKlass::implementor() {
|
||||
assert(is_interface(), "required");
|
||||
ciInstanceKlass* impl = _implementor;
|
||||
if (impl == nullptr) {
|
||||
if (is_shared()) {
|
||||
|
||||
@ -259,6 +259,7 @@ public:
|
||||
|
||||
ciInstanceKlass* unique_implementor() {
|
||||
assert(is_loaded(), "must be loaded");
|
||||
assert(is_interface(), "must be");
|
||||
ciInstanceKlass* impl = implementor();
|
||||
return (impl != this ? impl : nullptr);
|
||||
}
|
||||
|
||||
@ -47,6 +47,7 @@
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
#include "oops/annotations.hpp"
|
||||
#include "oops/bsmAttribute.inline.hpp"
|
||||
#include "oops/constantPool.inline.hpp"
|
||||
#include "oops/fieldInfo.hpp"
|
||||
#include "oops/fieldStreams.inline.hpp"
|
||||
@ -3298,8 +3299,9 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil
|
||||
TRAPS) {
|
||||
assert(cfs != nullptr, "invariant");
|
||||
assert(cp != nullptr, "invariant");
|
||||
const int cp_size = cp->length();
|
||||
|
||||
const u1* const current_start = cfs->current();
|
||||
const u1* const current_before_parsing = cfs->current();
|
||||
|
||||
guarantee_property(attribute_byte_length >= sizeof(u2),
|
||||
"Invalid BootstrapMethods attribute length %u in class file %s",
|
||||
@ -3308,57 +3310,40 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil
|
||||
|
||||
cfs->guarantee_more(attribute_byte_length, CHECK);
|
||||
|
||||
const int attribute_array_length = cfs->get_u2_fast();
|
||||
const int num_bootstrap_methods = cfs->get_u2_fast();
|
||||
|
||||
guarantee_property(_max_bootstrap_specifier_index < attribute_array_length,
|
||||
guarantee_property(_max_bootstrap_specifier_index < num_bootstrap_methods,
|
||||
"Short length on BootstrapMethods in class file %s",
|
||||
CHECK);
|
||||
|
||||
const u4 bootstrap_methods_u2_len = (attribute_byte_length - sizeof(u2)) / sizeof(u2);
|
||||
|
||||
// The attribute contains a counted array of counted tuples of shorts,
|
||||
// represending bootstrap specifiers:
|
||||
// length*{bootstrap_method_index, argument_count*{argument_index}}
|
||||
const unsigned int operand_count = (attribute_byte_length - (unsigned)sizeof(u2)) / (unsigned)sizeof(u2);
|
||||
// operand_count = number of shorts in attr, except for leading length
|
||||
|
||||
// The attribute is copied into a short[] array.
|
||||
// The array begins with a series of short[2] pairs, one for each tuple.
|
||||
const int index_size = (attribute_array_length * 2);
|
||||
|
||||
Array<u2>* const operands =
|
||||
MetadataFactory::new_array<u2>(_loader_data, index_size + operand_count, CHECK);
|
||||
|
||||
// Eagerly assign operands so they will be deallocated with the constant
|
||||
// Eagerly assign the arrays so that they will be deallocated with the constant
|
||||
// pool if there is an error.
|
||||
cp->set_operands(operands);
|
||||
BSMAttributeEntries::InsertionIterator iter =
|
||||
cp->bsm_entries().start_extension(num_bootstrap_methods,
|
||||
bootstrap_methods_u2_len,
|
||||
_loader_data,
|
||||
CHECK);
|
||||
|
||||
int operand_fill_index = index_size;
|
||||
const int cp_size = cp->length();
|
||||
|
||||
for (int n = 0; n < attribute_array_length; n++) {
|
||||
// Store a 32-bit offset into the header of the operand array.
|
||||
ConstantPool::operand_offset_at_put(operands, n, operand_fill_index);
|
||||
|
||||
// Read a bootstrap specifier.
|
||||
for (int i = 0; i < num_bootstrap_methods; i++) {
|
||||
cfs->guarantee_more(sizeof(u2) * 2, CHECK); // bsm, argc
|
||||
const u2 bootstrap_method_index = cfs->get_u2_fast();
|
||||
const u2 argument_count = cfs->get_u2_fast();
|
||||
u2 bootstrap_method_ref = cfs->get_u2_fast();
|
||||
u2 num_bootstrap_arguments = cfs->get_u2_fast();
|
||||
guarantee_property(
|
||||
valid_cp_range(bootstrap_method_index, cp_size) &&
|
||||
cp->tag_at(bootstrap_method_index).is_method_handle(),
|
||||
"bootstrap_method_index %u has bad constant type in class file %s",
|
||||
bootstrap_method_index,
|
||||
CHECK);
|
||||
valid_cp_range(bootstrap_method_ref, cp_size) &&
|
||||
cp->tag_at(bootstrap_method_ref).is_method_handle(),
|
||||
"bootstrap_method_index %u has bad constant type in class file %s",
|
||||
bootstrap_method_ref,
|
||||
CHECK);
|
||||
cfs->guarantee_more(sizeof(u2) * num_bootstrap_arguments, CHECK); // argv[argc]
|
||||
|
||||
guarantee_property((operand_fill_index + 1 + argument_count) < operands->length(),
|
||||
"Invalid BootstrapMethods num_bootstrap_methods or num_bootstrap_arguments value in class file %s",
|
||||
CHECK);
|
||||
BSMAttributeEntry* entry = iter.reserve_new_entry(bootstrap_method_ref, num_bootstrap_arguments);
|
||||
guarantee_property(entry != nullptr,
|
||||
"Invalid BootstrapMethods num_bootstrap_methods."
|
||||
" The total amount of space reserved for the BootstrapMethod attribute was not sufficient", CHECK);
|
||||
|
||||
operands->at_put(operand_fill_index++, bootstrap_method_index);
|
||||
operands->at_put(operand_fill_index++, argument_count);
|
||||
|
||||
cfs->guarantee_more(sizeof(u2) * argument_count, CHECK); // argv[argc]
|
||||
for (int j = 0; j < argument_count; j++) {
|
||||
for (int argi = 0; argi < num_bootstrap_arguments; argi++) {
|
||||
const u2 argument_index = cfs->get_u2_fast();
|
||||
guarantee_property(
|
||||
valid_cp_range(argument_index, cp_size) &&
|
||||
@ -3366,10 +3351,11 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil
|
||||
"argument_index %u has bad constant type in class file %s",
|
||||
argument_index,
|
||||
CHECK);
|
||||
operands->at_put(operand_fill_index++, argument_index);
|
||||
entry->set_argument(argi, argument_index);
|
||||
}
|
||||
}
|
||||
guarantee_property(current_start + attribute_byte_length == cfs->current(),
|
||||
cp->bsm_entries().end_extension(iter, _loader_data, CHECK);
|
||||
guarantee_property(current_before_parsing + attribute_byte_length == cfs->current(),
|
||||
"Bad length on BootstrapMethods in class file %s",
|
||||
CHECK);
|
||||
}
|
||||
|
||||
@ -412,31 +412,30 @@ ClassFileStream* ClassPathImageEntry::open_stream(JavaThread* current, const cha
|
||||
//
|
||||
ClassFileStream* ClassPathImageEntry::open_stream_for_loader(JavaThread* current, const char* name, ClassLoaderData* loader_data) {
|
||||
jlong size;
|
||||
JImageLocationRef location = (*JImageFindResource)(jimage_non_null(), "", get_jimage_version_string(), name, &size);
|
||||
JImageLocationRef location = 0;
|
||||
|
||||
if (location == 0) {
|
||||
TempNewSymbol class_name = SymbolTable::new_symbol(name);
|
||||
TempNewSymbol pkg_name = ClassLoader::package_from_class_name(class_name);
|
||||
TempNewSymbol class_name = SymbolTable::new_symbol(name);
|
||||
TempNewSymbol pkg_name = ClassLoader::package_from_class_name(class_name);
|
||||
|
||||
if (pkg_name != nullptr) {
|
||||
if (!Universe::is_module_initialized()) {
|
||||
location = (*JImageFindResource)(jimage_non_null(), JAVA_BASE_NAME, get_jimage_version_string(), name, &size);
|
||||
} else {
|
||||
PackageEntry* package_entry = ClassLoader::get_package_entry(pkg_name, loader_data);
|
||||
if (package_entry != nullptr) {
|
||||
ResourceMark rm(current);
|
||||
// Get the module name
|
||||
ModuleEntry* module = package_entry->module();
|
||||
assert(module != nullptr, "Boot classLoader package missing module");
|
||||
assert(module->is_named(), "Boot classLoader package is in unnamed module");
|
||||
const char* module_name = module->name()->as_C_string();
|
||||
if (module_name != nullptr) {
|
||||
location = (*JImageFindResource)(jimage_non_null(), module_name, get_jimage_version_string(), name, &size);
|
||||
}
|
||||
if (pkg_name != nullptr) {
|
||||
if (!Universe::is_module_initialized()) {
|
||||
location = (*JImageFindResource)(jimage_non_null(), JAVA_BASE_NAME, get_jimage_version_string(), name, &size);
|
||||
} else {
|
||||
PackageEntry* package_entry = ClassLoader::get_package_entry(pkg_name, loader_data);
|
||||
if (package_entry != nullptr) {
|
||||
ResourceMark rm(current);
|
||||
// Get the module name
|
||||
ModuleEntry* module = package_entry->module();
|
||||
assert(module != nullptr, "Boot classLoader package missing module");
|
||||
assert(module->is_named(), "Boot classLoader package is in unnamed module");
|
||||
const char* module_name = module->name()->as_C_string();
|
||||
if (module_name != nullptr) {
|
||||
location = (*JImageFindResource)(jimage_non_null(), module_name, get_jimage_version_string(), name, &size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (location != 0) {
|
||||
if (UsePerfData) {
|
||||
ClassLoader::perf_sys_classfile_bytes_read()->inc(size);
|
||||
|
||||
@ -1302,7 +1302,7 @@ nmethod::nmethod(
|
||||
}
|
||||
// Native wrappers do not have deopt handlers. Make the values
|
||||
// something that will never match a pc like the nmethod vtable entry
|
||||
_deopt_handler_offset = 0;
|
||||
_deopt_handler_entry_offset = 0;
|
||||
_unwind_handler_offset = 0;
|
||||
|
||||
CHECKED_CAST(_oops_size, uint16_t, align_up(code_buffer->total_oop_size(), oopSize));
|
||||
@ -1442,7 +1442,7 @@ nmethod::nmethod(const nmethod &nm) : CodeBlob(nm._name, nm._kind, nm._size, nm.
|
||||
_skipped_instructions_size = nm._skipped_instructions_size;
|
||||
_stub_offset = nm._stub_offset;
|
||||
_exception_offset = nm._exception_offset;
|
||||
_deopt_handler_offset = nm._deopt_handler_offset;
|
||||
_deopt_handler_entry_offset = nm._deopt_handler_entry_offset;
|
||||
_unwind_handler_offset = nm._unwind_handler_offset;
|
||||
_num_stack_arg_slots = nm._num_stack_arg_slots;
|
||||
_oops_size = nm._oops_size;
|
||||
@ -1704,19 +1704,26 @@ nmethod::nmethod(
|
||||
_exception_offset = -1;
|
||||
}
|
||||
if (offsets->value(CodeOffsets::Deopt) != -1) {
|
||||
_deopt_handler_offset = code_offset() + offsets->value(CodeOffsets::Deopt);
|
||||
_deopt_handler_entry_offset = code_offset() + offsets->value(CodeOffsets::Deopt);
|
||||
} else {
|
||||
_deopt_handler_offset = -1;
|
||||
_deopt_handler_entry_offset = -1;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Exception handler and deopt handler are in the stub section
|
||||
assert(offsets->value(CodeOffsets::Exceptions) != -1, "must be set");
|
||||
assert(offsets->value(CodeOffsets::Deopt ) != -1, "must be set");
|
||||
|
||||
_exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions);
|
||||
_deopt_handler_offset = _stub_offset + offsets->value(CodeOffsets::Deopt);
|
||||
bool has_exception_handler = (offsets->value(CodeOffsets::Exceptions) != -1);
|
||||
assert(has_exception_handler == (compiler->type() != compiler_c2),
|
||||
"C2 compiler doesn't provide exception handler stub code.");
|
||||
if (has_exception_handler) {
|
||||
_exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions);
|
||||
} else {
|
||||
_exception_offset = -1;
|
||||
}
|
||||
|
||||
_deopt_handler_entry_offset = _stub_offset + offsets->value(CodeOffsets::Deopt);
|
||||
}
|
||||
if (offsets->value(CodeOffsets::UnwindHandler) != -1) {
|
||||
// C1 generates UnwindHandler at the end of instructions section.
|
||||
@ -4024,7 +4031,7 @@ const char* nmethod::nmethod_section_label(address pos) const {
|
||||
// Check stub_code before checking exception_handler or deopt_handler.
|
||||
if (pos == this->stub_begin()) label = "[Stub Code]";
|
||||
if (JVMCI_ONLY(_exception_offset >= 0 &&) pos == exception_begin()) label = "[Exception Handler]";
|
||||
if (JVMCI_ONLY(_deopt_handler_offset != -1 &&) pos == deopt_handler_begin()) label = "[Deopt Handler Code]";
|
||||
if (JVMCI_ONLY(_deopt_handler_entry_offset != -1 &&) pos == deopt_handler_entry()) label = "[Deopt Handler Entry Point]";
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
@ -229,7 +229,7 @@ class nmethod : public CodeBlob {
|
||||
int _exception_offset;
|
||||
// All deoptee's will resume execution at this location described by
|
||||
// this offset.
|
||||
int _deopt_handler_offset;
|
||||
int _deopt_handler_entry_offset;
|
||||
// Offset (from insts_end) of the unwind handler if it exists
|
||||
int16_t _unwind_handler_offset;
|
||||
// Number of arguments passed on the stack
|
||||
@ -617,7 +617,7 @@ public:
|
||||
address stub_begin () const { return header_begin() + _stub_offset ; }
|
||||
address stub_end () const { return code_end() ; }
|
||||
address exception_begin () const { return header_begin() + _exception_offset ; }
|
||||
address deopt_handler_begin () const { return header_begin() + _deopt_handler_offset ; }
|
||||
address deopt_handler_entry () const { return header_begin() + _deopt_handler_entry_offset ; }
|
||||
address unwind_handler_begin () const { return _unwind_handler_offset != -1 ? (insts_end() - _unwind_handler_offset) : nullptr; }
|
||||
oop* oops_begin () const { return (oop*) data_begin(); }
|
||||
oop* oops_end () const { return (oop*) data_end(); }
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
inline bool nmethod::is_deopt_pc(address pc) { return is_deopt_entry(pc); }
|
||||
|
||||
inline bool nmethod::is_deopt_entry(address pc) {
|
||||
return pc == deopt_handler_begin();
|
||||
return pc == deopt_handler_entry();
|
||||
}
|
||||
|
||||
// class ExceptionCache methods
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "code/compiledIC.hpp"
|
||||
#include "code/nmethod.hpp"
|
||||
#include "code/relocInfo.hpp"
|
||||
#include "cppstdlib/new.hpp"
|
||||
#include "cppstdlib/type_traits.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
@ -37,8 +38,6 @@
|
||||
#include "utilities/checkedCast.hpp"
|
||||
#include "utilities/copy.hpp"
|
||||
|
||||
#include <new>
|
||||
|
||||
const RelocationHolder RelocationHolder::none; // its type is relocInfo::none
|
||||
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#ifndef SHARE_CODE_RELOCINFO_HPP
|
||||
#define SHARE_CODE_RELOCINFO_HPP
|
||||
|
||||
#include "cppstdlib/new.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "oops/oopsHierarchy.hpp"
|
||||
#include "runtime/osInfo.hpp"
|
||||
@ -32,8 +33,6 @@
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
|
||||
#include <new>
|
||||
|
||||
class CodeBlob;
|
||||
class Metadata;
|
||||
class NativeMovConstReg;
|
||||
|
||||
153
src/hotspot/share/cppstdlib/new.hpp
Normal file
153
src/hotspot/share/cppstdlib/new.hpp
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_CPPSTDLIB_NEW_HPP
|
||||
#define SHARE_CPPSTDLIB_NEW_HPP
|
||||
|
||||
#include "utilities/compilerWarnings.hpp"
|
||||
|
||||
// HotSpot usage:
|
||||
// Only the following may be used:
|
||||
// * std::nothrow_t, std::nothrow
|
||||
// * std::align_val_t
|
||||
// * The non-allocating forms of `operator new` and `operator new[]` are
|
||||
// implicitly used by the corresponding `new` and `new[]` expressions.
|
||||
// - operator new(size_t, void*) noexcept
|
||||
// - operator new[](size_t, void*) noexcept
|
||||
// Note that the non-allocating forms of `operator delete` and `operator
|
||||
// delete[]` are not used, since they are only invoked by a placement new
|
||||
// expression that fails by throwing an exception. But they might still
|
||||
// end up being referenced in such a situation.
|
||||
|
||||
BEGIN_ALLOW_FORBIDDEN_FUNCTIONS
|
||||
#include "utilities/vmassert_uninstall.hpp"
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "utilities/vmassert_reinstall.hpp" // don't reorder
|
||||
END_ALLOW_FORBIDDEN_FUNCTIONS
|
||||
|
||||
// Deprecation declarations to forbid use of the default global allocator.
|
||||
// See C++17 21.6.1 Header <new> synopsis.
|
||||
|
||||
namespace std {
|
||||
|
||||
#if 0
|
||||
// We could deprecate exception types, for completeness, but don't bother. We
|
||||
// already have exceptions disabled, and run into compiler bugs when we try.
|
||||
//
|
||||
// gcc -Wattributes => type attributes ignored after type is already defined
|
||||
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122167
|
||||
//
|
||||
// clang -Wignored-attributes => attribute declaration must precede definition
|
||||
// The clang warning is https://github.com/llvm/llvm-project/issues/135481,
|
||||
// which should be fixed in clang 21.
|
||||
class [[deprecated]] bad_alloc;
|
||||
class [[deprecated]] bad_array_new_length;
|
||||
#endif // #if 0
|
||||
|
||||
// Forbid new_handler manipulation by HotSpot code, leaving it untouched for
|
||||
// use by application code.
|
||||
[[deprecated]] new_handler get_new_handler() noexcept;
|
||||
[[deprecated]] new_handler set_new_handler(new_handler) noexcept;
|
||||
|
||||
// Prefer HotSpot mechanisms for padding.
|
||||
//
|
||||
// The syntax for redeclaring these for deprecation is tricky, and not
|
||||
// supported by some versions of some compilers. Dispatch on compiler and
|
||||
// version to decide whether to redeclare deprecated.
|
||||
|
||||
#if defined(__clang__)
|
||||
// Some versions of clang with some stdlibs reject the declaration. Others may
|
||||
// accept the declaration but go wrong with uses. Different warnings and
|
||||
// link-time failures are both possible.
|
||||
// Known to have problems at least through clang19.
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
#if (__GNUC__ > 13) || (__GNUC__ == 13 && __GNUC_MINOR__ >= 2)
|
||||
// g++11.5 accepts the declaration and reports deprecation for uses, but also
|
||||
// has link-time failure for uses. Haven't tested intermediate versions.
|
||||
#define CAN_DEPRECATE_HARDWARE_INTERFERENCE_SIZES 1
|
||||
#endif // restrict gcc version
|
||||
|
||||
#elif defined(_MSVC)
|
||||
// VS2022-17.13.2 => error C2370: '...': redefinition; different storage class
|
||||
|
||||
#endif // Compiler dispatch
|
||||
|
||||
// Redeclare deprecated if such is supported.
|
||||
#ifdef CAN_DEPRECATE_HARDWARE_INTERFERENCE_SIZES
|
||||
[[deprecated]] extern const size_t hardware_destructive_interference_size;
|
||||
[[deprecated]] extern const size_t hardware_constructive_interference_size;
|
||||
#undef CAN_DEPRECATE_HARDWARE_INTERFERENCE_SIZES
|
||||
#endif // CAN_DEPRECATE_HARDWARE_INTERFERENCE_SIZES
|
||||
|
||||
} // namespace std
|
||||
|
||||
// Forbid using the global allocator by HotSpot code.
|
||||
// This doesn't provide complete coverage. Some global allocation and
|
||||
// deallocation functions are implicitly declared in all translation units,
|
||||
// without needing to include <new>; see C++17 6.7.4. So this doesn't remove
|
||||
// the need for the link-time verification that these functions aren't used.
|
||||
//
|
||||
// But don't poison them when compiling gtests. The gtest framework, the
|
||||
// HotSpot wrapper around it (gtestMain.cpp), and even some tests, all have
|
||||
// new/new[] and delete/delete[] expressions that use the default global
|
||||
// allocator. We also don't apply the link-time check for gtests, for the
|
||||
// same reason.
|
||||
#ifndef HOTSPOT_GTEST
|
||||
|
||||
[[deprecated]] void* operator new(std::size_t);
|
||||
[[deprecated]] void* operator new(std::size_t, std::align_val_t);
|
||||
[[deprecated]] void* operator new(std::size_t, const std::nothrow_t&) noexcept;
|
||||
[[deprecated]] void* operator new(std::size_t, std::align_val_t,
|
||||
const std::nothrow_t&) noexcept;
|
||||
|
||||
[[deprecated]] void operator delete(void*) noexcept;
|
||||
[[deprecated]] void operator delete(void*, std::size_t) noexcept;
|
||||
[[deprecated]] void operator delete(void*, std::align_val_t) noexcept;
|
||||
[[deprecated]] void operator delete(void*, std::size_t, std::align_val_t) noexcept;
|
||||
[[deprecated]] void operator delete(void*, const std::nothrow_t&) noexcept;
|
||||
[[deprecated]] void operator delete(void*, std::align_val_t,
|
||||
const std::nothrow_t&) noexcept;
|
||||
|
||||
[[deprecated]] void* operator new[](std::size_t);
|
||||
[[deprecated]] void* operator new[](std::size_t, std::align_val_t);
|
||||
[[deprecated]] void* operator new[](std::size_t, const std::nothrow_t&) noexcept;
|
||||
[[deprecated]] void* operator new[](std::size_t, std::align_val_t,
|
||||
const std::nothrow_t&) noexcept;
|
||||
|
||||
[[deprecated]] void operator delete[](void*) noexcept;
|
||||
[[deprecated]] void operator delete[](void*, std::size_t) noexcept;
|
||||
[[deprecated]] void operator delete[](void*, std::align_val_t) noexcept;
|
||||
[[deprecated]] void operator delete[](void*, std::size_t, std::align_val_t) noexcept;
|
||||
[[deprecated]] void operator delete[](void*, const std::nothrow_t&) noexcept;
|
||||
[[deprecated]] void operator delete[](void*, std::align_val_t,
|
||||
const std::nothrow_t&) noexcept;
|
||||
|
||||
#endif // HOTSPOT_GTEST
|
||||
|
||||
// Allow (don't poison) the non-allocating forms from [new.delete.placement].
|
||||
|
||||
#endif // SHARE_CPPSTDLIB_NEW_HPP
|
||||
@ -478,11 +478,6 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_
|
||||
log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu words",
|
||||
Thread::current()->name(), word_size);
|
||||
|
||||
if (is_shutting_down()) {
|
||||
stall_for_vm_shutdown();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Has the gc overhead limit been reached in the meantime? If so, this mutator
|
||||
// should receive null even when unsuccessfully scheduling a collection as well
|
||||
// for global consistency.
|
||||
@ -738,11 +733,6 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {
|
||||
log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu",
|
||||
Thread::current()->name(), word_size);
|
||||
|
||||
if (is_shutting_down()) {
|
||||
stall_for_vm_shutdown();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Has the gc overhead limit been reached in the meantime? If so, this mutator
|
||||
// should receive null even when unsuccessfully scheduling a collection as well
|
||||
// for global consistency.
|
||||
@ -1645,6 +1635,10 @@ jint G1CollectedHeap::initialize() {
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
bool G1CollectedHeap::concurrent_mark_is_terminating() const {
|
||||
return _cm_thread->should_terminate();
|
||||
}
|
||||
|
||||
void G1CollectedHeap::stop() {
|
||||
// Stop all concurrent threads. We do this to make sure these threads
|
||||
// do not continue to execute and access resources (e.g. logging)
|
||||
@ -1965,8 +1959,8 @@ bool G1CollectedHeap::try_collect_concurrently(size_t allocation_word_size,
|
||||
}
|
||||
|
||||
// If VMOp skipped initiating concurrent marking cycle because
|
||||
// we're terminating, then we're done.
|
||||
if (is_shutting_down()) {
|
||||
// we're shutting down, then we're done.
|
||||
if (op.is_shutting_down()) {
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "skipped: terminating");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -917,6 +917,9 @@ public:
|
||||
// specified by the policy object.
|
||||
jint initialize() override;
|
||||
|
||||
// Returns whether concurrent mark threads (and the VM) are about to terminate.
|
||||
bool concurrent_mark_is_terminating() const;
|
||||
|
||||
void safepoint_synchronize_begin() override;
|
||||
void safepoint_synchronize_end() override;
|
||||
|
||||
|
||||
@ -267,8 +267,6 @@ void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candi
|
||||
// the same MixedGC.
|
||||
uint group_limit = p->calc_min_old_cset_length(num_candidates);
|
||||
|
||||
uint num_added_to_group = 0;
|
||||
|
||||
G1CSetCandidateGroup::reset_next_group_id();
|
||||
G1CSetCandidateGroup* current = nullptr;
|
||||
|
||||
@ -279,7 +277,7 @@ void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candi
|
||||
assert(!contains(r), "must not contain region %u", r->hrm_index());
|
||||
_contains_map[r->hrm_index()] = CandidateOrigin::Marking;
|
||||
|
||||
if (num_added_to_group == group_limit) {
|
||||
if (current->length() == group_limit) {
|
||||
if (group_limit != G1OldCSetGroupSize) {
|
||||
group_limit = G1OldCSetGroupSize;
|
||||
}
|
||||
@ -287,10 +285,8 @@ void G1CollectionSetCandidates::set_candidates_from_marking(G1HeapRegion** candi
|
||||
_from_marking_groups.append(current);
|
||||
|
||||
current = new G1CSetCandidateGroup();
|
||||
num_added_to_group = 0;
|
||||
}
|
||||
current->add(r);
|
||||
num_added_to_group++;
|
||||
}
|
||||
|
||||
_from_marking_groups.append(current);
|
||||
|
||||
@ -1883,7 +1883,7 @@ bool G1ConcurrentMark::concurrent_cycle_abort() {
|
||||
// nothing, but this situation should be extremely rare (a full gc after shutdown
|
||||
// has been signalled is already rare), and this work should be negligible compared
|
||||
// to actual full gc work.
|
||||
if (!cm_thread()->in_progress() && !_g1h->is_shutting_down()) {
|
||||
if (!cm_thread()->in_progress() && !_g1h->concurrent_mark_is_terminating()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -307,10 +307,6 @@ void G1HeapRegion::add_code_root(nmethod* nm) {
|
||||
rem_set()->add_code_root(nm);
|
||||
}
|
||||
|
||||
void G1HeapRegion::remove_code_root(nmethod* nm) {
|
||||
rem_set()->remove_code_root(nm);
|
||||
}
|
||||
|
||||
void G1HeapRegion::code_roots_do(NMethodClosure* blk) const {
|
||||
rem_set()->code_roots_do(blk);
|
||||
}
|
||||
|
||||
@ -543,7 +543,6 @@ public:
|
||||
// Routines for managing a list of code roots (attached to the
|
||||
// this region's RSet) that point into this heap region.
|
||||
void add_code_root(nmethod* nm);
|
||||
void remove_code_root(nmethod* nm);
|
||||
|
||||
// Applies blk->do_nmethod() to each of the entries in
|
||||
// the code roots list for this region
|
||||
|
||||
@ -28,14 +28,63 @@
|
||||
#include "gc/g1/g1Trace.hpp"
|
||||
#include "logging/log.hpp"
|
||||
|
||||
G1IHOPControl::G1IHOPControl(double initial_ihop_percent,
|
||||
G1OldGenAllocationTracker const* old_gen_alloc_tracker) :
|
||||
_initial_ihop_percent(initial_ihop_percent),
|
||||
_target_occupancy(0),
|
||||
_last_allocation_time_s(0.0),
|
||||
_old_gen_alloc_tracker(old_gen_alloc_tracker)
|
||||
{
|
||||
assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0, "Initial IHOP value must be between 0 and 100 but is %.3f", initial_ihop_percent);
|
||||
double G1IHOPControl::predict(const TruncatedSeq* seq) const {
|
||||
assert(_is_adaptive, "precondition");
|
||||
assert(_predictor != nullptr, "precondition");
|
||||
|
||||
return _predictor->predict_zero_bounded(seq);
|
||||
}
|
||||
|
||||
bool G1IHOPControl::have_enough_data_for_prediction() const {
|
||||
assert(_is_adaptive, "precondition");
|
||||
|
||||
return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) &&
|
||||
((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples);
|
||||
}
|
||||
|
||||
double G1IHOPControl::last_marking_length_s() const {
|
||||
return _marking_times_s.last();
|
||||
}
|
||||
|
||||
size_t G1IHOPControl::actual_target_threshold() const {
|
||||
assert(_is_adaptive, "precondition");
|
||||
|
||||
// The actual target threshold takes the heap reserve and the expected waste in
|
||||
// free space into account.
|
||||
// _heap_reserve is that part of the total heap capacity that is reserved for
|
||||
// eventual promotion failure.
|
||||
// _heap_waste is the amount of space will never be reclaimed in any
|
||||
// heap, so can not be used for allocation during marking and must always be
|
||||
// considered.
|
||||
double safe_total_heap_percentage =
|
||||
MIN2((double)(_heap_reserve_percent + _heap_waste_percent), 100.0);
|
||||
|
||||
return (size_t)MIN2(
|
||||
G1CollectedHeap::heap()->max_capacity() * (100.0 - safe_total_heap_percentage) / 100.0,
|
||||
_target_occupancy * (100.0 - _heap_waste_percent) / 100.0
|
||||
);
|
||||
}
|
||||
|
||||
G1IHOPControl::G1IHOPControl(double ihop_percent,
|
||||
const G1OldGenAllocationTracker* old_gen_alloc_tracker,
|
||||
bool adaptive,
|
||||
const G1Predictions* predictor,
|
||||
size_t heap_reserve_percent,
|
||||
size_t heap_waste_percent)
|
||||
: _is_adaptive(adaptive),
|
||||
_initial_ihop_percent(ihop_percent),
|
||||
_target_occupancy(0),
|
||||
_heap_reserve_percent(heap_reserve_percent),
|
||||
_heap_waste_percent(heap_waste_percent),
|
||||
_last_allocation_time_s(0.0),
|
||||
_old_gen_alloc_tracker(old_gen_alloc_tracker),
|
||||
_predictor(predictor),
|
||||
_marking_times_s(10, 0.05),
|
||||
_allocation_rate_s(10, 0.05),
|
||||
_last_unrestrained_young_size(0) {
|
||||
assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0,
|
||||
"IHOP percent out of range: %.3f", ihop_percent);
|
||||
assert(!_is_adaptive || _predictor != nullptr, "precondition");
|
||||
}
|
||||
|
||||
void G1IHOPControl::update_target_occupancy(size_t new_target_occupancy) {
|
||||
@ -50,9 +99,34 @@ void G1IHOPControl::report_statistics(G1NewTracer* new_tracer, size_t non_young_
|
||||
}
|
||||
|
||||
void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t additional_buffer_size) {
|
||||
assert(allocation_time_s >= 0.0, "Allocation time must be positive but is %.3f", allocation_time_s);
|
||||
|
||||
assert(allocation_time_s > 0, "Invalid allocation time: %.3f", allocation_time_s);
|
||||
_last_allocation_time_s = allocation_time_s;
|
||||
double alloc_rate = _old_gen_alloc_tracker->last_period_old_gen_growth() / allocation_time_s;
|
||||
_allocation_rate_s.add(alloc_rate);
|
||||
_last_unrestrained_young_size = additional_buffer_size;
|
||||
}
|
||||
|
||||
void G1IHOPControl::update_marking_length(double marking_length_s) {
|
||||
assert(marking_length_s >= 0.0, "Invalid marking length: %.3f", marking_length_s);
|
||||
_marking_times_s.add(marking_length_s);
|
||||
}
|
||||
|
||||
size_t G1IHOPControl::get_conc_mark_start_threshold() {
|
||||
guarantee(_target_occupancy > 0, "Target occupancy must be initialized");
|
||||
|
||||
if (!_is_adaptive || !have_enough_data_for_prediction()) {
|
||||
return (size_t)(_initial_ihop_percent * _target_occupancy / 100.0);
|
||||
}
|
||||
|
||||
double pred_marking_time = predict(&_marking_times_s);
|
||||
double pred_rate = predict(&_allocation_rate_s);
|
||||
size_t pred_bytes = (size_t)(pred_marking_time * pred_rate);
|
||||
size_t predicted_needed = pred_bytes + _last_unrestrained_young_size;
|
||||
size_t internal_threshold = actual_target_threshold();
|
||||
|
||||
return predicted_needed < internal_threshold
|
||||
? internal_threshold - predicted_needed
|
||||
: 0;
|
||||
}
|
||||
|
||||
void G1IHOPControl::print_log(size_t non_young_occupancy) {
|
||||
@ -68,6 +142,23 @@ void G1IHOPControl::print_log(size_t non_young_occupancy) {
|
||||
_last_allocation_time_s * 1000.0,
|
||||
_last_allocation_time_s > 0.0 ? _old_gen_alloc_tracker->last_period_old_gen_bytes() / _last_allocation_time_s : 0.0,
|
||||
last_marking_length_s() * 1000.0);
|
||||
|
||||
if (!_is_adaptive) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t actual_threshold = actual_target_threshold();
|
||||
log_debug(gc, ihop)("Adaptive IHOP information (value update), threshold: %zuB (%1.2f), internal target threshold: %zuB, "
|
||||
"non-young occupancy: %zuB, additional buffer size: %zuB, predicted old gen allocation rate: %1.2fB/s, "
|
||||
"predicted marking phase length: %1.2fms, prediction active: %s",
|
||||
cur_conc_mark_start_threshold,
|
||||
percent_of(cur_conc_mark_start_threshold, actual_threshold),
|
||||
actual_threshold,
|
||||
non_young_occupancy,
|
||||
_last_unrestrained_young_size,
|
||||
predict(&_allocation_rate_s),
|
||||
predict(&_marking_times_s) * 1000.0,
|
||||
have_enough_data_for_prediction() ? "true" : "false");
|
||||
}
|
||||
|
||||
void G1IHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) {
|
||||
@ -78,121 +169,14 @@ void G1IHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occup
|
||||
_old_gen_alloc_tracker->last_period_old_gen_bytes(),
|
||||
_last_allocation_time_s,
|
||||
last_marking_length_s());
|
||||
}
|
||||
|
||||
G1StaticIHOPControl::G1StaticIHOPControl(double ihop_percent,
|
||||
G1OldGenAllocationTracker const* old_gen_alloc_tracker) :
|
||||
G1IHOPControl(ihop_percent, old_gen_alloc_tracker),
|
||||
_last_marking_length_s(0.0) {
|
||||
}
|
||||
|
||||
G1AdaptiveIHOPControl::G1AdaptiveIHOPControl(double ihop_percent,
|
||||
G1OldGenAllocationTracker const* old_gen_alloc_tracker,
|
||||
G1Predictions const* predictor,
|
||||
size_t heap_reserve_percent,
|
||||
size_t heap_waste_percent) :
|
||||
G1IHOPControl(ihop_percent, old_gen_alloc_tracker),
|
||||
_heap_reserve_percent(heap_reserve_percent),
|
||||
_heap_waste_percent(heap_waste_percent),
|
||||
_predictor(predictor),
|
||||
_marking_times_s(10, 0.05),
|
||||
_allocation_rate_s(10, 0.05),
|
||||
_last_unrestrained_young_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
size_t G1AdaptiveIHOPControl::actual_target_threshold() const {
|
||||
guarantee(_target_occupancy > 0, "Target occupancy still not updated yet.");
|
||||
// The actual target threshold takes the heap reserve and the expected waste in
|
||||
// free space into account.
|
||||
// _heap_reserve is that part of the total heap capacity that is reserved for
|
||||
// eventual promotion failure.
|
||||
// _heap_waste is the amount of space will never be reclaimed in any
|
||||
// heap, so can not be used for allocation during marking and must always be
|
||||
// considered.
|
||||
|
||||
double safe_total_heap_percentage = MIN2((double)(_heap_reserve_percent + _heap_waste_percent), 100.0);
|
||||
|
||||
return (size_t)MIN2(
|
||||
G1CollectedHeap::heap()->max_capacity() * (100.0 - safe_total_heap_percentage) / 100.0,
|
||||
_target_occupancy * (100.0 - _heap_waste_percent) / 100.0
|
||||
);
|
||||
}
|
||||
|
||||
double G1AdaptiveIHOPControl::predict(TruncatedSeq const* seq) const {
|
||||
return _predictor->predict_zero_bounded(seq);
|
||||
}
|
||||
|
||||
bool G1AdaptiveIHOPControl::have_enough_data_for_prediction() const {
|
||||
return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) &&
|
||||
((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples);
|
||||
}
|
||||
|
||||
size_t G1AdaptiveIHOPControl::get_conc_mark_start_threshold() {
|
||||
if (have_enough_data_for_prediction()) {
|
||||
double pred_marking_time = predict(&_marking_times_s);
|
||||
double pred_promotion_rate = predict(&_allocation_rate_s);
|
||||
size_t pred_promotion_size = (size_t)(pred_marking_time * pred_promotion_rate);
|
||||
|
||||
size_t predicted_needed_bytes_during_marking =
|
||||
pred_promotion_size +
|
||||
// In reality we would need the maximum size of the young gen during
|
||||
// marking. This is a conservative estimate.
|
||||
_last_unrestrained_young_size;
|
||||
|
||||
size_t internal_threshold = actual_target_threshold();
|
||||
size_t predicted_initiating_threshold = predicted_needed_bytes_during_marking < internal_threshold ?
|
||||
internal_threshold - predicted_needed_bytes_during_marking :
|
||||
0;
|
||||
return predicted_initiating_threshold;
|
||||
} else {
|
||||
// Use the initial value.
|
||||
return (size_t)(_initial_ihop_percent * _target_occupancy / 100.0);
|
||||
if (_is_adaptive) {
|
||||
tracer->report_adaptive_ihop_statistics(get_conc_mark_start_threshold(),
|
||||
actual_target_threshold(),
|
||||
non_young_occupancy,
|
||||
_last_unrestrained_young_size,
|
||||
predict(&_allocation_rate_s),
|
||||
predict(&_marking_times_s),
|
||||
have_enough_data_for_prediction());
|
||||
}
|
||||
}
|
||||
|
||||
double G1AdaptiveIHOPControl::last_mutator_period_old_allocation_rate() const {
|
||||
assert(_last_allocation_time_s > 0, "This should not be called when the last GC is full");
|
||||
|
||||
return _old_gen_alloc_tracker->last_period_old_gen_growth() / _last_allocation_time_s;
|
||||
}
|
||||
|
||||
void G1AdaptiveIHOPControl::update_allocation_info(double allocation_time_s,
|
||||
size_t additional_buffer_size) {
|
||||
G1IHOPControl::update_allocation_info(allocation_time_s, additional_buffer_size);
|
||||
_allocation_rate_s.add(last_mutator_period_old_allocation_rate());
|
||||
|
||||
_last_unrestrained_young_size = additional_buffer_size;
|
||||
}
|
||||
|
||||
void G1AdaptiveIHOPControl::update_marking_length(double marking_length_s) {
|
||||
assert(marking_length_s >= 0.0, "Marking length must be larger than zero but is %.3f", marking_length_s);
|
||||
_marking_times_s.add(marking_length_s);
|
||||
}
|
||||
|
||||
void G1AdaptiveIHOPControl::print_log(size_t non_young_occupancy) {
|
||||
G1IHOPControl::print_log(non_young_occupancy);
|
||||
size_t actual_threshold = actual_target_threshold();
|
||||
log_debug(gc, ihop)("Adaptive IHOP information (value update), threshold: %zuB (%1.2f), internal target threshold: %zuB, "
|
||||
"non-young occupancy: %zuB, additional buffer size: %zuB, predicted old gen allocation rate: %1.2fB/s, "
|
||||
"predicted marking phase length: %1.2fms, prediction active: %s",
|
||||
get_conc_mark_start_threshold(),
|
||||
percent_of(get_conc_mark_start_threshold(), actual_threshold),
|
||||
actual_threshold,
|
||||
non_young_occupancy,
|
||||
_last_unrestrained_young_size,
|
||||
predict(&_allocation_rate_s),
|
||||
predict(&_marking_times_s) * 1000.0,
|
||||
have_enough_data_for_prediction() ? "true" : "false");
|
||||
}
|
||||
|
||||
void G1AdaptiveIHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) {
|
||||
G1IHOPControl::send_trace_event(tracer, non_young_occupancy);
|
||||
tracer->report_adaptive_ihop_statistics(get_conc_mark_start_threshold(),
|
||||
actual_target_threshold(),
|
||||
non_young_occupancy,
|
||||
_last_unrestrained_young_size,
|
||||
predict(&_allocation_rate_s),
|
||||
predict(&_marking_times_s),
|
||||
have_enough_data_for_prediction());
|
||||
}
|
||||
|
||||
@ -32,89 +32,32 @@
|
||||
class G1Predictions;
|
||||
class G1NewTracer;
|
||||
|
||||
// Base class for algorithms that calculate the heap occupancy at which
|
||||
// concurrent marking should start. This heap usage threshold should be relative
|
||||
// to old gen size.
|
||||
// Implements two strategies for calculating the concurrent mark starting occupancy threshold:
|
||||
// - Static mode: Uses a fixed percentage of the target heap occupancy.
|
||||
// - Adaptive mode: Predicts a threshold based on allocation rates and marking durations
|
||||
// to ensure the target occupancy is never exceeded during marking.
|
||||
class G1IHOPControl : public CHeapObj<mtGC> {
|
||||
protected:
|
||||
private:
|
||||
const bool _is_adaptive;
|
||||
|
||||
// The initial IHOP value relative to the target occupancy.
|
||||
double _initial_ihop_percent;
|
||||
|
||||
// The target maximum occupancy of the heap. The target occupancy is the number
|
||||
// of bytes when marking should be finished and reclaim started.
|
||||
size_t _target_occupancy;
|
||||
|
||||
// Percentage of maximum heap capacity we should avoid to touch
|
||||
const size_t _heap_reserve_percent;
|
||||
|
||||
// Percentage of free heap that should be considered as waste.
|
||||
const size_t _heap_waste_percent;
|
||||
|
||||
// Most recent complete mutator allocation period in seconds.
|
||||
double _last_allocation_time_s;
|
||||
|
||||
const G1OldGenAllocationTracker* _old_gen_alloc_tracker;
|
||||
// Initialize an instance with the old gen allocation tracker and the
|
||||
// initial IHOP value in percent. The target occupancy will be updated
|
||||
// at the first heap expansion.
|
||||
G1IHOPControl(double ihop_percent, G1OldGenAllocationTracker const* old_gen_alloc_tracker);
|
||||
|
||||
// Most recent time from the end of the concurrent start to the start of the first
|
||||
// mixed gc.
|
||||
virtual double last_marking_length_s() const = 0;
|
||||
|
||||
virtual void print_log(size_t non_young_occupancy);
|
||||
virtual void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy);
|
||||
|
||||
public:
|
||||
virtual ~G1IHOPControl() { }
|
||||
|
||||
// Get the current non-young occupancy at which concurrent marking should start.
|
||||
virtual size_t get_conc_mark_start_threshold() = 0;
|
||||
|
||||
// Adjust target occupancy.
|
||||
virtual void update_target_occupancy(size_t new_target_occupancy);
|
||||
// Update information about time during which allocations in the Java heap occurred,
|
||||
// how large these allocations were in bytes, and an additional buffer.
|
||||
// The allocations should contain any amount of space made unusable for further
|
||||
// allocation, e.g. any waste caused by TLAB allocation, space at the end of
|
||||
// humongous objects that can not be used for allocation, etc.
|
||||
// Together with the target occupancy, this additional buffer should contain the
|
||||
// difference between old gen size and total heap size at the start of reclamation,
|
||||
// and space required for that reclamation.
|
||||
virtual void update_allocation_info(double allocation_time_s, size_t additional_buffer_size);
|
||||
// Update the time spent in the mutator beginning from the end of concurrent start to
|
||||
// the first mixed gc.
|
||||
virtual void update_marking_length(double marking_length_s) = 0;
|
||||
|
||||
void report_statistics(G1NewTracer* tracer, size_t non_young_occupancy);
|
||||
};
|
||||
|
||||
// The returned concurrent mark starting occupancy threshold is a fixed value
|
||||
// relative to the maximum heap size.
|
||||
class G1StaticIHOPControl : public G1IHOPControl {
|
||||
// Most recent mutator time between the end of concurrent mark to the start of the
|
||||
// first mixed gc.
|
||||
double _last_marking_length_s;
|
||||
protected:
|
||||
double last_marking_length_s() const { return _last_marking_length_s; }
|
||||
public:
|
||||
G1StaticIHOPControl(double ihop_percent, G1OldGenAllocationTracker const* old_gen_alloc_tracker);
|
||||
|
||||
size_t get_conc_mark_start_threshold() {
|
||||
guarantee(_target_occupancy > 0, "Target occupancy must have been initialized.");
|
||||
return (size_t) (_initial_ihop_percent * _target_occupancy / 100.0);
|
||||
}
|
||||
|
||||
virtual void update_marking_length(double marking_length_s) {
|
||||
assert(marking_length_s > 0.0, "Marking length must be larger than zero but is %.3f", marking_length_s);
|
||||
_last_marking_length_s = marking_length_s;
|
||||
}
|
||||
};
|
||||
|
||||
// This algorithm tries to return a concurrent mark starting occupancy value that
|
||||
// makes sure that during marking the given target occupancy is never exceeded,
|
||||
// based on predictions of current allocation rate and time periods between
|
||||
// concurrent start and the first mixed gc.
|
||||
class G1AdaptiveIHOPControl : public G1IHOPControl {
|
||||
size_t _heap_reserve_percent; // Percentage of maximum heap capacity we should avoid to touch
|
||||
size_t _heap_waste_percent; // Percentage of free heap that should be considered as waste.
|
||||
|
||||
const G1Predictions * _predictor;
|
||||
|
||||
const G1Predictions* _predictor;
|
||||
TruncatedSeq _marking_times_s;
|
||||
TruncatedSeq _allocation_rate_s;
|
||||
|
||||
@ -128,35 +71,48 @@ class G1AdaptiveIHOPControl : public G1IHOPControl {
|
||||
size_t _last_unrestrained_young_size;
|
||||
|
||||
// Get a new prediction bounded below by zero from the given sequence.
|
||||
double predict(TruncatedSeq const* seq) const;
|
||||
double predict(const TruncatedSeq* seq) const;
|
||||
|
||||
bool have_enough_data_for_prediction() const;
|
||||
double last_marking_length_s() const;
|
||||
|
||||
// The "actual" target threshold the algorithm wants to keep during and at the
|
||||
// end of marking. This is typically lower than the requested threshold, as the
|
||||
// algorithm needs to consider restrictions by the environment.
|
||||
size_t actual_target_threshold() const;
|
||||
|
||||
// This method calculates the old gen allocation rate based on the net survived
|
||||
// bytes that are allocated in the old generation in the last mutator period.
|
||||
double last_mutator_period_old_allocation_rate() const;
|
||||
protected:
|
||||
virtual double last_marking_length_s() const { return _marking_times_s.last(); }
|
||||
|
||||
virtual void print_log(size_t non_young_occupancy);
|
||||
virtual void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy);
|
||||
void print_log(size_t non_young_occupancy);
|
||||
void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy);
|
||||
|
||||
public:
|
||||
G1AdaptiveIHOPControl(double ihop_percent,
|
||||
G1OldGenAllocationTracker const* old_gen_alloc_tracker,
|
||||
G1Predictions const* predictor,
|
||||
size_t heap_reserve_percent, // The percentage of total heap capacity that should not be tapped into.
|
||||
size_t heap_waste_percent); // The percentage of the free space in the heap that we think is not usable for allocation.
|
||||
G1IHOPControl(double ihop_percent,
|
||||
const G1OldGenAllocationTracker* old_gen_alloc_tracker,
|
||||
bool adaptive,
|
||||
const G1Predictions* predictor,
|
||||
size_t heap_reserve_percent,
|
||||
size_t heap_waste_percent);
|
||||
|
||||
virtual size_t get_conc_mark_start_threshold();
|
||||
// Adjust target occupancy.
|
||||
void update_target_occupancy(size_t new_target_occupancy);
|
||||
|
||||
virtual void update_allocation_info(double allocation_time_s, size_t additional_buffer_size);
|
||||
virtual void update_marking_length(double marking_length_s);
|
||||
// Update information about time during which allocations in the Java heap occurred,
|
||||
// how large these allocations were in bytes, and an additional buffer.
|
||||
// The allocations should contain any amount of space made unusable for further
|
||||
// allocation, e.g. any waste caused by TLAB allocation, space at the end of
|
||||
// humongous objects that can not be used for allocation, etc.
|
||||
// Together with the target occupancy, this additional buffer should contain the
|
||||
// difference between old gen size and total heap size at the start of reclamation,
|
||||
// and space required for that reclamation.
|
||||
void update_allocation_info(double allocation_time_s, size_t additional_buffer_size);
|
||||
|
||||
// Update the time spent in the mutator beginning from the end of concurrent start to
|
||||
// the first mixed gc.
|
||||
void update_marking_length(double marking_length_s);
|
||||
|
||||
// Get the current non-young occupancy at which concurrent marking should start.
|
||||
size_t get_conc_mark_start_threshold();
|
||||
|
||||
void report_statistics(G1NewTracer* tracer, size_t non_young_occupancy);
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_G1_G1IHOPCONTROL_HPP
|
||||
|
||||
@ -28,8 +28,6 @@
|
||||
#include "gc/g1/g1HeapRegion.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
|
||||
class G1AdaptiveIHOPControl;
|
||||
|
||||
// Track allocation details in the old generation.
|
||||
class G1OldGenAllocationTracker : public CHeapObj<mtGC> {
|
||||
// Total number of bytes allocated in the old generation at the end
|
||||
|
||||
@ -669,7 +669,6 @@ bool G1Policy::should_retain_evac_failed_region(uint index) const {
|
||||
}
|
||||
|
||||
void G1Policy::record_pause_start_time() {
|
||||
assert(!_g1h->is_shutting_down(), "Invariant!");
|
||||
Ticks now = Ticks::now();
|
||||
_cur_pause_start_sec = now.seconds();
|
||||
|
||||
@ -1026,15 +1025,12 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar
|
||||
|
||||
G1IHOPControl* G1Policy::create_ihop_control(const G1OldGenAllocationTracker* old_gen_alloc_tracker,
|
||||
const G1Predictions* predictor) {
|
||||
if (G1UseAdaptiveIHOP) {
|
||||
return new G1AdaptiveIHOPControl(InitiatingHeapOccupancyPercent,
|
||||
old_gen_alloc_tracker,
|
||||
predictor,
|
||||
G1ReservePercent,
|
||||
G1HeapWastePercent);
|
||||
} else {
|
||||
return new G1StaticIHOPControl(InitiatingHeapOccupancyPercent, old_gen_alloc_tracker);
|
||||
}
|
||||
return new G1IHOPControl(InitiatingHeapOccupancyPercent,
|
||||
old_gen_alloc_tracker,
|
||||
G1UseAdaptiveIHOP,
|
||||
predictor,
|
||||
G1ReservePercent,
|
||||
G1HeapWastePercent);
|
||||
}
|
||||
|
||||
bool G1Policy::update_ihop_prediction(double mutator_time_s,
|
||||
@ -1280,12 +1276,6 @@ void G1Policy::decide_on_concurrent_start_pause() {
|
||||
// concurrent start pause).
|
||||
assert(!collector_state()->in_concurrent_start_gc(), "pre-condition");
|
||||
|
||||
// We should not be starting a concurrent start pause if the concurrent mark
|
||||
// thread is terminating.
|
||||
if (_g1h->is_shutting_down()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (collector_state()->initiate_conc_mark_if_possible()) {
|
||||
// We had noticed on a previous pause that the heap occupancy has
|
||||
// gone over the initiating threshold and we should start a
|
||||
|
||||
@ -992,10 +992,11 @@ class G1MergeHeapRootsTask : public WorkerTask {
|
||||
}
|
||||
};
|
||||
|
||||
// Closure to make sure that the marking bitmap is clear for any old region in
|
||||
// the collection set.
|
||||
// This is needed to be able to use the bitmap for evacuation failure handling.
|
||||
class G1ClearBitmapClosure : public G1HeapRegionClosure {
|
||||
// Closure to prepare the collection set regions for evacuation failure, i.e. make
|
||||
// sure that the mark bitmap is clear for any old region in the collection set.
|
||||
//
|
||||
// These mark bitmaps record the evacuation failed objects.
|
||||
class G1PrepareRegionsForEvacFailClosure : public G1HeapRegionClosure {
|
||||
G1CollectedHeap* _g1h;
|
||||
G1RemSetScanState* _scan_state;
|
||||
bool _initial_evacuation;
|
||||
@ -1018,18 +1019,12 @@ class G1MergeHeapRootsTask : public WorkerTask {
|
||||
// the pause occurs during the Concurrent Cleanup for Next Mark phase.
|
||||
// Only at that point the region's bitmap may contain marks while being in the collection
|
||||
// set at the same time.
|
||||
//
|
||||
// There is one exception: shutdown might have aborted the Concurrent Cleanup for Next
|
||||
// Mark phase midway, which might have also left stale marks in old generation regions.
|
||||
// There might actually have been scheduled multiple collections, but at that point we do
|
||||
// not care that much about performance and just do the work multiple times if needed.
|
||||
return (_g1h->collector_state()->clear_bitmap_in_progress() ||
|
||||
_g1h->is_shutting_down()) &&
|
||||
hr->is_old();
|
||||
return _g1h->collector_state()->clear_bitmap_in_progress() &&
|
||||
hr->is_old();
|
||||
}
|
||||
|
||||
public:
|
||||
G1ClearBitmapClosure(G1CollectedHeap* g1h, G1RemSetScanState* scan_state, bool initial_evacuation) :
|
||||
G1PrepareRegionsForEvacFailClosure(G1CollectedHeap* g1h, G1RemSetScanState* scan_state, bool initial_evacuation) :
|
||||
_g1h(g1h),
|
||||
_scan_state(scan_state),
|
||||
_initial_evacuation(initial_evacuation)
|
||||
@ -1178,8 +1173,8 @@ public:
|
||||
|
||||
// Preparation for evacuation failure handling.
|
||||
{
|
||||
G1ClearBitmapClosure clear(g1h, _scan_state, _initial_evacuation);
|
||||
g1h->collection_set_iterate_increment_from(&clear, &_hr_claimer, worker_id);
|
||||
G1PrepareRegionsForEvacFailClosure prepare_evac_failure(g1h, _scan_state, _initial_evacuation);
|
||||
g1h->collection_set_iterate_increment_from(&prepare_evac_failure, &_hr_claimer, worker_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -344,11 +344,6 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) {
|
||||
assert(is_in_or_null(op.result()), "result not in heap");
|
||||
return op.result();
|
||||
}
|
||||
|
||||
if (is_shutting_down()) {
|
||||
stall_for_vm_shutdown();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Was the gc-overhead reached inside the safepoint? If so, this mutator
|
||||
@ -370,6 +365,55 @@ void ParallelScavengeHeap::do_full_collection(bool clear_all_soft_refs) {
|
||||
PSParallelCompact::invoke(clear_all_soft_refs, should_do_max_compaction);
|
||||
}
|
||||
|
||||
bool ParallelScavengeHeap::should_attempt_young_gc() const {
|
||||
const bool ShouldRunYoungGC = true;
|
||||
const bool ShouldRunFullGC = false;
|
||||
|
||||
if (!_young_gen->to_space()->is_empty()) {
|
||||
log_debug(gc, ergo)("To-space is not empty; run full-gc instead.");
|
||||
return ShouldRunFullGC;
|
||||
}
|
||||
|
||||
// Check if the predicted promoted bytes will overflow free space in old-gen.
|
||||
PSAdaptiveSizePolicy* policy = _size_policy;
|
||||
|
||||
size_t avg_promoted = (size_t) policy->padded_average_promoted_in_bytes();
|
||||
size_t promotion_estimate = MIN2(avg_promoted, _young_gen->used_in_bytes());
|
||||
// Total free size after possible old gen expansion
|
||||
size_t free_in_old_gen_with_expansion = _old_gen->max_gen_size() - _old_gen->used_in_bytes();
|
||||
|
||||
log_trace(gc, ergo)("average_promoted %zu; padded_average_promoted %zu",
|
||||
(size_t) policy->average_promoted_in_bytes(),
|
||||
(size_t) policy->padded_average_promoted_in_bytes());
|
||||
|
||||
if (promotion_estimate >= free_in_old_gen_with_expansion) {
|
||||
log_debug(gc, ergo)("Run full-gc; predicted promotion size >= max free space in old-gen: %zu >= %zu",
|
||||
promotion_estimate, free_in_old_gen_with_expansion);
|
||||
return ShouldRunFullGC;
|
||||
}
|
||||
|
||||
if (UseAdaptiveSizePolicy) {
|
||||
// Also checking OS has enough free memory to commit and expand old-gen.
|
||||
// Otherwise, the recorded gc-pause-time might be inflated to include time
|
||||
// of OS preparing free memory, resulting in inaccurate young-gen resizing.
|
||||
assert(_old_gen->committed().byte_size() >= _old_gen->used_in_bytes(), "inv");
|
||||
// Use uint64_t instead of size_t for 32bit compatibility.
|
||||
uint64_t free_mem_in_os;
|
||||
if (os::free_memory(free_mem_in_os)) {
|
||||
size_t actual_free = (size_t)MIN2(_old_gen->committed().byte_size() - _old_gen->used_in_bytes() + free_mem_in_os,
|
||||
(uint64_t)SIZE_MAX);
|
||||
if (promotion_estimate > actual_free) {
|
||||
log_debug(gc, ergo)("Run full-gc; predicted promotion size > free space in old-gen and OS: %zu > %zu",
|
||||
promotion_estimate, actual_free);
|
||||
return ShouldRunFullGC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No particular reasons to run full-gc, so young-gc.
|
||||
return ShouldRunYoungGC;
|
||||
}
|
||||
|
||||
static bool check_gc_heap_free_limit(size_t free_bytes, size_t capacity_bytes) {
|
||||
return (free_bytes * 100 / capacity_bytes) < GCHeapFreeLimit;
|
||||
}
|
||||
@ -516,17 +560,18 @@ void ParallelScavengeHeap::collect(GCCause::Cause cause) {
|
||||
VMThread::execute(&op);
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::collect_at_safepoint(bool full) {
|
||||
void ParallelScavengeHeap::collect_at_safepoint(bool is_full) {
|
||||
assert(!GCLocker::is_active(), "precondition");
|
||||
bool clear_soft_refs = GCCause::should_clear_all_soft_refs(_gc_cause);
|
||||
|
||||
if (!full) {
|
||||
bool success = PSScavenge::invoke(clear_soft_refs);
|
||||
if (success) {
|
||||
if (!is_full && should_attempt_young_gc()) {
|
||||
bool young_gc_success = PSScavenge::invoke(clear_soft_refs);
|
||||
if (young_gc_success) {
|
||||
return;
|
||||
}
|
||||
// Upgrade to Full-GC if young-gc fails
|
||||
log_debug(gc, heap)("Upgrade to Full-GC since Young-gc failed.");
|
||||
}
|
||||
|
||||
const bool should_do_max_compaction = false;
|
||||
PSParallelCompact::invoke(clear_soft_refs, should_do_max_compaction);
|
||||
}
|
||||
|
||||
@ -119,6 +119,9 @@ class ParallelScavengeHeap : public CollectedHeap {
|
||||
void print_tracing_info() const override;
|
||||
void stop() override {};
|
||||
|
||||
// Returns true if a young GC should be attempted, false if a full GC is preferred.
|
||||
bool should_attempt_young_gc() const;
|
||||
|
||||
public:
|
||||
ParallelScavengeHeap() :
|
||||
CollectedHeap(),
|
||||
@ -199,7 +202,6 @@ public:
|
||||
bool requires_barriers(stackChunkOop obj) const override;
|
||||
|
||||
MemRegion reserved_region() const { return _reserved; }
|
||||
HeapWord* base() const { return _reserved.start(); }
|
||||
|
||||
// Memory allocation.
|
||||
HeapWord* mem_allocate(size_t size) override;
|
||||
|
||||
@ -313,12 +313,6 @@ bool PSScavenge::invoke(bool clear_soft_refs) {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "should be at safepoint");
|
||||
assert(Thread::current() == (Thread*)VMThread::vm_thread(), "should be in vm thread");
|
||||
|
||||
// Check for potential problems.
|
||||
if (!should_attempt_scavenge()) {
|
||||
log_info(gc, ergo)("Young-gc might fail so skipping");
|
||||
return false;
|
||||
}
|
||||
|
||||
IsSTWGCActiveMark mark;
|
||||
|
||||
_gc_timer.register_gc_start();
|
||||
@ -336,8 +330,7 @@ bool PSScavenge::invoke(bool clear_soft_refs) {
|
||||
PSOldGen* old_gen = heap->old_gen();
|
||||
PSAdaptiveSizePolicy* size_policy = heap->size_policy();
|
||||
|
||||
assert(young_gen->to_space()->is_empty(),
|
||||
"Attempt to scavenge with live objects in to_space");
|
||||
assert(young_gen->to_space()->is_empty(), "precondition");
|
||||
|
||||
heap->increment_total_collections();
|
||||
|
||||
@ -520,59 +513,6 @@ void PSScavenge::clean_up_failed_promotion() {
|
||||
NOT_PRODUCT(ParallelScavengeHeap::heap()->reset_promotion_should_fail();)
|
||||
}
|
||||
|
||||
bool PSScavenge::should_attempt_scavenge() {
|
||||
const bool ShouldRunYoungGC = true;
|
||||
const bool ShouldRunFullGC = false;
|
||||
|
||||
ParallelScavengeHeap* heap = ParallelScavengeHeap::heap();
|
||||
PSYoungGen* young_gen = heap->young_gen();
|
||||
PSOldGen* old_gen = heap->old_gen();
|
||||
|
||||
if (!young_gen->to_space()->is_empty()) {
|
||||
log_debug(gc, ergo)("To-space is not empty; run full-gc instead.");
|
||||
return ShouldRunFullGC;
|
||||
}
|
||||
|
||||
// Check if the predicted promoted bytes will overflow free space in old-gen.
|
||||
PSAdaptiveSizePolicy* policy = heap->size_policy();
|
||||
|
||||
size_t avg_promoted = (size_t) policy->padded_average_promoted_in_bytes();
|
||||
size_t promotion_estimate = MIN2(avg_promoted, young_gen->used_in_bytes());
|
||||
// Total free size after possible old gen expansion
|
||||
size_t free_in_old_gen_with_expansion = old_gen->max_gen_size() - old_gen->used_in_bytes();
|
||||
|
||||
log_trace(gc, ergo)("average_promoted %zu; padded_average_promoted %zu",
|
||||
(size_t) policy->average_promoted_in_bytes(),
|
||||
(size_t) policy->padded_average_promoted_in_bytes());
|
||||
|
||||
if (promotion_estimate >= free_in_old_gen_with_expansion) {
|
||||
log_debug(gc, ergo)("Run full-gc; predicted promotion size >= max free space in old-gen: %zu >= %zu",
|
||||
promotion_estimate, free_in_old_gen_with_expansion);
|
||||
return ShouldRunFullGC;
|
||||
}
|
||||
|
||||
if (UseAdaptiveSizePolicy) {
|
||||
// Also checking OS has enough free memory to commit and expand old-gen.
|
||||
// Otherwise, the recorded gc-pause-time might be inflated to include time
|
||||
// of OS preparing free memory, resulting in inaccurate young-gen resizing.
|
||||
assert(old_gen->committed().byte_size() >= old_gen->used_in_bytes(), "inv");
|
||||
// Use uint64_t instead of size_t for 32bit compatibility.
|
||||
uint64_t free_mem_in_os;
|
||||
if (os::free_memory(free_mem_in_os)) {
|
||||
size_t actual_free = (size_t)MIN2(old_gen->committed().byte_size() - old_gen->used_in_bytes() + free_mem_in_os,
|
||||
(uint64_t)SIZE_MAX);
|
||||
if (promotion_estimate > actual_free) {
|
||||
log_debug(gc, ergo)("Run full-gc; predicted promotion size > free space in old-gen and OS: %zu > %zu",
|
||||
promotion_estimate, actual_free);
|
||||
return ShouldRunFullGC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No particular reasons to run full-gc, so young-gc.
|
||||
return ShouldRunYoungGC;
|
||||
}
|
||||
|
||||
// Adaptive size policy support.
|
||||
void PSScavenge::set_young_generation_boundary(HeapWord* v) {
|
||||
_young_generation_boundary = v;
|
||||
|
||||
@ -64,8 +64,6 @@ class PSScavenge: AllStatic {
|
||||
|
||||
static void clean_up_failed_promotion();
|
||||
|
||||
static bool should_attempt_scavenge();
|
||||
|
||||
// Private accessors
|
||||
static PSCardTable* card_table() { assert(_card_table != nullptr, "Sanity"); return _card_table; }
|
||||
static const ParallelScavengeTracer* gc_tracer() { return &_gc_tracer; }
|
||||
|
||||
@ -337,11 +337,6 @@ HeapWord* SerialHeap::mem_allocate_work(size_t size, bool is_tlab) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_shutting_down()) {
|
||||
stall_for_vm_shutdown();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Give a warning if we seem to be looping forever.
|
||||
if ((QueuedAllocationWarningCount > 0) &&
|
||||
(try_count % QueuedAllocationWarningCount == 0)) {
|
||||
|
||||
@ -22,12 +22,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cppstdlib/new.hpp"
|
||||
#include "gc/shared/bufferNode.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
|
||||
#include <new>
|
||||
|
||||
BufferNode::AllocatorConfig::AllocatorConfig(size_t size)
|
||||
: _buffer_capacity(size)
|
||||
{
|
||||
|
||||
@ -62,12 +62,14 @@
|
||||
|
||||
class ClassLoaderData;
|
||||
|
||||
bool CollectedHeap::_is_shutting_down = false;
|
||||
|
||||
size_t CollectedHeap::_lab_alignment_reserve = SIZE_MAX;
|
||||
Klass* CollectedHeap::_filler_object_klass = nullptr;
|
||||
size_t CollectedHeap::_filler_array_max_size = 0;
|
||||
size_t CollectedHeap::_stack_chunk_max_size = 0;
|
||||
|
||||
class GCLogMessage : public FormatBuffer<512> {};
|
||||
class GCLogMessage : public FormatBuffer<1024> {};
|
||||
|
||||
template <>
|
||||
void EventLogBase<GCLogMessage>::print(outputStream* st, GCLogMessage& m) {
|
||||
@ -377,8 +379,7 @@ MetaWord* CollectedHeap::satisfy_failed_metadata_allocation(ClassLoaderData* loa
|
||||
word_size,
|
||||
mdtype,
|
||||
gc_count,
|
||||
full_gc_count,
|
||||
GCCause::_metadata_GC_threshold);
|
||||
full_gc_count);
|
||||
|
||||
VMThread::execute(&op);
|
||||
|
||||
@ -386,11 +387,6 @@ MetaWord* CollectedHeap::satisfy_failed_metadata_allocation(ClassLoaderData* loa
|
||||
return op.result();
|
||||
}
|
||||
|
||||
if (is_shutting_down()) {
|
||||
stall_for_vm_shutdown();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
loop_count++;
|
||||
if ((QueuedAllocationWarningCount > 0) &&
|
||||
(loop_count % QueuedAllocationWarningCount == 0)) {
|
||||
@ -605,30 +601,20 @@ void CollectedHeap::post_initialize() {
|
||||
initialize_serviceability();
|
||||
}
|
||||
|
||||
bool CollectedHeap::is_shutting_down() const {
|
||||
return Universe::is_shutting_down();
|
||||
bool CollectedHeap::is_shutting_down() {
|
||||
assert(Heap_lock->owned_by_self(), "Protected by this lock");
|
||||
return _is_shutting_down;
|
||||
}
|
||||
|
||||
void CollectedHeap::stall_for_vm_shutdown() {
|
||||
assert(is_shutting_down(), "Precondition");
|
||||
// Stall the thread (2 seconds) instead of an indefinite wait to avoid deadlock
|
||||
// if the VM shutdown triggers a GC.
|
||||
// The 2-seconds sleep is:
|
||||
// - long enough to keep daemon threads stalled, while the shutdown
|
||||
// sequence completes in the common case.
|
||||
// - short enough to avoid excessive stall time if the shutdown itself
|
||||
// triggers a GC.
|
||||
JavaThread::current()->sleep(2 * MILLIUNITS);
|
||||
void CollectedHeap::initiate_shutdown() {
|
||||
{
|
||||
// Acquire the Heap_lock to synchronize with VM_Heap_Sync_Operations,
|
||||
// which may depend on the value of _is_shutting_down flag.
|
||||
MutexLocker hl(Heap_lock);
|
||||
_is_shutting_down = true;
|
||||
}
|
||||
|
||||
ResourceMark rm;
|
||||
log_warning(gc, alloc)("%s: Stall for VM-Shutdown timed out; allocation may fail with OOME", Thread::current()->name());
|
||||
}
|
||||
|
||||
void CollectedHeap::before_exit() {
|
||||
print_tracing_info();
|
||||
|
||||
// Stop any on-going concurrent work and prepare for exit.
|
||||
stop();
|
||||
}
|
||||
|
||||
size_t CollectedHeap::bootstrap_max_memory() const {
|
||||
|
||||
@ -96,6 +96,8 @@ class CollectedHeap : public CHeapObj<mtGC> {
|
||||
friend class MemAllocator;
|
||||
|
||||
private:
|
||||
static bool _is_shutting_down;
|
||||
|
||||
GCHeapLog* _heap_log;
|
||||
GCMetaspaceLog* _metaspace_log;
|
||||
|
||||
@ -209,11 +211,10 @@ protected:
|
||||
// Default implementation does nothing.
|
||||
virtual void print_tracing_info() const = 0;
|
||||
|
||||
public:
|
||||
// Stop any onging concurrent work and prepare for exit.
|
||||
virtual void stop() = 0;
|
||||
|
||||
public:
|
||||
|
||||
static inline size_t filler_array_max_size() {
|
||||
return _filler_array_max_size;
|
||||
}
|
||||
@ -245,14 +246,9 @@ protected:
|
||||
// This is the correct place to place such initialization methods.
|
||||
virtual void post_initialize();
|
||||
|
||||
bool is_shutting_down() const;
|
||||
static bool is_shutting_down();
|
||||
|
||||
// If the VM is shutting down, we may have skipped VM_CollectForAllocation.
|
||||
// In this case, stall the allocation request briefly in the hope that
|
||||
// the VM shutdown completes before the allocation request returns.
|
||||
void stall_for_vm_shutdown();
|
||||
|
||||
void before_exit();
|
||||
void initiate_shutdown();
|
||||
|
||||
// Stop and resume concurrent GC threads interfering with safepoint operations
|
||||
virtual void safepoint_synchronize_begin() {}
|
||||
|
||||
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