mirror of
https://github.com/openjdk/jdk.git
synced 2026-08-03 14:47:03 +00:00
Merge branch 'JDK-8348611' into JDK-8344159.
This commit is contained in:
commit
a76e2e3454
2
.github/actions/build-jtreg/action.yml
vendored
2
.github/actions/build-jtreg/action.yml
vendored
@ -65,4 +65,4 @@ runs:
|
||||
with:
|
||||
name: bundles-jtreg-${{ steps.version.outputs.value }}
|
||||
path: jtreg/installed
|
||||
retention-days: 1
|
||||
retention-days: 5
|
||||
|
||||
8
.github/actions/get-msys2/action.yml
vendored
8
.github/actions/get-msys2/action.yml
vendored
@ -30,15 +30,15 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: 'Install MSYS2'
|
||||
uses: msys2/setup-msys2@v2.22.0
|
||||
id: msys2
|
||||
uses: msys2/setup-msys2@v2.28.0
|
||||
with:
|
||||
install: 'autoconf tar unzip zip make'
|
||||
path-type: minimal
|
||||
location: ${{ runner.tool_cache }}/msys2
|
||||
release: false
|
||||
|
||||
# We can't run bash until this is completed, so stick with pwsh
|
||||
- name: 'Set MSYS2 path'
|
||||
run: |
|
||||
# Prepend msys2/msys64/usr/bin to the PATH
|
||||
echo "$env:RUNNER_TOOL_CACHE/msys2/msys64/usr/bin" >> $env:GITHUB_PATH
|
||||
echo "${{ steps.msys2.outputs.msys2-location }}/usr/bin" >> $env:GITHUB_PATH
|
||||
shell: pwsh
|
||||
|
||||
2
.github/actions/upload-bundles/action.yml
vendored
2
.github/actions/upload-bundles/action.yml
vendored
@ -91,5 +91,5 @@ runs:
|
||||
with:
|
||||
name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }}${{ inputs.static-suffix }}${{ inputs.bundle-suffix }}
|
||||
path: bundles
|
||||
retention-days: 1
|
||||
retention-days: 5
|
||||
if: steps.bundles.outputs.bundles-found == 'true'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -78,7 +78,6 @@ src/jdk.jdi : jdk/src/jdk.jdi
|
||||
src/jdk.jdwp.agent : jdk/src/jdk.jdwp.agent
|
||||
src/jdk.jlink : jdk/src/jdk.jlink
|
||||
src/jdk.jshell : langtools/src/jdk.jshell
|
||||
src/jdk.jsobject : jdk/src/jdk.jsobject
|
||||
src/jdk.jstatd : jdk/src/jdk.jstatd
|
||||
src/jdk.localedata : jdk/src/jdk.localedata
|
||||
src/jdk.management : jdk/src/jdk.management
|
||||
|
||||
@ -736,8 +736,15 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP],
|
||||
$1_CFLAGS_CPU_JVM="${$1_CFLAGS_CPU_JVM} -mminimal-toc"
|
||||
elif test "x$FLAGS_CPU" = xppc64le; then
|
||||
# Little endian machine uses ELFv2 ABI.
|
||||
# Use Power8, this is the first CPU to support PPC64 LE with ELFv2 ABI.
|
||||
$1_CFLAGS_CPU="-mcpu=power8 -mtune=power10"
|
||||
# Use Power8 for target cpu, this is the first CPU to support PPC64 LE with ELFv2 ABI.
|
||||
# Use Power10 for tuning target, this is supported by gcc >= 10
|
||||
POWER_TUNE_VERSION="-mtune=power10"
|
||||
FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${POWER_TUNE_VERSION}],
|
||||
IF_FALSE: [
|
||||
POWER_TUNE_VERSION="-mtune=power8"
|
||||
]
|
||||
)
|
||||
$1_CFLAGS_CPU="-mcpu=power8 ${POWER_TUNE_VERSION}"
|
||||
$1_CFLAGS_CPU_JVM="${$1_CFLAGS_CPU_JVM} -DABI_ELFv2"
|
||||
fi
|
||||
elif test "x$FLAGS_CPU" = xs390x; then
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -51,7 +51,6 @@ DOCS_MODULES= \
|
||||
jdk.jdwp.agent \
|
||||
jdk.jfr \
|
||||
jdk.jlink \
|
||||
jdk.jsobject \
|
||||
jdk.jshell \
|
||||
jdk.jstatd \
|
||||
jdk.localedata \
|
||||
|
||||
@ -62,7 +62,6 @@ UPGRADEABLE_PLATFORM_MODULES= \
|
||||
java.compiler \
|
||||
jdk.graal.compiler \
|
||||
jdk.graal.compiler.management \
|
||||
jdk.jsobject \
|
||||
#
|
||||
|
||||
PLATFORM_MODULES= \
|
||||
|
||||
@ -2362,17 +2362,34 @@ int Matcher::max_vector_size(const BasicType bt) {
|
||||
}
|
||||
|
||||
int Matcher::min_vector_size(const BasicType bt) {
|
||||
int max_size = max_vector_size(bt);
|
||||
// Limit the min vector size to 8 bytes.
|
||||
int size = 8 / type2aelembytes(bt);
|
||||
if (bt == T_BYTE) {
|
||||
// To support vector api shuffle/rearrange.
|
||||
size = 4;
|
||||
} else if (bt == T_BOOLEAN) {
|
||||
// To support vector api load/store mask.
|
||||
size = 2;
|
||||
// Usually, the shortest vector length supported by AArch64 ISA and
|
||||
// Vector API species is 64 bits. However, we allow 32-bit or 16-bit
|
||||
// vectors in a few special cases.
|
||||
int size;
|
||||
switch(bt) {
|
||||
case T_BOOLEAN:
|
||||
// Load/store a vector mask with only 2 elements for vector types
|
||||
// such as "2I/2F/2L/2D".
|
||||
size = 2;
|
||||
break;
|
||||
case T_BYTE:
|
||||
// Generate a "4B" vector, to support vector cast between "8B/16B"
|
||||
// and "4S/4I/4L/4F/4D".
|
||||
size = 4;
|
||||
break;
|
||||
case T_SHORT:
|
||||
// Generate a "2S" vector, to support vector cast between "4S/8S"
|
||||
// and "2I/2L/2F/2D".
|
||||
size = 2;
|
||||
break;
|
||||
default:
|
||||
// Limit the min vector length to 64-bit.
|
||||
size = 8 / type2aelembytes(bt);
|
||||
// The number of elements in a vector should be at least 2.
|
||||
size = MAX2(size, 2);
|
||||
}
|
||||
if (size < 2) size = 2;
|
||||
|
||||
int max_size = max_vector_size(bt);
|
||||
return MIN2(size, max_size);
|
||||
}
|
||||
|
||||
@ -3450,10 +3467,6 @@ encode %{
|
||||
__ mov(dst_reg, (uint64_t)1);
|
||||
%}
|
||||
|
||||
enc_class aarch64_enc_mov_byte_map_base(iRegP dst, immByteMapBase src) %{
|
||||
__ load_byte_map_base($dst$$Register);
|
||||
%}
|
||||
|
||||
enc_class aarch64_enc_mov_n(iRegN dst, immN src) %{
|
||||
Register dst_reg = as_Register($dst$$reg);
|
||||
address con = (address)$src$$constant;
|
||||
@ -4554,20 +4567,6 @@ operand immP_1()
|
||||
interface(CONST_INTER);
|
||||
%}
|
||||
|
||||
// Card Table Byte Map Base
|
||||
operand immByteMapBase()
|
||||
%{
|
||||
// Get base of card map
|
||||
predicate(BarrierSet::barrier_set()->is_a(BarrierSet::CardTableBarrierSet) &&
|
||||
SHENANDOAHGC_ONLY(!BarrierSet::barrier_set()->is_a(BarrierSet::ShenandoahBarrierSet) &&)
|
||||
(CardTable::CardValue*)n->get_ptr() == ((CardTableBarrierSet*)(BarrierSet::barrier_set()))->card_table()->byte_map_base());
|
||||
match(ConP);
|
||||
|
||||
op_cost(0);
|
||||
format %{ %}
|
||||
interface(CONST_INTER);
|
||||
%}
|
||||
|
||||
// Float and Double operands
|
||||
// Double Immediate
|
||||
operand immD()
|
||||
@ -6854,20 +6853,6 @@ instruct loadConP1(iRegPNoSp dst, immP_1 con)
|
||||
ins_pipe(ialu_imm);
|
||||
%}
|
||||
|
||||
// Load Byte Map Base Constant
|
||||
|
||||
instruct loadByteMapBase(iRegPNoSp dst, immByteMapBase con)
|
||||
%{
|
||||
match(Set dst con);
|
||||
|
||||
ins_cost(INSN_COST);
|
||||
format %{ "adr $dst, $con\t# Byte Map Base" %}
|
||||
|
||||
ins_encode(aarch64_enc_mov_byte_map_base(dst, con));
|
||||
|
||||
ins_pipe(ialu_imm);
|
||||
%}
|
||||
|
||||
// Load Narrow Pointer Constant
|
||||
|
||||
instruct loadConN(iRegNNoSp dst, immN con)
|
||||
|
||||
@ -131,7 +131,7 @@ source %{
|
||||
// These operations are not profitable to be vectorized on NEON, because no direct
|
||||
// NEON instructions support them. But the match rule support for them is profitable for
|
||||
// Vector API intrinsics.
|
||||
if ((opcode == Op_VectorCastD2X && bt == T_INT) ||
|
||||
if ((opcode == Op_VectorCastD2X && (bt == T_INT || bt == T_SHORT)) ||
|
||||
(opcode == Op_VectorCastL2X && bt == T_FLOAT) ||
|
||||
(opcode == Op_CountLeadingZerosV && bt == T_LONG) ||
|
||||
(opcode == Op_CountTrailingZerosV && bt == T_LONG) ||
|
||||
@ -189,6 +189,18 @@ source %{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Op_AddReductionVI:
|
||||
case Op_AndReductionV:
|
||||
case Op_OrReductionV:
|
||||
case Op_XorReductionV:
|
||||
case Op_MinReductionV:
|
||||
case Op_MaxReductionV:
|
||||
// Reductions with less than 8 bytes vector length are
|
||||
// not supported.
|
||||
if (length_in_bytes < 8) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Op_MulReductionVD:
|
||||
case Op_MulReductionVF:
|
||||
case Op_MulReductionVI:
|
||||
@ -4244,8 +4256,8 @@ instruct vzeroExtStoX(vReg dst, vReg src) %{
|
||||
assert(bt == T_INT || bt == T_LONG, "must be");
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
if (VM_Version::use_neon_for_vector(length_in_bytes)) {
|
||||
// 4S to 4I
|
||||
__ neon_vector_extend($dst$$FloatRegister, T_INT, length_in_bytes,
|
||||
// 2S to 2I/2L, 4S to 4I
|
||||
__ neon_vector_extend($dst$$FloatRegister, bt, length_in_bytes,
|
||||
$src$$FloatRegister, T_SHORT, /* is_unsigned */ true);
|
||||
} else {
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
@ -4265,11 +4277,11 @@ instruct vzeroExtItoX(vReg dst, vReg src) %{
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
if (VM_Version::use_neon_for_vector(length_in_bytes)) {
|
||||
// 2I to 2L
|
||||
__ neon_vector_extend($dst$$FloatRegister, T_LONG, length_in_bytes,
|
||||
__ neon_vector_extend($dst$$FloatRegister, bt, length_in_bytes,
|
||||
$src$$FloatRegister, T_INT, /* is_unsigned */ true);
|
||||
} else {
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
__ sve_vector_extend($dst$$FloatRegister, __ D,
|
||||
__ sve_vector_extend($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$src$$FloatRegister, __ S, /* is_unsigned */ true);
|
||||
}
|
||||
%}
|
||||
@ -4343,11 +4355,15 @@ instruct vcvtStoX_extend(vReg dst, vReg src) %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
if (VM_Version::use_neon_for_vector(length_in_bytes)) {
|
||||
// 4S to 4I/4F
|
||||
__ neon_vector_extend($dst$$FloatRegister, T_INT, length_in_bytes,
|
||||
$src$$FloatRegister, T_SHORT);
|
||||
if (bt == T_FLOAT) {
|
||||
__ scvtfv(__ T4S, $dst$$FloatRegister, $dst$$FloatRegister);
|
||||
if (is_floating_point_type(bt)) {
|
||||
// 2S to 2F/2D, 4S to 4F
|
||||
__ neon_vector_extend($dst$$FloatRegister, bt == T_FLOAT ? T_INT : T_LONG,
|
||||
length_in_bytes, $src$$FloatRegister, T_SHORT);
|
||||
__ scvtfv(get_arrangement(this), $dst$$FloatRegister, $dst$$FloatRegister);
|
||||
} else {
|
||||
// 2S to 2I/2L, 4S to 4I
|
||||
__ neon_vector_extend($dst$$FloatRegister, bt, length_in_bytes,
|
||||
$src$$FloatRegister, T_SHORT);
|
||||
}
|
||||
} else {
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
@ -4371,7 +4387,7 @@ instruct vcvtItoX_narrow_neon(vReg dst, vReg src) %{
|
||||
effect(TEMP_DEF dst);
|
||||
format %{ "vcvtItoX_narrow_neon $dst, $src" %}
|
||||
ins_encode %{
|
||||
// 4I to 4B/4S
|
||||
// 2I to 2S, 4I to 4B/4S
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src);
|
||||
__ neon_vector_narrow($dst$$FloatRegister, bt,
|
||||
@ -4434,28 +4450,29 @@ instruct vcvtItoX(vReg dst, vReg src) %{
|
||||
|
||||
// VectorCastL2X
|
||||
|
||||
instruct vcvtLtoI_neon(vReg dst, vReg src) %{
|
||||
predicate(Matcher::vector_element_basic_type(n) == T_INT &&
|
||||
instruct vcvtLtoX_narrow_neon(vReg dst, vReg src) %{
|
||||
predicate((Matcher::vector_element_basic_type(n) == T_INT ||
|
||||
Matcher::vector_element_basic_type(n) == T_SHORT) &&
|
||||
VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(1))));
|
||||
match(Set dst (VectorCastL2X src));
|
||||
format %{ "vcvtLtoI_neon $dst, $src" %}
|
||||
format %{ "vcvtLtoX_narrow_neon $dst, $src" %}
|
||||
ins_encode %{
|
||||
// 2L to 2I
|
||||
// 2L to 2S/2I
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src);
|
||||
__ neon_vector_narrow($dst$$FloatRegister, T_INT,
|
||||
__ neon_vector_narrow($dst$$FloatRegister, bt,
|
||||
$src$$FloatRegister, T_LONG, length_in_bytes);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vcvtLtoI_sve(vReg dst, vReg src, vReg tmp) %{
|
||||
predicate((Matcher::vector_element_basic_type(n) == T_INT &&
|
||||
!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(1)))) ||
|
||||
Matcher::vector_element_basic_type(n) == T_BYTE ||
|
||||
Matcher::vector_element_basic_type(n) == T_SHORT);
|
||||
instruct vcvtLtoX_narrow_sve(vReg dst, vReg src, vReg tmp) %{
|
||||
predicate(!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(1))) &&
|
||||
!is_floating_point_type(Matcher::vector_element_basic_type(n)) &&
|
||||
type2aelembytes(Matcher::vector_element_basic_type(n)) <= 4);
|
||||
match(Set dst (VectorCastL2X src));
|
||||
effect(TEMP_DEF dst, TEMP tmp);
|
||||
format %{ "vcvtLtoI_sve $dst, $src\t# KILL $tmp" %}
|
||||
format %{ "vcvtLtoX_narrow_sve $dst, $src\t# KILL $tmp" %}
|
||||
ins_encode %{
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
@ -4521,10 +4538,11 @@ instruct vcvtFtoX_narrow_neon(vReg dst, vReg src) %{
|
||||
effect(TEMP_DEF dst);
|
||||
format %{ "vcvtFtoX_narrow_neon $dst, $src" %}
|
||||
ins_encode %{
|
||||
// 4F to 4B/4S
|
||||
// 2F to 2S, 4F to 4B/4S
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src);
|
||||
__ fcvtzs($dst$$FloatRegister, __ T4S, $src$$FloatRegister);
|
||||
__ fcvtzs($dst$$FloatRegister, length_in_bytes == 16 ? __ T4S : __ T2S,
|
||||
$src$$FloatRegister);
|
||||
__ neon_vector_narrow($dst$$FloatRegister, bt,
|
||||
$dst$$FloatRegister, T_INT, length_in_bytes);
|
||||
%}
|
||||
@ -4590,12 +4608,14 @@ instruct vcvtFtoX(vReg dst, vReg src) %{
|
||||
// VectorCastD2X
|
||||
|
||||
instruct vcvtDtoI_neon(vReg dst, vReg src) %{
|
||||
predicate(UseSVE == 0 && Matcher::vector_element_basic_type(n) == T_INT);
|
||||
predicate(UseSVE == 0 &&
|
||||
(Matcher::vector_element_basic_type(n) == T_INT ||
|
||||
Matcher::vector_element_basic_type(n) == T_SHORT));
|
||||
match(Set dst (VectorCastD2X src));
|
||||
effect(TEMP_DEF dst);
|
||||
format %{ "vcvtDtoI_neon $dst, $src\t# 2D to 2I" %}
|
||||
format %{ "vcvtDtoI_neon $dst, $src\t# 2D to 2S/2I" %}
|
||||
ins_encode %{
|
||||
// 2D to 2I
|
||||
// 2D to 2S/2I
|
||||
__ ins($dst$$FloatRegister, __ D, $src$$FloatRegister, 0, 1);
|
||||
// We can't use fcvtzs(vector, integer) instruction here because we need
|
||||
// saturation arithmetic. See JDK-8276151.
|
||||
@ -4603,6 +4623,10 @@ instruct vcvtDtoI_neon(vReg dst, vReg src) %{
|
||||
__ fcvtzdw(rscratch2, $dst$$FloatRegister);
|
||||
__ fmovs($dst$$FloatRegister, rscratch1);
|
||||
__ mov($dst$$FloatRegister, __ S, 1, rscratch2);
|
||||
if (Matcher::vector_element_basic_type(this) == T_SHORT) {
|
||||
__ neon_vector_narrow($dst$$FloatRegister, T_SHORT,
|
||||
$dst$$FloatRegister, T_INT, 8);
|
||||
}
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -4676,7 +4700,7 @@ instruct vcvtHFtoF(vReg dst, vReg src) %{
|
||||
ins_encode %{
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
if (VM_Version::use_neon_for_vector(length_in_bytes)) {
|
||||
// 4HF to 4F
|
||||
// 2HF to 2F, 4HF to 4F
|
||||
__ fcvtl($dst$$FloatRegister, __ T4S, $src$$FloatRegister, __ T4H);
|
||||
} else {
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
@ -4692,9 +4716,9 @@ instruct vcvtHFtoF(vReg dst, vReg src) %{
|
||||
instruct vcvtFtoHF_neon(vReg dst, vReg src) %{
|
||||
predicate(VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(1))));
|
||||
match(Set dst (VectorCastF2HF src));
|
||||
format %{ "vcvtFtoHF_neon $dst, $src\t# 4F to 4HF" %}
|
||||
format %{ "vcvtFtoHF_neon $dst, $src\t# 2F/4F to 2HF/4HF" %}
|
||||
ins_encode %{
|
||||
// 4F to 4HF
|
||||
// 2F to 2HF, 4F to 4HF
|
||||
__ fcvtn($dst$$FloatRegister, __ T4H, $src$$FloatRegister, __ T4S);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -6396,14 +6420,12 @@ instruct vpopcountI(vReg dst, vReg src) %{
|
||||
} else {
|
||||
assert(bt == T_SHORT || bt == T_INT, "unsupported");
|
||||
if (UseSVE == 0) {
|
||||
assert(length_in_bytes == 8 || length_in_bytes == 16, "unsupported");
|
||||
__ cnt($dst$$FloatRegister, length_in_bytes == 16 ? __ T16B : __ T8B,
|
||||
$src$$FloatRegister);
|
||||
__ uaddlp($dst$$FloatRegister, length_in_bytes == 16 ? __ T16B : __ T8B,
|
||||
$dst$$FloatRegister);
|
||||
assert(length_in_bytes <= 16, "unsupported");
|
||||
bool isQ = length_in_bytes == 16;
|
||||
__ cnt($dst$$FloatRegister, isQ ? __ T16B : __ T8B, $src$$FloatRegister);
|
||||
__ uaddlp($dst$$FloatRegister, isQ ? __ T16B : __ T8B, $dst$$FloatRegister);
|
||||
if (bt == T_INT) {
|
||||
__ uaddlp($dst$$FloatRegister, length_in_bytes == 16 ? __ T8H : __ T4H,
|
||||
$dst$$FloatRegister);
|
||||
__ uaddlp($dst$$FloatRegister, isQ ? __ T8H : __ T4H, $dst$$FloatRegister);
|
||||
}
|
||||
} else {
|
||||
__ sve_cnt($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
@ -6465,7 +6487,7 @@ instruct vblend_neon(vReg dst, vReg src1, vReg src2) %{
|
||||
format %{ "vblend_neon $dst, $src1, $src2" %}
|
||||
ins_encode %{
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
assert(length_in_bytes == 8 || length_in_bytes == 16, "must be");
|
||||
assert(length_in_bytes <= 16, "must be");
|
||||
__ bsl($dst$$FloatRegister, length_in_bytes == 16 ? __ T16B : __ T8B,
|
||||
$src2$$FloatRegister, $src1$$FloatRegister);
|
||||
%}
|
||||
@ -6852,7 +6874,7 @@ instruct vcountTrailingZeros(vReg dst, vReg src) %{
|
||||
} else {
|
||||
assert(bt == T_SHORT || bt == T_INT || bt == T_LONG, "unsupported type");
|
||||
if (UseSVE == 0) {
|
||||
assert(length_in_bytes == 8 || length_in_bytes == 16, "unsupported");
|
||||
assert(length_in_bytes <= 16, "unsupported");
|
||||
__ neon_reverse_bits($dst$$FloatRegister, $src$$FloatRegister,
|
||||
bt, /* isQ */ length_in_bytes == 16);
|
||||
if (bt != T_LONG) {
|
||||
@ -6911,7 +6933,7 @@ instruct vreverse(vReg dst, vReg src) %{
|
||||
} else {
|
||||
assert(bt == T_SHORT || bt == T_INT || bt == T_LONG, "unsupported type");
|
||||
if (UseSVE == 0) {
|
||||
assert(length_in_bytes == 8 || length_in_bytes == 16, "unsupported");
|
||||
assert(length_in_bytes <= 16, "unsupported");
|
||||
__ neon_reverse_bits($dst$$FloatRegister, $src$$FloatRegister,
|
||||
bt, /* isQ */ length_in_bytes == 16);
|
||||
} else {
|
||||
@ -6947,7 +6969,7 @@ instruct vreverseBytes(vReg dst, vReg src) %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
if (VM_Version::use_neon_for_vector(length_in_bytes)) {
|
||||
assert(length_in_bytes == 8 || length_in_bytes == 16, "unsupported");
|
||||
assert(length_in_bytes <= 16, "unsupported");
|
||||
if (bt == T_BYTE) {
|
||||
if ($dst$$FloatRegister != $src$$FloatRegister) {
|
||||
__ orr($dst$$FloatRegister, length_in_bytes == 16 ? __ T16B : __ T8B,
|
||||
|
||||
@ -121,7 +121,7 @@ source %{
|
||||
// These operations are not profitable to be vectorized on NEON, because no direct
|
||||
// NEON instructions support them. But the match rule support for them is profitable for
|
||||
// Vector API intrinsics.
|
||||
if ((opcode == Op_VectorCastD2X && bt == T_INT) ||
|
||||
if ((opcode == Op_VectorCastD2X && (bt == T_INT || bt == T_SHORT)) ||
|
||||
(opcode == Op_VectorCastL2X && bt == T_FLOAT) ||
|
||||
(opcode == Op_CountLeadingZerosV && bt == T_LONG) ||
|
||||
(opcode == Op_CountTrailingZerosV && bt == T_LONG) ||
|
||||
@ -179,6 +179,18 @@ source %{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Op_AddReductionVI:
|
||||
case Op_AndReductionV:
|
||||
case Op_OrReductionV:
|
||||
case Op_XorReductionV:
|
||||
case Op_MinReductionV:
|
||||
case Op_MaxReductionV:
|
||||
// Reductions with less than 8 bytes vector length are
|
||||
// not supported.
|
||||
if (length_in_bytes < 8) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Op_MulReductionVD:
|
||||
case Op_MulReductionVF:
|
||||
case Op_MulReductionVI:
|
||||
@ -2502,31 +2514,31 @@ instruct reinterpret_resize_gt128b(vReg dst, vReg src, pReg ptmp, rFlagsReg cr)
|
||||
%}
|
||||
|
||||
// ---------------------------- Vector zero extend --------------------------------
|
||||
dnl VECTOR_ZERO_EXTEND($1, $2, $3, $4, $5 $6, $7, )
|
||||
dnl VECTOR_ZERO_EXTEND(op_name, dst_bt, src_bt, dst_size, src_size, assertion, neon_comment)
|
||||
dnl VECTOR_ZERO_EXTEND($1, $2, $3, $4, $5, )
|
||||
dnl VECTOR_ZERO_EXTEND(op_name, src_bt, src_size, assertion, neon_comment)
|
||||
define(`VECTOR_ZERO_EXTEND', `
|
||||
instruct vzeroExt$1toX(vReg dst, vReg src) %{
|
||||
match(Set dst (VectorUCast`$1'2X src));
|
||||
format %{ "vzeroExt$1toX $dst, $src" %}
|
||||
ins_encode %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
assert($6, "must be");
|
||||
assert($4, "must be");
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
if (VM_Version::use_neon_for_vector(length_in_bytes)) {
|
||||
// $7
|
||||
__ neon_vector_extend($dst$$FloatRegister, $2, length_in_bytes,
|
||||
$src$$FloatRegister, $3, /* is_unsigned */ true);
|
||||
// $5
|
||||
__ neon_vector_extend($dst$$FloatRegister, bt, length_in_bytes,
|
||||
$src$$FloatRegister, $2, /* is_unsigned */ true);
|
||||
} else {
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
__ sve_vector_extend($dst$$FloatRegister, __ $4,
|
||||
$src$$FloatRegister, __ $5, /* is_unsigned */ true);
|
||||
__ sve_vector_extend($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$src$$FloatRegister, __ $3, /* is_unsigned */ true);
|
||||
}
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}')dnl
|
||||
VECTOR_ZERO_EXTEND(B, bt, T_BYTE, elemType_to_regVariant(bt), B, bt == T_SHORT || bt == T_INT || bt == T_LONG, `4B to 4S/4I, 8B to 8S')
|
||||
VECTOR_ZERO_EXTEND(S, T_INT, T_SHORT, elemType_to_regVariant(bt), H, bt == T_INT || bt == T_LONG, `4S to 4I')
|
||||
VECTOR_ZERO_EXTEND(I, T_LONG, T_INT, D, S, bt == T_LONG, `2I to 2L')
|
||||
VECTOR_ZERO_EXTEND(B, T_BYTE, B, bt == T_SHORT || bt == T_INT || bt == T_LONG, `4B to 4S/4I, 8B to 8S')
|
||||
VECTOR_ZERO_EXTEND(S, T_SHORT, H, bt == T_INT || bt == T_LONG, `2S to 2I/2L, 4S to 4I')
|
||||
VECTOR_ZERO_EXTEND(I, T_INT, S, bt == T_LONG, `2I to 2L')
|
||||
|
||||
// ------------------------------ Vector cast ----------------------------------
|
||||
|
||||
@ -2595,11 +2607,15 @@ instruct vcvtStoX_extend(vReg dst, vReg src) %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
if (VM_Version::use_neon_for_vector(length_in_bytes)) {
|
||||
// 4S to 4I/4F
|
||||
__ neon_vector_extend($dst$$FloatRegister, T_INT, length_in_bytes,
|
||||
$src$$FloatRegister, T_SHORT);
|
||||
if (bt == T_FLOAT) {
|
||||
__ scvtfv(__ T4S, $dst$$FloatRegister, $dst$$FloatRegister);
|
||||
if (is_floating_point_type(bt)) {
|
||||
// 2S to 2F/2D, 4S to 4F
|
||||
__ neon_vector_extend($dst$$FloatRegister, bt == T_FLOAT ? T_INT : T_LONG,
|
||||
length_in_bytes, $src$$FloatRegister, T_SHORT);
|
||||
__ scvtfv(get_arrangement(this), $dst$$FloatRegister, $dst$$FloatRegister);
|
||||
} else {
|
||||
// 2S to 2I/2L, 4S to 4I
|
||||
__ neon_vector_extend($dst$$FloatRegister, bt, length_in_bytes,
|
||||
$src$$FloatRegister, T_SHORT);
|
||||
}
|
||||
} else {
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
@ -2623,7 +2639,7 @@ instruct vcvtItoX_narrow_neon(vReg dst, vReg src) %{
|
||||
effect(TEMP_DEF dst);
|
||||
format %{ "vcvtItoX_narrow_neon $dst, $src" %}
|
||||
ins_encode %{
|
||||
// 4I to 4B/4S
|
||||
// 2I to 2S, 4I to 4B/4S
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src);
|
||||
__ neon_vector_narrow($dst$$FloatRegister, bt,
|
||||
@ -2686,28 +2702,29 @@ instruct vcvtItoX(vReg dst, vReg src) %{
|
||||
|
||||
// VectorCastL2X
|
||||
|
||||
instruct vcvtLtoI_neon(vReg dst, vReg src) %{
|
||||
predicate(Matcher::vector_element_basic_type(n) == T_INT &&
|
||||
instruct vcvtLtoX_narrow_neon(vReg dst, vReg src) %{
|
||||
predicate((Matcher::vector_element_basic_type(n) == T_INT ||
|
||||
Matcher::vector_element_basic_type(n) == T_SHORT) &&
|
||||
VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(1))));
|
||||
match(Set dst (VectorCastL2X src));
|
||||
format %{ "vcvtLtoI_neon $dst, $src" %}
|
||||
format %{ "vcvtLtoX_narrow_neon $dst, $src" %}
|
||||
ins_encode %{
|
||||
// 2L to 2I
|
||||
// 2L to 2S/2I
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src);
|
||||
__ neon_vector_narrow($dst$$FloatRegister, T_INT,
|
||||
__ neon_vector_narrow($dst$$FloatRegister, bt,
|
||||
$src$$FloatRegister, T_LONG, length_in_bytes);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vcvtLtoI_sve(vReg dst, vReg src, vReg tmp) %{
|
||||
predicate((Matcher::vector_element_basic_type(n) == T_INT &&
|
||||
!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(1)))) ||
|
||||
Matcher::vector_element_basic_type(n) == T_BYTE ||
|
||||
Matcher::vector_element_basic_type(n) == T_SHORT);
|
||||
instruct vcvtLtoX_narrow_sve(vReg dst, vReg src, vReg tmp) %{
|
||||
predicate(!VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(1))) &&
|
||||
!is_floating_point_type(Matcher::vector_element_basic_type(n)) &&
|
||||
type2aelembytes(Matcher::vector_element_basic_type(n)) <= 4);
|
||||
match(Set dst (VectorCastL2X src));
|
||||
effect(TEMP_DEF dst, TEMP tmp);
|
||||
format %{ "vcvtLtoI_sve $dst, $src\t# KILL $tmp" %}
|
||||
format %{ "vcvtLtoX_narrow_sve $dst, $src\t# KILL $tmp" %}
|
||||
ins_encode %{
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
@ -2773,10 +2790,11 @@ instruct vcvtFtoX_narrow_neon(vReg dst, vReg src) %{
|
||||
effect(TEMP_DEF dst);
|
||||
format %{ "vcvtFtoX_narrow_neon $dst, $src" %}
|
||||
ins_encode %{
|
||||
// 4F to 4B/4S
|
||||
// 2F to 2S, 4F to 4B/4S
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this, $src);
|
||||
__ fcvtzs($dst$$FloatRegister, __ T4S, $src$$FloatRegister);
|
||||
__ fcvtzs($dst$$FloatRegister, length_in_bytes == 16 ? __ T4S : __ T2S,
|
||||
$src$$FloatRegister);
|
||||
__ neon_vector_narrow($dst$$FloatRegister, bt,
|
||||
$dst$$FloatRegister, T_INT, length_in_bytes);
|
||||
%}
|
||||
@ -2842,12 +2860,14 @@ instruct vcvtFtoX(vReg dst, vReg src) %{
|
||||
// VectorCastD2X
|
||||
|
||||
instruct vcvtDtoI_neon(vReg dst, vReg src) %{
|
||||
predicate(UseSVE == 0 && Matcher::vector_element_basic_type(n) == T_INT);
|
||||
predicate(UseSVE == 0 &&
|
||||
(Matcher::vector_element_basic_type(n) == T_INT ||
|
||||
Matcher::vector_element_basic_type(n) == T_SHORT));
|
||||
match(Set dst (VectorCastD2X src));
|
||||
effect(TEMP_DEF dst);
|
||||
format %{ "vcvtDtoI_neon $dst, $src\t# 2D to 2I" %}
|
||||
format %{ "vcvtDtoI_neon $dst, $src\t# 2D to 2S/2I" %}
|
||||
ins_encode %{
|
||||
// 2D to 2I
|
||||
// 2D to 2S/2I
|
||||
__ ins($dst$$FloatRegister, __ D, $src$$FloatRegister, 0, 1);
|
||||
// We can't use fcvtzs(vector, integer) instruction here because we need
|
||||
// saturation arithmetic. See JDK-8276151.
|
||||
@ -2855,6 +2875,10 @@ instruct vcvtDtoI_neon(vReg dst, vReg src) %{
|
||||
__ fcvtzdw(rscratch2, $dst$$FloatRegister);
|
||||
__ fmovs($dst$$FloatRegister, rscratch1);
|
||||
__ mov($dst$$FloatRegister, __ S, 1, rscratch2);
|
||||
if (Matcher::vector_element_basic_type(this) == T_SHORT) {
|
||||
__ neon_vector_narrow($dst$$FloatRegister, T_SHORT,
|
||||
$dst$$FloatRegister, T_INT, 8);
|
||||
}
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -2928,7 +2952,7 @@ instruct vcvtHFtoF(vReg dst, vReg src) %{
|
||||
ins_encode %{
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
if (VM_Version::use_neon_for_vector(length_in_bytes)) {
|
||||
// 4HF to 4F
|
||||
// 2HF to 2F, 4HF to 4F
|
||||
__ fcvtl($dst$$FloatRegister, __ T4S, $src$$FloatRegister, __ T4H);
|
||||
} else {
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
@ -2944,9 +2968,9 @@ instruct vcvtHFtoF(vReg dst, vReg src) %{
|
||||
instruct vcvtFtoHF_neon(vReg dst, vReg src) %{
|
||||
predicate(VM_Version::use_neon_for_vector(Matcher::vector_length_in_bytes(n->in(1))));
|
||||
match(Set dst (VectorCastF2HF src));
|
||||
format %{ "vcvtFtoHF_neon $dst, $src\t# 4F to 4HF" %}
|
||||
format %{ "vcvtFtoHF_neon $dst, $src\t# 2F/4F to 2HF/4HF" %}
|
||||
ins_encode %{
|
||||
// 4F to 4HF
|
||||
// 2F to 2HF, 4F to 4HF
|
||||
__ fcvtn($dst$$FloatRegister, __ T4H, $src$$FloatRegister, __ T4S);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -4417,14 +4441,12 @@ instruct vpopcountI(vReg dst, vReg src) %{
|
||||
} else {
|
||||
assert(bt == T_SHORT || bt == T_INT, "unsupported");
|
||||
if (UseSVE == 0) {
|
||||
assert(length_in_bytes == 8 || length_in_bytes == 16, "unsupported");
|
||||
__ cnt($dst$$FloatRegister, length_in_bytes == 16 ? __ T16B : __ T8B,
|
||||
$src$$FloatRegister);
|
||||
__ uaddlp($dst$$FloatRegister, length_in_bytes == 16 ? __ T16B : __ T8B,
|
||||
$dst$$FloatRegister);
|
||||
assert(length_in_bytes <= 16, "unsupported");
|
||||
bool isQ = length_in_bytes == 16;
|
||||
__ cnt($dst$$FloatRegister, isQ ? __ T16B : __ T8B, $src$$FloatRegister);
|
||||
__ uaddlp($dst$$FloatRegister, isQ ? __ T16B : __ T8B, $dst$$FloatRegister);
|
||||
if (bt == T_INT) {
|
||||
__ uaddlp($dst$$FloatRegister, length_in_bytes == 16 ? __ T8H : __ T4H,
|
||||
$dst$$FloatRegister);
|
||||
__ uaddlp($dst$$FloatRegister, isQ ? __ T8H : __ T4H, $dst$$FloatRegister);
|
||||
}
|
||||
} else {
|
||||
__ sve_cnt($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
@ -4475,7 +4497,7 @@ instruct vblend_neon(vReg dst, vReg src1, vReg src2) %{
|
||||
format %{ "vblend_neon $dst, $src1, $src2" %}
|
||||
ins_encode %{
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
assert(length_in_bytes == 8 || length_in_bytes == 16, "must be");
|
||||
assert(length_in_bytes <= 16, "must be");
|
||||
__ bsl($dst$$FloatRegister, length_in_bytes == 16 ? __ T16B : __ T8B,
|
||||
$src2$$FloatRegister, $src1$$FloatRegister);
|
||||
%}
|
||||
@ -4851,7 +4873,7 @@ instruct vcountTrailingZeros(vReg dst, vReg src) %{
|
||||
} else {
|
||||
assert(bt == T_SHORT || bt == T_INT || bt == T_LONG, "unsupported type");
|
||||
if (UseSVE == 0) {
|
||||
assert(length_in_bytes == 8 || length_in_bytes == 16, "unsupported");
|
||||
assert(length_in_bytes <= 16, "unsupported");
|
||||
__ neon_reverse_bits($dst$$FloatRegister, $src$$FloatRegister,
|
||||
bt, /* isQ */ length_in_bytes == 16);
|
||||
if (bt != T_LONG) {
|
||||
@ -4910,7 +4932,7 @@ instruct vreverse(vReg dst, vReg src) %{
|
||||
} else {
|
||||
assert(bt == T_SHORT || bt == T_INT || bt == T_LONG, "unsupported type");
|
||||
if (UseSVE == 0) {
|
||||
assert(length_in_bytes == 8 || length_in_bytes == 16, "unsupported");
|
||||
assert(length_in_bytes <= 16, "unsupported");
|
||||
__ neon_reverse_bits($dst$$FloatRegister, $src$$FloatRegister,
|
||||
bt, /* isQ */ length_in_bytes == 16);
|
||||
} else {
|
||||
@ -4935,7 +4957,7 @@ instruct vreverseBytes(vReg dst, vReg src) %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
uint length_in_bytes = Matcher::vector_length_in_bytes(this);
|
||||
if (VM_Version::use_neon_for_vector(length_in_bytes)) {
|
||||
assert(length_in_bytes == 8 || length_in_bytes == 16, "unsupported");
|
||||
assert(length_in_bytes <= 16, "unsupported");
|
||||
if (bt == T_BYTE) {
|
||||
if ($dst$$FloatRegister != $src$$FloatRegister) {
|
||||
__ orr($dst$$FloatRegister, length_in_bytes == 16 ? __ T16B : __ T8B,
|
||||
|
||||
@ -1778,19 +1778,21 @@ void C2_MacroAssembler::sve_vmask_lasttrue(Register dst, BasicType bt, PRegister
|
||||
void C2_MacroAssembler::neon_vector_extend(FloatRegister dst, BasicType dst_bt, unsigned dst_vlen_in_bytes,
|
||||
FloatRegister src, BasicType src_bt, bool is_unsigned) {
|
||||
if (src_bt == T_BYTE) {
|
||||
if (dst_bt == T_SHORT) {
|
||||
// 4B/8B to 4S/8S
|
||||
_xshll(is_unsigned, dst, T8H, src, T8B, 0);
|
||||
} else {
|
||||
// 4B to 4I
|
||||
assert(dst_vlen_in_bytes == 16 && dst_bt == T_INT, "unsupported");
|
||||
_xshll(is_unsigned, dst, T8H, src, T8B, 0);
|
||||
// 4B to 4S/4I, 8B to 8S
|
||||
assert(dst_vlen_in_bytes == 8 || dst_vlen_in_bytes == 16, "unsupported");
|
||||
assert(dst_bt == T_SHORT || dst_bt == T_INT, "unsupported");
|
||||
_xshll(is_unsigned, dst, T8H, src, T8B, 0);
|
||||
if (dst_bt == T_INT) {
|
||||
_xshll(is_unsigned, dst, T4S, dst, T4H, 0);
|
||||
}
|
||||
} else if (src_bt == T_SHORT) {
|
||||
// 4S to 4I
|
||||
assert(dst_vlen_in_bytes == 16 && dst_bt == T_INT, "unsupported");
|
||||
// 2S to 2I/2L, 4S to 4I
|
||||
assert(dst_vlen_in_bytes == 8 || dst_vlen_in_bytes == 16, "unsupported");
|
||||
assert(dst_bt == T_INT || dst_bt == T_LONG, "unsupported");
|
||||
_xshll(is_unsigned, dst, T4S, src, T4H, 0);
|
||||
if (dst_bt == T_LONG) {
|
||||
_xshll(is_unsigned, dst, T2D, dst, T2S, 0);
|
||||
}
|
||||
} else if (src_bt == T_INT) {
|
||||
// 2I to 2L
|
||||
assert(dst_vlen_in_bytes == 16 && dst_bt == T_LONG, "unsupported");
|
||||
@ -1810,18 +1812,21 @@ void C2_MacroAssembler::neon_vector_narrow(FloatRegister dst, BasicType dst_bt,
|
||||
assert(dst_bt == T_BYTE, "unsupported");
|
||||
xtn(dst, T8B, src, T8H);
|
||||
} else if (src_bt == T_INT) {
|
||||
// 4I to 4B/4S
|
||||
assert(src_vlen_in_bytes == 16, "unsupported");
|
||||
// 2I to 2S, 4I to 4B/4S
|
||||
assert(src_vlen_in_bytes == 8 || src_vlen_in_bytes == 16, "unsupported");
|
||||
assert(dst_bt == T_BYTE || dst_bt == T_SHORT, "unsupported");
|
||||
xtn(dst, T4H, src, T4S);
|
||||
if (dst_bt == T_BYTE) {
|
||||
xtn(dst, T8B, dst, T8H);
|
||||
}
|
||||
} else if (src_bt == T_LONG) {
|
||||
// 2L to 2I
|
||||
// 2L to 2S/2I
|
||||
assert(src_vlen_in_bytes == 16, "unsupported");
|
||||
assert(dst_bt == T_INT, "unsupported");
|
||||
assert(dst_bt == T_INT || dst_bt == T_SHORT, "unsupported");
|
||||
xtn(dst, T2S, src, T2D);
|
||||
if (dst_bt == T_SHORT) {
|
||||
xtn(dst, T4H, dst, T4S);
|
||||
}
|
||||
} else {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
@ -292,7 +292,8 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm,
|
||||
} else {
|
||||
assert(is_phantom, "only remaining strength");
|
||||
assert(!is_narrow, "phantom access cannot be narrow");
|
||||
__ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom));
|
||||
// AOT saved adapters need relocation for this call.
|
||||
__ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom)));
|
||||
}
|
||||
__ blr(lr);
|
||||
__ mov(rscratch1, r0);
|
||||
|
||||
@ -117,7 +117,8 @@ define_pd_global(intx, InlineSmallCode, 1000);
|
||||
product(ccstr, OnSpinWaitInst, "yield", DIAGNOSTIC, \
|
||||
"The instruction to use to implement " \
|
||||
"java.lang.Thread.onSpinWait()." \
|
||||
"Options: none, nop, isb, yield, sb.") \
|
||||
"Valid values are: none, nop, isb, yield, sb.") \
|
||||
constraint(OnSpinWaitInstNameConstraintFunc, AtParse) \
|
||||
product(uint, OnSpinWaitInstCount, 1, DIAGNOSTIC, \
|
||||
"The number of OnSpinWaitInst instructions to generate." \
|
||||
"It cannot be used with OnSpinWaitInst=none.") \
|
||||
|
||||
@ -6816,6 +6816,7 @@ void MacroAssembler::spin_wait() {
|
||||
yield();
|
||||
break;
|
||||
case SpinWait::SB:
|
||||
assert(VM_Version::supports_sb(), "current CPU does not support SB instruction");
|
||||
sb();
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright Amazon.com Inc. 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
|
||||
@ -21,18 +21,32 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary run CTW for all classes from jdk.jsobject module
|
||||
*
|
||||
* @library /test/lib / /testlibrary/ctw/src
|
||||
* @modules java.base/jdk.internal.access
|
||||
* java.base/jdk.internal.jimage
|
||||
* java.base/jdk.internal.misc
|
||||
* java.base/jdk.internal.reflect
|
||||
* @modules jdk.jsobject
|
||||
*
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run driver/timeout=7200 sun.hotspot.tools.ctw.CtwRunner modules:jdk.jsobject
|
||||
*/
|
||||
#include "spin_wait_aarch64.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
bool SpinWait::supports(const char *name) {
|
||||
return name != nullptr &&
|
||||
(strcmp(name, "nop") == 0 ||
|
||||
strcmp(name, "isb") == 0 ||
|
||||
strcmp(name, "yield") == 0 ||
|
||||
strcmp(name, "sb") == 0 ||
|
||||
strcmp(name, "none") == 0);
|
||||
}
|
||||
|
||||
SpinWait::Inst SpinWait::from_name(const char* name) {
|
||||
assert(supports(name), "checked by OnSpinWaitInstNameConstraintFunc");
|
||||
|
||||
if (strcmp(name, "nop") == 0) {
|
||||
return SpinWait::NOP;
|
||||
} else if (strcmp(name, "isb") == 0) {
|
||||
return SpinWait::ISB;
|
||||
} else if (strcmp(name, "yield") == 0) {
|
||||
return SpinWait::YIELD;
|
||||
} else if (strcmp(name, "sb") == 0) {
|
||||
return SpinWait::SB;
|
||||
}
|
||||
|
||||
return SpinWait::NONE;
|
||||
}
|
||||
@ -19,7 +19,6 @@
|
||||
* 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 CPU_AARCH64_SPIN_WAIT_AARCH64_HPP
|
||||
@ -39,11 +38,16 @@ private:
|
||||
Inst _inst;
|
||||
int _count;
|
||||
|
||||
Inst from_name(const char *name);
|
||||
|
||||
public:
|
||||
SpinWait(Inst inst = NONE, int count = 0) : _inst(inst), _count(count) {}
|
||||
SpinWait(Inst inst = NONE, int count = 0) : _inst(inst), _count(inst == NONE ? 0 : count) {}
|
||||
SpinWait(const char *name, int count) : SpinWait(from_name(name), count) {}
|
||||
|
||||
Inst inst() const { return _inst; }
|
||||
int inst_count() const { return _count; }
|
||||
|
||||
static bool supports(const char *name);
|
||||
};
|
||||
|
||||
#endif // CPU_AARCH64_SPIN_WAIT_AARCH64_HPP
|
||||
|
||||
@ -11680,8 +11680,6 @@ class StubGenerator: public StubCodeGenerator {
|
||||
}
|
||||
|
||||
if (UseCRC32Intrinsics) {
|
||||
// set table address before stub generation which use it
|
||||
StubRoutines::_crc_table_adr = (address)StubRoutines::aarch64::_crc_table;
|
||||
StubRoutines::_updateBytesCRC32 = generate_updateBytesCRC32();
|
||||
}
|
||||
|
||||
|
||||
@ -71,6 +71,10 @@ ATTRIBUTE_ALIGNED(64) uint32_t StubRoutines::aarch64::_dilithiumConsts[] =
|
||||
/**
|
||||
* crc_table[] from jdk/src/share/native/java/util/zip/zlib-1.2.5/crc32.h
|
||||
*/
|
||||
|
||||
address StubRoutines::crc_table_addr() { return (address)StubRoutines::aarch64::_crc_table; }
|
||||
address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; }
|
||||
|
||||
ATTRIBUTE_ALIGNED(4096) juint StubRoutines::aarch64::_crc_table[] =
|
||||
{
|
||||
// Table 0
|
||||
|
||||
@ -47,6 +47,7 @@ enum platform_dependent_constants {
|
||||
|
||||
class aarch64 {
|
||||
friend class StubGenerator;
|
||||
friend class StubRoutines;
|
||||
#if INCLUDE_JVMCI
|
||||
friend class JVMCIVMStructs;
|
||||
#endif
|
||||
|
||||
@ -51,26 +51,12 @@ uintptr_t VM_Version::_pac_mask;
|
||||
SpinWait VM_Version::_spin_wait;
|
||||
|
||||
static SpinWait get_spin_wait_desc() {
|
||||
if (strcmp(OnSpinWaitInst, "nop") == 0) {
|
||||
return SpinWait(SpinWait::NOP, OnSpinWaitInstCount);
|
||||
} else if (strcmp(OnSpinWaitInst, "isb") == 0) {
|
||||
return SpinWait(SpinWait::ISB, OnSpinWaitInstCount);
|
||||
} else if (strcmp(OnSpinWaitInst, "yield") == 0) {
|
||||
return SpinWait(SpinWait::YIELD, OnSpinWaitInstCount);
|
||||
} else if (strcmp(OnSpinWaitInst, "sb") == 0) {
|
||||
if (!VM_Version::supports_sb()) {
|
||||
vm_exit_during_initialization("OnSpinWaitInst is SB but current CPU does not support SB instruction");
|
||||
}
|
||||
return SpinWait(SpinWait::SB, OnSpinWaitInstCount);
|
||||
} else if (strcmp(OnSpinWaitInst, "none") != 0) {
|
||||
vm_exit_during_initialization("The options for OnSpinWaitInst are nop, isb, yield, sb, and none", OnSpinWaitInst);
|
||||
SpinWait spin_wait(OnSpinWaitInst, OnSpinWaitInstCount);
|
||||
if (spin_wait.inst() == SpinWait::SB && !VM_Version::supports_sb()) {
|
||||
vm_exit_during_initialization("OnSpinWaitInst is SB but current CPU does not support SB instruction");
|
||||
}
|
||||
|
||||
if (!FLAG_IS_DEFAULT(OnSpinWaitInstCount) && OnSpinWaitInstCount > 0) {
|
||||
vm_exit_during_initialization("OnSpinWaitInstCount cannot be used for OnSpinWaitInst 'none'");
|
||||
}
|
||||
|
||||
return SpinWait{};
|
||||
return spin_wait;
|
||||
}
|
||||
|
||||
void VM_Version::initialize() {
|
||||
|
||||
@ -36,3 +36,6 @@ STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT)
|
||||
|
||||
#undef DEFINE_ARCH_ENTRY_INIT
|
||||
#undef DEFINE_ARCH_ENTRY
|
||||
|
||||
address StubRoutines::crc_table_addr() { ShouldNotCallThis(); return nullptr; }
|
||||
address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; }
|
||||
|
||||
@ -4982,13 +4982,11 @@ void generate_lookup_secondary_supers_table_stub() {
|
||||
|
||||
// CRC32 Intrinsics.
|
||||
if (UseCRC32Intrinsics) {
|
||||
StubRoutines::_crc_table_adr = StubRoutines::ppc::generate_crc_constants(REVERSE_CRC32_POLY);
|
||||
StubRoutines::_updateBytesCRC32 = generate_CRC32_updateBytes(StubId::stubgen_updateBytesCRC32_id);
|
||||
}
|
||||
|
||||
// CRC32C Intrinsics.
|
||||
if (UseCRC32CIntrinsics) {
|
||||
StubRoutines::_crc32c_table_addr = StubRoutines::ppc::generate_crc_constants(REVERSE_CRC32C_POLY);
|
||||
StubRoutines::_updateBytesCRC32C = generate_CRC32_updateBytes(StubId::stubgen_updateBytesCRC32C_id);
|
||||
}
|
||||
|
||||
|
||||
@ -54,6 +54,7 @@ enum platform_dependent_constants {
|
||||
|
||||
class ppc {
|
||||
friend class StubGenerator;
|
||||
friend class StubRoutines;
|
||||
|
||||
private:
|
||||
public:
|
||||
|
||||
@ -74,6 +74,22 @@ static julong compute_inverse_poly(julong long_poly) {
|
||||
return div;
|
||||
}
|
||||
|
||||
static address _crc_table_addr = nullptr;
|
||||
static address _crc32c_table_addr = nullptr;
|
||||
|
||||
address StubRoutines::crc_table_addr() {
|
||||
if (_crc_table_addr == nullptr) {
|
||||
_crc_table_addr = StubRoutines::ppc::generate_crc_constants(REVERSE_CRC32_POLY);
|
||||
}
|
||||
return _crc_table_addr;
|
||||
}
|
||||
address StubRoutines::crc32c_table_addr() {
|
||||
if (_crc32c_table_addr == nullptr) {
|
||||
_crc32c_table_addr = StubRoutines::ppc::generate_crc_constants(REVERSE_CRC32C_POLY);
|
||||
}
|
||||
return _crc32c_table_addr;
|
||||
}
|
||||
|
||||
// Constants to fold n words as needed by macroAssembler.
|
||||
address StubRoutines::ppc::generate_crc_constants(juint reverse_poly) {
|
||||
// Layout of constant table:
|
||||
|
||||
@ -772,7 +772,7 @@ void LIRGenerator::do_ArrayCopy(Intrinsic* x) {
|
||||
ciArrayKlass* expected_type = nullptr;
|
||||
arraycopy_helper(x, &flags, &expected_type);
|
||||
if (x->check_flag(Instruction::OmitChecksFlag)) {
|
||||
flags = 0;
|
||||
flags = (flags & LIR_OpArrayCopy::get_initial_copy_flags());
|
||||
}
|
||||
|
||||
__ arraycopy(src.result(), src_pos.result(), dst.result(), dst_pos.result(), length.result(), tmp,
|
||||
|
||||
@ -1952,16 +1952,15 @@ void C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register res
|
||||
mv(pow31_3, 29791); // [31^^3]
|
||||
mv(pow31_2, 961); // [31^^2]
|
||||
|
||||
slli(chunks_end, chunks, chunks_end_shift);
|
||||
add(chunks_end, ary, chunks_end);
|
||||
shadd(chunks_end, chunks, ary, t0, chunks_end_shift);
|
||||
andi(cnt, cnt, stride - 1); // don't forget about tail!
|
||||
|
||||
bind(WIDE_LOOP);
|
||||
mulw(result, result, pow31_4); // 31^^4 * h
|
||||
arrays_hashcode_elload(t0, Address(ary, 0 * elsize), eltype);
|
||||
arrays_hashcode_elload(t1, Address(ary, 1 * elsize), eltype);
|
||||
arrays_hashcode_elload(tmp5, Address(ary, 2 * elsize), eltype);
|
||||
arrays_hashcode_elload(tmp6, Address(ary, 3 * elsize), eltype);
|
||||
mulw(result, result, pow31_4); // 31^^4 * h
|
||||
mulw(t0, t0, pow31_3); // 31^^3 * ary[i+0]
|
||||
addw(result, result, t0);
|
||||
mulw(t1, t1, pow31_2); // 31^^2 * ary[i+1]
|
||||
@ -1976,8 +1975,7 @@ void C2_MacroAssembler::arrays_hashcode(Register ary, Register cnt, Register res
|
||||
beqz(cnt, DONE);
|
||||
|
||||
bind(TAIL);
|
||||
slli(chunks_end, cnt, chunks_end_shift);
|
||||
add(chunks_end, ary, chunks_end);
|
||||
shadd(chunks_end, cnt, ary, t0, chunks_end_shift);
|
||||
|
||||
bind(TAIL_LOOP);
|
||||
arrays_hashcode_elload(t0, Address(ary), eltype);
|
||||
|
||||
@ -2276,10 +2276,6 @@ encode %{
|
||||
__ mv(dst_reg, 1);
|
||||
%}
|
||||
|
||||
enc_class riscv_enc_mov_byte_map_base(iRegP dst) %{
|
||||
__ load_byte_map_base($dst$$Register);
|
||||
%}
|
||||
|
||||
enc_class riscv_enc_mov_n(iRegN dst, immN src) %{
|
||||
Register dst_reg = as_Register($dst$$reg);
|
||||
address con = (address)$src$$constant;
|
||||
@ -2834,21 +2830,6 @@ operand immP_1()
|
||||
interface(CONST_INTER);
|
||||
%}
|
||||
|
||||
// Card Table Byte Map Base
|
||||
operand immByteMapBase()
|
||||
%{
|
||||
// Get base of card map
|
||||
predicate(BarrierSet::barrier_set()->is_a(BarrierSet::CardTableBarrierSet) &&
|
||||
SHENANDOAHGC_ONLY(!BarrierSet::barrier_set()->is_a(BarrierSet::ShenandoahBarrierSet) &&)
|
||||
(CardTable::CardValue*)n->get_ptr() ==
|
||||
((CardTableBarrierSet*)(BarrierSet::barrier_set()))->card_table()->byte_map_base());
|
||||
match(ConP);
|
||||
|
||||
op_cost(0);
|
||||
format %{ %}
|
||||
interface(CONST_INTER);
|
||||
%}
|
||||
|
||||
// Int Immediate: low 16-bit mask
|
||||
operand immI_16bits()
|
||||
%{
|
||||
@ -4808,18 +4789,6 @@ instruct loadConP1(iRegPNoSp dst, immP_1 con)
|
||||
ins_pipe(ialu_imm);
|
||||
%}
|
||||
|
||||
// Load Byte Map Base Constant
|
||||
instruct loadByteMapBase(iRegPNoSp dst, immByteMapBase con)
|
||||
%{
|
||||
match(Set dst con);
|
||||
ins_cost(ALU_COST);
|
||||
format %{ "mv $dst, $con\t# Byte Map Base, #@loadByteMapBase" %}
|
||||
|
||||
ins_encode(riscv_enc_mov_byte_map_base(dst));
|
||||
|
||||
ins_pipe(ialu_imm);
|
||||
%}
|
||||
|
||||
// Load Narrow Pointer Constant
|
||||
instruct loadConN(iRegNNoSp dst, immN con)
|
||||
%{
|
||||
|
||||
@ -6686,8 +6686,6 @@ static const int64_t right_3_bits = right_n_bits(3);
|
||||
StubRoutines::_catch_exception_entry = generate_catch_exception();
|
||||
|
||||
if (UseCRC32Intrinsics) {
|
||||
// set table address before stub generation which use it
|
||||
StubRoutines::_crc_table_adr = (address)StubRoutines::riscv::_crc_table;
|
||||
StubRoutines::_updateBytesCRC32 = generate_updateBytesCRC32();
|
||||
}
|
||||
|
||||
|
||||
@ -52,6 +52,10 @@ bool StubRoutines::riscv::_completed = false;
|
||||
/**
|
||||
* crc_table[] from jdk/src/java.base/share/native/libzip/zlib/crc32.h
|
||||
*/
|
||||
|
||||
address StubRoutines::crc_table_addr() { return (address)StubRoutines::riscv::_crc_table; }
|
||||
address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; }
|
||||
|
||||
ATTRIBUTE_ALIGNED(4096) juint StubRoutines::riscv::_crc_table[] =
|
||||
{
|
||||
// Table 0
|
||||
|
||||
@ -48,6 +48,7 @@ enum platform_dependent_constants {
|
||||
|
||||
class riscv {
|
||||
friend class StubGenerator;
|
||||
friend class StubRoutines;
|
||||
#if INCLUDE_JVMCI
|
||||
friend class JVMCIVMStructs;
|
||||
#endif
|
||||
|
||||
@ -3308,12 +3308,10 @@ class StubGenerator: public StubCodeGenerator {
|
||||
}
|
||||
|
||||
if (UseCRC32Intrinsics) {
|
||||
StubRoutines::_crc_table_adr = (address)StubRoutines::zarch::_crc_table;
|
||||
StubRoutines::_updateBytesCRC32 = generate_CRC32_updateBytes();
|
||||
}
|
||||
|
||||
if (UseCRC32CIntrinsics) {
|
||||
StubRoutines::_crc32c_table_addr = (address)StubRoutines::zarch::_crc32c_table;
|
||||
StubRoutines::_updateBytesCRC32C = generate_CRC32C_updateBytes();
|
||||
}
|
||||
|
||||
|
||||
@ -78,14 +78,17 @@ void StubRoutines::zarch::generate_load_absolute_address(MacroAssembler* masm, R
|
||||
#endif
|
||||
}
|
||||
|
||||
address StubRoutines::crc_table_addr() { return (address)StubRoutines::zarch::_crc_table; }
|
||||
address StubRoutines::crc32c_table_addr() { return (address)StubRoutines::zarch::_crc32c_table; }
|
||||
|
||||
void StubRoutines::zarch::generate_load_crc_table_addr(MacroAssembler* masm, Register table) {
|
||||
const uint64_t table_contents = 0x77073096UL; // required contents of table[1]
|
||||
generate_load_absolute_address(masm, table, StubRoutines::_crc_table_adr, table_contents);
|
||||
generate_load_absolute_address(masm, table, StubRoutines::crc_table_addr(), table_contents);
|
||||
}
|
||||
|
||||
void StubRoutines::zarch::generate_load_crc32c_table_addr(MacroAssembler* masm, Register table) {
|
||||
const uint64_t table_contents = 0xf26b8303UL; // required contents of table[1]
|
||||
generate_load_absolute_address(masm, table, StubRoutines::_crc32c_table_addr, table_contents);
|
||||
generate_load_absolute_address(masm, table, StubRoutines::crc32c_table_addr(), table_contents);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -62,6 +62,7 @@ enum method_handles_platform_dependent_constants {
|
||||
|
||||
class zarch {
|
||||
friend class StubGenerator;
|
||||
friend class StubRoutines;
|
||||
|
||||
public:
|
||||
enum { nof_instance_allocators = 10 };
|
||||
|
||||
@ -306,10 +306,10 @@ void PatchingStub::emit_code(LIR_Assembler* ce) {
|
||||
}
|
||||
assert(_obj != noreg, "must be a valid register");
|
||||
Register tmp = rax;
|
||||
__ push(tmp);
|
||||
__ push_ppx(tmp);
|
||||
__ movptr(tmp, Address(_obj, java_lang_Class::klass_offset()));
|
||||
__ cmpptr(r15_thread, Address(tmp, InstanceKlass::init_thread_offset()));
|
||||
__ pop(tmp); // pop it right away, no matter which path we take
|
||||
__ pop_ppx(tmp); // pop it right away, no matter which path we take
|
||||
__ jccb(Assembler::notEqual, call_patch);
|
||||
|
||||
// access_field patches may execute the patched code before it's
|
||||
|
||||
@ -1385,11 +1385,11 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L
|
||||
__ cmpptr(klass_RInfo, k_RInfo);
|
||||
__ jcc(Assembler::equal, *success_target);
|
||||
|
||||
__ push(klass_RInfo);
|
||||
__ push(k_RInfo);
|
||||
__ push_ppx(klass_RInfo);
|
||||
__ push_ppx(k_RInfo);
|
||||
__ call(RuntimeAddress(Runtime1::entry_for(StubId::c1_slow_subtype_check_id)));
|
||||
__ pop(klass_RInfo);
|
||||
__ pop(klass_RInfo);
|
||||
__ pop_ppx(klass_RInfo);
|
||||
__ pop_ppx(klass_RInfo);
|
||||
// result is a boolean
|
||||
__ testl(klass_RInfo, klass_RInfo);
|
||||
__ jcc(Assembler::equal, *failure_target);
|
||||
@ -1399,11 +1399,11 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L
|
||||
// perform the fast part of the checking logic
|
||||
__ check_klass_subtype_fast_path(klass_RInfo, k_RInfo, Rtmp1, success_target, failure_target, nullptr);
|
||||
// call out-of-line instance of __ check_klass_subtype_slow_path(...):
|
||||
__ push(klass_RInfo);
|
||||
__ push(k_RInfo);
|
||||
__ push_ppx(klass_RInfo);
|
||||
__ push_ppx(k_RInfo);
|
||||
__ call(RuntimeAddress(Runtime1::entry_for(StubId::c1_slow_subtype_check_id)));
|
||||
__ pop(klass_RInfo);
|
||||
__ pop(k_RInfo);
|
||||
__ pop_ppx(klass_RInfo);
|
||||
__ pop_ppx(k_RInfo);
|
||||
// result is a boolean
|
||||
__ testl(k_RInfo, k_RInfo);
|
||||
__ jcc(Assembler::equal, *failure_target);
|
||||
@ -1478,11 +1478,11 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) {
|
||||
// perform the fast part of the checking logic
|
||||
__ check_klass_subtype_fast_path(klass_RInfo, k_RInfo, Rtmp1, success_target, failure_target, nullptr);
|
||||
// call out-of-line instance of __ check_klass_subtype_slow_path(...):
|
||||
__ push(klass_RInfo);
|
||||
__ push(k_RInfo);
|
||||
__ push_ppx(klass_RInfo);
|
||||
__ push_ppx(k_RInfo);
|
||||
__ call(RuntimeAddress(Runtime1::entry_for(StubId::c1_slow_subtype_check_id)));
|
||||
__ pop(klass_RInfo);
|
||||
__ pop(k_RInfo);
|
||||
__ pop_ppx(klass_RInfo);
|
||||
__ pop_ppx(k_RInfo);
|
||||
// result is a boolean
|
||||
__ testl(k_RInfo, k_RInfo);
|
||||
__ jcc(Assembler::equal, *failure_target);
|
||||
@ -2536,26 +2536,26 @@ void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) {
|
||||
// safely do the copy.
|
||||
Label cont, slow;
|
||||
|
||||
__ push(src);
|
||||
__ push(dst);
|
||||
__ push_ppx(src);
|
||||
__ push_ppx(dst);
|
||||
|
||||
__ load_klass(src, src, tmp_load_klass);
|
||||
__ load_klass(dst, dst, tmp_load_klass);
|
||||
|
||||
__ check_klass_subtype_fast_path(src, dst, tmp, &cont, &slow, nullptr);
|
||||
|
||||
__ push(src);
|
||||
__ push(dst);
|
||||
__ push_ppx(src);
|
||||
__ push_ppx(dst);
|
||||
__ call(RuntimeAddress(Runtime1::entry_for(StubId::c1_slow_subtype_check_id)));
|
||||
__ pop(dst);
|
||||
__ pop(src);
|
||||
__ pop_ppx(dst);
|
||||
__ pop_ppx(src);
|
||||
|
||||
__ testl(src, src);
|
||||
__ jcc(Assembler::notEqual, cont);
|
||||
|
||||
__ bind(slow);
|
||||
__ pop(dst);
|
||||
__ pop(src);
|
||||
__ pop_ppx(dst);
|
||||
__ pop_ppx(src);
|
||||
|
||||
address copyfunc_addr = StubRoutines::checkcast_arraycopy();
|
||||
if (copyfunc_addr != nullptr) { // use stub if available
|
||||
@ -2904,13 +2904,13 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) {
|
||||
if (exact_klass != nullptr) {
|
||||
Label ok;
|
||||
__ load_klass(tmp, obj, tmp_load_klass);
|
||||
__ push(tmp);
|
||||
__ push_ppx(tmp);
|
||||
__ mov_metadata(tmp, exact_klass->constant_encoding());
|
||||
__ cmpptr(tmp, Address(rsp, 0));
|
||||
__ jcc(Assembler::equal, ok);
|
||||
__ stop("exact klass and actual klass differ");
|
||||
__ bind(ok);
|
||||
__ pop(tmp);
|
||||
__ pop_ppx(tmp);
|
||||
}
|
||||
#endif
|
||||
if (!no_conflict) {
|
||||
@ -2975,7 +2975,7 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) {
|
||||
|
||||
{
|
||||
Label ok;
|
||||
__ push(tmp);
|
||||
__ push_ppx(tmp);
|
||||
__ testptr(mdo_addr, TypeEntries::type_mask);
|
||||
__ jcc(Assembler::zero, ok);
|
||||
// may have been set by another thread
|
||||
@ -2986,7 +2986,7 @@ void LIR_Assembler::emit_profile_type(LIR_OpProfileType* op) {
|
||||
|
||||
__ stop("unexpected profiling mismatch");
|
||||
__ bind(ok);
|
||||
__ pop(tmp);
|
||||
__ pop_ppx(tmp);
|
||||
}
|
||||
#else
|
||||
__ jccb(Assembler::zero, next);
|
||||
|
||||
@ -719,7 +719,7 @@ OopMapSet* Runtime1::generate_patching(StubAssembler* sasm, address target) {
|
||||
// verify callee-saved register
|
||||
#ifdef ASSERT
|
||||
guarantee(thread != rax, "change this code");
|
||||
__ push(rax);
|
||||
__ push_ppx(rax);
|
||||
{ Label L;
|
||||
__ get_thread_slow(rax);
|
||||
__ cmpptr(thread, rax);
|
||||
@ -727,7 +727,7 @@ OopMapSet* Runtime1::generate_patching(StubAssembler* sasm, address target) {
|
||||
__ stop("StubAssembler::call_RT: rdi/r15 not callee saved?");
|
||||
__ bind(L);
|
||||
}
|
||||
__ pop(rax);
|
||||
__ pop_ppx(rax);
|
||||
#endif
|
||||
__ reset_last_Java_frame(true);
|
||||
|
||||
@ -1070,10 +1070,10 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) {
|
||||
};
|
||||
|
||||
__ set_info("slow_subtype_check", dont_gc_arguments);
|
||||
__ push(rdi);
|
||||
__ push(rsi);
|
||||
__ push(rcx);
|
||||
__ push(rax);
|
||||
__ push_ppx(rdi);
|
||||
__ push_ppx(rsi);
|
||||
__ push_ppx(rcx);
|
||||
__ push_ppx(rax);
|
||||
|
||||
// This is called by pushing args and not with C abi
|
||||
__ movptr(rsi, Address(rsp, (klass_off) * VMRegImpl::stack_slot_size)); // subclass
|
||||
@ -1084,10 +1084,10 @@ OopMapSet* Runtime1::generate_code_for(StubId id, StubAssembler* sasm) {
|
||||
|
||||
// fallthrough on success:
|
||||
__ movptr(Address(rsp, (result_off) * VMRegImpl::stack_slot_size), 1); // result
|
||||
__ pop(rax);
|
||||
__ pop(rcx);
|
||||
__ pop(rsi);
|
||||
__ pop(rdi);
|
||||
__ pop_ppx(rax);
|
||||
__ pop_ppx(rcx);
|
||||
__ pop_ppx(rsi);
|
||||
__ pop_ppx(rdi);
|
||||
__ ret(0);
|
||||
|
||||
__ bind(miss);
|
||||
|
||||
@ -352,8 +352,8 @@ static void generate_string_indexof_stubs(StubGenerator *stubgen, address *fnptr
|
||||
__ movdq(save_r15, r15);
|
||||
__ movdq(save_rbx, rbx);
|
||||
#ifdef _WIN64
|
||||
__ push(rsi);
|
||||
__ push(rdi);
|
||||
__ push_ppx(rsi);
|
||||
__ push_ppx(rdi);
|
||||
|
||||
// Move to Linux-style ABI
|
||||
__ movq(rdi, rcx);
|
||||
@ -368,7 +368,7 @@ static void generate_string_indexof_stubs(StubGenerator *stubgen, address *fnptr
|
||||
const Register needle_len = rcx;
|
||||
const Register save_ndl_len = r12;
|
||||
|
||||
__ push(rbp);
|
||||
__ push_ppx(rbp);
|
||||
__ subptr(rsp, STACK_SPACE);
|
||||
|
||||
if (isReallyUL) {
|
||||
@ -459,10 +459,10 @@ static void generate_string_indexof_stubs(StubGenerator *stubgen, address *fnptr
|
||||
// Restore stack, vzeroupper and return
|
||||
__ bind(L_return);
|
||||
__ addptr(rsp, STACK_SPACE);
|
||||
__ pop(rbp);
|
||||
__ pop_ppx(rbp);
|
||||
#ifdef _WIN64
|
||||
__ pop(rdi);
|
||||
__ pop(rsi);
|
||||
__ pop_ppx(rdi);
|
||||
__ pop_ppx(rsi);
|
||||
#endif
|
||||
__ movdq(r12, save_r12);
|
||||
__ movdq(r13, save_r13);
|
||||
|
||||
@ -500,8 +500,8 @@ void G1BarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler*
|
||||
__ prologue("g1_pre_barrier", false);
|
||||
// arg0 : previous value of memory
|
||||
|
||||
__ push(rax);
|
||||
__ push(rdx);
|
||||
__ push_ppx(rax);
|
||||
__ push_ppx(rdx);
|
||||
|
||||
const Register pre_val = rax;
|
||||
const Register thread = r15_thread;
|
||||
@ -549,8 +549,8 @@ void G1BarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler*
|
||||
|
||||
__ bind(done);
|
||||
|
||||
__ pop(rdx);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(rdx);
|
||||
__ pop_ppx(rax);
|
||||
|
||||
__ epilogue();
|
||||
}
|
||||
@ -573,8 +573,8 @@ void G1BarrierSetAssembler::generate_c1_post_barrier_runtime_stub(StubAssembler*
|
||||
Address queue_index(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_index_offset()));
|
||||
Address buffer(thread, in_bytes(G1ThreadLocalData::dirty_card_queue_buffer_offset()));
|
||||
|
||||
__ push(rax);
|
||||
__ push(rcx);
|
||||
__ push_ppx(rax);
|
||||
__ push_ppx(rcx);
|
||||
|
||||
const Register cardtable = rax;
|
||||
const Register card_addr = rcx;
|
||||
@ -599,7 +599,7 @@ void G1BarrierSetAssembler::generate_c1_post_barrier_runtime_stub(StubAssembler*
|
||||
__ movb(Address(card_addr, 0), CardTable::dirty_card_val());
|
||||
|
||||
const Register tmp = rdx;
|
||||
__ push(rdx);
|
||||
__ push_ppx(rdx);
|
||||
|
||||
__ movptr(tmp, queue_index);
|
||||
__ testptr(tmp, tmp);
|
||||
@ -618,11 +618,11 @@ void G1BarrierSetAssembler::generate_c1_post_barrier_runtime_stub(StubAssembler*
|
||||
__ pop_call_clobbered_registers();
|
||||
|
||||
__ bind(enqueued);
|
||||
__ pop(rdx);
|
||||
__ pop_ppx(rdx);
|
||||
|
||||
__ bind(done);
|
||||
__ pop(rcx);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(rcx);
|
||||
__ pop_ppx(rax);
|
||||
|
||||
__ epilogue();
|
||||
}
|
||||
|
||||
@ -288,7 +288,7 @@ void ZBarrierSetAssembler::load_at(MacroAssembler* masm,
|
||||
Register scratch = tmp1;
|
||||
if (tmp1 == noreg) {
|
||||
scratch = r12;
|
||||
__ push(scratch);
|
||||
__ push_ppx(scratch);
|
||||
}
|
||||
|
||||
assert_different_registers(dst, scratch);
|
||||
@ -348,7 +348,7 @@ void ZBarrierSetAssembler::load_at(MacroAssembler* masm,
|
||||
|
||||
// Restore scratch register
|
||||
if (tmp1 == noreg) {
|
||||
__ pop(scratch);
|
||||
__ pop_ppx(scratch);
|
||||
}
|
||||
|
||||
BLOCK_COMMENT("} ZBarrierSetAssembler::load_at");
|
||||
@ -462,10 +462,10 @@ void ZBarrierSetAssembler::store_barrier_fast(MacroAssembler* masm,
|
||||
__ movptr(rnew_zpointer, rnew_zaddress);
|
||||
}
|
||||
assert_different_registers(rcx, rnew_zpointer);
|
||||
__ push(rcx);
|
||||
__ push_ppx(rcx);
|
||||
__ movptr(rcx, ExternalAddress((address)&ZPointerLoadShift));
|
||||
__ shlq(rnew_zpointer);
|
||||
__ pop(rcx);
|
||||
__ pop_ppx(rcx);
|
||||
__ orq(rnew_zpointer, Address(r15_thread, ZThreadLocalData::store_good_mask_offset()));
|
||||
}
|
||||
}
|
||||
@ -483,7 +483,7 @@ static void store_barrier_buffer_add(MacroAssembler* masm,
|
||||
__ jcc(Assembler::equal, slow_path);
|
||||
|
||||
Register tmp2 = r15_thread;
|
||||
__ push(tmp2);
|
||||
__ push_ppx(tmp2);
|
||||
|
||||
// Bump the pointer
|
||||
__ movq(tmp2, Address(tmp1, ZStoreBarrierBuffer::current_offset()));
|
||||
@ -501,7 +501,7 @@ static void store_barrier_buffer_add(MacroAssembler* masm,
|
||||
__ movptr(tmp1, Address(tmp1, 0));
|
||||
__ movptr(Address(tmp2, in_bytes(ZStoreBarrierEntry::prev_offset())), tmp1);
|
||||
|
||||
__ pop(tmp2);
|
||||
__ pop_ppx(tmp2);
|
||||
}
|
||||
|
||||
void ZBarrierSetAssembler::store_barrier_medium(MacroAssembler* masm,
|
||||
@ -528,9 +528,9 @@ void ZBarrierSetAssembler::store_barrier_medium(MacroAssembler* masm,
|
||||
|
||||
// If we get this far, we know there is a young raw null value in the field.
|
||||
// Try to self-heal null values for atomic accesses
|
||||
__ push(rax);
|
||||
__ push(rbx);
|
||||
__ push(rcx);
|
||||
__ push_ppx(rax);
|
||||
__ push_ppx(rbx);
|
||||
__ push_ppx(rcx);
|
||||
|
||||
__ lea(rcx, ref_addr);
|
||||
__ xorq(rax, rax);
|
||||
@ -539,9 +539,9 @@ void ZBarrierSetAssembler::store_barrier_medium(MacroAssembler* masm,
|
||||
__ lock();
|
||||
__ cmpxchgq(rbx, Address(rcx, 0));
|
||||
|
||||
__ pop(rcx);
|
||||
__ pop(rbx);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(rcx);
|
||||
__ pop_ppx(rbx);
|
||||
__ pop_ppx(rax);
|
||||
|
||||
__ jcc(Assembler::notEqual, slow_path);
|
||||
|
||||
@ -583,10 +583,10 @@ void ZBarrierSetAssembler::store_at(MacroAssembler* masm,
|
||||
} else {
|
||||
__ movptr(tmp1, src);
|
||||
}
|
||||
__ push(rcx);
|
||||
__ push_ppx(rcx);
|
||||
__ movptr(rcx, ExternalAddress((address)&ZPointerLoadShift));
|
||||
__ shlq(tmp1);
|
||||
__ pop(rcx);
|
||||
__ pop_ppx(rcx);
|
||||
__ orq(tmp1, Address(r15_thread, ZThreadLocalData::store_good_mask_offset()));
|
||||
} else {
|
||||
Label done;
|
||||
@ -1007,10 +1007,10 @@ void ZBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm,
|
||||
__ shrq(tmp);
|
||||
__ movptr(obj, tmp);
|
||||
} else {
|
||||
__ push(rcx);
|
||||
__ push_ppx(rcx);
|
||||
__ movptr(rcx, ExternalAddress((address)&ZPointerLoadShift));
|
||||
__ shrq(obj);
|
||||
__ pop(rcx);
|
||||
__ pop_ppx(rcx);
|
||||
}
|
||||
|
||||
__ bind(done);
|
||||
@ -1089,7 +1089,7 @@ void ZBarrierSetAssembler::generate_c1_load_barrier_stub(LIR_Assembler* ce,
|
||||
|
||||
// Save rax unless it is the result or tmp register
|
||||
if (ref != rax && tmp != rax) {
|
||||
__ push(rax);
|
||||
__ push_ppx(rax);
|
||||
}
|
||||
|
||||
// Setup arguments and call runtime stub
|
||||
@ -1109,7 +1109,7 @@ void ZBarrierSetAssembler::generate_c1_load_barrier_stub(LIR_Assembler* ce,
|
||||
|
||||
// Restore rax unless it is the result or tmp register
|
||||
if (ref != rax && tmp != rax) {
|
||||
__ pop(rax);
|
||||
__ pop_ppx(rax);
|
||||
}
|
||||
|
||||
// Stub exit
|
||||
@ -1451,12 +1451,12 @@ void ZBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Registe
|
||||
// Uncolor presumed zpointer
|
||||
assert(obj != rcx, "bad choice of register");
|
||||
if (rcx != tmp1 && rcx != tmp2) {
|
||||
__ push(rcx);
|
||||
__ push_ppx(rcx);
|
||||
}
|
||||
__ movl(rcx, Address(tmp2, tmp1, Address::times_4, 0));
|
||||
__ shrq(obj);
|
||||
if (rcx != tmp1 && rcx != tmp2) {
|
||||
__ pop(rcx);
|
||||
__ pop_ppx(rcx);
|
||||
}
|
||||
|
||||
__ jmp(check_zaddress);
|
||||
|
||||
@ -41,16 +41,16 @@ void x86_generate_icache_fence(MacroAssembler* _masm) {
|
||||
__ sfence();
|
||||
break;
|
||||
case 4:
|
||||
__ push(rax);
|
||||
__ push(rbx);
|
||||
__ push(rcx);
|
||||
__ push(rdx);
|
||||
__ push_ppx(rax);
|
||||
__ push_ppx(rbx);
|
||||
__ push_ppx(rcx);
|
||||
__ push_ppx(rdx);
|
||||
__ xorptr(rax, rax);
|
||||
__ cpuid();
|
||||
__ pop(rdx);
|
||||
__ pop(rcx);
|
||||
__ pop(rbx);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(rdx);
|
||||
__ pop_ppx(rcx);
|
||||
__ pop_ppx(rbx);
|
||||
__ pop_ppx(rax);
|
||||
break;
|
||||
case 5:
|
||||
__ serialize();
|
||||
|
||||
@ -795,6 +795,22 @@ void MacroAssembler::pop_d(XMMRegister r) {
|
||||
addptr(rsp, 2 * Interpreter::stackElementSize);
|
||||
}
|
||||
|
||||
void MacroAssembler::push_ppx(Register src) {
|
||||
if (VM_Version::supports_apx_f()) {
|
||||
pushp(src);
|
||||
} else {
|
||||
Assembler::push(src);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::pop_ppx(Register dst) {
|
||||
if (VM_Version::supports_apx_f()) {
|
||||
popp(dst);
|
||||
} else {
|
||||
Assembler::pop(dst);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::andpd(XMMRegister dst, AddressLiteral src, Register rscratch) {
|
||||
// Used in sign-masking with aligned address.
|
||||
assert((UseAVX > 0) || (((intptr_t)src.target() & 15) == 0), "SSE mode requires address alignment 16 bytes");
|
||||
|
||||
@ -989,6 +989,9 @@ public:
|
||||
void push_d(XMMRegister r);
|
||||
void pop_d(XMMRegister r);
|
||||
|
||||
void push_ppx(Register src);
|
||||
void pop_ppx(Register dst);
|
||||
|
||||
void andpd(XMMRegister dst, XMMRegister src) { Assembler::andpd(dst, src); }
|
||||
void andpd(XMMRegister dst, Address src) { Assembler::andpd(dst, src); }
|
||||
void andpd(XMMRegister dst, AddressLiteral src, Register rscratch = noreg);
|
||||
|
||||
@ -131,7 +131,7 @@ void MethodHandles::verify_method(MacroAssembler* _masm, Register method, Regist
|
||||
|
||||
const Register method_holder = temp;
|
||||
__ load_method_holder(method_holder, method);
|
||||
__ push(method_holder); // keep holder around for diagnostic purposes
|
||||
__ push_ppx(method_holder); // keep holder around for diagnostic purposes
|
||||
|
||||
switch (iid) {
|
||||
case vmIntrinsicID::_invokeBasic:
|
||||
@ -165,7 +165,7 @@ void MethodHandles::verify_method(MacroAssembler* _masm, Register method, Regist
|
||||
__ STOP("Method holder klass is not initialized");
|
||||
|
||||
__ BIND(L_ok);
|
||||
__ pop(method_holder); // restore stack layout
|
||||
__ pop_ppx(method_holder); // restore stack layout
|
||||
}
|
||||
BLOCK_COMMENT("} verify_method");
|
||||
}
|
||||
|
||||
@ -292,7 +292,7 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() {
|
||||
address start = __ pc();
|
||||
|
||||
// Exception pc is 'return address' for stack walker
|
||||
__ push(rdx);
|
||||
__ push_ppx(rdx);
|
||||
__ subptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Prolog
|
||||
|
||||
// Save callee-saved registers. See x86_64.ad.
|
||||
@ -347,7 +347,7 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() {
|
||||
__ movptr(rbp, Address(rsp, SimpleRuntimeFrame::rbp_off << LogBytesPerInt));
|
||||
|
||||
__ addptr(rsp, SimpleRuntimeFrame::return_off << LogBytesPerInt); // Epilog
|
||||
__ pop(rdx); // No need for exception pc anymore
|
||||
__ pop_ppx(rdx); // No need for exception pc anymore
|
||||
|
||||
// rax: exception handler
|
||||
|
||||
|
||||
@ -574,7 +574,7 @@ address StubGenerator::generate_verify_mxcsr() {
|
||||
if (CheckJNICalls) {
|
||||
Label ok_ret;
|
||||
ExternalAddress mxcsr_std(StubRoutines::x86::addr_mxcsr_std());
|
||||
__ push(rax);
|
||||
__ push_ppx(rax);
|
||||
__ subptr(rsp, wordSize); // allocate a temp location
|
||||
__ cmp32_mxcsr_std(mxcsr_save, rax, rscratch1);
|
||||
__ jcc(Assembler::equal, ok_ret);
|
||||
@ -585,7 +585,7 @@ address StubGenerator::generate_verify_mxcsr() {
|
||||
|
||||
__ bind(ok_ret);
|
||||
__ addptr(rsp, wordSize);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(rax);
|
||||
}
|
||||
|
||||
__ ret(0);
|
||||
@ -602,10 +602,10 @@ address StubGenerator::generate_f2i_fixup() {
|
||||
|
||||
Label L;
|
||||
|
||||
__ push(rax);
|
||||
__ push(c_rarg3);
|
||||
__ push(c_rarg2);
|
||||
__ push(c_rarg1);
|
||||
__ push_ppx(rax);
|
||||
__ push_ppx(c_rarg3);
|
||||
__ push_ppx(c_rarg2);
|
||||
__ push_ppx(c_rarg1);
|
||||
|
||||
__ movl(rax, 0x7f800000);
|
||||
__ xorl(c_rarg3, c_rarg3);
|
||||
@ -622,10 +622,10 @@ address StubGenerator::generate_f2i_fixup() {
|
||||
__ bind(L);
|
||||
__ movptr(inout, c_rarg3);
|
||||
|
||||
__ pop(c_rarg1);
|
||||
__ pop(c_rarg2);
|
||||
__ pop(c_rarg3);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(c_rarg1);
|
||||
__ pop_ppx(c_rarg2);
|
||||
__ pop_ppx(c_rarg3);
|
||||
__ pop_ppx(rax);
|
||||
|
||||
__ ret(0);
|
||||
|
||||
@ -640,10 +640,10 @@ address StubGenerator::generate_f2l_fixup() {
|
||||
|
||||
Label L;
|
||||
|
||||
__ push(rax);
|
||||
__ push(c_rarg3);
|
||||
__ push(c_rarg2);
|
||||
__ push(c_rarg1);
|
||||
__ push_ppx(rax);
|
||||
__ push_ppx(c_rarg3);
|
||||
__ push_ppx(c_rarg2);
|
||||
__ push_ppx(c_rarg1);
|
||||
|
||||
__ movl(rax, 0x7f800000);
|
||||
__ xorl(c_rarg3, c_rarg3);
|
||||
@ -660,10 +660,10 @@ address StubGenerator::generate_f2l_fixup() {
|
||||
__ bind(L);
|
||||
__ movptr(inout, c_rarg3);
|
||||
|
||||
__ pop(c_rarg1);
|
||||
__ pop(c_rarg2);
|
||||
__ pop(c_rarg3);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(c_rarg1);
|
||||
__ pop_ppx(c_rarg2);
|
||||
__ pop_ppx(c_rarg3);
|
||||
__ pop_ppx(rax);
|
||||
|
||||
__ ret(0);
|
||||
|
||||
@ -679,11 +679,11 @@ address StubGenerator::generate_d2i_fixup() {
|
||||
|
||||
Label L;
|
||||
|
||||
__ push(rax);
|
||||
__ push(c_rarg3);
|
||||
__ push(c_rarg2);
|
||||
__ push(c_rarg1);
|
||||
__ push(c_rarg0);
|
||||
__ push_ppx(rax);
|
||||
__ push_ppx(c_rarg3);
|
||||
__ push_ppx(c_rarg2);
|
||||
__ push_ppx(c_rarg1);
|
||||
__ push_ppx(c_rarg0);
|
||||
|
||||
__ movl(rax, 0x7ff00000);
|
||||
__ movq(c_rarg2, inout);
|
||||
@ -707,11 +707,11 @@ address StubGenerator::generate_d2i_fixup() {
|
||||
__ bind(L);
|
||||
__ movptr(inout, c_rarg2);
|
||||
|
||||
__ pop(c_rarg0);
|
||||
__ pop(c_rarg1);
|
||||
__ pop(c_rarg2);
|
||||
__ pop(c_rarg3);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(c_rarg0);
|
||||
__ pop_ppx(c_rarg1);
|
||||
__ pop_ppx(c_rarg2);
|
||||
__ pop_ppx(c_rarg3);
|
||||
__ pop_ppx(rax);
|
||||
|
||||
__ ret(0);
|
||||
|
||||
@ -727,11 +727,11 @@ address StubGenerator::generate_d2l_fixup() {
|
||||
|
||||
Label L;
|
||||
|
||||
__ push(rax);
|
||||
__ push(c_rarg3);
|
||||
__ push(c_rarg2);
|
||||
__ push(c_rarg1);
|
||||
__ push(c_rarg0);
|
||||
__ push_ppx(rax);
|
||||
__ push_ppx(c_rarg3);
|
||||
__ push_ppx(c_rarg2);
|
||||
__ push_ppx(c_rarg1);
|
||||
__ push_ppx(c_rarg0);
|
||||
|
||||
__ movl(rax, 0x7ff00000);
|
||||
__ movq(c_rarg2, inout);
|
||||
@ -755,11 +755,11 @@ address StubGenerator::generate_d2l_fixup() {
|
||||
__ bind(L);
|
||||
__ movq(inout, c_rarg2);
|
||||
|
||||
__ pop(c_rarg0);
|
||||
__ pop(c_rarg1);
|
||||
__ pop(c_rarg2);
|
||||
__ pop(c_rarg3);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(c_rarg0);
|
||||
__ pop_ppx(c_rarg1);
|
||||
__ pop_ppx(c_rarg2);
|
||||
__ pop_ppx(c_rarg3);
|
||||
__ pop_ppx(rax);
|
||||
|
||||
__ ret(0);
|
||||
|
||||
@ -1180,11 +1180,11 @@ address StubGenerator::generate_verify_oop() {
|
||||
__ pushf();
|
||||
__ incrementl(ExternalAddress((address) StubRoutines::verify_oop_count_addr()), rscratch1);
|
||||
|
||||
__ push(r12);
|
||||
__ push_ppx(r12);
|
||||
|
||||
// save c_rarg2 and c_rarg3
|
||||
__ push(c_rarg2);
|
||||
__ push(c_rarg3);
|
||||
__ push_ppx(c_rarg2);
|
||||
__ push_ppx(c_rarg3);
|
||||
|
||||
enum {
|
||||
// After previous pushes.
|
||||
@ -1211,9 +1211,9 @@ address StubGenerator::generate_verify_oop() {
|
||||
__ bind(exit);
|
||||
__ movptr(rax, Address(rsp, saved_rax)); // get saved rax back
|
||||
__ movptr(rscratch1, Address(rsp, saved_r10)); // get saved r10 back
|
||||
__ pop(c_rarg3); // restore c_rarg3
|
||||
__ pop(c_rarg2); // restore c_rarg2
|
||||
__ pop(r12); // restore r12
|
||||
__ pop_ppx(c_rarg3); // restore c_rarg3
|
||||
__ pop_ppx(c_rarg2); // restore c_rarg2
|
||||
__ pop_ppx(r12); // restore r12
|
||||
__ popf(); // restore flags
|
||||
__ ret(4 * wordSize); // pop caller saved stuff
|
||||
|
||||
@ -1221,9 +1221,9 @@ address StubGenerator::generate_verify_oop() {
|
||||
__ bind(error);
|
||||
__ movptr(rax, Address(rsp, saved_rax)); // get saved rax back
|
||||
__ movptr(rscratch1, Address(rsp, saved_r10)); // get saved r10 back
|
||||
__ pop(c_rarg3); // get saved c_rarg3 back
|
||||
__ pop(c_rarg2); // get saved c_rarg2 back
|
||||
__ pop(r12); // get saved r12 back
|
||||
__ pop_ppx(c_rarg3); // get saved c_rarg3 back
|
||||
__ pop_ppx(c_rarg2); // get saved c_rarg2 back
|
||||
__ pop_ppx(r12); // get saved r12 back
|
||||
__ popf(); // get saved flags off stack --
|
||||
// will be ignored
|
||||
|
||||
@ -1431,10 +1431,10 @@ address StubGenerator::generate_md5_implCompress(StubId stub_id) {
|
||||
const Address limit_param(rsp, 1 * wordSize + 4);
|
||||
|
||||
__ enter();
|
||||
__ push(rbx);
|
||||
__ push(rdi);
|
||||
__ push(rsi);
|
||||
__ push(r15);
|
||||
__ push_ppx(rbx);
|
||||
__ push_ppx(rdi);
|
||||
__ push_ppx(rsi);
|
||||
__ push_ppx(r15);
|
||||
__ subptr(rsp, 2 * wordSize);
|
||||
|
||||
__ movptr(buf_param, c_rarg0);
|
||||
@ -1446,10 +1446,10 @@ address StubGenerator::generate_md5_implCompress(StubId stub_id) {
|
||||
__ fast_md5(buf_param, state_param, ofs_param, limit_param, multi_block);
|
||||
|
||||
__ addptr(rsp, 2 * wordSize);
|
||||
__ pop(r15);
|
||||
__ pop(rsi);
|
||||
__ pop(rdi);
|
||||
__ pop(rbx);
|
||||
__ pop_ppx(r15);
|
||||
__ pop_ppx(rsi);
|
||||
__ pop_ppx(rdi);
|
||||
__ pop_ppx(rbx);
|
||||
__ leave();
|
||||
__ ret(0);
|
||||
|
||||
@ -1790,10 +1790,10 @@ address StubGenerator::generate_base64_encodeBlock()
|
||||
__ enter();
|
||||
|
||||
// Save callee-saved registers before using them
|
||||
__ push(r12);
|
||||
__ push(r13);
|
||||
__ push(r14);
|
||||
__ push(r15);
|
||||
__ push_ppx(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r14);
|
||||
__ push_ppx(r15);
|
||||
|
||||
// arguments
|
||||
const Register source = c_rarg0; // Source Array
|
||||
@ -2153,10 +2153,10 @@ address StubGenerator::generate_base64_encodeBlock()
|
||||
__ jcc(Assembler::aboveEqual, L_processdata);
|
||||
|
||||
__ BIND(L_exit);
|
||||
__ pop(r15);
|
||||
__ pop(r14);
|
||||
__ pop(r13);
|
||||
__ pop(r12);
|
||||
__ pop_ppx(r15);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r13);
|
||||
__ pop_ppx(r12);
|
||||
__ leave();
|
||||
__ ret(0);
|
||||
|
||||
@ -2490,11 +2490,11 @@ address StubGenerator::generate_base64_decodeBlock() {
|
||||
__ enter();
|
||||
|
||||
// Save callee-saved registers before using them
|
||||
__ push(r12);
|
||||
__ push(r13);
|
||||
__ push(r14);
|
||||
__ push(r15);
|
||||
__ push(rbx);
|
||||
__ push_ppx(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r14);
|
||||
__ push_ppx(r15);
|
||||
__ push_ppx(rbx);
|
||||
|
||||
// arguments
|
||||
const Register source = c_rarg0; // Source Array
|
||||
@ -2562,7 +2562,7 @@ address StubGenerator::generate_base64_decodeBlock() {
|
||||
// calculate length from offsets
|
||||
__ movl(length, end_offset);
|
||||
__ subl(length, start_offset);
|
||||
__ push(dest); // Save for return value calc
|
||||
__ push_ppx(dest); // Save for return value calc
|
||||
|
||||
// If AVX512 VBMI not supported, just compile non-AVX code
|
||||
if(VM_Version::supports_avx512_vbmi() &&
|
||||
@ -2793,14 +2793,14 @@ address StubGenerator::generate_base64_decodeBlock() {
|
||||
|
||||
__ BIND(L_exit);
|
||||
__ vzeroupper();
|
||||
__ pop(rax); // Get original dest value
|
||||
__ pop_ppx(rax); // Get original dest value
|
||||
__ subptr(dest, rax); // Number of bytes converted
|
||||
__ movptr(rax, dest);
|
||||
__ pop(rbx);
|
||||
__ pop(r15);
|
||||
__ pop(r14);
|
||||
__ pop(r13);
|
||||
__ pop(r12);
|
||||
__ pop_ppx(rbx);
|
||||
__ pop_ppx(r15);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r13);
|
||||
__ pop_ppx(r12);
|
||||
__ leave();
|
||||
__ ret(0);
|
||||
|
||||
@ -2987,14 +2987,14 @@ address StubGenerator::generate_base64_decodeBlock() {
|
||||
__ jcc(Assembler::positive, L_forceLoop);
|
||||
|
||||
__ BIND(L_exit_no_vzero);
|
||||
__ pop(rax); // Get original dest value
|
||||
__ subptr(dest, rax); // Number of bytes converted
|
||||
__ pop_ppx(rax); // Get original dest value
|
||||
__ subptr(dest, rax); // Number of bytes converted
|
||||
__ movptr(rax, dest);
|
||||
__ pop(rbx);
|
||||
__ pop(r15);
|
||||
__ pop(r14);
|
||||
__ pop(r13);
|
||||
__ pop(r12);
|
||||
__ pop_ppx(rbx);
|
||||
__ pop_ppx(r15);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r13);
|
||||
__ pop_ppx(r12);
|
||||
__ leave();
|
||||
__ ret(0);
|
||||
|
||||
@ -3117,8 +3117,8 @@ address StubGenerator::generate_updateBytesCRC32C(bool is_pclmulqdq_supported) {
|
||||
__ bind(L_doSmall);
|
||||
}
|
||||
#ifdef _WIN64
|
||||
__ push(y);
|
||||
__ push(z);
|
||||
__ push_ppx(y);
|
||||
__ push_ppx(z);
|
||||
#endif
|
||||
__ crc32c_ipl_alg2_alt2(crc, buf, len,
|
||||
a, j, k,
|
||||
@ -3126,8 +3126,8 @@ address StubGenerator::generate_updateBytesCRC32C(bool is_pclmulqdq_supported) {
|
||||
c_farg0, c_farg1, c_farg2,
|
||||
is_pclmulqdq_supported);
|
||||
#ifdef _WIN64
|
||||
__ pop(z);
|
||||
__ pop(y);
|
||||
__ pop_ppx(z);
|
||||
__ pop_ppx(y);
|
||||
#endif
|
||||
|
||||
__ bind(L_continue);
|
||||
@ -3313,7 +3313,7 @@ address StubGenerator::generate_method_entry_barrier() {
|
||||
|
||||
// save c_rarg0, because we want to use that value.
|
||||
// We could do without it but then we depend on the number of slots used by pusha
|
||||
__ push(c_rarg0);
|
||||
__ push_ppx(c_rarg0);
|
||||
|
||||
__ lea(c_rarg0, Address(rsp, wordSize * 3)); // 1 for cookie, 1 for rbp, 1 for c_rarg0 - this should be the return address
|
||||
|
||||
@ -3350,7 +3350,7 @@ address StubGenerator::generate_method_entry_barrier() {
|
||||
__ jcc(Assembler::equal, deoptimize_label);
|
||||
|
||||
__ popa();
|
||||
__ pop(c_rarg0);
|
||||
__ pop_ppx(c_rarg0);
|
||||
|
||||
__ leave();
|
||||
|
||||
@ -3361,7 +3361,7 @@ address StubGenerator::generate_method_entry_barrier() {
|
||||
__ BIND(deoptimize_label);
|
||||
|
||||
__ popa();
|
||||
__ pop(c_rarg0);
|
||||
__ pop_ppx(c_rarg0);
|
||||
|
||||
__ leave();
|
||||
|
||||
@ -3465,10 +3465,10 @@ address StubGenerator::generate_bigIntegerRightShift() {
|
||||
// For windows, since last argument is on stack, we need to move it to the appropriate register.
|
||||
__ movl(totalNumIter, Address(rsp, 6 * wordSize));
|
||||
// Save callee save registers.
|
||||
__ push(tmp3);
|
||||
__ push(tmp4);
|
||||
__ push_ppx(tmp3);
|
||||
__ push_ppx(tmp4);
|
||||
#endif
|
||||
__ push(tmp5);
|
||||
__ push_ppx(tmp5);
|
||||
|
||||
// Rename temps used throughout the code.
|
||||
const Register idx = tmp1;
|
||||
@ -3541,10 +3541,10 @@ address StubGenerator::generate_bigIntegerRightShift() {
|
||||
__ BIND(Exit);
|
||||
__ vzeroupper();
|
||||
// Restore callee save registers.
|
||||
__ pop(tmp5);
|
||||
__ pop_ppx(tmp5);
|
||||
#ifdef _WIN64
|
||||
__ pop(tmp4);
|
||||
__ pop(tmp3);
|
||||
__ pop_ppx(tmp4);
|
||||
__ pop_ppx(tmp3);
|
||||
restore_arg_regs();
|
||||
#endif
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
@ -3598,10 +3598,10 @@ address StubGenerator::generate_bigIntegerLeftShift() {
|
||||
// For windows, since last argument is on stack, we need to move it to the appropriate register.
|
||||
__ movl(totalNumIter, Address(rsp, 6 * wordSize));
|
||||
// Save callee save registers.
|
||||
__ push(tmp3);
|
||||
__ push(tmp4);
|
||||
__ push_ppx(tmp3);
|
||||
__ push_ppx(tmp4);
|
||||
#endif
|
||||
__ push(tmp5);
|
||||
__ push_ppx(tmp5);
|
||||
|
||||
// Rename temps used throughout the code
|
||||
const Register idx = tmp1;
|
||||
@ -3666,10 +3666,10 @@ address StubGenerator::generate_bigIntegerLeftShift() {
|
||||
__ BIND(Exit);
|
||||
__ vzeroupper();
|
||||
// Restore callee save registers.
|
||||
__ pop(tmp5);
|
||||
__ pop_ppx(tmp5);
|
||||
#ifdef _WIN64
|
||||
__ pop(tmp4);
|
||||
__ pop(tmp3);
|
||||
__ pop_ppx(tmp4);
|
||||
__ pop_ppx(tmp3);
|
||||
restore_arg_regs();
|
||||
#endif
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
@ -3813,7 +3813,7 @@ address StubGenerator::generate_cont_thaw(StubId stub_id) {
|
||||
|
||||
if (return_barrier) {
|
||||
// Preserve possible return value from a method returning to the return barrier.
|
||||
__ push(rax);
|
||||
__ push_ppx(rax);
|
||||
__ push_d(xmm0);
|
||||
}
|
||||
|
||||
@ -3826,7 +3826,7 @@ address StubGenerator::generate_cont_thaw(StubId stub_id) {
|
||||
// Restore return value from a method returning to the return barrier.
|
||||
// No safepoint in the call to thaw, so even an oop return value should be OK.
|
||||
__ pop_d(xmm0);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(rax);
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
@ -3852,7 +3852,7 @@ address StubGenerator::generate_cont_thaw(StubId stub_id) {
|
||||
|
||||
if (return_barrier) {
|
||||
// Preserve possible return value from a method returning to the return barrier. (Again.)
|
||||
__ push(rax);
|
||||
__ push_ppx(rax);
|
||||
__ push_d(xmm0);
|
||||
}
|
||||
|
||||
@ -3866,7 +3866,7 @@ address StubGenerator::generate_cont_thaw(StubId stub_id) {
|
||||
// Restore return value from a method returning to the return barrier. (Again.)
|
||||
// No safepoint in the call to thaw, so even an oop return value should be OK.
|
||||
__ pop_d(xmm0);
|
||||
__ pop(rax);
|
||||
__ pop_ppx(rax);
|
||||
} else {
|
||||
// Return 0 (success) from doYield.
|
||||
__ xorptr(rax, rax);
|
||||
@ -3882,7 +3882,7 @@ address StubGenerator::generate_cont_thaw(StubId stub_id) {
|
||||
__ movptr(c_rarg1, Address(rsp, wordSize)); // return address
|
||||
|
||||
// rax still holds the original exception oop, save it before the call
|
||||
__ push(rax);
|
||||
__ push_ppx(rax);
|
||||
|
||||
__ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), 2);
|
||||
__ movptr(rbx, rax);
|
||||
@ -3891,7 +3891,7 @@ address StubGenerator::generate_cont_thaw(StubId stub_id) {
|
||||
// rax: exception oop
|
||||
// rbx: exception handler
|
||||
// rdx: exception pc
|
||||
__ pop(rax);
|
||||
__ pop_ppx(rax);
|
||||
__ verify_oop(rax);
|
||||
__ pop(rbp); // pop out RBP here too
|
||||
__ pop(rdx);
|
||||
@ -4095,15 +4095,11 @@ void StubGenerator::generate_initial_stubs() {
|
||||
StubRoutines::x86::_double_sign_flip = generate_fp_mask(StubId::stubgen_double_sign_flip_id, 0x8000000000000000);
|
||||
|
||||
if (UseCRC32Intrinsics) {
|
||||
// set table address before stub generation which use it
|
||||
StubRoutines::_crc_table_adr = (address)StubRoutines::x86::_crc_table;
|
||||
StubRoutines::_updateBytesCRC32 = generate_updateBytesCRC32();
|
||||
}
|
||||
|
||||
if (UseCRC32CIntrinsics) {
|
||||
bool supports_clmul = VM_Version::supports_clmul();
|
||||
StubRoutines::x86::generate_CRC32C_table(supports_clmul);
|
||||
StubRoutines::_crc32c_table_addr = (address)StubRoutines::x86::_crc32c_table;
|
||||
StubRoutines::_updateBytesCRC32C = generate_updateBytesCRC32C(supports_clmul);
|
||||
}
|
||||
|
||||
|
||||
@ -279,14 +279,14 @@ address StubGenerator::generate_galoisCounterMode_AESCrypt() {
|
||||
#endif
|
||||
__ enter();
|
||||
// Save state before entering routine
|
||||
__ push(r12);//holds pointer to avx512_subkeyHtbl
|
||||
__ push(r14);//holds CTR_CHECK value to check for overflow
|
||||
__ push(r15);//holds number of rounds
|
||||
__ push(rbx);//scratch register
|
||||
__ push_ppx(r12);//holds pointer to avx512_subkeyHtbl
|
||||
__ push_ppx(r14);//holds CTR_CHECK value to check for overflow
|
||||
__ push_ppx(r15);//holds number of rounds
|
||||
__ push_ppx(rbx);//scratch register
|
||||
#ifdef _WIN64
|
||||
// on win64, fill len_reg from stack position
|
||||
__ push(rsi);
|
||||
__ push(rdi);
|
||||
__ push_ppx(rsi);
|
||||
__ push_ppx(rdi);
|
||||
__ movptr(key, key_mem);
|
||||
__ movptr(state, state_mem);
|
||||
#endif
|
||||
@ -304,15 +304,15 @@ address StubGenerator::generate_galoisCounterMode_AESCrypt() {
|
||||
// Restore state before leaving routine
|
||||
#ifdef _WIN64
|
||||
__ lea(rsp, Address(rbp, -6 * wordSize));
|
||||
__ pop(rdi);
|
||||
__ pop(rsi);
|
||||
__ pop_ppx(rdi);
|
||||
__ pop_ppx(rsi);
|
||||
#else
|
||||
__ lea(rsp, Address(rbp, -4 * wordSize));
|
||||
#endif
|
||||
__ pop(rbx);
|
||||
__ pop(r15);
|
||||
__ pop(r14);
|
||||
__ pop(r12);
|
||||
__ pop_ppx(rbx);
|
||||
__ pop_ppx(r15);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r12);
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -364,15 +364,15 @@ address StubGenerator::generate_avx2_galoisCounterMode_AESCrypt() {
|
||||
#endif
|
||||
__ enter();
|
||||
// Save state before entering routine
|
||||
__ push(r12);
|
||||
__ push(r13);
|
||||
__ push(r14);
|
||||
__ push(r15);
|
||||
__ push(rbx);
|
||||
__ push_ppx(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r14);
|
||||
__ push_ppx(r15);
|
||||
__ push_ppx(rbx);
|
||||
#ifdef _WIN64
|
||||
// on win64, fill len_reg from stack position
|
||||
__ push(rsi);
|
||||
__ push(rdi);
|
||||
__ push_ppx(rsi);
|
||||
__ push_ppx(rdi);
|
||||
__ movptr(key, key_mem);
|
||||
__ movptr(state, state_mem);
|
||||
#endif
|
||||
@ -390,14 +390,14 @@ address StubGenerator::generate_avx2_galoisCounterMode_AESCrypt() {
|
||||
__ movq(rsp, r14);
|
||||
// Restore state before leaving routine
|
||||
#ifdef _WIN64
|
||||
__ pop(rdi);
|
||||
__ pop(rsi);
|
||||
__ pop_ppx(rdi);
|
||||
__ pop_ppx(rsi);
|
||||
#endif
|
||||
__ pop(rbx);
|
||||
__ pop(r15);
|
||||
__ pop(r14);
|
||||
__ pop(r13);
|
||||
__ pop(r12);
|
||||
__ pop_ppx(rbx);
|
||||
__ pop_ppx(r15);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r13);
|
||||
__ pop_ppx(r12);
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -434,10 +434,10 @@ address StubGenerator::generate_counterMode_VectorAESCrypt() {
|
||||
#endif
|
||||
__ enter();
|
||||
// Save state before entering routine
|
||||
__ push(r12);
|
||||
__ push(r13);
|
||||
__ push(r14);
|
||||
__ push(r15);
|
||||
__ push_ppx(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r14);
|
||||
__ push_ppx(r15);
|
||||
#ifdef _WIN64
|
||||
// on win64, fill len_reg from stack position
|
||||
__ movl(len_reg, len_mem);
|
||||
@ -445,26 +445,26 @@ address StubGenerator::generate_counterMode_VectorAESCrypt() {
|
||||
__ movptr(used_addr, used_mem);
|
||||
__ movl(used, Address(used_addr, 0));
|
||||
#else
|
||||
__ push(len_reg); // Save
|
||||
__ push_ppx(len_reg); // Save
|
||||
__ movptr(used_addr, used_mem);
|
||||
__ movl(used, Address(used_addr, 0));
|
||||
#endif
|
||||
__ push(rbx);
|
||||
__ push_ppx(rbx);
|
||||
|
||||
aesctr_encrypt(from, to, key, counter, len_reg, used, used_addr, saved_encCounter_start);
|
||||
|
||||
__ vzeroupper();
|
||||
// Restore state before leaving routine
|
||||
__ pop(rbx);
|
||||
__ pop_ppx(rbx);
|
||||
#ifdef _WIN64
|
||||
__ movl(rax, len_mem); // return length
|
||||
#else
|
||||
__ pop(rax); // return length
|
||||
__ pop_ppx(rax); // return length
|
||||
#endif
|
||||
__ pop(r15);
|
||||
__ pop(r14);
|
||||
__ pop(r13);
|
||||
__ pop(r12);
|
||||
__ pop_ppx(r15);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r13);
|
||||
__ pop_ppx(r12);
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -576,12 +576,12 @@ address StubGenerator::generate_counterMode_AESCrypt_Parallel() {
|
||||
__ movptr(used_addr, used_mem);
|
||||
__ movl(used, Address(used_addr, 0));
|
||||
#else
|
||||
__ push(len_reg); // Save
|
||||
__ push_ppx(len_reg); // Save
|
||||
__ movptr(used_addr, used_mem);
|
||||
__ movl(used, Address(used_addr, 0));
|
||||
#endif
|
||||
|
||||
__ push(rbx); // Save RBX
|
||||
__ push_ppx(rbx); // Save RBX
|
||||
__ movdqu(xmm_curr_counter, Address(counter, 0x00)); // initialize counter with initial counter
|
||||
__ movdqu(xmm_counter_shuf_mask, ExternalAddress(counter_shuffle_mask_addr()), pos /*rscratch*/);
|
||||
__ pshufb(xmm_curr_counter, xmm_counter_shuf_mask); //counter is shuffled
|
||||
@ -767,14 +767,14 @@ address StubGenerator::generate_counterMode_AESCrypt_Parallel() {
|
||||
__ BIND(L_exit);
|
||||
__ pshufb(xmm_curr_counter, xmm_counter_shuf_mask); //counter is shuffled back.
|
||||
__ movdqu(Address(counter, 0), xmm_curr_counter); //save counter back
|
||||
__ pop(rbx); // pop the saved RBX.
|
||||
__ pop_ppx(rbx); // pop the saved RBX.
|
||||
#ifdef _WIN64
|
||||
__ movl(rax, len_mem);
|
||||
__ movptr(r13, Address(rsp, saved_r13_offset * wordSize));
|
||||
__ movptr(r14, Address(rsp, saved_r14_offset * wordSize));
|
||||
__ addptr(rsp, 2 * wordSize);
|
||||
#else
|
||||
__ pop(rax); // return 'len'
|
||||
__ pop_ppx(rax); // return 'len'
|
||||
#endif
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -810,9 +810,9 @@ address StubGenerator::generate_cipherBlockChaining_decryptVectorAESCrypt() {
|
||||
// on win64, fill len_reg from stack position
|
||||
__ movl(len_reg, len_mem);
|
||||
#else
|
||||
__ push(len_reg); // Save
|
||||
__ push_ppx(len_reg); // Save
|
||||
#endif
|
||||
__ push(rbx);
|
||||
__ push_ppx(rbx);
|
||||
__ vzeroupper();
|
||||
|
||||
// Temporary variable declaration for swapping key bytes
|
||||
@ -1046,11 +1046,11 @@ address StubGenerator::generate_cipherBlockChaining_decryptVectorAESCrypt() {
|
||||
|
||||
__ BIND(Lcbc_exit);
|
||||
__ vzeroupper();
|
||||
__ pop(rbx);
|
||||
__ pop_ppx(rbx);
|
||||
#ifdef _WIN64
|
||||
__ movl(rax, len_mem);
|
||||
#else
|
||||
__ pop(rax); // return length
|
||||
__ pop_ppx(rax); // return length
|
||||
#endif
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1301,7 +1301,7 @@ address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() {
|
||||
// on win64, fill len_reg from stack position
|
||||
__ movl(len_reg, len_mem);
|
||||
#else
|
||||
__ push(len_reg); // Save
|
||||
__ push_ppx(len_reg); // Save
|
||||
#endif
|
||||
|
||||
const XMMRegister xmm_key_shuf_mask = xmm_temp; // used temporarily to swap key bytes up front
|
||||
@ -1343,7 +1343,7 @@ address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() {
|
||||
#ifdef _WIN64
|
||||
__ movl(rax, len_mem);
|
||||
#else
|
||||
__ pop(rax); // return length
|
||||
__ pop_ppx(rax); // return length
|
||||
#endif
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1456,9 +1456,9 @@ address StubGenerator::generate_cipherBlockChaining_decryptAESCrypt_Parallel() {
|
||||
// on win64, fill len_reg from stack position
|
||||
__ movl(len_reg, len_mem);
|
||||
#else
|
||||
__ push(len_reg); // Save
|
||||
__ push_ppx(len_reg); // Save
|
||||
#endif
|
||||
__ push(rbx);
|
||||
__ push_ppx(rbx);
|
||||
// the java expanded key ordering is rotated one position from what we want
|
||||
// so we start from 0x10 here and hit 0x00 last
|
||||
const XMMRegister xmm_key_shuf_mask = xmm1; // used temporarily to swap key bytes up front
|
||||
@ -1646,11 +1646,11 @@ __ opc(xmm_result3, src_reg); \
|
||||
|
||||
__ BIND(L_exit);
|
||||
__ movdqu(Address(rvec, 0), xmm_prev_block_cipher); // final value of r stored in rvec of CipherBlockChaining object
|
||||
__ pop(rbx);
|
||||
__ pop_ppx(rbx);
|
||||
#ifdef _WIN64
|
||||
__ movl(rax, len_mem);
|
||||
#else
|
||||
__ pop(rax); // return length
|
||||
__ pop_ppx(rax); // return length
|
||||
#endif
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
@ -1801,8 +1801,8 @@ void StubGenerator::aesecb_encrypt(Register src_addr, Register dest_addr, Regist
|
||||
const Register rounds = r12;
|
||||
|
||||
Label NO_PARTS, LOOP, Loop_start, LOOP2, AES192, END_LOOP, AES256, REMAINDER, LAST2, END, KEY_192, KEY_256, EXIT;
|
||||
__ push(r13);
|
||||
__ push(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r12);
|
||||
|
||||
// For EVEX with VL and BW, provide a standard mask, VL = 128 will guide the merge
|
||||
// context for the registers used, where all instructions below are using 128-bit mode
|
||||
@ -1811,8 +1811,8 @@ void StubGenerator::aesecb_encrypt(Register src_addr, Register dest_addr, Regist
|
||||
__ movl(rax, 0xffff);
|
||||
__ kmovql(k1, rax);
|
||||
}
|
||||
__ push(len); // Save
|
||||
__ push(rbx);
|
||||
__ push_ppx(len); // Save
|
||||
__ push_ppx(rbx);
|
||||
|
||||
__ vzeroupper();
|
||||
|
||||
@ -1999,10 +1999,10 @@ void StubGenerator::aesecb_encrypt(Register src_addr, Register dest_addr, Regist
|
||||
__ evpxorq(xmm21, xmm21, xmm21, Assembler::AVX_512bit);
|
||||
__ evpxorq(xmm22, xmm22, xmm22, Assembler::AVX_512bit);
|
||||
__ bind(EXIT);
|
||||
__ pop(rbx);
|
||||
__ pop(rax); // return length
|
||||
__ pop(r12);
|
||||
__ pop(r13);
|
||||
__ pop_ppx(rbx);
|
||||
__ pop_ppx(rax); // return length
|
||||
__ pop_ppx(r12);
|
||||
__ pop_ppx(r13);
|
||||
}
|
||||
|
||||
// AES-ECB Decrypt Operation
|
||||
@ -2011,8 +2011,8 @@ void StubGenerator::aesecb_decrypt(Register src_addr, Register dest_addr, Regist
|
||||
Label NO_PARTS, LOOP, Loop_start, LOOP2, AES192, END_LOOP, AES256, REMAINDER, LAST2, END, KEY_192, KEY_256, EXIT;
|
||||
const Register pos = rax;
|
||||
const Register rounds = r12;
|
||||
__ push(r13);
|
||||
__ push(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r12);
|
||||
|
||||
// For EVEX with VL and BW, provide a standard mask, VL = 128 will guide the merge
|
||||
// context for the registers used, where all instructions below are using 128-bit mode
|
||||
@ -2022,8 +2022,8 @@ void StubGenerator::aesecb_decrypt(Register src_addr, Register dest_addr, Regist
|
||||
__ kmovql(k1, rax);
|
||||
}
|
||||
|
||||
__ push(len); // Save
|
||||
__ push(rbx);
|
||||
__ push_ppx(len); // Save
|
||||
__ push_ppx(rbx);
|
||||
|
||||
__ vzeroupper();
|
||||
|
||||
@ -2210,10 +2210,10 @@ void StubGenerator::aesecb_decrypt(Register src_addr, Register dest_addr, Regist
|
||||
__ evpxorq(xmm22, xmm22, xmm22, Assembler::AVX_512bit);
|
||||
|
||||
__ bind(EXIT);
|
||||
__ pop(rbx);
|
||||
__ pop(rax); // return length
|
||||
__ pop(r12);
|
||||
__ pop(r13);
|
||||
__ pop_ppx(rbx);
|
||||
__ pop_ppx(rax); // return length
|
||||
__ pop_ppx(r12);
|
||||
__ pop_ppx(r13);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -2943,7 +2943,7 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh
|
||||
__ enter(); // required for proper stackwalking of RuntimeStub frame
|
||||
|
||||
#ifdef _WIN64
|
||||
__ push(rklass_tmp); // rdi is callee-save on Windows
|
||||
__ push_ppx(rklass_tmp); // rdi is callee-save on Windows
|
||||
#endif
|
||||
|
||||
// bump this on entry, not on exit:
|
||||
@ -3077,7 +3077,7 @@ address StubGenerator::generate_generic_copy(address byte_copy_entry, address sh
|
||||
__ andl(rax_lh, Klass::_lh_log2_element_size_mask); // rax_lh -> rax_elsize
|
||||
|
||||
#ifdef _WIN64
|
||||
__ pop(rklass_tmp); // Restore callee-save rdi
|
||||
__ pop_ppx(rklass_tmp); // Restore callee-save rdi
|
||||
#endif
|
||||
|
||||
// next registers should be set before the jump to corresponding stub
|
||||
@ -3149,7 +3149,7 @@ __ BIND(L_objArray);
|
||||
__ movl2ptr(count, r11_length); // length
|
||||
__ BIND(L_plain_copy);
|
||||
#ifdef _WIN64
|
||||
__ pop(rklass_tmp); // Restore callee-save rdi
|
||||
__ pop_ppx(rklass_tmp); // Restore callee-save rdi
|
||||
#endif
|
||||
__ jump(RuntimeAddress(oop_copy_entry));
|
||||
|
||||
@ -3191,7 +3191,7 @@ __ BIND(L_checkcast_copy);
|
||||
assert_clean_int(sco_temp, rax);
|
||||
|
||||
#ifdef _WIN64
|
||||
__ pop(rklass_tmp); // Restore callee-save rdi
|
||||
__ pop_ppx(rklass_tmp); // Restore callee-save rdi
|
||||
#endif
|
||||
|
||||
// the checkcast_copy loop needs two extra arguments:
|
||||
@ -3204,7 +3204,7 @@ __ BIND(L_checkcast_copy);
|
||||
|
||||
__ BIND(L_failed);
|
||||
#ifdef _WIN64
|
||||
__ pop(rklass_tmp); // Restore callee-save rdi
|
||||
__ pop_ppx(rklass_tmp); // Restore callee-save rdi
|
||||
#endif
|
||||
__ xorptr(rax, rax);
|
||||
__ notptr(rax); // return -1
|
||||
|
||||
@ -185,11 +185,11 @@ address StubGenerator::generate_libmCos() {
|
||||
__ enter(); // required for proper stackwalking of RuntimeStub frame
|
||||
|
||||
#ifdef _WIN64
|
||||
__ push(rsi);
|
||||
__ push(rdi);
|
||||
__ push_ppx(rsi);
|
||||
__ push_ppx(rdi);
|
||||
#endif
|
||||
|
||||
__ push(rbx);
|
||||
__ push_ppx(rbx);
|
||||
__ subq(rsp, 16);
|
||||
__ movsd(Address(rsp, 8), xmm0);
|
||||
|
||||
@ -609,11 +609,11 @@ address StubGenerator::generate_libmCos() {
|
||||
|
||||
__ bind(B1_4);
|
||||
__ addq(rsp, 16);
|
||||
__ pop(rbx);
|
||||
__ pop_ppx(rbx);
|
||||
|
||||
#ifdef _WIN64
|
||||
__ pop(rdi);
|
||||
__ pop(rsi);
|
||||
__ pop_ppx(rdi);
|
||||
__ pop_ppx(rsi);
|
||||
#endif
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
|
||||
@ -105,7 +105,7 @@ address StubGenerator::generate_ghash_processBlocks() {
|
||||
|
||||
__ enter();
|
||||
|
||||
__ push(rbx); // scratch
|
||||
__ push_ppx(rbx); // scratch
|
||||
|
||||
__ movdqu(xmm_temp10, ExternalAddress(ghash_long_swap_mask_addr()), rbx /*rscratch*/);
|
||||
|
||||
@ -206,7 +206,7 @@ address StubGenerator::generate_ghash_processBlocks() {
|
||||
__ pshufb(xmm_temp6, xmm_temp10); // Byte swap 16-byte result
|
||||
__ movdqu(Address(state, 0), xmm_temp6); // store the result
|
||||
|
||||
__ pop(rbx);
|
||||
__ pop_ppx(rbx);
|
||||
|
||||
__ leave();
|
||||
__ ret(0);
|
||||
@ -229,11 +229,11 @@ address StubGenerator::generate_avx_ghash_processBlocks() {
|
||||
const Register data = c_rarg2;
|
||||
const Register blocks = c_rarg3;
|
||||
__ enter();
|
||||
__ push(rbx);
|
||||
__ push_ppx(rbx);
|
||||
|
||||
avx_ghash(state, htbl, data, blocks);
|
||||
|
||||
__ pop(rbx);
|
||||
__ pop_ppx(rbx);
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
|
||||
|
||||
@ -603,7 +603,7 @@ address generate_kyberNttMult_avx512(StubGenerator *stubgen,
|
||||
const Register perms = r11;
|
||||
const Register loopCnt = r12;
|
||||
|
||||
__ push(r12);
|
||||
__ push_ppx(r12);
|
||||
__ movl(loopCnt, 2);
|
||||
|
||||
Label Loop;
|
||||
@ -692,7 +692,7 @@ address generate_kyberNttMult_avx512(StubGenerator *stubgen,
|
||||
__ subl(loopCnt, 1);
|
||||
__ jcc(Assembler::greater, Loop);
|
||||
|
||||
__ pop(r12);
|
||||
__ pop_ppx(r12);
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ mov64(rax, 0); // return 0
|
||||
|
||||
@ -916,15 +916,15 @@ address StubGenerator::generate_poly1305_processBlocks() {
|
||||
__ enter();
|
||||
|
||||
// Save all 'SOE' registers
|
||||
__ push(rbx);
|
||||
__ push_ppx(rbx);
|
||||
#ifdef _WIN64
|
||||
__ push(rsi);
|
||||
__ push(rdi);
|
||||
__ push_ppx(rsi);
|
||||
__ push_ppx(rdi);
|
||||
#endif
|
||||
__ push(r12);
|
||||
__ push(r13);
|
||||
__ push(r14);
|
||||
__ push(r15);
|
||||
__ push_ppx(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r14);
|
||||
__ push_ppx(r15);
|
||||
|
||||
// Register Map
|
||||
const Register input = rdi; // msg
|
||||
@ -1016,15 +1016,15 @@ address StubGenerator::generate_poly1305_processBlocks() {
|
||||
// Write output
|
||||
poly1305_limbs_out(a0, a1, a2, accumulator, t0, t1);
|
||||
|
||||
__ pop(r15);
|
||||
__ pop(r14);
|
||||
__ pop(r13);
|
||||
__ pop(r12);
|
||||
__ pop_ppx(r15);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r13);
|
||||
__ pop_ppx(r12);
|
||||
#ifdef _WIN64
|
||||
__ pop(rdi);
|
||||
__ pop(rsi);
|
||||
__ pop_ppx(rdi);
|
||||
__ pop_ppx(rsi);
|
||||
#endif
|
||||
__ pop(rbx);
|
||||
__ pop_ppx(rbx);
|
||||
|
||||
__ leave();
|
||||
__ ret(0);
|
||||
@ -1169,7 +1169,7 @@ void StubGenerator::poly1305_process_blocks_avx2(
|
||||
|
||||
// Setup stack frame
|
||||
// Save rbp and rsp
|
||||
__ push(rbp);
|
||||
__ push_ppx(rbp);
|
||||
__ movq(rbp, rsp);
|
||||
// Align stack and reserve space
|
||||
__ andq(rsp, -32);
|
||||
@ -1483,7 +1483,7 @@ void StubGenerator::poly1305_process_blocks_avx2(
|
||||
|
||||
// Save rbp and rsp; clear stack frame
|
||||
__ movq(rsp, rbp);
|
||||
__ pop(rbp);
|
||||
__ pop_ppx(rbp);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -574,14 +574,14 @@ address StubGenerator::generate_intpoly_montgomeryMult_P256() {
|
||||
montgomeryMultiply(aLimbs, bLimbs, rLimbs, tmp, _masm);
|
||||
} else {
|
||||
assert(VM_Version::supports_avxifma(), "Require AVX_IFMA support");
|
||||
__ push(r12);
|
||||
__ push(r13);
|
||||
__ push(r14);
|
||||
__ push_ppx(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r14);
|
||||
#ifdef _WIN64
|
||||
__ push(rsi);
|
||||
__ push(rdi);
|
||||
__ push_ppx(rsi);
|
||||
__ push_ppx(rdi);
|
||||
#endif
|
||||
__ push(rbp);
|
||||
__ push_ppx(rbp);
|
||||
__ movq(rbp, rsp);
|
||||
__ andq(rsp, -32);
|
||||
__ subptr(rsp, 32);
|
||||
@ -608,14 +608,14 @@ address StubGenerator::generate_intpoly_montgomeryMult_P256() {
|
||||
tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, _masm);
|
||||
|
||||
__ movq(rsp, rbp);
|
||||
__ pop(rbp);
|
||||
__ pop_ppx(rbp);
|
||||
#ifdef _WIN64
|
||||
__ pop(rdi);
|
||||
__ pop(rsi);
|
||||
__ pop_ppx(rdi);
|
||||
__ pop_ppx(rsi);
|
||||
#endif
|
||||
__ pop(r14);
|
||||
__ pop(r13);
|
||||
__ pop(r12);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r13);
|
||||
__ pop_ppx(r12);
|
||||
}
|
||||
|
||||
__ leave();
|
||||
|
||||
@ -130,9 +130,9 @@ static address generate_sha3_implCompress(StubId stub_id,
|
||||
|
||||
__ enter();
|
||||
|
||||
__ push(r12);
|
||||
__ push(r13);
|
||||
__ push(r14);
|
||||
__ push_ppx(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r14);
|
||||
|
||||
#ifdef _WIN64
|
||||
// on win64, fill limit from stack position
|
||||
@ -309,9 +309,9 @@ static address generate_sha3_implCompress(StubId stub_id,
|
||||
__ evmovdquq(Address(state, i * 40), k5, xmm(i), true, Assembler::AVX_512bit);
|
||||
}
|
||||
|
||||
__ pop(r14);
|
||||
__ pop(r13);
|
||||
__ pop(r12);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r13);
|
||||
__ pop_ppx(r12);
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
|
||||
@ -195,11 +195,11 @@ address StubGenerator::generate_libmSin() {
|
||||
__ enter(); // required for proper stackwalking of RuntimeStub frame
|
||||
|
||||
#ifdef _WIN64
|
||||
__ push(rsi);
|
||||
__ push(rdi);
|
||||
__ push_ppx(rsi);
|
||||
__ push_ppx(rdi);
|
||||
#endif
|
||||
|
||||
__ push(rbx);
|
||||
__ push_ppx(rbx);
|
||||
__ subq(rsp, 16);
|
||||
__ movsd(Address(rsp, 8), xmm0);
|
||||
__ movl(rax, Address(rsp, 12));
|
||||
@ -635,11 +635,11 @@ address StubGenerator::generate_libmSin() {
|
||||
|
||||
__ bind(B1_4);
|
||||
__ addq(rsp, 16);
|
||||
__ pop(rbx);
|
||||
__ pop_ppx(rbx);
|
||||
|
||||
#ifdef _WIN64
|
||||
__ pop(rdi);
|
||||
__ pop(rsi);
|
||||
__ pop_ppx(rdi);
|
||||
__ pop_ppx(rsi);
|
||||
#endif
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
|
||||
@ -483,11 +483,11 @@ address StubGenerator::generate_libmTan() {
|
||||
__ enter(); // required for proper stackwalking of RuntimeStub frame
|
||||
|
||||
#ifdef _WIN64
|
||||
__ push(rsi);
|
||||
__ push(rdi);
|
||||
__ push_ppx(rsi);
|
||||
__ push_ppx(rdi);
|
||||
#endif
|
||||
|
||||
__ push(rbx);
|
||||
__ push_ppx(rbx);
|
||||
__ subq(rsp, 16);
|
||||
__ movsd(Address(rsp, 8), xmm0);
|
||||
|
||||
@ -1015,11 +1015,11 @@ address StubGenerator::generate_libmTan() {
|
||||
|
||||
__ bind(B1_4);
|
||||
__ addq(rsp, 16);
|
||||
__ pop(rbx);
|
||||
__ pop_ppx(rbx);
|
||||
|
||||
#ifdef _WIN64
|
||||
__ pop(rdi);
|
||||
__ pop(rsi);
|
||||
__ pop_ppx(rdi);
|
||||
__ pop_ppx(rsi);
|
||||
#endif
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
|
||||
@ -45,6 +45,17 @@ STUBGEN_ARCH_ENTRIES_DO(DEFINE_ARCH_ENTRY, DEFINE_ARCH_ENTRY_INIT)
|
||||
#undef DEFINE_ARCH_ENTRY_INIT
|
||||
#undef DEFINE_ARCH_ENTRY
|
||||
|
||||
address StubRoutines::crc_table_addr() {
|
||||
return (address)StubRoutines::x86::_crc_table;
|
||||
}
|
||||
address StubRoutines::crc32c_table_addr() {
|
||||
if (StubRoutines::x86::_crc32c_table == nullptr) {
|
||||
bool supports_clmul = VM_Version::supports_clmul();
|
||||
StubRoutines::x86::generate_CRC32C_table(supports_clmul);
|
||||
}
|
||||
return (address)StubRoutines::x86::_crc32c_table;
|
||||
}
|
||||
|
||||
address StubRoutines::x86::_k256_adr = nullptr;
|
||||
address StubRoutines::x86::_k256_W_adr = nullptr;
|
||||
address StubRoutines::x86::_k512_W_addr = nullptr;
|
||||
@ -291,7 +302,7 @@ static uint32_t crc32c_f_pow_n(uint32_t n) {
|
||||
return result;
|
||||
}
|
||||
|
||||
juint *StubRoutines::x86::_crc32c_table;
|
||||
juint* StubRoutines::x86::_crc32c_table = nullptr;
|
||||
|
||||
void StubRoutines::x86::generate_CRC32C_table(bool is_pclmulqdq_table_supported) {
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ enum platform_dependent_constants {
|
||||
|
||||
class x86 {
|
||||
friend class StubGenerator;
|
||||
friend class StubRoutines;
|
||||
friend class VMStructs;
|
||||
|
||||
// declare fields for arch-specific entries
|
||||
|
||||
@ -49,7 +49,7 @@ VM_Version::CpuidInfo VM_Version::_cpuid_info = { 0, };
|
||||
|
||||
#define DECLARE_CPU_FEATURE_NAME(id, name, bit) name,
|
||||
const char* VM_Version::_features_names[] = { CPU_FEATURE_FLAGS(DECLARE_CPU_FEATURE_NAME)};
|
||||
#undef DECLARE_CPU_FEATURE_FLAG
|
||||
#undef DECLARE_CPU_FEATURE_NAME
|
||||
|
||||
// Address of instruction which causes SEGV
|
||||
address VM_Version::_cpuinfo_segv_addr = nullptr;
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
do_arch_blob, \
|
||||
do_arch_entry, \
|
||||
do_arch_entry_init) \
|
||||
do_arch_blob(initial, 0) \
|
||||
do_arch_blob(initial, 32) \
|
||||
|
||||
|
||||
#define STUBGEN_CONTINUATION_BLOBS_ARCH_DO(do_stub, \
|
||||
@ -58,7 +58,7 @@
|
||||
do_arch_blob, \
|
||||
do_arch_entry, \
|
||||
do_arch_entry_init) \
|
||||
do_arch_blob(final, 0) \
|
||||
do_arch_blob(final, 32) \
|
||||
|
||||
|
||||
#endif // CPU_ZERO_STUBDECLARATIONS_HPP
|
||||
|
||||
@ -28,4 +28,5 @@
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
|
||||
// zero has no arch-specific stubs nor any associated entries
|
||||
address StubRoutines::crc_table_addr() { ShouldNotCallThis(); return nullptr; }
|
||||
address StubRoutines::crc32c_table_addr() { ShouldNotCallThis(); return nullptr; }
|
||||
|
||||
@ -1110,7 +1110,10 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ebuf == nullptr || ebuflen < 1) {
|
||||
// no error reporting requested
|
||||
return nullptr;
|
||||
}
|
||||
Events::log_dll_message(nullptr, "Loading shared library %s failed, %s", filename, error_report);
|
||||
log_info(os)("shared library load of %s failed, %s", filename, error_report);
|
||||
int diag_msg_max_length=ebuflen-strlen(ebuf);
|
||||
|
||||
@ -1685,6 +1685,11 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ebuf == nullptr || ebuflen < 1) {
|
||||
// no error reporting requested
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Elf32_Ehdr elf_head;
|
||||
size_t prefix_len = strlen(ebuf);
|
||||
ssize_t diag_msg_max_length = ebuflen - prefix_len;
|
||||
|
||||
@ -1729,6 +1729,12 @@ void * os::dll_load(const char *name, char *ebuf, int ebuflen) {
|
||||
log_info(os)("shared library load of %s was successful", name);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ebuf == nullptr || ebuflen < 1) {
|
||||
// no error reporting requested
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DWORD errcode = GetLastError();
|
||||
// Read system error message into ebuf
|
||||
// It may or may not be overwritten below (in the for loop and just above)
|
||||
@ -2261,6 +2267,8 @@ void os::jvm_path(char *buf, jint buflen) {
|
||||
// from src/windows/hpi/src/system_md.c
|
||||
|
||||
size_t os::lasterror(char* buf, size_t len) {
|
||||
assert(buf != nullptr && len > 0, "invalid buffer passed");
|
||||
|
||||
DWORD errval;
|
||||
|
||||
if ((errval = GetLastError()) != 0) {
|
||||
|
||||
@ -49,8 +49,10 @@
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "runtime/timer.hpp"
|
||||
#include "runtime/vm_version.hpp"
|
||||
#include "signals_posix.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/events.hpp"
|
||||
#include "utilities/vmError.hpp"
|
||||
|
||||
@ -524,40 +526,32 @@ static inline void atomic_copy64(const volatile void *src, volatile void *dst) {
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// needs local assembler label '1:' to avoid trouble when using linktime optimization
|
||||
int SpinPause() {
|
||||
// We don't use StubRoutines::aarch64::spin_wait stub in order to
|
||||
// avoid a costly call to os::current_thread_enable_wx() on MacOS.
|
||||
// We should return 1 if SpinPause is implemented, and since there
|
||||
// will be a sequence of 11 instructions for NONE and YIELD and 12
|
||||
// instructions for NOP and ISB, SpinPause will always return 1.
|
||||
uint64_t br_dst;
|
||||
const int instructions_per_case = 2;
|
||||
int64_t off = VM_Version::spin_wait_desc().inst() * instructions_per_case * Assembler::instruction_size;
|
||||
|
||||
assert(VM_Version::spin_wait_desc().inst() >= SpinWait::NONE &&
|
||||
VM_Version::spin_wait_desc().inst() <= SpinWait::YIELD, "must be");
|
||||
assert(-1 == SpinWait::NONE, "must be");
|
||||
assert( 0 == SpinWait::NOP, "must be");
|
||||
assert( 1 == SpinWait::ISB, "must be");
|
||||
assert( 2 == SpinWait::YIELD, "must be");
|
||||
|
||||
asm volatile(
|
||||
" adr %[d], 20 \n" // 20 == PC here + 5 instructions => address
|
||||
// to entry for case SpinWait::NOP
|
||||
" add %[d], %[d], %[o] \n"
|
||||
" br %[d] \n"
|
||||
" b 1f \n" // case SpinWait::NONE (-1)
|
||||
" nop \n" // padding
|
||||
" nop \n" // case SpinWait::NOP ( 0)
|
||||
" b 1f \n"
|
||||
" isb \n" // case SpinWait::ISB ( 1)
|
||||
" b 1f \n"
|
||||
" yield \n" // case SpinWait::YIELD ( 2)
|
||||
"1: \n"
|
||||
: [d]"=&r"(br_dst)
|
||||
: [o]"r"(off)
|
||||
: "memory");
|
||||
// will be always a sequence of instructions, SpinPause will always return 1.
|
||||
switch (VM_Version::spin_wait_desc().inst()) {
|
||||
case SpinWait::NONE:
|
||||
break;
|
||||
case SpinWait::NOP:
|
||||
asm volatile("nop" : : : "memory");
|
||||
break;
|
||||
case SpinWait::ISB:
|
||||
asm volatile("isb" : : : "memory");
|
||||
break;
|
||||
case SpinWait::YIELD:
|
||||
asm volatile("yield" : : : "memory");
|
||||
break;
|
||||
case SpinWait::SB:
|
||||
assert(VM_Version::supports_sb(), "current CPU does not support SB instruction");
|
||||
asm volatile(".inst 0xd50330ff" : : : "memory");
|
||||
break;
|
||||
#ifdef ASSERT
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -626,7 +626,7 @@ csize_t CodeBuffer::total_relocation_size() const {
|
||||
return (csize_t) align_up(total, HeapWordSize);
|
||||
}
|
||||
|
||||
csize_t CodeBuffer::copy_relocations_to(address buf, csize_t buf_limit, bool only_inst) const {
|
||||
csize_t CodeBuffer::copy_relocations_to(address buf, csize_t buf_limit) const {
|
||||
csize_t buf_offset = 0;
|
||||
csize_t code_end_so_far = 0;
|
||||
csize_t code_point_so_far = 0;
|
||||
@ -635,10 +635,6 @@ csize_t CodeBuffer::copy_relocations_to(address buf, csize_t buf_limit, bool onl
|
||||
assert(buf_limit % HeapWordSize == 0, "buf must be evenly sized");
|
||||
|
||||
for (int n = (int) SECT_FIRST; n < (int)SECT_LIMIT; n++) {
|
||||
if (only_inst && (n != (int)SECT_INSTS)) {
|
||||
// Need only relocation info for code.
|
||||
continue;
|
||||
}
|
||||
// pull relocs out of each section
|
||||
const CodeSection* cs = code_section(n);
|
||||
assert(!(cs->is_empty() && cs->locs_count() > 0), "sanity");
|
||||
@ -705,7 +701,7 @@ csize_t CodeBuffer::copy_relocations_to(address buf, csize_t buf_limit, bool onl
|
||||
buf_offset += sizeof(relocInfo);
|
||||
}
|
||||
|
||||
assert(only_inst || code_end_so_far == total_content_size(), "sanity");
|
||||
assert(code_end_so_far == total_content_size(), "sanity");
|
||||
|
||||
return buf_offset;
|
||||
}
|
||||
@ -721,7 +717,7 @@ csize_t CodeBuffer::copy_relocations_to(CodeBlob* dest) const {
|
||||
}
|
||||
// if dest is null, this is just the sizing pass
|
||||
//
|
||||
buf_offset = copy_relocations_to(buf, buf_limit, false);
|
||||
buf_offset = copy_relocations_to(buf, buf_limit);
|
||||
|
||||
return buf_offset;
|
||||
}
|
||||
|
||||
@ -641,6 +641,7 @@ class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) {
|
||||
// copies combined relocations to the blob, returns bytes copied
|
||||
// (if target is null, it is a dry run only, just for sizing)
|
||||
csize_t copy_relocations_to(CodeBlob* blob) const;
|
||||
csize_t copy_relocations_to(address buf, csize_t buf_limit) const;
|
||||
|
||||
// copies combined code to the blob (assumes relocs are already in there)
|
||||
void copy_code_to(CodeBlob* blob);
|
||||
@ -791,8 +792,6 @@ class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) {
|
||||
|
||||
int total_skipped_instructions_size() const;
|
||||
|
||||
csize_t copy_relocations_to(address buf, csize_t buf_limit, bool only_inst) const;
|
||||
|
||||
// allocated size of any and all recorded oops
|
||||
csize_t total_oop_size() const {
|
||||
OopRecorder* recorder = oop_recorder();
|
||||
|
||||
@ -351,7 +351,8 @@ LIR_OpArrayCopy::LIR_OpArrayCopy(LIR_Opr src, LIR_Opr src_pos, LIR_Opr dst, LIR_
|
||||
, _expected_type(expected_type)
|
||||
, _flags(flags) {
|
||||
#if defined(X86) || defined(AARCH64) || defined(S390) || defined(RISCV64) || defined(PPC64)
|
||||
if (expected_type != nullptr && flags == 0) {
|
||||
if (expected_type != nullptr &&
|
||||
((flags & ~LIR_OpArrayCopy::get_initial_copy_flags()) == 0)) {
|
||||
_stub = nullptr;
|
||||
} else {
|
||||
_stub = new ArrayCopyStub(this);
|
||||
|
||||
@ -1282,6 +1282,8 @@ public:
|
||||
int flags() const { return _flags; }
|
||||
ciArrayKlass* expected_type() const { return _expected_type; }
|
||||
ArrayCopyStub* stub() const { return _stub; }
|
||||
static int get_initial_copy_flags() { return LIR_OpArrayCopy::unaligned |
|
||||
LIR_OpArrayCopy::overlapping; }
|
||||
|
||||
virtual void emit_code(LIR_Assembler* masm);
|
||||
virtual LIR_OpArrayCopy* as_OpArrayCopy() { return this; }
|
||||
|
||||
@ -598,6 +598,7 @@ void CDSConfig::check_aotmode_create() {
|
||||
//
|
||||
// Since application is not executed in the assembly phase, there's no need to load
|
||||
// the agents anyway -- no one will notice that the agents are not loaded.
|
||||
log_info(aot)("Disabled all JVMTI agents during -XX:AOTMode=create");
|
||||
JvmtiAgentList::disable_agent_list();
|
||||
}
|
||||
|
||||
@ -702,6 +703,13 @@ bool CDSConfig::check_vm_args_consistency(bool patch_mod_javabase, bool mode_fla
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dumping_classic_static_archive() && AOTClassLinking) {
|
||||
if (JvmtiAgentList::disable_agent_list()) {
|
||||
FLAG_SET_ERGO(AllowArchivingWithJavaAgent, false);
|
||||
log_warning(cds)("Disabled all JVMTI agents with -Xshare:dump -XX:+AOTClassLinking");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -110,11 +110,13 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
|
||||
|
||||
ADD_EXCL("java/lang/System", "bootLayer"); // A
|
||||
|
||||
ADD_EXCL("java/util/Collections", "EMPTY_LIST"); // E
|
||||
ADD_EXCL("java/util/Collections", "EMPTY_LIST"); // E
|
||||
|
||||
// A dummy object used by HashSet. The value doesn't matter and it's never
|
||||
// tested for equality.
|
||||
ADD_EXCL("java/util/HashSet", "PRESENT"); // E
|
||||
|
||||
ADD_EXCL("jdk/internal/loader/BootLoader", "UNNAMED_MODULE"); // A
|
||||
ADD_EXCL("jdk/internal/loader/BuiltinClassLoader", "packageToModule"); // A
|
||||
ADD_EXCL("jdk/internal/loader/ClassLoaders", "BOOT_LOADER", // A
|
||||
"APP_LOADER", // A
|
||||
|
||||
@ -120,7 +120,6 @@ PackageEntry* CDSProtectionDomain::get_package_entry_from_class(InstanceKlass* i
|
||||
if (CDSConfig::is_using_full_module_graph() && ik->is_shared() && pkg_entry != nullptr) {
|
||||
assert(MetaspaceShared::is_in_shared_metaspace(pkg_entry), "must be");
|
||||
assert(!ik->defined_by_other_loaders(), "unexpected archived package entry for an unregistered class");
|
||||
assert(ik->module()->is_named(), "unexpected archived package entry for a class in an unnamed module");
|
||||
return pkg_entry;
|
||||
}
|
||||
TempNewSymbol pkg_name = ClassLoader::package_from_class_name(ik->name());
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include "cds/aotLogging.hpp"
|
||||
#include "cds/cdsConfig.hpp"
|
||||
#include "cds/heapShared.hpp"
|
||||
#include "cds/serializeClosure.hpp"
|
||||
#include "classfile/classLoaderData.inline.hpp"
|
||||
#include "classfile/classLoaderDataShared.hpp"
|
||||
@ -42,6 +43,7 @@ bool ClassLoaderDataShared::_full_module_graph_loaded = false;
|
||||
class ArchivedClassLoaderData {
|
||||
Array<PackageEntry*>* _packages;
|
||||
Array<ModuleEntry*>* _modules;
|
||||
ModuleEntry* _unnamed_module;
|
||||
|
||||
void assert_valid(ClassLoaderData* loader_data) {
|
||||
// loader_data may be null if the boot layer has loaded no modules for the platform or
|
||||
@ -52,15 +54,19 @@ class ArchivedClassLoaderData {
|
||||
}
|
||||
}
|
||||
public:
|
||||
ArchivedClassLoaderData() : _packages(nullptr), _modules(nullptr) {}
|
||||
ArchivedClassLoaderData() : _packages(nullptr), _modules(nullptr), _unnamed_module(nullptr) {}
|
||||
|
||||
void iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure);
|
||||
void allocate(ClassLoaderData* loader_data);
|
||||
void init_archived_entries(ClassLoaderData* loader_data);
|
||||
ModuleEntry* unnamed_module() {
|
||||
return _unnamed_module;
|
||||
}
|
||||
|
||||
void serialize(SerializeClosure* f) {
|
||||
f->do_ptr(&_packages);
|
||||
f->do_ptr(&_modules);
|
||||
f->do_ptr(&_unnamed_module);
|
||||
}
|
||||
|
||||
void restore(ClassLoaderData* loader_data, bool do_entries, bool do_oops);
|
||||
@ -71,6 +77,8 @@ static ArchivedClassLoaderData _archived_boot_loader_data;
|
||||
static ArchivedClassLoaderData _archived_platform_loader_data;
|
||||
static ArchivedClassLoaderData _archived_system_loader_data;
|
||||
static ModuleEntry* _archived_javabase_moduleEntry = nullptr;
|
||||
static int _platform_loader_root_index = -1;
|
||||
static int _system_loader_root_index = -1;
|
||||
|
||||
void ArchivedClassLoaderData::iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure) {
|
||||
assert(CDSConfig::is_dumping_full_module_graph(), "must be");
|
||||
@ -78,6 +86,7 @@ void ArchivedClassLoaderData::iterate_symbols(ClassLoaderData* loader_data, Meta
|
||||
if (loader_data != nullptr) {
|
||||
loader_data->packages()->iterate_symbols(closure);
|
||||
loader_data->modules() ->iterate_symbols(closure);
|
||||
loader_data->unnamed_module()->iterate_symbols(closure);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +100,7 @@ void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) {
|
||||
// the hashtables using these arrays.
|
||||
_packages = loader_data->packages()->allocate_archived_entries();
|
||||
_modules = loader_data->modules() ->allocate_archived_entries();
|
||||
_unnamed_module = loader_data->unnamed_module()->allocate_archived_entry();
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,6 +110,7 @@ void ArchivedClassLoaderData::init_archived_entries(ClassLoaderData* loader_data
|
||||
if (loader_data != nullptr) {
|
||||
loader_data->packages()->init_archived_entries(_packages);
|
||||
loader_data->modules() ->init_archived_entries(_modules);
|
||||
_unnamed_module->init_as_archived_entry();
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,6 +128,12 @@ void ArchivedClassLoaderData::restore(ClassLoaderData* loader_data, bool do_entr
|
||||
}
|
||||
if (do_oops) {
|
||||
modules->restore_archived_oops(loader_data, _modules);
|
||||
if (_unnamed_module != nullptr) {
|
||||
oop module_oop = _unnamed_module->module_oop();
|
||||
assert(module_oop != nullptr, "must be already set");
|
||||
assert(_unnamed_module == java_lang_Module::module_entry(module_oop), "must be already set");
|
||||
assert(loader_data->class_loader() == java_lang_Module::loader(module_oop), "must be set in dump time");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,6 +144,9 @@ void ArchivedClassLoaderData::clear_archived_oops() {
|
||||
for (int i = 0; i < _modules->length(); i++) {
|
||||
_modules->at(i)->clear_archived_oops();
|
||||
}
|
||||
if (_unnamed_module != nullptr) {
|
||||
_unnamed_module->clear_archived_oops();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,10 +197,15 @@ void ClassLoaderDataShared::allocate_archived_tables() {
|
||||
|
||||
void ClassLoaderDataShared::init_archived_tables() {
|
||||
assert(CDSConfig::is_dumping_full_module_graph(), "must be");
|
||||
|
||||
_archived_boot_loader_data.init_archived_entries (null_class_loader_data());
|
||||
_archived_platform_loader_data.init_archived_entries(java_platform_loader_data_or_null());
|
||||
_archived_system_loader_data.init_archived_entries (java_system_loader_data_or_null());
|
||||
|
||||
_archived_javabase_moduleEntry = ModuleEntry::get_archived_entry(ModuleEntryTable::javabase_moduleEntry());
|
||||
|
||||
_platform_loader_root_index = HeapShared::append_root(SystemDictionary::java_platform_loader());
|
||||
_system_loader_root_index = HeapShared::append_root(SystemDictionary::java_system_loader());
|
||||
}
|
||||
|
||||
void ClassLoaderDataShared::serialize(SerializeClosure* f) {
|
||||
@ -188,21 +213,54 @@ void ClassLoaderDataShared::serialize(SerializeClosure* f) {
|
||||
_archived_platform_loader_data.serialize(f);
|
||||
_archived_system_loader_data.serialize(f);
|
||||
f->do_ptr(&_archived_javabase_moduleEntry);
|
||||
f->do_int(&_platform_loader_root_index);
|
||||
f->do_int(&_system_loader_root_index);
|
||||
}
|
||||
|
||||
if (f->reading() && CDSConfig::is_using_full_module_graph()) {
|
||||
// Must be done before ClassLoader::create_javabase()
|
||||
_archived_boot_loader_data.restore(null_class_loader_data(), true, false);
|
||||
ModuleEntryTable::set_javabase_moduleEntry(_archived_javabase_moduleEntry);
|
||||
aot_log_info(aot)("use_full_module_graph = true; java.base = " INTPTR_FORMAT,
|
||||
p2i(_archived_javabase_moduleEntry));
|
||||
ModuleEntry* ClassLoaderDataShared::archived_boot_unnamed_module() {
|
||||
if (CDSConfig::is_using_full_module_graph()) {
|
||||
return _archived_boot_loader_data.unnamed_module();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ModuleEntry* ClassLoaderDataShared::archived_unnamed_module(ClassLoaderData* loader_data) {
|
||||
ModuleEntry* archived_module = nullptr;
|
||||
|
||||
if (!Universe::is_module_initialized() && CDSConfig::is_using_full_module_graph()) {
|
||||
precond(_platform_loader_root_index >= 0);
|
||||
precond(_system_loader_root_index >= 0);
|
||||
|
||||
if (loader_data->class_loader() == HeapShared::get_root(_platform_loader_root_index)) {
|
||||
archived_module = _archived_platform_loader_data.unnamed_module();
|
||||
} else if (loader_data->class_loader() == HeapShared::get_root(_system_loader_root_index)) {
|
||||
archived_module = _archived_system_loader_data.unnamed_module();
|
||||
}
|
||||
}
|
||||
|
||||
return archived_module;
|
||||
}
|
||||
|
||||
|
||||
void ClassLoaderDataShared::clear_archived_oops() {
|
||||
assert(!CDSConfig::is_using_full_module_graph(), "must be");
|
||||
_archived_boot_loader_data.clear_archived_oops();
|
||||
_archived_platform_loader_data.clear_archived_oops();
|
||||
_archived_system_loader_data.clear_archived_oops();
|
||||
if (_platform_loader_root_index >= 0) {
|
||||
HeapShared::clear_root(_platform_loader_root_index);
|
||||
HeapShared::clear_root(_system_loader_root_index);
|
||||
}
|
||||
}
|
||||
|
||||
// Must be done before ClassLoader::create_javabase()
|
||||
void ClassLoaderDataShared::restore_archived_entries_for_null_class_loader_data() {
|
||||
precond(CDSConfig::is_using_full_module_graph());
|
||||
_archived_boot_loader_data.restore(null_class_loader_data(), true, false);
|
||||
ModuleEntryTable::set_javabase_moduleEntry(_archived_javabase_moduleEntry);
|
||||
aot_log_info(aot)("use_full_module_graph = true; java.base = " INTPTR_FORMAT,
|
||||
p2i(_archived_javabase_moduleEntry));
|
||||
}
|
||||
|
||||
oop ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data() {
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
|
||||
class ClassLoaderData;
|
||||
class MetaspaceClosure;
|
||||
class ModuleEntry;
|
||||
class SerializeClosure;
|
||||
|
||||
class ClassLoaderDataShared : AllStatic {
|
||||
@ -42,9 +43,12 @@ public:
|
||||
static void init_archived_tables();
|
||||
static void serialize(SerializeClosure* f);
|
||||
static void clear_archived_oops();
|
||||
static void restore_archived_entries_for_null_class_loader_data();
|
||||
static oop restore_archived_oops_for_null_class_loader_data();
|
||||
static void restore_java_platform_loader_from_archive(ClassLoaderData* loader_data);
|
||||
static void restore_java_system_loader_from_archive(ClassLoaderData* loader_data);
|
||||
static ModuleEntry* archived_boot_unnamed_module();
|
||||
static ModuleEntry* archived_unnamed_module(ClassLoaderData* loader_data);
|
||||
static bool is_full_module_graph_loaded() { return _full_module_graph_loaded; }
|
||||
};
|
||||
|
||||
|
||||
@ -658,13 +658,11 @@ static void find_empty_vtable_slots(GrowableArray<EmptyVtableSlot*>* slots,
|
||||
if (super->default_methods() != nullptr) {
|
||||
for (int i = 0; i < super->default_methods()->length(); ++i) {
|
||||
Method* m = super->default_methods()->at(i);
|
||||
// m is a method that would have been a miranda if not for the
|
||||
// default method processing that occurred on behalf of our superclass,
|
||||
// so it's a method we want to re-examine in this new context. That is,
|
||||
// unless we have a real implementation of it in the current class.
|
||||
if (!already_in_vtable_slots(slots, m)) {
|
||||
// m is a method that we need to re-examine, unless we have a valid concrete
|
||||
// implementation in the current class - see FindMethodsByErasedSig::visit.
|
||||
Method* impl = klass->lookup_method(m->name(), m->signature());
|
||||
if (impl == nullptr || impl->is_overpass() || impl->is_static()) {
|
||||
if (impl == nullptr || impl->is_overpass() || impl->is_static() || impl->is_private()) {
|
||||
slots->append(new EmptyVtableSlot(m));
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,9 +29,11 @@
|
||||
#include "cds/heapShared.hpp"
|
||||
#include "classfile/classLoader.hpp"
|
||||
#include "classfile/classLoaderData.inline.hpp"
|
||||
#include "classfile/classLoaderDataShared.hpp"
|
||||
#include "classfile/javaClasses.inline.hpp"
|
||||
#include "classfile/moduleEntry.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
#include "jni.h"
|
||||
#include "logging/log.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
@ -317,6 +319,15 @@ ModuleEntry* ModuleEntry::create_unnamed_module(ClassLoaderData* cld) {
|
||||
// corresponding unnamed module can be found in the java.lang.ClassLoader object.
|
||||
oop module = java_lang_ClassLoader::unnamedModule(cld->class_loader());
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
ModuleEntry* archived_unnamed_module = ClassLoaderDataShared::archived_unnamed_module(cld);
|
||||
if (archived_unnamed_module != nullptr) {
|
||||
archived_unnamed_module->load_from_archive(cld);
|
||||
archived_unnamed_module->restore_archived_oops(cld);
|
||||
return archived_unnamed_module;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Ensure that the unnamed module was correctly set when the class loader was constructed.
|
||||
// Guarantee will cause a recognizable crash if the user code has circumvented calling the ClassLoader constructor.
|
||||
ResourceMark rm;
|
||||
@ -333,6 +344,16 @@ ModuleEntry* ModuleEntry::create_unnamed_module(ClassLoaderData* cld) {
|
||||
}
|
||||
|
||||
ModuleEntry* ModuleEntry::create_boot_unnamed_module(ClassLoaderData* cld) {
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
ModuleEntry* archived_unnamed_module = ClassLoaderDataShared::archived_boot_unnamed_module();
|
||||
if (archived_unnamed_module != nullptr) {
|
||||
archived_unnamed_module->load_from_archive(cld);
|
||||
// It's too early to call archived_unnamed_module->restore_archived_oops(cld).
|
||||
// We will do it inside Modules::set_bootloader_unnamed_module()
|
||||
return archived_unnamed_module;
|
||||
}
|
||||
#endif
|
||||
|
||||
// For the boot loader, the java.lang.Module for the unnamed module
|
||||
// is not known until a call to JVM_SetBootLoaderUnnamedModule is made. At
|
||||
// this point initially create the ModuleEntry for the unnamed module.
|
||||
@ -345,7 +366,6 @@ ModuleEntry* ModuleEntry::create_boot_unnamed_module(ClassLoaderData* cld) {
|
||||
// This is okay because the unnamed module gets created before the ClassLoaderData
|
||||
// is available to other threads.
|
||||
ModuleEntry* ModuleEntry::new_unnamed_module_entry(Handle module_handle, ClassLoaderData* cld) {
|
||||
|
||||
ModuleEntry* entry = new ModuleEntry(module_handle, /*is_open*/true, /*name*/nullptr,
|
||||
/*version*/ nullptr, /*location*/ nullptr,
|
||||
cld);
|
||||
@ -395,17 +415,17 @@ static int _num_archived_module_entries = 0;
|
||||
static int _num_inited_module_entries = 0;
|
||||
#endif
|
||||
|
||||
bool ModuleEntry::should_be_archived() const {
|
||||
return SystemDictionaryShared::is_builtin_loader(loader_data());
|
||||
}
|
||||
|
||||
ModuleEntry* ModuleEntry::allocate_archived_entry() const {
|
||||
assert(is_named(), "unnamed packages/modules are not archived");
|
||||
precond(should_be_archived());
|
||||
precond(CDSConfig::is_dumping_full_module_graph());
|
||||
ModuleEntry* archived_entry = (ModuleEntry*)ArchiveBuilder::rw_region_alloc(sizeof(ModuleEntry));
|
||||
memcpy((void*)archived_entry, (void*)this, sizeof(ModuleEntry));
|
||||
|
||||
if (CDSConfig::is_dumping_full_module_graph()) {
|
||||
archived_entry->_archived_module_index = HeapShared::append_root(module_oop());
|
||||
} else {
|
||||
archived_entry->_archived_module_index = -1;
|
||||
}
|
||||
|
||||
archived_entry->_archived_module_index = HeapShared::append_root(module_oop());
|
||||
if (_archive_modules_entries == nullptr) {
|
||||
_archive_modules_entries = new (mtClass)ArchivedModuleEntries();
|
||||
}
|
||||
@ -489,10 +509,14 @@ void ModuleEntry::init_as_archived_entry() {
|
||||
set_archived_reads(write_growable_array(reads()));
|
||||
|
||||
_loader_data = nullptr; // re-init at runtime
|
||||
_shared_path_index = AOTClassLocationConfig::dumptime()->get_module_shared_path_index(_location);
|
||||
if (name() != nullptr) {
|
||||
_shared_path_index = AOTClassLocationConfig::dumptime()->get_module_shared_path_index(_location);
|
||||
_name = ArchiveBuilder::get_buffered_symbol(_name);
|
||||
ArchivePtrMarker::mark_pointer((address*)&_name);
|
||||
} else {
|
||||
// _shared_path_index is used only by SystemDictionary::is_shared_class_visible_impl()
|
||||
// for checking classes in named modules.
|
||||
_shared_path_index = -1;
|
||||
}
|
||||
if (_version != nullptr) {
|
||||
_version = ArchiveBuilder::get_buffered_symbol(_version);
|
||||
@ -741,7 +765,7 @@ void ModuleEntryTable::modules_do(ModuleClosure* closure) {
|
||||
_table.iterate_all(do_f);
|
||||
}
|
||||
|
||||
void ModuleEntry::print(outputStream* st) {
|
||||
void ModuleEntry::print(outputStream* st) const {
|
||||
st->print_cr("entry " PTR_FORMAT " name %s module " PTR_FORMAT " loader %s version %s location %s strict %s",
|
||||
p2i(this),
|
||||
name_as_C_string(),
|
||||
|
||||
@ -186,10 +186,10 @@ public:
|
||||
static ModuleEntry* new_unnamed_module_entry(Handle module_handle, ClassLoaderData* cld);
|
||||
|
||||
// Note caller requires ResourceMark
|
||||
const char* name_as_C_string() {
|
||||
const char* name_as_C_string() const {
|
||||
return is_named() ? name()->as_C_string() : UNNAMED_MODULE;
|
||||
}
|
||||
void print(outputStream* st = tty);
|
||||
void print(outputStream* st = tty) const;
|
||||
void verify();
|
||||
|
||||
CDS_ONLY(int shared_path_index() { return _shared_path_index;})
|
||||
@ -197,6 +197,7 @@ public:
|
||||
JFR_ONLY(DEFINE_TRACE_ID_METHODS;)
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
bool should_be_archived() const;
|
||||
void iterate_symbols(MetaspaceClosure* closure);
|
||||
ModuleEntry* allocate_archived_entry() const;
|
||||
void init_as_archived_entry();
|
||||
|
||||
@ -474,6 +474,7 @@ void Modules::define_module(Handle module, jboolean is_open, jstring version,
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
static bool _seen_boot_unnamed_module = false;
|
||||
static bool _seen_platform_unnamed_module = false;
|
||||
static bool _seen_system_unnamed_module = false;
|
||||
|
||||
@ -509,24 +510,20 @@ void Modules::check_archived_module_oop(oop orig_module_obj) {
|
||||
// For each named module, we archive both the java.lang.Module oop and the ModuleEntry.
|
||||
assert(orig_module_ent->has_been_archived(), "sanity");
|
||||
} else {
|
||||
// We only archive two unnamed module oops (for platform and system loaders). These do NOT have an archived
|
||||
// ModuleEntry.
|
||||
//
|
||||
// At runtime, these oops are fetched from java_lang_ClassLoader::unnamedModule(loader) and
|
||||
// are initialized in ClassLoaderData::ClassLoaderData() => ModuleEntry::create_unnamed_module(), where
|
||||
// a new ModuleEntry is allocated.
|
||||
assert(!loader_data->is_boot_class_loader_data(), "unnamed module for boot loader should be not archived");
|
||||
assert(!orig_module_ent->has_been_archived(), "sanity");
|
||||
// We always archive unnamed module oop for boot, platform, and system loaders.
|
||||
precond(orig_module_ent->should_be_archived());
|
||||
precond(orig_module_ent->has_been_archived());
|
||||
|
||||
if (SystemDictionary::is_platform_class_loader(loader_data->class_loader())) {
|
||||
if (loader_data->is_boot_class_loader_data()) {
|
||||
assert(!_seen_boot_unnamed_module, "only once");
|
||||
_seen_boot_unnamed_module = true;
|
||||
} else if (SystemDictionary::is_platform_class_loader(loader_data->class_loader())) {
|
||||
assert(!_seen_platform_unnamed_module, "only once");
|
||||
_seen_platform_unnamed_module = true;
|
||||
} else if (SystemDictionary::is_system_class_loader(loader_data->class_loader())) {
|
||||
assert(!_seen_system_unnamed_module, "only once");
|
||||
_seen_system_unnamed_module = true;
|
||||
} else {
|
||||
// The java.lang.Module oop and ModuleEntry of the unnamed module of the boot loader are
|
||||
// not in the archived module graph. These are always allocated at runtime.
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
@ -777,9 +774,18 @@ void Modules::set_bootloader_unnamed_module(Handle module, TRAPS) {
|
||||
ClassLoaderData* boot_loader_data = ClassLoaderData::the_null_class_loader_data();
|
||||
ModuleEntry* unnamed_module = boot_loader_data->unnamed_module();
|
||||
assert(unnamed_module != nullptr, "boot loader's unnamed ModuleEntry not defined");
|
||||
unnamed_module->set_module_handle(boot_loader_data->add_handle(module));
|
||||
// Store pointer to the ModuleEntry in the unnamed module's java.lang.Module object.
|
||||
java_lang_Module::set_module_entry(module(), unnamed_module);
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
if (CDSConfig::is_using_full_module_graph()) {
|
||||
precond(unnamed_module == ClassLoaderDataShared::archived_boot_unnamed_module());
|
||||
unnamed_module->restore_archived_oops(boot_loader_data);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
unnamed_module->set_module_handle(boot_loader_data->add_handle(module));
|
||||
// Store pointer to the ModuleEntry in the unnamed module's java.lang.Module object.
|
||||
java_lang_Module::set_module_entry(module(), unnamed_module);
|
||||
}
|
||||
}
|
||||
|
||||
void Modules::add_module_exports(Handle from_module, jstring package_name, Handle to_module, TRAPS) {
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "classfile/packageEntry.hpp"
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/array.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
@ -218,8 +219,12 @@ typedef ResourceHashtable<
|
||||
AnyObj::C_HEAP> ArchivedPackageEntries;
|
||||
static ArchivedPackageEntries* _archived_packages_entries = nullptr;
|
||||
|
||||
bool PackageEntry::should_be_archived() const {
|
||||
return module()->should_be_archived();
|
||||
}
|
||||
|
||||
PackageEntry* PackageEntry::allocate_archived_entry() const {
|
||||
assert(!in_unnamed_module(), "unnamed packages/modules are not archived");
|
||||
precond(should_be_archived());
|
||||
PackageEntry* archived_entry = (PackageEntry*)ArchiveBuilder::rw_region_alloc(sizeof(PackageEntry));
|
||||
memcpy((void*)archived_entry, (void*)this, sizeof(PackageEntry));
|
||||
|
||||
@ -257,6 +262,12 @@ void PackageEntry::init_as_archived_entry() {
|
||||
ArchivePtrMarker::mark_pointer((address*)&_name);
|
||||
ArchivePtrMarker::mark_pointer((address*)&_module);
|
||||
ArchivePtrMarker::mark_pointer((address*)&_qualified_exports);
|
||||
|
||||
LogStreamHandle(Info, aot, package) st;
|
||||
if (st.is_enabled()) {
|
||||
st.print("archived ");
|
||||
print(&st);
|
||||
}
|
||||
}
|
||||
|
||||
void PackageEntry::load_from_archive() {
|
||||
@ -280,7 +291,7 @@ Array<PackageEntry*>* PackageEntryTable::allocate_archived_entries() {
|
||||
// First count the packages in named modules
|
||||
int n = 0;
|
||||
auto count = [&] (const SymbolHandle& key, PackageEntry*& p) {
|
||||
if (p->module()->is_named()) {
|
||||
if (p->should_be_archived()) {
|
||||
n++;
|
||||
}
|
||||
};
|
||||
@ -290,9 +301,7 @@ Array<PackageEntry*>* PackageEntryTable::allocate_archived_entries() {
|
||||
// reset n
|
||||
n = 0;
|
||||
auto grab = [&] (const SymbolHandle& key, PackageEntry*& p) {
|
||||
if (p->module()->is_named()) {
|
||||
// We don't archive unnamed modules, or packages in unnamed modules. They will be
|
||||
// created on-demand at runtime as classes in such packages are loaded.
|
||||
if (p->should_be_archived()) {
|
||||
archived_packages->at_put(n++, p);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -208,6 +208,7 @@ public:
|
||||
void print(outputStream* st = tty);
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
bool should_be_archived() const;
|
||||
void iterate_symbols(MetaspaceClosure* closure);
|
||||
PackageEntry* allocate_archived_entry() const;
|
||||
void init_as_archived_entry();
|
||||
|
||||
@ -379,8 +379,8 @@ public:
|
||||
static void init2() NOT_CDS_RETURN;
|
||||
static void close() NOT_CDS_RETURN;
|
||||
static bool is_on() CDS_ONLY({ return cache() != nullptr && !_cache->closing(); }) NOT_CDS_RETURN_(false);
|
||||
static bool is_on_for_use() { return is_on() && _cache->for_use(); }
|
||||
static bool is_on_for_dump() { return is_on() && _cache->for_dump(); }
|
||||
static bool is_on_for_use() CDS_ONLY({ return is_on() && _cache->for_use(); }) NOT_CDS_RETURN_(false);
|
||||
static bool is_on_for_dump() CDS_ONLY({ return is_on() && _cache->for_dump(); }) NOT_CDS_RETURN_(false);
|
||||
static bool is_dumping_stub() NOT_CDS_RETURN_(false);
|
||||
static bool is_dumping_adapter() NOT_CDS_RETURN_(false);
|
||||
static bool is_using_stub() NOT_CDS_RETURN_(false);
|
||||
|
||||
@ -2446,7 +2446,7 @@ void nmethod::do_unloading(bool unloading_occurred) {
|
||||
}
|
||||
}
|
||||
|
||||
void nmethod::oops_do(OopClosure* f, bool allow_dead) {
|
||||
void nmethod::oops_do(OopClosure* f) {
|
||||
// Prevent extra code cache walk for platforms that don't have immediate oops.
|
||||
if (relocInfo::mustIterateImmediateOopsInCode()) {
|
||||
RelocIterator iter(this, oops_reloc_begin());
|
||||
|
||||
@ -919,8 +919,7 @@ public:
|
||||
bool jvmci_skip_profile_deopt() const;
|
||||
#endif
|
||||
|
||||
void oops_do(OopClosure* f) { oops_do(f, false); }
|
||||
void oops_do(OopClosure* f, bool allow_dead);
|
||||
void oops_do(OopClosure* f);
|
||||
|
||||
// All-in-one claiming of nmethods: returns true if the caller successfully claimed that
|
||||
// nmethod.
|
||||
|
||||
@ -367,33 +367,31 @@ void CompileQueue::add(CompileTask* task) {
|
||||
*/
|
||||
void CompileQueue::delete_all() {
|
||||
MutexLocker mu(MethodCompileQueue_lock);
|
||||
CompileTask* next = _first;
|
||||
CompileTask* current = _first;
|
||||
|
||||
// Iterate over all tasks in the compile queue
|
||||
while (next != nullptr) {
|
||||
CompileTask* current = next;
|
||||
next = current->next();
|
||||
bool found_waiter = false;
|
||||
{
|
||||
MutexLocker ct_lock(CompileTaskWait_lock);
|
||||
assert(current->waiting_for_completion_count() <= 1, "more than one thread are waiting for task");
|
||||
if (current->waiting_for_completion_count() > 0) {
|
||||
// If another thread waits for this task, we must wake them up
|
||||
// so they will stop waiting and free the task.
|
||||
CompileTaskWait_lock->notify_all();
|
||||
found_waiter = true;
|
||||
}
|
||||
}
|
||||
if (!found_waiter) {
|
||||
// If no one was waiting for this task, we need to delete it ourselves.
|
||||
// In this case, the task is also certainly unlocked, because, again, there is no waiter.
|
||||
// Otherwise, by convention, it's the waiters responsibility to delete the task.
|
||||
while (current != nullptr) {
|
||||
if (!current->is_blocking()) {
|
||||
// Non-blocking task. No one is waiting for it, delete it now.
|
||||
delete current;
|
||||
} else {
|
||||
// Blocking task. By convention, it is the waiters responsibility
|
||||
// to delete the task. We cannot delete it here, because we do not
|
||||
// coordinate with waiters. We will notify the waiters later.
|
||||
}
|
||||
current = current->next();
|
||||
}
|
||||
_first = nullptr;
|
||||
_last = nullptr;
|
||||
|
||||
// Wake up all blocking task waiters to deal with remaining blocking
|
||||
// tasks. This is not a performance sensitive path, so we do this
|
||||
// unconditionally to simplify coding/testing.
|
||||
{
|
||||
MonitorLocker ml(Thread::current(), CompileTaskWait_lock);
|
||||
ml.notify_all();
|
||||
}
|
||||
|
||||
// Wake up all threads that block on the queue.
|
||||
MethodCompileQueue_lock->notify_all();
|
||||
}
|
||||
@ -1720,23 +1718,26 @@ void CompileBroker::wait_for_completion(CompileTask* task) {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
MonitorLocker ml(thread, CompileTaskWait_lock);
|
||||
free_task = true;
|
||||
task->inc_waiting_for_completion();
|
||||
// Wait until the task is complete or compilation is shut down.
|
||||
MonitorLocker ml(thread, CompileTaskWait_lock);
|
||||
while (!task->is_complete() && !is_compilation_disabled_forever()) {
|
||||
ml.wait();
|
||||
}
|
||||
task->dec_waiting_for_completion();
|
||||
}
|
||||
|
||||
// It is harmless to check this status without the lock, because
|
||||
// completion is a stable property.
|
||||
if (!task->is_complete() && is_compilation_disabled_forever()) {
|
||||
// Task is not complete, and we are exiting for compilation shutdown.
|
||||
// The task can still be executed by some compiler thread, therefore
|
||||
// we cannot delete it. This will leave task allocated, which leaks it.
|
||||
// At this (degraded) point, it is less risky to abandon the task,
|
||||
// rather than attempting a more complicated deletion protocol.
|
||||
free_task = false;
|
||||
}
|
||||
|
||||
if (free_task) {
|
||||
if (is_compilation_disabled_forever()) {
|
||||
delete task;
|
||||
return;
|
||||
}
|
||||
|
||||
// It is harmless to check this status without the lock, because
|
||||
// completion is a stable property (until the task object is deleted).
|
||||
assert(task->is_complete(), "Compilation should have completed");
|
||||
|
||||
// By convention, the waiter is responsible for deleting a
|
||||
|
||||
@ -381,7 +381,7 @@ public:
|
||||
}
|
||||
|
||||
static bool is_compilation_disabled_forever() {
|
||||
return _should_compile_new_jobs == shutdown_compilation;
|
||||
return Atomic::load(&_should_compile_new_jobs) == shutdown_compilation;
|
||||
}
|
||||
|
||||
static void wait_for_no_active_tasks();
|
||||
|
||||
@ -56,8 +56,6 @@ CompileTask::CompileTask(int compile_id,
|
||||
_comp_level = comp_level;
|
||||
_num_inlined_bytecodes = 0;
|
||||
|
||||
_waiting_count = 0;
|
||||
|
||||
_is_complete = false;
|
||||
_is_success = false;
|
||||
|
||||
|
||||
@ -99,7 +99,6 @@ class CompileTask : public CHeapObj<mtCompiler> {
|
||||
// Compilation state for a blocking JVMCI compilation
|
||||
JVMCICompileState* _blocking_jvmci_compile_state;
|
||||
#endif
|
||||
int _waiting_count; // See waiting_for_completion_count()
|
||||
int _comp_level;
|
||||
int _num_inlined_bytecodes;
|
||||
CompileTask* _next, *_prev;
|
||||
@ -164,23 +163,6 @@ class CompileTask : public CHeapObj<mtCompiler> {
|
||||
}
|
||||
#endif
|
||||
|
||||
// See how many threads are waiting for this task. Must have lock to read this.
|
||||
int waiting_for_completion_count() {
|
||||
assert(CompileTaskWait_lock->owned_by_self(), "must have lock to use waiting_for_completion_count()");
|
||||
return _waiting_count;
|
||||
}
|
||||
// Indicates that a thread is waiting for this task to complete. Must have lock to use this.
|
||||
void inc_waiting_for_completion() {
|
||||
assert(CompileTaskWait_lock->owned_by_self(), "must have lock to use inc_waiting_for_completion()");
|
||||
_waiting_count++;
|
||||
}
|
||||
// Indicates that a thread stopped waiting for this task to complete. Must have lock to use this.
|
||||
void dec_waiting_for_completion() {
|
||||
assert(CompileTaskWait_lock->owned_by_self(), "must have lock to use dec_waiting_for_completion()");
|
||||
assert(_waiting_count > 0, "waiting count is not positive");
|
||||
_waiting_count--;
|
||||
}
|
||||
|
||||
void mark_complete() { _is_complete = true; }
|
||||
void mark_success() { _is_success = true; }
|
||||
void mark_started(jlong time) { _time_started = time; }
|
||||
|
||||
@ -1732,6 +1732,66 @@ static bool gc_counter_less_than(uint x, uint y) {
|
||||
#define LOG_COLLECT_CONCURRENTLY_COMPLETE(cause, result) \
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "complete %s", BOOL_TO_STR(result))
|
||||
|
||||
bool G1CollectedHeap::wait_full_mark_finished(GCCause::Cause cause,
|
||||
uint old_marking_started_before,
|
||||
uint old_marking_started_after,
|
||||
uint old_marking_completed_after) {
|
||||
// Request is finished if a full collection (concurrent or stw)
|
||||
// was started after this request and has completed, e.g.
|
||||
// started_before < completed_after.
|
||||
if (gc_counter_less_than(old_marking_started_before,
|
||||
old_marking_completed_after)) {
|
||||
LOG_COLLECT_CONCURRENTLY_COMPLETE(cause, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (old_marking_started_after != old_marking_completed_after) {
|
||||
// If there is an in-progress cycle (possibly started by us), then
|
||||
// wait for that cycle to complete, e.g.
|
||||
// while completed_now < started_after.
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "wait");
|
||||
MonitorLocker ml(G1OldGCCount_lock);
|
||||
while (gc_counter_less_than(_old_marking_cycles_completed,
|
||||
old_marking_started_after)) {
|
||||
ml.wait();
|
||||
}
|
||||
// Request is finished if the collection we just waited for was
|
||||
// started after this request.
|
||||
if (old_marking_started_before != old_marking_started_after) {
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "complete after wait");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// After calling wait_full_mark_finished(), this method determines whether we
|
||||
// previously failed for ordinary reasons (concurrent cycle in progress, whitebox
|
||||
// has control). Returns if this has been such an ordinary reason.
|
||||
static bool should_retry_vm_op(GCCause::Cause cause,
|
||||
VM_G1TryInitiateConcMark* op) {
|
||||
if (op->cycle_already_in_progress()) {
|
||||
// If VMOp failed because a cycle was already in progress, it
|
||||
// is now complete. But it didn't finish this user-requested
|
||||
// GC, so try again.
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "retry after in-progress");
|
||||
return true;
|
||||
} else if (op->whitebox_attached()) {
|
||||
// If WhiteBox wants control, wait for notification of a state
|
||||
// change in the controller, then try again. Don't wait for
|
||||
// release of control, since collections may complete while in
|
||||
// control. Note: This won't recognize a STW full collection
|
||||
// while waiting; we can't wait on multiple monitors.
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "whitebox control stall");
|
||||
MonitorLocker ml(ConcurrentGCBreakpoints::monitor());
|
||||
if (ConcurrentGCBreakpoints::is_controlled()) {
|
||||
ml.wait();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool G1CollectedHeap::try_collect_concurrently(GCCause::Cause cause,
|
||||
uint gc_counter,
|
||||
uint old_marking_started_before) {
|
||||
@ -1792,7 +1852,45 @@ bool G1CollectedHeap::try_collect_concurrently(GCCause::Cause cause,
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "ignoring STW full GC");
|
||||
old_marking_started_before = old_marking_started_after;
|
||||
}
|
||||
} else if (GCCause::is_codecache_requested_gc(cause)) {
|
||||
// For a CodeCache requested GC, before marking, progress is ensured as the
|
||||
// following Remark pause unloads code (and signals the requester such).
|
||||
// Otherwise we must ensure that it is restarted.
|
||||
//
|
||||
// For a CodeCache requested GC, a successful GC operation means that
|
||||
// (1) marking is in progress. I.e. the VMOp started the marking or a
|
||||
// Remark pause is pending from a different VM op; we will potentially
|
||||
// abort a mixed phase if needed.
|
||||
// (2) a new cycle was started (by this thread or some other), or
|
||||
// (3) a Full GC was performed.
|
||||
//
|
||||
// Cases (2) and (3) are detected together by a change to
|
||||
// _old_marking_cycles_started.
|
||||
//
|
||||
// Compared to other "automatic" GCs (see below), we do not consider being
|
||||
// in whitebox as sufficient too because we might be anywhere within that
|
||||
// cycle and we need to make progress.
|
||||
if (op.mark_in_progress() ||
|
||||
(old_marking_started_before != old_marking_started_after)) {
|
||||
LOG_COLLECT_CONCURRENTLY_COMPLETE(cause, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wait_full_mark_finished(cause,
|
||||
old_marking_started_before,
|
||||
old_marking_started_after,
|
||||
old_marking_completed_after)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (should_retry_vm_op(cause, &op)) {
|
||||
continue;
|
||||
}
|
||||
} else if (!GCCause::is_user_requested_gc(cause)) {
|
||||
assert(cause == GCCause::_g1_humongous_allocation ||
|
||||
cause == GCCause::_g1_periodic_collection,
|
||||
"Unsupported cause %s", GCCause::to_string(cause));
|
||||
|
||||
// For an "automatic" (not user-requested) collection, we just need to
|
||||
// ensure that progress is made.
|
||||
//
|
||||
@ -1804,11 +1902,6 @@ bool G1CollectedHeap::try_collect_concurrently(GCCause::Cause cause,
|
||||
// (5) a Full GC was performed.
|
||||
// Cases (4) and (5) are detected together by a change to
|
||||
// _old_marking_cycles_started.
|
||||
//
|
||||
// Note that (1) does not imply (4). If we're still in the mixed
|
||||
// phase of an earlier concurrent collection, the request to make the
|
||||
// collection a concurrent start won't be honored. If we don't check for
|
||||
// both conditions we'll spin doing back-to-back collections.
|
||||
if (op.gc_succeeded() ||
|
||||
op.cycle_already_in_progress() ||
|
||||
op.whitebox_attached() ||
|
||||
@ -1832,56 +1925,20 @@ bool G1CollectedHeap::try_collect_concurrently(GCCause::Cause cause,
|
||||
BOOL_TO_STR(op.gc_succeeded()),
|
||||
old_marking_started_before, old_marking_started_after);
|
||||
|
||||
// Request is finished if a full collection (concurrent or stw)
|
||||
// was started after this request and has completed, e.g.
|
||||
// started_before < completed_after.
|
||||
if (gc_counter_less_than(old_marking_started_before,
|
||||
old_marking_completed_after)) {
|
||||
LOG_COLLECT_CONCURRENTLY_COMPLETE(cause, true);
|
||||
if (wait_full_mark_finished(cause,
|
||||
old_marking_started_before,
|
||||
old_marking_started_after,
|
||||
old_marking_completed_after)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (old_marking_started_after != old_marking_completed_after) {
|
||||
// If there is an in-progress cycle (possibly started by us), then
|
||||
// wait for that cycle to complete, e.g.
|
||||
// while completed_now < started_after.
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "wait");
|
||||
MonitorLocker ml(G1OldGCCount_lock);
|
||||
while (gc_counter_less_than(_old_marking_cycles_completed,
|
||||
old_marking_started_after)) {
|
||||
ml.wait();
|
||||
}
|
||||
// Request is finished if the collection we just waited for was
|
||||
// started after this request.
|
||||
if (old_marking_started_before != old_marking_started_after) {
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "complete after wait");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If VMOp was successful then it started a new cycle that the above
|
||||
// wait &etc should have recognized as finishing this request. This
|
||||
// differs from a non-user-request, where gc_succeeded does not imply
|
||||
// a new cycle was started.
|
||||
assert(!op.gc_succeeded(), "invariant");
|
||||
|
||||
if (op.cycle_already_in_progress()) {
|
||||
// If VMOp failed because a cycle was already in progress, it
|
||||
// is now complete. But it didn't finish this user-requested
|
||||
// GC, so try again.
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "retry after in-progress");
|
||||
continue;
|
||||
} else if (op.whitebox_attached()) {
|
||||
// If WhiteBox wants control, wait for notification of a state
|
||||
// change in the controller, then try again. Don't wait for
|
||||
// release of control, since collections may complete while in
|
||||
// control. Note: This won't recognize a STW full collection
|
||||
// while waiting; we can't wait on multiple monitors.
|
||||
LOG_COLLECT_CONCURRENTLY(cause, "whitebox control stall");
|
||||
MonitorLocker ml(ConcurrentGCBreakpoints::monitor());
|
||||
if (ConcurrentGCBreakpoints::is_controlled()) {
|
||||
ml.wait();
|
||||
}
|
||||
if (should_retry_vm_op(cause, &op)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,6 +274,14 @@ private:
|
||||
// (e) cause == _g1_periodic_collection and +G1PeriodicGCInvokesConcurrent.
|
||||
bool should_do_concurrent_full_gc(GCCause::Cause cause);
|
||||
|
||||
// Wait until a full mark (either currently in progress or one that completed
|
||||
// after the current request) has finished. Returns whether that full mark started
|
||||
// after this request. If so, we typically do not need another one.
|
||||
bool wait_full_mark_finished(GCCause::Cause cause,
|
||||
uint old_marking_started_before,
|
||||
uint old_marking_started_after,
|
||||
uint old_marking_completed_after);
|
||||
|
||||
// Attempt to start a concurrent cycle with the indicated cause.
|
||||
// precondition: should_do_concurrent_full_gc(cause)
|
||||
bool try_collect_concurrently(GCCause::Cause cause,
|
||||
|
||||
@ -60,6 +60,9 @@ class G1CollectorState {
|
||||
// do the concurrent start phase work.
|
||||
volatile bool _initiate_conc_mark_if_possible;
|
||||
|
||||
// Marking is in progress. Set from start of the concurrent start pause to the
|
||||
// end of the Remark pause.
|
||||
bool _mark_in_progress;
|
||||
// Marking or rebuilding remembered set work is in progress. Set from the end
|
||||
// of the concurrent start pause to the end of the Cleanup pause.
|
||||
bool _mark_or_rebuild_in_progress;
|
||||
@ -78,6 +81,7 @@ public:
|
||||
_in_concurrent_start_gc(false),
|
||||
_initiate_conc_mark_if_possible(false),
|
||||
|
||||
_mark_in_progress(false),
|
||||
_mark_or_rebuild_in_progress(false),
|
||||
_clear_bitmap_in_progress(false),
|
||||
_in_full_gc(false) { }
|
||||
@ -92,6 +96,7 @@ public:
|
||||
|
||||
void set_initiate_conc_mark_if_possible(bool v) { _initiate_conc_mark_if_possible = v; }
|
||||
|
||||
void set_mark_in_progress(bool v) { _mark_in_progress = v; }
|
||||
void set_mark_or_rebuild_in_progress(bool v) { _mark_or_rebuild_in_progress = v; }
|
||||
void set_clear_bitmap_in_progress(bool v) { _clear_bitmap_in_progress = v; }
|
||||
|
||||
@ -106,6 +111,7 @@ public:
|
||||
|
||||
bool initiate_conc_mark_if_possible() const { return _initiate_conc_mark_if_possible; }
|
||||
|
||||
bool mark_in_progress() const { return _mark_in_progress; }
|
||||
bool mark_or_rebuild_in_progress() const { return _mark_or_rebuild_in_progress; }
|
||||
bool clear_bitmap_in_progress() const { return _clear_bitmap_in_progress; }
|
||||
|
||||
|
||||
@ -591,6 +591,7 @@ void G1Policy::record_full_collection_end() {
|
||||
collector_state()->set_in_young_gc_before_mixed(false);
|
||||
collector_state()->set_initiate_conc_mark_if_possible(need_to_start_conc_mark("end of Full GC"));
|
||||
collector_state()->set_in_concurrent_start_gc(false);
|
||||
collector_state()->set_mark_in_progress(false);
|
||||
collector_state()->set_mark_or_rebuild_in_progress(false);
|
||||
collector_state()->set_clear_bitmap_in_progress(false);
|
||||
|
||||
@ -703,6 +704,7 @@ void G1Policy::record_concurrent_mark_remark_end() {
|
||||
double elapsed_time_ms = (end_time_sec - start_time_sec) * 1000.0;
|
||||
_analytics->report_concurrent_mark_remark_times_ms(elapsed_time_ms);
|
||||
record_pause(G1GCPauseType::Remark, start_time_sec, end_time_sec);
|
||||
collector_state()->set_mark_in_progress(false);
|
||||
}
|
||||
|
||||
G1CollectionSetCandidates* G1Policy::candidates() const {
|
||||
@ -936,6 +938,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar
|
||||
assert(!(G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause) && collector_state()->mark_or_rebuild_in_progress()),
|
||||
"If the last pause has been concurrent start, we should not have been in the marking window");
|
||||
if (G1GCPauseTypeHelper::is_concurrent_start_pause(this_pause)) {
|
||||
collector_state()->set_mark_in_progress(concurrent_operation_is_full_mark);
|
||||
collector_state()->set_mark_or_rebuild_in_progress(concurrent_operation_is_full_mark);
|
||||
}
|
||||
|
||||
@ -1222,6 +1225,17 @@ void G1Policy::initiate_conc_mark() {
|
||||
collector_state()->set_initiate_conc_mark_if_possible(false);
|
||||
}
|
||||
|
||||
static const char* requester_for_mixed_abort(GCCause::Cause cause) {
|
||||
if (cause == GCCause::_wb_breakpoint) {
|
||||
return "run_to breakpoint";
|
||||
} else if (GCCause::is_codecache_requested_gc(cause)) {
|
||||
return "codecache";
|
||||
} else {
|
||||
assert(G1CollectedHeap::heap()->is_user_requested_concurrent_full_gc(cause), "must be");
|
||||
return "user";
|
||||
}
|
||||
}
|
||||
|
||||
void G1Policy::decide_on_concurrent_start_pause() {
|
||||
// We are about to decide on whether this pause will be a
|
||||
// concurrent start pause.
|
||||
@ -1254,8 +1268,7 @@ void G1Policy::decide_on_concurrent_start_pause() {
|
||||
initiate_conc_mark();
|
||||
log_debug(gc, ergo)("Initiate concurrent cycle (concurrent cycle initiation requested)");
|
||||
} else if (_g1h->is_user_requested_concurrent_full_gc(cause) ||
|
||||
(cause == GCCause::_codecache_GC_threshold) ||
|
||||
(cause == GCCause::_codecache_GC_aggressive) ||
|
||||
GCCause::is_codecache_requested_gc(cause) ||
|
||||
(cause == GCCause::_wb_breakpoint)) {
|
||||
// Initiate a concurrent start. A concurrent start must be a young only
|
||||
// GC, so the collector state must be updated to reflect this.
|
||||
@ -1270,7 +1283,7 @@ void G1Policy::decide_on_concurrent_start_pause() {
|
||||
abort_time_to_mixed_tracking();
|
||||
initiate_conc_mark();
|
||||
log_debug(gc, ergo)("Initiate concurrent cycle (%s requested concurrent cycle)",
|
||||
(cause == GCCause::_wb_breakpoint) ? "run_to breakpoint" : "user");
|
||||
requester_for_mixed_abort(cause));
|
||||
} else {
|
||||
// The concurrent marking thread is still finishing up the
|
||||
// previous cycle. If we start one right now the two cycles
|
||||
|
||||
@ -59,6 +59,7 @@ VM_G1TryInitiateConcMark::VM_G1TryInitiateConcMark(uint gc_count_before,
|
||||
GCCause::Cause gc_cause) :
|
||||
VM_GC_Collect_Operation(gc_count_before, gc_cause),
|
||||
_transient_failure(false),
|
||||
_mark_in_progress(false),
|
||||
_cycle_already_in_progress(false),
|
||||
_whitebox_attached(false),
|
||||
_terminating(false),
|
||||
@ -83,6 +84,9 @@ void VM_G1TryInitiateConcMark::doit() {
|
||||
// Record for handling by caller.
|
||||
_terminating = g1h->concurrent_mark_is_terminating();
|
||||
|
||||
_mark_in_progress = g1h->collector_state()->mark_in_progress();
|
||||
_cycle_already_in_progress = g1h->concurrent_mark()->cm_thread()->in_progress();
|
||||
|
||||
if (_terminating && GCCause::is_user_requested_gc(_gc_cause)) {
|
||||
// When terminating, the request to initiate a concurrent cycle will be
|
||||
// ignored by do_collection_pause_at_safepoint; instead it will just do
|
||||
@ -91,9 +95,8 @@ void VM_G1TryInitiateConcMark::doit() {
|
||||
// requests the alternative GC might still be needed.
|
||||
} else if (!g1h->policy()->force_concurrent_start_if_outside_cycle(_gc_cause)) {
|
||||
// Failure to force the next GC pause to be a concurrent start indicates
|
||||
// there is already a concurrent marking cycle in progress. Set flag
|
||||
// to notify the caller and return immediately.
|
||||
_cycle_already_in_progress = true;
|
||||
// there is already a concurrent marking cycle in progress. Flags to indicate
|
||||
// that were already set, so return immediately.
|
||||
} else if ((_gc_cause != GCCause::_wb_breakpoint) &&
|
||||
ConcurrentGCBreakpoints::is_controlled()) {
|
||||
// WhiteBox wants to be in control of concurrent cycles, so don't try to
|
||||
|
||||
@ -45,6 +45,7 @@ public:
|
||||
|
||||
class VM_G1TryInitiateConcMark : public VM_GC_Collect_Operation {
|
||||
bool _transient_failure;
|
||||
bool _mark_in_progress;
|
||||
bool _cycle_already_in_progress;
|
||||
bool _whitebox_attached;
|
||||
bool _terminating;
|
||||
@ -59,6 +60,7 @@ public:
|
||||
virtual bool doit_prologue();
|
||||
virtual void doit();
|
||||
bool transient_failure() const { return _transient_failure; }
|
||||
bool mark_in_progress() const { return _mark_in_progress; }
|
||||
bool cycle_already_in_progress() const { return _cycle_already_in_progress; }
|
||||
bool whitebox_attached() const { return _whitebox_attached; }
|
||||
bool terminating() const { return _terminating; }
|
||||
|
||||
@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gc/parallel/gcAdaptivePolicyCounters.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
|
||||
// This class keeps statistical information and computes the
|
||||
// size of the heap.
|
||||
|
||||
GCAdaptivePolicyCounters::GCAdaptivePolicyCounters(const char* name,
|
||||
int collectors,
|
||||
int generations,
|
||||
AdaptiveSizePolicy* size_policy_arg)
|
||||
: GCPolicyCounters(name, collectors, generations),
|
||||
_size_policy(size_policy_arg) {
|
||||
if (UsePerfData) {
|
||||
EXCEPTION_MARK;
|
||||
ResourceMark rm;
|
||||
|
||||
const char* cname = PerfDataManager::counter_name(name_space(), "edenSize");
|
||||
_eden_size_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, _size_policy->calculated_eden_size_in_bytes(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "promoSize");
|
||||
_promo_size_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, size_policy()->calculated_promo_size_in_bytes(),
|
||||
CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "youngCapacity");
|
||||
size_t young_capacity_in_bytes =
|
||||
_size_policy->calculated_eden_size_in_bytes() +
|
||||
_size_policy->calculated_survivor_size_in_bytes();
|
||||
_young_capacity_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, young_capacity_in_bytes, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgSurvivedAvg");
|
||||
_avg_survived_avg_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, size_policy()->calculated_survivor_size_in_bytes(),
|
||||
CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgSurvivedDev");
|
||||
_avg_survived_dev_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, (jlong) 0 , CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgSurvivedPaddedAvg");
|
||||
_avg_survived_padded_avg_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes,
|
||||
size_policy()->calculated_survivor_size_in_bytes(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgMinorPauseTime");
|
||||
_avg_minor_pause_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Ticks, (jlong) _size_policy->_avg_minor_pause->average(),
|
||||
CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgMinorIntervalTime");
|
||||
_avg_minor_interval_counter = PerfDataManager::create_variable(SUN_GC,
|
||||
cname,
|
||||
PerfData::U_Ticks,
|
||||
(jlong) _size_policy->_avg_minor_interval->average(),
|
||||
CHECK);
|
||||
|
||||
#ifdef NOT_PRODUCT
|
||||
// This is a counter for the most recent minor pause time
|
||||
// (the last sample, not the average). It is useful for
|
||||
// verifying the average pause time but not worth putting
|
||||
// into the product.
|
||||
cname = PerfDataManager::counter_name(name_space(), "minorPauseTime");
|
||||
_minor_pause_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Ticks, (jlong) _size_policy->_avg_minor_pause->last_sample(),
|
||||
CHECK);
|
||||
#endif
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "minorGcCost");
|
||||
_minor_gc_cost_counter = PerfDataManager::create_variable(SUN_GC,
|
||||
cname,
|
||||
PerfData::U_Ticks,
|
||||
(jlong) _size_policy->minor_gc_cost(),
|
||||
CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "mutatorCost");
|
||||
_mutator_cost_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Ticks, (jlong) _size_policy->mutator_cost(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "survived");
|
||||
_survived_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, (jlong) 0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "promoted");
|
||||
_promoted_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, (jlong) 0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgYoungLive");
|
||||
_avg_young_live_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, (jlong) size_policy()->avg_young_live()->average(),
|
||||
CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgOldLive");
|
||||
_avg_old_live_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, (jlong) size_policy()->avg_old_live()->average(),
|
||||
CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "survivorOverflowed");
|
||||
_survivor_overflowed_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Events, (jlong)0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"decrementTenuringThresholdForGcCost");
|
||||
_decrement_tenuring_threshold_for_gc_cost_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events,
|
||||
(jlong)0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"incrementTenuringThresholdForGcCost");
|
||||
_increment_tenuring_threshold_for_gc_cost_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events,
|
||||
(jlong)0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"decrementTenuringThresholdForSurvivorLimit");
|
||||
_decrement_tenuring_threshold_for_survivor_limit_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events,
|
||||
(jlong)0, CHECK);
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"changeYoungGenForMinPauses");
|
||||
_change_young_gen_for_min_pauses_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events,
|
||||
(jlong)0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"changeOldGenForMajPauses");
|
||||
_change_old_gen_for_maj_pauses_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events,
|
||||
(jlong)0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"increaseOldGenForThroughput");
|
||||
_change_old_gen_for_throughput_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events,
|
||||
(jlong)0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"increaseYoungGenForThroughput");
|
||||
_change_young_gen_for_throughput_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events,
|
||||
(jlong)0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"decreaseForFootprint");
|
||||
_decrease_for_footprint_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Events, (jlong)0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "decideAtFullGc");
|
||||
_decide_at_full_gc_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_None, (jlong)0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "minorPauseYoungSlope");
|
||||
_minor_pause_young_slope_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_None, (jlong) 0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "majorCollectionSlope");
|
||||
_major_collection_slope_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_None, (jlong) 0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "minorCollectionSlope");
|
||||
_minor_collection_slope_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_None, (jlong) 0, CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
void GCAdaptivePolicyCounters::update_counters_from_policy() {
|
||||
if (UsePerfData && (size_policy() != nullptr)) {
|
||||
update_avg_minor_pause_counter();
|
||||
update_avg_minor_interval_counter();
|
||||
#ifdef NOT_PRODUCT
|
||||
update_minor_pause_counter();
|
||||
#endif
|
||||
update_minor_gc_cost_counter();
|
||||
update_avg_young_live_counter();
|
||||
|
||||
update_survivor_size_counters();
|
||||
update_avg_survived_avg_counters();
|
||||
update_avg_survived_dev_counters();
|
||||
update_avg_survived_padded_avg_counters();
|
||||
|
||||
update_change_old_gen_for_throughput();
|
||||
update_change_young_gen_for_throughput();
|
||||
update_decrease_for_footprint();
|
||||
update_change_young_gen_for_min_pauses();
|
||||
update_change_old_gen_for_maj_pauses();
|
||||
|
||||
update_minor_pause_young_slope_counter();
|
||||
update_minor_collection_slope_counter();
|
||||
update_major_collection_slope_counter();
|
||||
}
|
||||
}
|
||||
|
||||
void GCAdaptivePolicyCounters::update_counters() {
|
||||
if (UsePerfData) {
|
||||
update_counters_from_policy();
|
||||
}
|
||||
}
|
||||
@ -1,228 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_GC_PARALLEL_GCADAPTIVEPOLICYCOUNTERS_HPP
|
||||
#define SHARE_GC_PARALLEL_GCADAPTIVEPOLICYCOUNTERS_HPP
|
||||
|
||||
#include "gc/shared/adaptiveSizePolicy.hpp"
|
||||
#include "gc/shared/gcPolicyCounters.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
|
||||
// This class keeps statistical information and computes the
|
||||
// size of the heap.
|
||||
|
||||
class GCAdaptivePolicyCounters : public GCPolicyCounters {
|
||||
protected:
|
||||
PerfVariable* _eden_size_counter;
|
||||
PerfVariable* _promo_size_counter;
|
||||
|
||||
PerfVariable* _young_capacity_counter;
|
||||
|
||||
PerfVariable* _minor_gc_cost_counter;
|
||||
PerfVariable* _major_gc_cost_counter;
|
||||
PerfVariable* _mutator_cost_counter;
|
||||
|
||||
PerfVariable* _avg_young_live_counter;
|
||||
PerfVariable* _avg_old_live_counter;
|
||||
|
||||
PerfVariable* _avg_minor_pause_counter;
|
||||
PerfVariable* _avg_minor_interval_counter;
|
||||
|
||||
#ifdef NOT_PRODUCT
|
||||
PerfVariable* _minor_pause_counter;
|
||||
#endif
|
||||
|
||||
PerfVariable* _change_young_gen_for_min_pauses_counter;
|
||||
PerfVariable* _change_young_gen_for_throughput_counter;
|
||||
PerfVariable* _change_old_gen_for_maj_pauses_counter;
|
||||
PerfVariable* _change_old_gen_for_throughput_counter;
|
||||
PerfVariable* _decrease_for_footprint_counter;
|
||||
|
||||
PerfVariable* _minor_pause_young_slope_counter;
|
||||
|
||||
PerfVariable* _decide_at_full_gc_counter;
|
||||
|
||||
PerfVariable* _survived_counter;
|
||||
PerfVariable* _promoted_counter;
|
||||
|
||||
PerfVariable* _avg_survived_avg_counter;
|
||||
PerfVariable* _avg_survived_dev_counter;
|
||||
PerfVariable* _avg_survived_padded_avg_counter;
|
||||
|
||||
PerfVariable* _survivor_overflowed_counter;
|
||||
PerfVariable* _increment_tenuring_threshold_for_gc_cost_counter;
|
||||
PerfVariable* _decrement_tenuring_threshold_for_gc_cost_counter;
|
||||
PerfVariable* _decrement_tenuring_threshold_for_survivor_limit_counter;
|
||||
|
||||
PerfVariable* _minor_collection_slope_counter;
|
||||
PerfVariable* _major_collection_slope_counter;
|
||||
|
||||
AdaptiveSizePolicy* _size_policy;
|
||||
|
||||
inline void update_eden_size() {
|
||||
size_t eden_size_in_bytes = size_policy()->calculated_eden_size_in_bytes();
|
||||
_eden_size_counter->set_value(eden_size_in_bytes);
|
||||
}
|
||||
|
||||
inline void update_promo_size() {
|
||||
_promo_size_counter->set_value(
|
||||
size_policy()->calculated_promo_size_in_bytes());
|
||||
}
|
||||
|
||||
inline void update_avg_minor_pause_counter() {
|
||||
_avg_minor_pause_counter->set_value((jlong)
|
||||
(size_policy()->avg_minor_pause()->average() * 1000.0));
|
||||
}
|
||||
inline void update_avg_minor_interval_counter() {
|
||||
_avg_minor_interval_counter->set_value((jlong)
|
||||
(size_policy()->avg_minor_interval()->average() * 1000.0));
|
||||
}
|
||||
|
||||
#ifdef NOT_PRODUCT
|
||||
inline void update_minor_pause_counter() {
|
||||
_minor_pause_counter->set_value((jlong)
|
||||
(size_policy()->avg_minor_pause()->last_sample() * 1000.0));
|
||||
}
|
||||
#endif
|
||||
inline void update_minor_gc_cost_counter() {
|
||||
_minor_gc_cost_counter->set_value((jlong)
|
||||
(size_policy()->minor_gc_cost() * 100.0));
|
||||
}
|
||||
|
||||
inline void update_avg_young_live_counter() {
|
||||
_avg_young_live_counter->set_value(
|
||||
(jlong)(size_policy()->avg_young_live()->average())
|
||||
);
|
||||
}
|
||||
|
||||
inline void update_avg_survived_avg_counters() {
|
||||
_avg_survived_avg_counter->set_value(
|
||||
(jlong)(size_policy()->_avg_survived->average())
|
||||
);
|
||||
}
|
||||
inline void update_avg_survived_dev_counters() {
|
||||
_avg_survived_dev_counter->set_value(
|
||||
(jlong)(size_policy()->_avg_survived->deviation())
|
||||
);
|
||||
}
|
||||
inline void update_avg_survived_padded_avg_counters() {
|
||||
_avg_survived_padded_avg_counter->set_value(
|
||||
(jlong)(size_policy()->_avg_survived->padded_average())
|
||||
);
|
||||
}
|
||||
|
||||
inline void update_change_old_gen_for_throughput() {
|
||||
_change_old_gen_for_throughput_counter->set_value(
|
||||
size_policy()->change_old_gen_for_throughput());
|
||||
}
|
||||
inline void update_change_young_gen_for_throughput() {
|
||||
_change_young_gen_for_throughput_counter->set_value(
|
||||
size_policy()->change_young_gen_for_throughput());
|
||||
}
|
||||
inline void update_decrease_for_footprint() {
|
||||
_decrease_for_footprint_counter->set_value(
|
||||
size_policy()->decrease_for_footprint());
|
||||
}
|
||||
|
||||
inline void update_decide_at_full_gc_counter() {
|
||||
_decide_at_full_gc_counter->set_value(
|
||||
size_policy()->decide_at_full_gc());
|
||||
}
|
||||
|
||||
inline void update_minor_pause_young_slope_counter() {
|
||||
_minor_pause_young_slope_counter->set_value(
|
||||
(jlong)(size_policy()->minor_pause_young_slope() * 1000)
|
||||
);
|
||||
}
|
||||
|
||||
virtual void update_counters_from_policy();
|
||||
|
||||
protected:
|
||||
virtual AdaptiveSizePolicy* size_policy() { return _size_policy; }
|
||||
|
||||
public:
|
||||
GCAdaptivePolicyCounters(const char* name,
|
||||
int collectors,
|
||||
int generations,
|
||||
AdaptiveSizePolicy* size_policy);
|
||||
|
||||
inline void update_survived(size_t survived) {
|
||||
_survived_counter->set_value(survived);
|
||||
}
|
||||
inline void update_promoted(size_t promoted) {
|
||||
_promoted_counter->set_value(promoted);
|
||||
}
|
||||
inline void update_young_capacity(size_t size_in_bytes) {
|
||||
_young_capacity_counter->set_value(size_in_bytes);
|
||||
}
|
||||
|
||||
virtual void update_counters();
|
||||
|
||||
inline void update_survivor_size_counters() {
|
||||
desired_survivor_size()->set_value(
|
||||
size_policy()->calculated_survivor_size_in_bytes());
|
||||
}
|
||||
inline void update_survivor_overflowed(bool survivor_overflowed) {
|
||||
_survivor_overflowed_counter->set_value(survivor_overflowed);
|
||||
}
|
||||
inline void update_tenuring_threshold(uint threshold) {
|
||||
tenuring_threshold()->set_value(threshold);
|
||||
}
|
||||
inline void update_increment_tenuring_threshold_for_gc_cost() {
|
||||
_increment_tenuring_threshold_for_gc_cost_counter->set_value(
|
||||
size_policy()->increment_tenuring_threshold_for_gc_cost());
|
||||
}
|
||||
inline void update_decrement_tenuring_threshold_for_gc_cost() {
|
||||
_decrement_tenuring_threshold_for_gc_cost_counter->set_value(
|
||||
size_policy()->decrement_tenuring_threshold_for_gc_cost());
|
||||
}
|
||||
inline void update_decrement_tenuring_threshold_for_survivor_limit() {
|
||||
_decrement_tenuring_threshold_for_survivor_limit_counter->set_value(
|
||||
size_policy()->decrement_tenuring_threshold_for_survivor_limit());
|
||||
}
|
||||
inline void update_change_young_gen_for_min_pauses() {
|
||||
_change_young_gen_for_min_pauses_counter->set_value(
|
||||
size_policy()->change_young_gen_for_min_pauses());
|
||||
}
|
||||
inline void update_change_old_gen_for_maj_pauses() {
|
||||
_change_old_gen_for_maj_pauses_counter->set_value(
|
||||
size_policy()->change_old_gen_for_maj_pauses());
|
||||
}
|
||||
|
||||
inline void update_minor_collection_slope_counter() {
|
||||
_minor_collection_slope_counter->set_value(
|
||||
(jlong)(size_policy()->minor_collection_slope() * 1000)
|
||||
);
|
||||
}
|
||||
|
||||
inline void update_major_collection_slope_counter() {
|
||||
_major_collection_slope_counter->set_value(
|
||||
(jlong)(size_policy()->major_collection_slope() * 1000)
|
||||
);
|
||||
}
|
||||
|
||||
void set_size_policy(AdaptiveSizePolicy* v) { _size_policy = v; }
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_PARALLEL_GCADAPTIVEPOLICYCOUNTERS_HPP
|
||||
@ -66,6 +66,11 @@ void ParallelArguments::initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
// True in product build, since tests using debug build often stress GC
|
||||
if (FLAG_IS_DEFAULT(UseGCOverheadLimit)) {
|
||||
FLAG_SET_DEFAULT(UseGCOverheadLimit, trueInProduct);
|
||||
}
|
||||
|
||||
if (InitialSurvivorRatio < MinSurvivorRatio) {
|
||||
if (FLAG_IS_CMDLINE(InitialSurvivorRatio)) {
|
||||
if (FLAG_IS_CMDLINE(MinSurvivorRatio)) {
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
#include "memory/universe.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/cpuTimeCounters.hpp"
|
||||
#include "runtime/globals_extension.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/java.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
@ -58,7 +59,7 @@
|
||||
PSYoungGen* ParallelScavengeHeap::_young_gen = nullptr;
|
||||
PSOldGen* ParallelScavengeHeap::_old_gen = nullptr;
|
||||
PSAdaptiveSizePolicy* ParallelScavengeHeap::_size_policy = nullptr;
|
||||
PSGCAdaptivePolicyCounters* ParallelScavengeHeap::_gc_policy_counters = nullptr;
|
||||
GCPolicyCounters* ParallelScavengeHeap::_gc_policy_counters = nullptr;
|
||||
|
||||
jint ParallelScavengeHeap::initialize() {
|
||||
const size_t reserved_heap_size = ParallelArguments::heap_reserved_size_bytes();
|
||||
@ -100,24 +101,15 @@ jint ParallelScavengeHeap::initialize() {
|
||||
|
||||
double max_gc_pause_sec = ((double) MaxGCPauseMillis)/1000.0;
|
||||
|
||||
const size_t eden_capacity = _young_gen->eden_space()->capacity_in_bytes();
|
||||
const size_t old_capacity = _old_gen->capacity_in_bytes();
|
||||
const size_t initial_promo_size = MIN2(eden_capacity, old_capacity);
|
||||
_size_policy =
|
||||
new PSAdaptiveSizePolicy(eden_capacity,
|
||||
initial_promo_size,
|
||||
young_gen()->to_space()->capacity_in_bytes(),
|
||||
SpaceAlignment,
|
||||
max_gc_pause_sec,
|
||||
GCTimeRatio
|
||||
);
|
||||
_size_policy = new PSAdaptiveSizePolicy(SpaceAlignment,
|
||||
max_gc_pause_sec,
|
||||
GCTimeRatio);
|
||||
|
||||
assert((old_gen()->virtual_space()->high_boundary() ==
|
||||
young_gen()->virtual_space()->low_boundary()),
|
||||
"Boundaries must meet");
|
||||
// initialize the policy counters - 2 collectors, 2 generations
|
||||
_gc_policy_counters =
|
||||
new PSGCAdaptivePolicyCounters("ParScav:MSC", 2, 2, _size_policy);
|
||||
_gc_policy_counters = new GCPolicyCounters("ParScav:MSC", 2, 2);
|
||||
|
||||
if (!PSParallelCompact::initialize_aux_data()) {
|
||||
return JNI_ENOMEM;
|
||||
@ -190,6 +182,21 @@ void ParallelScavengeHeap::post_initialize() {
|
||||
GCLocker::initialize();
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::gc_epilogue(bool full) {
|
||||
if (_is_heap_almost_full) {
|
||||
// Reset emergency state if eden is empty after a young/full gc
|
||||
if (_young_gen->eden_space()->is_empty()) {
|
||||
log_debug(gc)("Leaving memory constrained state; back to normal");
|
||||
_is_heap_almost_full = false;
|
||||
}
|
||||
} else {
|
||||
if (full && !_young_gen->eden_space()->is_empty()) {
|
||||
log_debug(gc)("Non-empty young-gen after full-gc; in memory constrained state");
|
||||
_is_heap_almost_full = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::update_counters() {
|
||||
young_gen()->update_counters();
|
||||
old_gen()->update_counters();
|
||||
@ -272,18 +279,17 @@ HeapWord* ParallelScavengeHeap::mem_allocate(size_t size,
|
||||
HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size,
|
||||
bool is_tlab,
|
||||
bool* gc_overhead_limit_was_exceeded) {
|
||||
|
||||
// In general gc_overhead_limit_was_exceeded should be false so
|
||||
// set it so here and reset it to true only if the gc time
|
||||
// limit is being exceeded as checked below.
|
||||
*gc_overhead_limit_was_exceeded = false;
|
||||
|
||||
HeapWord* result = young_gen()->allocate(size);
|
||||
{
|
||||
HeapWord* result = young_gen()->allocate(size);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
uint loop_count = 0;
|
||||
uint gc_count = 0;
|
||||
|
||||
while (result == nullptr) {
|
||||
while (true) {
|
||||
// We don't want to have multiple collections for a single filled generation.
|
||||
// To prevent this, each thread tracks the total_collections() value, and if
|
||||
// the count has changed, does not do a new collection.
|
||||
@ -299,21 +305,20 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size,
|
||||
MutexLocker ml(Heap_lock);
|
||||
gc_count = total_collections();
|
||||
|
||||
result = young_gen()->allocate(size);
|
||||
HeapWord* result = young_gen()->allocate(size);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// If certain conditions hold, try allocating from the old gen.
|
||||
if (!is_tlab) {
|
||||
result = mem_allocate_old_gen(size);
|
||||
if (!is_tlab && !should_alloc_in_eden(size)) {
|
||||
result = old_gen()->cas_allocate_noexpand(size);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(result == nullptr, "inv");
|
||||
{
|
||||
VM_ParallelCollectForAllocation op(size, is_tlab, gc_count);
|
||||
VMThread::execute(&op);
|
||||
@ -324,76 +329,61 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size,
|
||||
if (op.gc_succeeded()) {
|
||||
assert(is_in_or_null(op.result()), "result not in heap");
|
||||
|
||||
// Exit the loop if the gc time limit has been exceeded.
|
||||
// The allocation must have failed above ("result" guarding
|
||||
// this path is null) and the most recent collection has exceeded the
|
||||
// gc overhead limit (although enough may have been collected to
|
||||
// satisfy the allocation). Exit the loop so that an out-of-memory
|
||||
// will be thrown (return a null ignoring the contents of
|
||||
// op.result()),
|
||||
// but clear gc_overhead_limit_exceeded so that the next collection
|
||||
// starts with a clean slate (i.e., forgets about previous overhead
|
||||
// excesses). Fill op.result() with a filler object so that the
|
||||
// heap remains parsable.
|
||||
const bool limit_exceeded = size_policy()->gc_overhead_limit_exceeded();
|
||||
const bool softrefs_clear = soft_ref_policy()->all_soft_refs_clear();
|
||||
|
||||
if (limit_exceeded && softrefs_clear) {
|
||||
*gc_overhead_limit_was_exceeded = true;
|
||||
size_policy()->set_gc_overhead_limit_exceeded(false);
|
||||
log_trace(gc)("ParallelScavengeHeap::mem_allocate: return null because gc_overhead_limit_exceeded is set");
|
||||
if (op.result() != nullptr) {
|
||||
CollectedHeap::fill_with_object(op.result(), size);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return op.result();
|
||||
}
|
||||
// Was the gc-overhead reached inside the safepoint? If so, this mutator should return null as well for global consistency.
|
||||
if (_gc_overhead_counter >= GCOverheadLimitThreshold) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// The policy object will prevent us from looping forever. If the
|
||||
// time spent in gc crosses a threshold, we will bail out.
|
||||
loop_count++;
|
||||
if ((result == nullptr) && (QueuedAllocationWarningCount > 0) &&
|
||||
if ((QueuedAllocationWarningCount > 0) &&
|
||||
(loop_count % QueuedAllocationWarningCount == 0)) {
|
||||
log_warning(gc)("ParallelScavengeHeap::mem_allocate retries %d times", loop_count);
|
||||
log_warning(gc)("\tsize=%zu", size);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
HeapWord* ParallelScavengeHeap::allocate_old_gen_and_record(size_t size) {
|
||||
assert_locked_or_safepoint(Heap_lock);
|
||||
HeapWord* res = old_gen()->allocate(size);
|
||||
if (res != nullptr) {
|
||||
_size_policy->tenured_allocation(size * HeapWordSize);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
HeapWord* ParallelScavengeHeap::mem_allocate_old_gen(size_t size) {
|
||||
if (!should_alloc_in_eden(size)) {
|
||||
// Size is too big for eden.
|
||||
return allocate_old_gen_and_record(size);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::do_full_collection(bool clear_all_soft_refs) {
|
||||
PSParallelCompact::invoke(clear_all_soft_refs);
|
||||
}
|
||||
|
||||
HeapWord* ParallelScavengeHeap::expand_heap_and_allocate(size_t size, bool is_tlab) {
|
||||
HeapWord* result = nullptr;
|
||||
static bool check_gc_heap_free_limit(size_t free_bytes, size_t capacity_bytes) {
|
||||
return (free_bytes * 100 / capacity_bytes) < GCHeapFreeLimit;
|
||||
}
|
||||
|
||||
bool ParallelScavengeHeap::check_gc_overhead_limit() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "precondition");
|
||||
|
||||
if (UseGCOverheadLimit) {
|
||||
// The goal here is to return null prematurely so that apps can exit
|
||||
// gracefully when GC takes the most time.
|
||||
bool little_mutator_time = _size_policy->mutator_time_percent() * 100 < (100 - GCTimeLimit);
|
||||
bool little_free_space = check_gc_heap_free_limit(_young_gen->free_in_bytes(), _young_gen->capacity_in_bytes())
|
||||
&& check_gc_heap_free_limit( _old_gen->free_in_bytes(), _old_gen->capacity_in_bytes());
|
||||
if (little_mutator_time && little_free_space) {
|
||||
_gc_overhead_counter++;
|
||||
if (_gc_overhead_counter >= GCOverheadLimitThreshold) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
_gc_overhead_counter = 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
HeapWord* ParallelScavengeHeap::expand_heap_and_allocate(size_t size, bool is_tlab) {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "precondition");
|
||||
// We just finished a young/full gc, try everything to satisfy this allocation request.
|
||||
HeapWord* result = young_gen()->expand_and_allocate(size);
|
||||
|
||||
result = young_gen()->allocate(size);
|
||||
if (result == nullptr && !is_tlab) {
|
||||
result = old_gen()->expand_and_allocate(size);
|
||||
}
|
||||
|
||||
return result; // Could be null if we are out of space.
|
||||
}
|
||||
|
||||
@ -402,13 +392,19 @@ HeapWord* ParallelScavengeHeap::satisfy_failed_allocation(size_t size, bool is_t
|
||||
|
||||
HeapWord* result = nullptr;
|
||||
|
||||
// If young-gen can handle this allocation, attempt young-gc firstly.
|
||||
bool should_run_young_gc = is_tlab || should_alloc_in_eden(size);
|
||||
collect_at_safepoint(!should_run_young_gc);
|
||||
if (!_is_heap_almost_full) {
|
||||
// If young-gen can handle this allocation, attempt young-gc firstly, as young-gc is usually cheaper.
|
||||
bool should_run_young_gc = is_tlab || should_alloc_in_eden(size);
|
||||
|
||||
result = expand_heap_and_allocate(size, is_tlab);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
collect_at_safepoint(!should_run_young_gc);
|
||||
|
||||
// If gc-overhead is reached, we will skip allocation.
|
||||
if (!check_gc_overhead_limit()) {
|
||||
result = expand_heap_and_allocate(size, is_tlab);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach this point, we're really out of memory. Try every trick
|
||||
@ -428,18 +424,15 @@ HeapWord* ParallelScavengeHeap::satisfy_failed_allocation(size_t size, bool is_t
|
||||
HeapMaximumCompactionInterval = old_interval;
|
||||
}
|
||||
|
||||
result = expand_heap_and_allocate(size, is_tlab);
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
if (check_gc_overhead_limit()) {
|
||||
log_info(gc)("GCOverheadLimitThreshold %zu reached.", GCOverheadLimitThreshold);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// What else? We might try synchronous finalization later. If the total
|
||||
// space available is large enough for the allocation, then a more
|
||||
// complete compaction phase than we've tried so far might be
|
||||
// appropriate.
|
||||
return nullptr;
|
||||
}
|
||||
result = expand_heap_and_allocate(size, is_tlab);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::ensure_parsability(bool retire_tlabs) {
|
||||
CollectedHeap::ensure_parsability(retire_tlabs);
|
||||
@ -666,7 +659,6 @@ void ParallelScavengeHeap::gc_threads_do(ThreadClosure* tc) const {
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::print_tracing_info() const {
|
||||
AdaptiveSizePolicyOutput::print();
|
||||
log_debug(gc, heap, exit)("Accumulated young generation GC time %3.7f secs", PSScavenge::accumulated_time()->seconds());
|
||||
log_debug(gc, heap, exit)("Accumulated old generation GC time %3.7f secs", PSParallelCompact::accumulated_time()->seconds());
|
||||
}
|
||||
@ -763,15 +755,96 @@ PSCardTable* ParallelScavengeHeap::card_table() {
|
||||
return static_cast<PSCardTable*>(barrier_set()->card_table());
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::resize_young_gen(size_t eden_size,
|
||||
size_t survivor_size) {
|
||||
// Delegate the resize to the generation.
|
||||
_young_gen->resize(eden_size, survivor_size);
|
||||
static size_t calculate_free_from_free_ratio_flag(size_t live, uintx free_percent) {
|
||||
assert(free_percent != 100, "precondition");
|
||||
// We want to calculate how much free memory there can be based on the
|
||||
// live size.
|
||||
// percent * (free + live) = free
|
||||
// =>
|
||||
// free = (live * percent) / (1 - percent)
|
||||
|
||||
const double percent = free_percent / 100.0;
|
||||
return live * percent / (1.0 - percent);
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::resize_old_gen(size_t desired_free_space) {
|
||||
// Delegate the resize to the generation.
|
||||
_old_gen->resize(desired_free_space);
|
||||
size_t ParallelScavengeHeap::calculate_desired_old_gen_capacity(size_t old_gen_live_size) {
|
||||
// If min free percent is 100%, the old-gen should always be in its max capacity
|
||||
if (MinHeapFreeRatio == 100) {
|
||||
return _old_gen->max_gen_size();
|
||||
}
|
||||
|
||||
// Using recorded data to calculate the new capacity of old-gen to avoid
|
||||
// excessive expansion but also keep footprint low
|
||||
|
||||
size_t promoted_estimate = _size_policy->padded_average_promoted_in_bytes();
|
||||
// Should have at least this free room for the next young-gc promotion.
|
||||
size_t free_size = promoted_estimate;
|
||||
|
||||
size_t largest_live_size = MAX2((size_t)_size_policy->peak_old_gen_used_estimate(), old_gen_live_size);
|
||||
free_size += largest_live_size - old_gen_live_size;
|
||||
|
||||
// Respect free percent
|
||||
if (MinHeapFreeRatio != 0) {
|
||||
size_t min_free = calculate_free_from_free_ratio_flag(old_gen_live_size, MinHeapFreeRatio);
|
||||
free_size = MAX2(free_size, min_free);
|
||||
}
|
||||
|
||||
if (MaxHeapFreeRatio != 100) {
|
||||
size_t max_free = calculate_free_from_free_ratio_flag(old_gen_live_size, MaxHeapFreeRatio);
|
||||
free_size = MIN2(max_free, free_size);
|
||||
}
|
||||
|
||||
return old_gen_live_size + free_size;
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::resize_old_gen_after_full_gc() {
|
||||
size_t current_capacity = _old_gen->capacity_in_bytes();
|
||||
size_t desired_capacity = calculate_desired_old_gen_capacity(old_gen()->used_in_bytes());
|
||||
|
||||
// If MinHeapFreeRatio is at its default value; shrink cautiously. Otherwise, users expect prompt shrinking.
|
||||
if (FLAG_IS_DEFAULT(MinHeapFreeRatio)) {
|
||||
if (desired_capacity < current_capacity) {
|
||||
// Shrinking
|
||||
if (total_full_collections() < AdaptiveSizePolicyReadyThreshold) {
|
||||
// No enough data for shrinking
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_old_gen->resize(desired_capacity);
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::resize_after_young_gc(bool is_survivor_overflowing) {
|
||||
_young_gen->resize_after_young_gc(is_survivor_overflowing);
|
||||
|
||||
// Consider if should shrink old-gen
|
||||
if (!is_survivor_overflowing) {
|
||||
// Upper bound for a single step shrink
|
||||
size_t max_shrink_bytes = SpaceAlignment;
|
||||
size_t shrink_bytes = _size_policy->compute_old_gen_shrink_bytes(old_gen()->free_in_bytes(), max_shrink_bytes);
|
||||
if (shrink_bytes != 0) {
|
||||
if (MinHeapFreeRatio != 0) {
|
||||
size_t new_capacity = old_gen()->capacity_in_bytes() - shrink_bytes;
|
||||
size_t new_free_size = old_gen()->free_in_bytes() - shrink_bytes;
|
||||
if ((double)new_free_size / new_capacity * 100 < MinHeapFreeRatio) {
|
||||
// Would violate MinHeapFreeRatio
|
||||
return;
|
||||
}
|
||||
}
|
||||
old_gen()->shrink(shrink_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::resize_after_full_gc() {
|
||||
resize_old_gen_after_full_gc();
|
||||
// We don't resize young-gen after full-gc because:
|
||||
// 1. eden-size directly affects young-gc frequency (GCTimeRatio), and we
|
||||
// don't have enough info to determine its desired size.
|
||||
// 2. eden can contain live objs after a full-gc, which is unsafe for
|
||||
// resizing. We will perform expansion on allocation if needed, in
|
||||
// satisfy_failed_allocation().
|
||||
}
|
||||
|
||||
HeapWord* ParallelScavengeHeap::allocate_loaded_archive_space(size_t size) {
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
#ifndef SHARE_GC_PARALLEL_PARALLELSCAVENGEHEAP_HPP
|
||||
#define SHARE_GC_PARALLEL_PARALLELSCAVENGEHEAP_HPP
|
||||
|
||||
#include "gc/parallel/psGCAdaptivePolicyCounters.hpp"
|
||||
#include "gc/parallel/psAdaptiveSizePolicy.hpp"
|
||||
#include "gc/parallel/psOldGen.hpp"
|
||||
#include "gc/parallel/psYoungGen.hpp"
|
||||
#include "gc/shared/cardTableBarrierSet.hpp"
|
||||
@ -60,11 +60,11 @@ class ReservedSpace;
|
||||
// +-- generation boundary (fixed after startup)
|
||||
// |
|
||||
// |<- old gen (reserved) ->|<- young gen (reserved) ->|
|
||||
// +---------------+--------+-----------------+--------+--------+--------+
|
||||
// | old | | eden | from | to | |
|
||||
// | | | | (to) | (from) | |
|
||||
// +---------------+--------+-----------------+--------+--------+--------+
|
||||
// |<- committed ->| |<- committed ->|
|
||||
// +---------------+--------+--------+--------+------------------+-------+
|
||||
// | old | | from | to | eden | |
|
||||
// | | | (to) | (from) | | |
|
||||
// +---------------+--------+--------+--------+------------------+-------+
|
||||
// |<- committed ->| |<- committed ->|
|
||||
//
|
||||
class ParallelScavengeHeap : public CollectedHeap {
|
||||
friend class VMStructs;
|
||||
@ -74,7 +74,7 @@ class ParallelScavengeHeap : public CollectedHeap {
|
||||
|
||||
// Sizing policy for entire heap
|
||||
static PSAdaptiveSizePolicy* _size_policy;
|
||||
static PSGCAdaptivePolicyCounters* _gc_policy_counters;
|
||||
static GCPolicyCounters* _gc_policy_counters;
|
||||
|
||||
GCMemoryManager* _young_manager;
|
||||
GCMemoryManager* _old_manager;
|
||||
@ -85,14 +85,15 @@ class ParallelScavengeHeap : public CollectedHeap {
|
||||
|
||||
WorkerThreads _workers;
|
||||
|
||||
uint _gc_overhead_counter;
|
||||
|
||||
bool _is_heap_almost_full;
|
||||
|
||||
void initialize_serviceability() override;
|
||||
|
||||
void trace_actual_reserved_page_size(const size_t reserved_heap_size, const ReservedSpace rs);
|
||||
void trace_heap(GCWhen::Type when, const GCTracer* tracer) override;
|
||||
|
||||
// Allocate in oldgen and record the allocation with the size_policy.
|
||||
HeapWord* allocate_old_gen_and_record(size_t word_size);
|
||||
|
||||
void update_parallel_worker_threads_cpu_time();
|
||||
|
||||
bool must_clear_all_soft_refs();
|
||||
@ -101,8 +102,6 @@ class ParallelScavengeHeap : public CollectedHeap {
|
||||
|
||||
inline bool should_alloc_in_eden(size_t size) const;
|
||||
|
||||
HeapWord* mem_allocate_old_gen(size_t size);
|
||||
|
||||
HeapWord* mem_allocate_work(size_t size,
|
||||
bool is_tlab,
|
||||
bool* gc_overhead_limit_was_exceeded);
|
||||
@ -111,6 +110,12 @@ class ParallelScavengeHeap : public CollectedHeap {
|
||||
|
||||
void do_full_collection(bool clear_all_soft_refs) override;
|
||||
|
||||
bool check_gc_overhead_limit();
|
||||
|
||||
size_t calculate_desired_old_gen_capacity(size_t old_gen_live_size);
|
||||
|
||||
void resize_old_gen_after_full_gc();
|
||||
|
||||
void print_tracing_info() const override;
|
||||
void stop() override {};
|
||||
|
||||
@ -122,7 +127,9 @@ public:
|
||||
_eden_pool(nullptr),
|
||||
_survivor_pool(nullptr),
|
||||
_old_pool(nullptr),
|
||||
_workers("GC Thread", ParallelGCThreads) { }
|
||||
_workers("GC Thread", ParallelGCThreads),
|
||||
_gc_overhead_counter(0),
|
||||
_is_heap_almost_full(false) {}
|
||||
|
||||
Name kind() const override {
|
||||
return CollectedHeap::Parallel;
|
||||
@ -132,6 +139,9 @@ public:
|
||||
return "Parallel";
|
||||
}
|
||||
|
||||
// Invoked at gc-pause-end
|
||||
void gc_epilogue(bool full);
|
||||
|
||||
GrowableArray<GCMemoryManager*> memory_managers() override;
|
||||
GrowableArray<MemoryPool*> memory_pools() override;
|
||||
|
||||
@ -140,7 +150,7 @@ public:
|
||||
|
||||
PSAdaptiveSizePolicy* size_policy() { return _size_policy; }
|
||||
|
||||
static PSGCAdaptivePolicyCounters* gc_policy_counters() { return _gc_policy_counters; }
|
||||
static GCPolicyCounters* gc_policy_counters() { return _gc_policy_counters; }
|
||||
|
||||
static ParallelScavengeHeap* heap() {
|
||||
return named_heap<ParallelScavengeHeap>(CollectedHeap::Parallel);
|
||||
@ -226,13 +236,8 @@ public:
|
||||
|
||||
void verify(VerifyOption option /* ignored */) override;
|
||||
|
||||
// Resize the young generation. The reserved space for the
|
||||
// generation may be expanded in preparation for the resize.
|
||||
void resize_young_gen(size_t eden_size, size_t survivor_size);
|
||||
|
||||
// Resize the old generation. The reserved space for the
|
||||
// generation may be expanded in preparation for the resize.
|
||||
void resize_old_gen(size_t desired_free_space);
|
||||
void resize_after_young_gc(bool is_survivor_overflowing);
|
||||
void resize_after_full_gc();
|
||||
|
||||
GCMemoryManager* old_gc_manager() const { return _old_manager; }
|
||||
GCMemoryManager* young_gc_manager() const { return _young_manager; }
|
||||
@ -250,33 +255,4 @@ public:
|
||||
void unpin_object(JavaThread* thread, oop obj) override;
|
||||
};
|
||||
|
||||
// Class that can be used to print information about the
|
||||
// adaptive size policy at intervals specified by
|
||||
// AdaptiveSizePolicyOutputInterval. Only print information
|
||||
// if an adaptive size policy is in use.
|
||||
class AdaptiveSizePolicyOutput : AllStatic {
|
||||
static bool enabled() {
|
||||
return UseParallelGC &&
|
||||
UseAdaptiveSizePolicy &&
|
||||
log_is_enabled(Debug, gc, ergo);
|
||||
}
|
||||
public:
|
||||
static void print() {
|
||||
if (enabled()) {
|
||||
ParallelScavengeHeap::heap()->size_policy()->print();
|
||||
}
|
||||
}
|
||||
|
||||
static void print(AdaptiveSizePolicy* size_policy, uint count) {
|
||||
bool do_print =
|
||||
enabled() &&
|
||||
(AdaptiveSizePolicyOutputInterval > 0) &&
|
||||
(count % AdaptiveSizePolicyOutputInterval) == 0;
|
||||
|
||||
if (do_print) {
|
||||
size_policy->print();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_PARALLEL_PARALLELSCAVENGEHEAP_HPP
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,6 @@
|
||||
#define SHARE_GC_PARALLEL_PSADAPTIVESIZEPOLICY_HPP
|
||||
|
||||
#include "gc/shared/adaptiveSizePolicy.hpp"
|
||||
#include "gc/shared/gcCause.hpp"
|
||||
#include "gc/shared/gcUtil.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
|
||||
@ -34,151 +33,34 @@
|
||||
// optimal free space for both the young and old generation
|
||||
// based on current application characteristics (based on gc cost
|
||||
// and application footprint).
|
||||
//
|
||||
// It also computes an optimal tenuring threshold between the young
|
||||
// and old generations, so as to equalize the cost of collections
|
||||
// of those generations, as well as optimal survivor space sizes
|
||||
// for the young generation.
|
||||
//
|
||||
// While this class is specifically intended for a generational system
|
||||
// consisting of a young gen (containing an Eden and two semi-spaces)
|
||||
// and a tenured gen, as well as a perm gen for reflective data, it
|
||||
// makes NO references to specific generations.
|
||||
//
|
||||
// 05/02/2003 Update
|
||||
// The 1.5 policy makes use of data gathered for the costs of GC on
|
||||
// specific generations. That data does reference specific
|
||||
// generation. Also diagnostics specific to generations have
|
||||
// been added.
|
||||
|
||||
// Forward decls
|
||||
class elapsedTimer;
|
||||
|
||||
class PSAdaptiveSizePolicy : public AdaptiveSizePolicy {
|
||||
friend class PSGCAdaptivePolicyCounters;
|
||||
private:
|
||||
// These values are used to record decisions made during the
|
||||
// policy. For example, if the young generation was decreased
|
||||
// to decrease the GC cost of minor collections the value
|
||||
// decrease_young_gen_for_throughput_true is used.
|
||||
|
||||
// Last calculated sizes, in bytes, and aligned
|
||||
// NEEDS_CLEANUP should use sizes.hpp, but it works in ints, not size_t's
|
||||
|
||||
// Time statistics
|
||||
AdaptivePaddedAverage* _avg_major_pause;
|
||||
|
||||
// Footprint statistics
|
||||
AdaptiveWeightedAverage* _avg_base_footprint;
|
||||
|
||||
// Statistics for promoted objs
|
||||
AdaptivePaddedNoZeroDevAverage* _avg_promoted;
|
||||
|
||||
// Variable for estimating the major and minor pause times.
|
||||
// These variables represent linear least-squares fits of
|
||||
// the data.
|
||||
// major pause time vs. old gen size
|
||||
LinearLeastSquareFit* _major_pause_old_estimator;
|
||||
// major pause time vs. young gen size
|
||||
LinearLeastSquareFit* _major_pause_young_estimator;
|
||||
|
||||
|
||||
// These record the most recent collection times. They
|
||||
// are available as an alternative to using the averages
|
||||
// for making ergonomic decisions.
|
||||
double _latest_major_mutator_interval_seconds;
|
||||
|
||||
const size_t _space_alignment; // alignment for eden, survivors
|
||||
|
||||
// The amount of live data in the heap at the last full GC, used
|
||||
// as a baseline to help us determine when we need to perform the
|
||||
// next full GC.
|
||||
size_t _live_at_last_full_gc;
|
||||
|
||||
// decrease/increase the old generation for minor pause time
|
||||
int _change_old_gen_for_min_pauses;
|
||||
|
||||
// increase/decrease the young generation for major pause time
|
||||
int _change_young_gen_for_maj_pauses;
|
||||
|
||||
// To facilitate faster growth at start up, supplement the normal
|
||||
// growth percentage for the young gen eden and the
|
||||
// old gen space for promotion with these value which decay
|
||||
// with increasing collections.
|
||||
uint _young_gen_size_increment_supplement;
|
||||
uint _old_gen_size_increment_supplement;
|
||||
|
||||
private:
|
||||
size_t decrease_eden_for_minor_pause_time(size_t current_eden_size);
|
||||
|
||||
void adjust_eden_for_minor_pause_time(size_t* desired_eden_size_ptr);
|
||||
// Change the generation sizes to achieve a GC pause time goal
|
||||
// Returned sizes are not necessarily aligned.
|
||||
void adjust_promo_for_pause_time(size_t* desired_promo_size_ptr);
|
||||
void adjust_eden_for_pause_time(size_t* desired_eden_size_ptr);
|
||||
// Change the generation sizes to achieve an application throughput goal
|
||||
// Returned sizes are not necessarily aligned.
|
||||
void adjust_promo_for_throughput(bool is_full_gc,
|
||||
size_t* desired_promo_size_ptr);
|
||||
void adjust_eden_for_throughput(bool is_full_gc,
|
||||
size_t* desired_eden_size_ptr);
|
||||
// Change the generation sizes to achieve minimum footprint
|
||||
// Returned sizes are not aligned.
|
||||
size_t adjust_promo_for_footprint(size_t desired_promo_size,
|
||||
size_t desired_total);
|
||||
size_t adjust_eden_for_footprint(size_t desired_promo_size,
|
||||
size_t desired_total);
|
||||
size_t increase_eden(size_t current_eden_size);
|
||||
|
||||
// Size in bytes for an increment or decrement of eden.
|
||||
size_t eden_decrement_aligned_down(size_t cur_eden);
|
||||
size_t eden_increment_with_supplement_aligned_up(size_t cur_eden);
|
||||
|
||||
// Size in bytes for an increment or decrement of the promotion area
|
||||
size_t promo_decrement_aligned_down(size_t cur_promo);
|
||||
size_t promo_increment_with_supplement_aligned_up(size_t cur_promo);
|
||||
|
||||
// Returns a change that has been scaled down. Result
|
||||
// is not aligned. (If useful, move to some shared
|
||||
// location.)
|
||||
size_t scale_down(size_t change, double part, double total);
|
||||
|
||||
protected:
|
||||
|
||||
// Footprint accessors
|
||||
size_t live_space() const {
|
||||
return (size_t)(avg_young_live()->average() +
|
||||
avg_old_live()->average());
|
||||
}
|
||||
size_t free_space() const {
|
||||
return _eden_size + _promo_size;
|
||||
}
|
||||
|
||||
void set_promo_size(size_t new_size) {
|
||||
_promo_size = new_size;
|
||||
}
|
||||
|
||||
// Update estimators
|
||||
void update_minor_pause_old_estimator(double minor_pause_in_ms);
|
||||
|
||||
virtual GCPolicyKind kind() const { return _gc_ps_adaptive_size_policy; }
|
||||
|
||||
public:
|
||||
// Accessors for use by performance counters
|
||||
AdaptivePaddedNoZeroDevAverage* avg_promoted() const {
|
||||
return _avg_promoted;
|
||||
}
|
||||
AdaptiveWeightedAverage* avg_base_footprint() const {
|
||||
return _avg_base_footprint;
|
||||
}
|
||||
public:
|
||||
|
||||
// Input arguments are initial free space sizes for young and old
|
||||
// generations, the initial survivor space size, the
|
||||
// alignment values and the pause & throughput goals.
|
||||
//
|
||||
// NEEDS_CLEANUP this is a singleton object
|
||||
PSAdaptiveSizePolicy(size_t init_eden_size,
|
||||
size_t init_promo_size,
|
||||
size_t init_survivor_size,
|
||||
size_t space_alignment,
|
||||
PSAdaptiveSizePolicy(size_t space_alignment,
|
||||
double gc_pause_goal_sec,
|
||||
uint gc_time_ratio);
|
||||
|
||||
@ -186,18 +68,11 @@ class PSAdaptiveSizePolicy : public AdaptiveSizePolicy {
|
||||
// called by GC algorithms. It is the responsibility of users of this
|
||||
// policy to call these methods at the correct times!
|
||||
void major_collection_begin();
|
||||
void major_collection_end(size_t amount_live, GCCause::Cause gc_cause);
|
||||
void major_collection_end();
|
||||
|
||||
void tenured_allocation(size_t size) {
|
||||
_avg_pretenured->sample(size);
|
||||
}
|
||||
void print_stats(bool is_survivor_overflowing);
|
||||
|
||||
// Accessors
|
||||
// NEEDS_CLEANUP should use sizes.hpp
|
||||
|
||||
static size_t calculate_free_based_on_live(size_t live, uintx ratio_as_percentage);
|
||||
|
||||
size_t calculated_old_free_size_in_bytes() const;
|
||||
|
||||
size_t average_promoted_in_bytes() const {
|
||||
return (size_t)avg_promoted()->average();
|
||||
@ -207,63 +82,14 @@ class PSAdaptiveSizePolicy : public AdaptiveSizePolicy {
|
||||
return (size_t)avg_promoted()->padded_average();
|
||||
}
|
||||
|
||||
int change_young_gen_for_maj_pauses() {
|
||||
return _change_young_gen_for_maj_pauses;
|
||||
}
|
||||
void set_change_young_gen_for_maj_pauses(int v) {
|
||||
_change_young_gen_for_maj_pauses = v;
|
||||
}
|
||||
size_t compute_desired_eden_size(bool is_survivor_overflowing, size_t cur_eden);
|
||||
|
||||
int change_old_gen_for_min_pauses() {
|
||||
return _change_old_gen_for_min_pauses;
|
||||
}
|
||||
void set_change_old_gen_for_min_pauses(int v) {
|
||||
_change_old_gen_for_min_pauses = v;
|
||||
}
|
||||
size_t compute_desired_survivor_size(size_t current_survivor_size, size_t max_gen_size);
|
||||
|
||||
// Accessors for estimators. The slope of the linear fit is
|
||||
// currently all that is used for making decisions.
|
||||
size_t compute_old_gen_shrink_bytes(size_t old_gen_free_bytes, size_t max_shrink_bytes);
|
||||
|
||||
LinearLeastSquareFit* major_pause_old_estimator() {
|
||||
return _major_pause_old_estimator;
|
||||
}
|
||||
|
||||
virtual void clear_generation_free_space_flags();
|
||||
|
||||
double major_pause_old_slope() { return _major_pause_old_estimator->slope(); }
|
||||
double major_pause_young_slope() {
|
||||
return _major_pause_young_estimator->slope();
|
||||
}
|
||||
|
||||
// Calculates optimal (free) space sizes for both the young and old
|
||||
// generations. Stores results in _eden_size and _promo_size.
|
||||
// Takes current used space in all generations as input, as well
|
||||
// as an indication if a full gc has just been performed, for use
|
||||
// in deciding if an OOM error should be thrown.
|
||||
void compute_generations_free_space(size_t young_live,
|
||||
size_t eden_live,
|
||||
size_t old_live,
|
||||
size_t cur_eden, // current eden in bytes
|
||||
size_t max_old_gen_size,
|
||||
size_t max_eden_size,
|
||||
bool is_full_gc);
|
||||
|
||||
void compute_eden_space_size(size_t young_live,
|
||||
size_t eden_live,
|
||||
size_t cur_eden, // current eden in bytes
|
||||
size_t max_eden_size,
|
||||
bool is_full_gc);
|
||||
|
||||
void compute_old_gen_free_space(size_t old_live,
|
||||
size_t cur_eden, // current eden in bytes
|
||||
size_t max_old_gen_size,
|
||||
bool is_full_gc);
|
||||
|
||||
// Calculates new survivor space size; returns a new tenuring threshold
|
||||
// value. Stores new survivor size in _survivor_size.
|
||||
uint compute_survivor_space_size_and_threshold(bool is_survivor_overflow,
|
||||
uint tenuring_threshold,
|
||||
size_t survivor_limit);
|
||||
uint compute_tenuring_threshold(bool is_survivor_overflowing,
|
||||
uint tenuring_threshold);
|
||||
|
||||
// Return the maximum size of a survivor space if the young generation were of
|
||||
// size gen_size.
|
||||
@ -279,21 +105,14 @@ class PSAdaptiveSizePolicy : public AdaptiveSizePolicy {
|
||||
return sz > alignment ? align_down(sz, alignment) : alignment;
|
||||
}
|
||||
|
||||
size_t live_at_last_full_gc() {
|
||||
return _live_at_last_full_gc;
|
||||
}
|
||||
|
||||
// Update averages that are always used (even
|
||||
// if adaptive sizing is turned off).
|
||||
void update_averages(bool is_survivor_overflow,
|
||||
size_t survived,
|
||||
size_t promoted);
|
||||
|
||||
// Printing support
|
||||
virtual bool print() const;
|
||||
|
||||
// Decay the supplemental growth additive.
|
||||
void decay_supplemental_growth(bool is_full_gc);
|
||||
void decay_supplemental_growth(uint num_minor_gcs);
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_PARALLEL_PSADAPTIVESIZEPOLICY_HPP
|
||||
|
||||
@ -1,179 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gc/parallel/psGCAdaptivePolicyCounters.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
|
||||
PSGCAdaptivePolicyCounters::PSGCAdaptivePolicyCounters(const char* name_arg,
|
||||
int collectors,
|
||||
int generations,
|
||||
PSAdaptiveSizePolicy* size_policy_arg)
|
||||
: GCAdaptivePolicyCounters(name_arg,
|
||||
collectors,
|
||||
generations,
|
||||
size_policy_arg) {
|
||||
if (UsePerfData) {
|
||||
EXCEPTION_MARK;
|
||||
ResourceMark rm;
|
||||
|
||||
const char* cname;
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "oldPromoSize");
|
||||
_old_promo_size = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, ps_size_policy()->calculated_promo_size_in_bytes(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "oldEdenSize");
|
||||
_old_eden_size = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, ps_size_policy()->calculated_eden_size_in_bytes(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "oldCapacity");
|
||||
_old_capacity = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, (jlong) InitialHeapSize, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgPromotedAvg");
|
||||
_avg_promoted_avg_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes,
|
||||
ps_size_policy()->calculated_promo_size_in_bytes(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgPromotedDev");
|
||||
_avg_promoted_dev_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes,
|
||||
(jlong) 0 , CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgPromotedPaddedAvg");
|
||||
_avg_promoted_padded_avg_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes,
|
||||
ps_size_policy()->calculated_promo_size_in_bytes(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"avgPretenuredPaddedAvg");
|
||||
_avg_pretenured_padded_avg =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes,
|
||||
(jlong) 0, CHECK);
|
||||
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"changeYoungGenForMajPauses");
|
||||
_change_young_gen_for_maj_pauses_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events,
|
||||
(jlong)0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(),
|
||||
"changeOldGenForMinPauses");
|
||||
_change_old_gen_for_min_pauses =
|
||||
PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Events,
|
||||
(jlong)0, CHECK);
|
||||
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgMajorPauseTime");
|
||||
_avg_major_pause = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Ticks, (jlong) ps_size_policy()->_avg_major_pause->average(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "avgMajorIntervalTime");
|
||||
_avg_major_interval = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Ticks, (jlong) ps_size_policy()->_avg_major_interval->average(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "majorGcCost");
|
||||
_major_gc_cost_counter = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Ticks, (jlong) ps_size_policy()->major_gc_cost(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "liveSpace");
|
||||
_live_space = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, ps_size_policy()->live_space(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "freeSpace");
|
||||
_free_space = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, ps_size_policy()->free_space(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "liveAtLastFullGc");
|
||||
_live_at_last_full_gc_counter =
|
||||
PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_Bytes, ps_size_policy()->live_at_last_full_gc(), CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "majorPauseOldSlope");
|
||||
_major_pause_old_slope = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_None, (jlong) 0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "minorPauseOldSlope");
|
||||
_minor_pause_old_slope = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_None, (jlong) 0, CHECK);
|
||||
|
||||
cname = PerfDataManager::counter_name(name_space(), "majorPauseYoungSlope");
|
||||
_major_pause_young_slope = PerfDataManager::create_variable(SUN_GC, cname,
|
||||
PerfData::U_None, (jlong) 0, CHECK);
|
||||
|
||||
_counter_time_stamp.update();
|
||||
}
|
||||
|
||||
assert(size_policy()->is_gc_ps_adaptive_size_policy(),
|
||||
"Wrong type of size policy");
|
||||
}
|
||||
|
||||
void PSGCAdaptivePolicyCounters::update_counters_from_policy() {
|
||||
if (UsePerfData) {
|
||||
GCAdaptivePolicyCounters::update_counters_from_policy();
|
||||
update_eden_size();
|
||||
update_promo_size();
|
||||
update_avg_old_live();
|
||||
update_survivor_size_counters();
|
||||
update_avg_promoted_avg();
|
||||
update_avg_promoted_dev();
|
||||
update_avg_promoted_padded_avg();
|
||||
update_avg_pretenured_padded_avg();
|
||||
|
||||
update_avg_major_pause();
|
||||
update_avg_major_interval();
|
||||
update_minor_gc_cost_counter();
|
||||
update_major_gc_cost_counter();
|
||||
update_mutator_cost_counter();
|
||||
update_decrement_tenuring_threshold_for_gc_cost();
|
||||
update_increment_tenuring_threshold_for_gc_cost();
|
||||
update_decrement_tenuring_threshold_for_survivor_limit();
|
||||
update_live_space();
|
||||
update_free_space();
|
||||
|
||||
update_change_old_gen_for_maj_pauses();
|
||||
update_change_young_gen_for_maj_pauses();
|
||||
update_change_old_gen_for_min_pauses();
|
||||
|
||||
update_change_old_gen_for_throughput();
|
||||
update_change_young_gen_for_throughput();
|
||||
|
||||
update_decrease_for_footprint();
|
||||
update_decide_at_full_gc_counter();
|
||||
|
||||
update_major_pause_old_slope();
|
||||
update_minor_pause_old_slope();
|
||||
update_major_pause_young_slope();
|
||||
update_minor_collection_slope_counter();
|
||||
update_gc_overhead_limit_exceeded_counter();
|
||||
update_live_at_last_full_gc_counter();
|
||||
}
|
||||
}
|
||||
|
||||
void PSGCAdaptivePolicyCounters::update_counters() {
|
||||
if (UsePerfData) {
|
||||
update_counters_from_policy();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user