Merge branch 'master' into JDK-8367530-3

This commit is contained in:
Jan Lahoda 2025-11-25 18:24:30 +01:00
commit cfa2efafd4
734 changed files with 38695 additions and 14533 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) \

View File

@ -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],

View File

@ -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)
])

View File

@ -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.

View File

@ -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],

View File

@ -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

View File

@ -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)) \

View File

@ -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)), \

View File

@ -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, \

View File

@ -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

View File

@ -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(".", "/");

View File

@ -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), \

View File

@ -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)));

View File

@ -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) {

View File

@ -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:

View File

@ -879,7 +879,6 @@ void ZBarrierSetAssembler::patch_barrier_relocation(address addr, int format) {
ShouldNotReachHere();
}
OrderAccess::fence();
ICache::invalidate_word((address)patch_addr);
}

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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,

View File

@ -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) {

View File

@ -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;
}

View File

@ -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:

View File

@ -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; }

View File

@ -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

View File

@ -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;
}

View File

@ -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()

View File

@ -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);

View File

@ -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;
}
//=============================================================================

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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
};

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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, ...) {

View File

@ -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) {

View File

@ -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; }

View File

@ -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

View File

@ -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;
}
//=============================================================================

View File

@ -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;

View File

@ -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) {

View File

@ -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:

View File

@ -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) {

View File

@ -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

View File

@ -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) {

View File

@ -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) {
}

View File

@ -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) {
}

View File

@ -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 */

View File

@ -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;

View File

@ -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; }

View File

@ -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);

View File

@ -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); }

View File

@ -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.");

View File

@ -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().

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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;

View File

@ -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 {

View File

@ -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);

View File

@ -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.

View File

@ -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(),

View File

@ -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()) {

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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(); }

View File

@ -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

View File

@ -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

View File

@ -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;

View 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

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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

View File

@ -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());
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}
}
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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; }

View File

@ -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)) {

View File

@ -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)
{

View File

@ -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 {

View File

@ -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