diff --git a/.github/actions/build-jtreg/action.yml b/.github/actions/build-jtreg/action.yml
index 3abfa260c17..0ba9937fb45 100644
--- a/.github/actions/build-jtreg/action.yml
+++ b/.github/actions/build-jtreg/action.yml
@@ -65,4 +65,4 @@ runs:
with:
name: bundles-jtreg-${{ steps.version.outputs.value }}
path: jtreg/installed
- retention-days: 1
+ retention-days: 5
diff --git a/.github/actions/get-msys2/action.yml b/.github/actions/get-msys2/action.yml
index 4ca5d2ab847..d93b6e3763b 100644
--- a/.github/actions/get-msys2/action.yml
+++ b/.github/actions/get-msys2/action.yml
@@ -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
diff --git a/.github/actions/upload-bundles/action.yml b/.github/actions/upload-bundles/action.yml
index dfa994baac0..ca5366f3d6c 100644
--- a/.github/actions/upload-bundles/action.yml
+++ b/.github/actions/upload-bundles/action.yml
@@ -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'
diff --git a/.github/workflows/build-alpine-linux.yml b/.github/workflows/build-alpine-linux.yml
index ac5870ca675..a39b342a248 100644
--- a/.github/workflows/build-alpine-linux.yml
+++ b/.github/workflows/build-alpine-linux.yml
@@ -51,11 +51,15 @@ on:
make-arguments:
required: false
type: string
+ dry-run:
+ required: false
+ type: boolean
+ default: false
jobs:
build-linux:
name: build
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
container:
image: alpine:3.20
@@ -104,9 +108,11 @@ jobs:
make-target: '${{ inputs.make-target }} ${{ inputs.make-arguments }}'
platform: ${{ inputs.platform }}
debug-suffix: '${{ matrix.suffix }}'
+ if: ${{ inputs.dry-run == false }}
- name: 'Upload bundles'
uses: ./.github/actions/upload-bundles
with:
platform: ${{ inputs.platform }}
debug-suffix: '${{ matrix.suffix }}'
+ if: ${{ inputs.dry-run == false }}
diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml
index 3f4ba67f996..e70937f57b6 100644
--- a/.github/workflows/build-cross-compile.yml
+++ b/.github/workflows/build-cross-compile.yml
@@ -40,11 +40,15 @@ on:
make-arguments:
required: false
type: string
+ dry-run:
+ required: false
+ type: boolean
+ default: false
jobs:
build-cross-compile:
name: build
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
@@ -60,33 +64,33 @@ jobs:
gnu-arch: aarch64
debian-arch: arm64
debian-repository: https://httpredir.debian.org/debian/
- debian-version: bullseye
+ debian-version: trixie
tolerate-sysroot-errors: false
- target-cpu: arm
gnu-arch: arm
debian-arch: armhf
debian-repository: https://httpredir.debian.org/debian/
- debian-version: bullseye
+ debian-version: trixie
tolerate-sysroot-errors: false
gnu-abi: eabihf
- target-cpu: s390x
gnu-arch: s390x
debian-arch: s390x
debian-repository: https://httpredir.debian.org/debian/
- debian-version: bullseye
+ debian-version: trixie
tolerate-sysroot-errors: false
- target-cpu: ppc64le
gnu-arch: powerpc64le
debian-arch: ppc64el
debian-repository: https://httpredir.debian.org/debian/
- debian-version: bullseye
+ debian-version: trixie
tolerate-sysroot-errors: false
- target-cpu: riscv64
gnu-arch: riscv64
debian-arch: riscv64
debian-repository: https://httpredir.debian.org/debian/
- debian-version: sid
- tolerate-sysroot-errors: true
+ debian-version: trixie
+ tolerate-sysroot-errors: false
steps:
- name: 'Checkout the JDK source'
@@ -189,4 +193,4 @@ jobs:
with:
make-target: 'hotspot ${{ inputs.make-arguments }}'
platform: linux-${{ matrix.target-cpu }}
- if: steps.create-sysroot.outcome == 'success' || steps.get-cached-sysroot.outputs.cache-hit == 'true'
+ if: ((steps.create-sysroot.outcome == 'success' || steps.get-cached-sysroot.outputs.cache-hit == 'true') && inputs.dry-run == false)
diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml
index 9c991eed419..0680dea6bbe 100644
--- a/.github/workflows/build-linux.yml
+++ b/.github/workflows/build-linux.yml
@@ -61,6 +61,10 @@ on:
make-arguments:
required: false
type: string
+ dry-run:
+ required: false
+ type: boolean
+ default: false
bundle-suffix:
required: false
type: string
@@ -71,7 +75,7 @@ on:
jobs:
build-linux:
name: build
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
strategy:
fail-fast: false
@@ -111,9 +115,21 @@ jobs:
if [[ '${{ inputs.apt-architecture }}' != '' ]]; then
sudo dpkg --add-architecture ${{ inputs.apt-architecture }}
fi
- sudo apt-get update
- sudo apt-get install --only-upgrade apt
- sudo apt-get install gcc-${{ inputs.gcc-major-version }}${{ inputs.gcc-package-suffix }} g++-${{ inputs.gcc-major-version }}${{ inputs.gcc-package-suffix }} libxrandr-dev${{ steps.arch.outputs.suffix }} libxtst-dev${{ steps.arch.outputs.suffix }} libcups2-dev${{ steps.arch.outputs.suffix }} libasound2-dev${{ steps.arch.outputs.suffix }} ${{ inputs.apt-extra-packages }}
+ sudo apt update
+ sudo apt install --only-upgrade apt
+ sudo apt install \
+ gcc-${{ inputs.gcc-major-version }}${{ inputs.gcc-package-suffix }} \
+ g++-${{ inputs.gcc-major-version }}${{ inputs.gcc-package-suffix }} \
+ libasound2-dev${{ steps.arch.outputs.suffix }} \
+ libcups2-dev${{ steps.arch.outputs.suffix }} \
+ libfontconfig1-dev${{ steps.arch.outputs.suffix }} \
+ libx11-dev${{ steps.arch.outputs.suffix }} \
+ libxext-dev${{ steps.arch.outputs.suffix }} \
+ libxrandr-dev${{ steps.arch.outputs.suffix }} \
+ libxrender-dev${{ steps.arch.outputs.suffix }} \
+ libxt-dev${{ steps.arch.outputs.suffix }} \
+ libxtst-dev${{ steps.arch.outputs.suffix }} \
+ ${{ inputs.apt-extra-packages }}
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${{ inputs.gcc-major-version }} 100 --slave /usr/bin/g++ g++ /usr/bin/g++-${{ inputs.gcc-major-version }}
- name: 'Configure'
@@ -139,6 +155,7 @@ jobs:
make-target: '${{ inputs.make-target }} ${{ inputs.make-arguments }}'
platform: ${{ inputs.platform }}
debug-suffix: "${{ matrix.debug-level == 'debug' && '-debug' || '' }}"
+ if: ${{ inputs.dry-run == false }}
- name: 'Upload bundles'
uses: ./.github/actions/upload-bundles
@@ -147,3 +164,4 @@ jobs:
debug-suffix: "${{ matrix.debug-level == 'debug' && '-debug' || '' }}"
bundle-suffix: ${{ inputs.bundle-suffix }}
static-suffix: ${{ inputs.static-suffix }}
+ if: ${{ inputs.dry-run == false }}
diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml
index 90bb6af044f..0a12df668e5 100644
--- a/.github/workflows/build-macos.yml
+++ b/.github/workflows/build-macos.yml
@@ -54,6 +54,10 @@ on:
make-arguments:
required: false
type: string
+ dry-run:
+ required: false
+ type: boolean
+ default: false
jobs:
build-macos:
@@ -118,9 +122,11 @@ jobs:
make-target: '${{ inputs.make-target }} ${{ inputs.make-arguments }}'
platform: ${{ inputs.platform }}
debug-suffix: '${{ matrix.suffix }}'
+ if: ${{ inputs.dry-run == false }}
- name: 'Upload bundles'
uses: ./.github/actions/upload-bundles
with:
platform: ${{ inputs.platform }}
debug-suffix: '${{ matrix.suffix }}'
+ if: ${{ inputs.dry-run == false }}
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
index 9bb43a8b83c..a3091b94cef 100644
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -54,6 +54,10 @@ on:
make-arguments:
required: false
type: string
+ dry-run:
+ required: false
+ type: boolean
+ default: false
env:
# These are needed to make the MSYS2 bash work properly
@@ -139,6 +143,7 @@ jobs:
# Set PATH to "", so just GITHUB_PATH is included
PATH: ''
shell: env /usr/bin/bash --login -eo pipefail {0}
+ if: ${{ inputs.dry-run == false }}
- name: 'Build'
id: build
@@ -147,9 +152,11 @@ jobs:
make-target: '${{ inputs.make-target }} ${{ inputs.make-arguments }}'
platform: ${{ inputs.platform }}
debug-suffix: '${{ matrix.suffix }}'
+ if: ${{ inputs.dry-run == false }}
- name: 'Upload bundles'
uses: ./.github/actions/upload-bundles
with:
platform: ${{ inputs.platform }}
debug-suffix: '${{ matrix.suffix }}'
+ if: ${{ inputs.dry-run == false }}
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 8c058440ad7..09e6ed65a47 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -28,9 +28,7 @@ name: 'OpenJDK GHA Sanity Checks'
on:
push:
branches-ignore:
- - master
- pr/*
- - jdk*
workflow_dispatch:
inputs:
platforms:
@@ -43,6 +41,9 @@ on:
make-arguments:
description: 'Additional make arguments'
required: false
+ dry-run:
+ description: 'Dry run: skip actual builds and tests'
+ required: false
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -56,7 +57,7 @@ jobs:
prepare:
name: 'Prepare the run'
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
env:
# List of platforms to exclude by default
EXCLUDED_PLATFORMS: 'alpine-linux-x64'
@@ -70,6 +71,7 @@ jobs:
windows-x64: ${{ steps.include.outputs.windows-x64 }}
windows-aarch64: ${{ steps.include.outputs.windows-aarch64 }}
docs: ${{ steps.include.outputs.docs }}
+ dry-run: ${{ steps.include.outputs.dry-run }}
steps:
- name: 'Checkout the scripts'
@@ -143,6 +145,35 @@ jobs:
echo 'false'
}
+ function check_dry_run() {
+ if [[ $GITHUB_EVENT_NAME == workflow_dispatch ]]; then
+ # Take the user-specified one.
+ echo '${{ github.event.inputs.dry-run }}'
+ return
+ elif [[ $GITHUB_EVENT_NAME == push ]]; then
+ # Cut out the real branch name
+ BRANCH=${GITHUB_REF##*/}
+
+ # Dry run rebuilds the caches in current branch, so they can be reused
+ # for any child PR branches. Because of this, we want to trigger this
+ # workflow in master branch, so that actual PR branches can use the cache.
+ # This workflow would trigger every time contributors sync their master
+ # branches in their personal forks.
+ if [[ $BRANCH == "master" ]]; then
+ echo 'true'
+ return
+ fi
+
+ # ...same for stabilization branches
+ if [[ $BRANCH =~ "jdk(.*)" ]]; then
+ echo 'true'
+ return
+ fi
+ fi
+
+ echo 'false'
+ }
+
echo "linux-x64=$(check_platform linux-x64 linux x64)" >> $GITHUB_OUTPUT
echo "linux-x64-variants=$(check_platform linux-x64-variants variants)" >> $GITHUB_OUTPUT
echo "linux-cross-compile=$(check_platform linux-cross-compile cross-compile)" >> $GITHUB_OUTPUT
@@ -152,6 +183,7 @@ jobs:
echo "windows-x64=$(check_platform windows-x64 windows x64)" >> $GITHUB_OUTPUT
echo "windows-aarch64=$(check_platform windows-aarch64 windows aarch64)" >> $GITHUB_OUTPUT
echo "docs=$(check_platform docs)" >> $GITHUB_OUTPUT
+ echo "dry-run=$(check_dry_run)" >> $GITHUB_OUTPUT
###
### Build jobs
@@ -166,6 +198,7 @@ jobs:
gcc-major-version: '10'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.linux-x64 == 'true'
build-linux-x64-hs-nopch:
@@ -180,6 +213,7 @@ jobs:
extra-conf-options: '--disable-precompiled-headers'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.linux-x64-variants == 'true'
build-linux-x64-hs-zero:
@@ -194,6 +228,7 @@ jobs:
extra-conf-options: '--with-jvm-variants=zero --disable-precompiled-headers'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.linux-x64-variants == 'true'
build-linux-x64-hs-minimal:
@@ -208,6 +243,7 @@ jobs:
extra-conf-options: '--with-jvm-variants=minimal --disable-precompiled-headers'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.linux-x64-variants == 'true'
build-linux-x64-hs-optimized:
@@ -223,6 +259,7 @@ jobs:
extra-conf-options: '--with-debug-level=optimized --disable-precompiled-headers'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.linux-x64-variants == 'true'
build-linux-x64-static:
@@ -238,6 +275,7 @@ jobs:
gcc-major-version: '10'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
static-suffix: "-static"
if: needs.prepare.outputs.linux-x64 == 'true'
@@ -254,6 +292,7 @@ jobs:
gcc-major-version: '10'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
# Upload static libs bundles separately to avoid interference with normal linux-x64 bundle.
# This bundle is not used by testing jobs, but downstreams use it to check that
# dependent projects, e.g. libgraal, builds fine.
@@ -268,6 +307,7 @@ jobs:
gcc-major-version: '10'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.linux-cross-compile == 'true'
build-alpine-linux-x64:
@@ -278,6 +318,7 @@ jobs:
platform: alpine-linux-x64
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.alpine-linux-x64 == 'true'
build-macos-x64:
@@ -286,10 +327,11 @@ jobs:
uses: ./.github/workflows/build-macos.yml
with:
platform: macos-x64
- runs-on: 'macos-13'
- xcode-toolset-version: '14.3.1'
+ runs-on: 'macos-15-intel'
+ xcode-toolset-version: '16.4'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.macos-x64 == 'true'
build-macos-aarch64:
@@ -298,10 +340,11 @@ jobs:
uses: ./.github/workflows/build-macos.yml
with:
platform: macos-aarch64
- runs-on: 'macos-14'
- xcode-toolset-version: '15.4'
+ runs-on: 'macos-15'
+ xcode-toolset-version: '16.4'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.macos-aarch64 == 'true'
build-windows-x64:
@@ -314,6 +357,7 @@ jobs:
msvc-toolset-architecture: 'x86.x64'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.windows-x64 == 'true'
build-windows-aarch64:
@@ -328,6 +372,7 @@ jobs:
extra-conf-options: '--openjdk-target=aarch64-unknown-cygwin'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.windows-aarch64 == 'true'
build-docs:
@@ -344,6 +389,7 @@ jobs:
gcc-major-version: '10'
configure-arguments: ${{ github.event.inputs.configure-arguments }}
make-arguments: ${{ github.event.inputs.make-arguments }}
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
if: needs.prepare.outputs.docs == 'true'
###
@@ -353,45 +399,53 @@ jobs:
test-linux-x64:
name: linux-x64
needs:
+ - prepare
- build-linux-x64
uses: ./.github/workflows/test.yml
with:
platform: linux-x64
bootjdk-platform: linux-x64
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
debug-suffix: -debug
test-linux-x64-static:
name: linux-x64-static
needs:
+ - prepare
- build-linux-x64
- build-linux-x64-static
uses: ./.github/workflows/test.yml
with:
platform: linux-x64
bootjdk-platform: linux-x64
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
static-suffix: "-static"
test-macos-aarch64:
name: macos-aarch64
needs:
+ - prepare
- build-macos-aarch64
uses: ./.github/workflows/test.yml
with:
platform: macos-aarch64
bootjdk-platform: macos-aarch64
- runs-on: macos-14
- xcode-toolset-version: '15.4'
+ runs-on: macos-15
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
+ xcode-toolset-version: '16.4'
debug-suffix: -debug
test-windows-x64:
name: windows-x64
needs:
+ - prepare
- build-windows-x64
uses: ./.github/workflows/test.yml
with:
platform: windows-x64
bootjdk-platform: windows-x64
runs-on: windows-2025
+ dry-run: ${{ needs.prepare.outputs.dry-run == 'true' }}
debug-suffix: -debug
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 665ae224372..f2c8916a369 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -40,6 +40,10 @@ on:
xcode-toolset-version:
required: false
type: string
+ dry-run:
+ required: false
+ type: boolean
+ default: false
debug-suffix:
required: false
type: string
@@ -147,6 +151,7 @@ jobs:
platform: ${{ inputs.platform }}
debug-suffix: ${{ matrix.debug-suffix }}
static-suffix: ${{ inputs.static-suffix }}
+ if: ${{ inputs.dry-run == false }}
- name: 'Install dependencies'
run: |
@@ -199,6 +204,7 @@ jobs:
&& bash ./.github/scripts/gen-test-summary.sh "$GITHUB_STEP_SUMMARY" "$GITHUB_OUTPUT"
env:
PATH: ${{ steps.path.outputs.value }}
+ if: ${{ inputs.dry-run == false }}
# This is a separate step, since if the markdown from a step gets bigger than
# 1024 kB it is skipped, but then the short summary above is still generated
diff --git a/.jcheck/conf b/.jcheck/conf
index 6ab5c2d64c2..27adc61e534 100644
--- a/.jcheck/conf
+++ b/.jcheck/conf
@@ -1,7 +1,7 @@
[general]
-project=jdk
+project=jdk-updates
jbs=JDK
-version=25
+version=25.0.3
[checks]
error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists,copyright
diff --git a/README.md b/README.md
index b3f30676b3c..e5800fecd57 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,11 @@
-# Welcome to the JDK!
+# Welcome to OpenJDK 25 Updates!
+
+The JDK 25 Updates project uses two GitHub repositories.
+Updates are continuously developed in the repository [jdk25u-dev](https://github.com/openjdk/jdk25u-dev). This is the repository usually targeted by contributors.
+The [jdk25u](https://github.com/openjdk/jdk25u) repository is used for rampdown of the update releases of jdk25u and only accepts critical changes that must make the next release during rampdown. (You probably do not want to target jdk25u).
+
+For more OpenJDK 25 updates specific information such as timelines and contribution guidelines see the [project wiki page](https://wiki.openjdk.org/display/JDKUpdates/JDK+25u/).
+
For build instructions please see the
[online documentation](https://openjdk.org/groups/build/doc/building.html),
diff --git a/doc/building.html b/doc/building.html
index da8465bc532..51ced17fe99 100644
--- a/doc/building.html
+++ b/doc/building.html
@@ -668,7 +668,7 @@
Microsoft Visual Studio
(Note that this version is often presented as "MSVC 14.28", and reported
by cl.exe as 19.28.) Older versions will not be accepted by
configure and will not work. The maximum accepted version
-of Visual Studio is 2022.
+of Visual Studio is 2026.
If you have multiple versions of Visual Studio installed,
configure will by default pick the latest. You can request
a specific version to be used by setting
diff --git a/doc/building.md b/doc/building.md
index 1a9fe6b2e78..11af23d9447 100644
--- a/doc/building.md
+++ b/doc/building.md
@@ -468,7 +468,7 @@ available for this update.
The minimum accepted version is Visual Studio 2019 version 16.8. (Note that
this version is often presented as "MSVC 14.28", and reported by cl.exe as
19.28.) Older versions will not be accepted by `configure` and will not work.
-The maximum accepted version of Visual Studio is 2022.
+The maximum accepted version of Visual Studio is 2026.
If you have multiple versions of Visual Studio installed, `configure` will by
default pick the latest. You can request a specific version to be used by
diff --git a/make/StaticLibs.gmk b/make/StaticLibs.gmk
index 3cf2a4dd136..17fd19800e9 100644
--- a/make/StaticLibs.gmk
+++ b/make/StaticLibs.gmk
@@ -116,6 +116,9 @@ else ifeq ($(call isTargetOs, aix), true)
$(eval STATIC_LIB_EXPORT_FILES += $(lib).exp) \
)
STATIC_LIBS := -Wl,-bexpfull $(STATIC_LIB_FILES) $(addprefix -Wl$(COMMA)-bE:, $(STATIC_LIB_EXPORT_FILES))
+ ifeq ($(DEBUG_LEVEL), slowdebug)
+ STATIC_LIBS += -Wl,-bbigtoc
+ endif
else
$(error Unsupported platform)
endif
diff --git a/make/autoconf/basic_tools.m4 b/make/autoconf/basic_tools.m4
index 5815c55c962..0f5691759b6 100644
--- a/make/autoconf/basic_tools.m4
+++ b/make/autoconf/basic_tools.m4
@@ -378,7 +378,7 @@ AC_DEFUN_ONCE([BASIC_SETUP_COMPLEX_TOOLS],
# Check if it's a GNU date compatible version
AC_MSG_CHECKING([if date is a GNU compatible version])
- check_date=`$DATE --version 2>&1 | $GREP "GNU\|BusyBox"`
+ check_date=`$DATE --version 2>&1 | $GREP "GNU\|BusyBox\|uutils"`
if test "x$check_date" != x; then
AC_MSG_RESULT([yes])
IS_GNU_DATE=yes
diff --git a/make/autoconf/boot-jdk.m4 b/make/autoconf/boot-jdk.m4
index feb16c7d179..0015109825b 100644
--- a/make/autoconf/boot-jdk.m4
+++ b/make/autoconf/boot-jdk.m4
@@ -446,6 +446,9 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS],
# Force en-US environment
UTIL_ADD_JVM_ARG_IF_OK([-Duser.language=en -Duser.country=US],boot_jdk_jvmargs,[$JAVA])
+ UTIL_ADD_JVM_ARG_IF_OK([-Xlog:all=off:stdout],boot_jdk_jvmargs,[$JAVA])
+ UTIL_ADD_JVM_ARG_IF_OK([-Xlog:all=warning:stderr],boot_jdk_jvmargs,[$JAVA])
+
if test "x$BOOTJDK_USE_LOCAL_CDS" = xtrue; then
# Use our own CDS archive
UTIL_ADD_JVM_ARG_IF_OK([$boot_jdk_cds_args -Xshare:auto],boot_jdk_jvmargs,[$JAVA])
diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac
index e05b5ae3b90..2e608f893d6 100644
--- a/make/autoconf/configure.ac
+++ b/make/autoconf/configure.ac
@@ -221,6 +221,9 @@ JDKOPT_SETUP_UNDEFINED_BEHAVIOR_SANITIZER
# LeakSanitizer
JDKOPT_SETUP_LEAK_SANITIZER
+# Setup static analyzer
+JDKOPT_SETUP_STATIC_ANALYZER
+
# Fallback linker
# This needs to go before 'LIB_DETERMINE_DEPENDENCIES'
JDKOPT_SETUP_FALLBACK_LINKER
diff --git a/make/autoconf/flags-cflags.m4 b/make/autoconf/flags-cflags.m4
index e80d9a98957..3a61840e8c9 100644
--- a/make/autoconf/flags-cflags.m4
+++ b/make/autoconf/flags-cflags.m4
@@ -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
@@ -933,7 +940,7 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP],
# ACLE and this flag are required to build the aarch64 SVE related functions in
# libvectormath. Apple Silicon does not support SVE; use macOS as a proxy for
# that check.
- if test "x$OPENJDK_TARGET_CPU" = "xaarch64" && test "x$OPENJDK_TARGET_CPU" = "xlinux"; then
+ if test "x$OPENJDK_TARGET_CPU" = "xaarch64" && test "x$OPENJDK_TARGET_OS" = "xlinux"; then
if test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang; then
AC_LANG_PUSH(C)
OLD_CFLAGS="$CFLAGS"
@@ -947,6 +954,17 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_CPU_DEP],
[
AC_MSG_RESULT([yes])
$2SVE_CFLAGS="-march=armv8-a+sve"
+ # Switching the initialization mode with gcc from 'pattern' to 'zero'
+ # avoids the use of unsupported `__builtin_clear_padding` for variable
+ # length aggregates
+ if test "x$DEBUG_LEVEL" != xrelease && test "x$TOOLCHAIN_TYPE" = xgcc ; then
+ INIT_ZERO_FLAG="-ftrivial-auto-var-init=zero"
+ FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [$INIT_ZERO_FLAG],
+ IF_TRUE: [
+ $2SVE_CFLAGS="${$2SVE_CFLAGS} $INIT_ZERO_FLAG"
+ ]
+ )
+ fi
],
[
AC_MSG_RESULT([no])
diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4
index 289ed935fdf..d4299078abf 100644
--- a/make/autoconf/jdk-options.m4
+++ b/make/autoconf/jdk-options.m4
@@ -479,6 +479,31 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_ADDRESS_SANITIZER],
AC_SUBST(ASAN_ENABLED)
])
+################################################################################
+#
+# Static analyzer
+#
+AC_DEFUN_ONCE([JDKOPT_SETUP_STATIC_ANALYZER],
+[
+ UTIL_ARG_ENABLE(NAME: static-analyzer, DEFAULT: false, RESULT: STATIC_ANALYZER_ENABLED,
+ DESC: [enable the GCC static analyzer],
+ CHECK_AVAILABLE: [
+ AC_MSG_CHECKING([if static analyzer is available])
+ if test "x$TOOLCHAIN_TYPE" = "xgcc"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AVAILABLE=false
+ fi
+ ],
+ IF_ENABLED: [
+ STATIC_ANALYZER_CFLAGS="-fanalyzer -Wno-analyzer-fd-leak"
+ CFLAGS_JDKLIB="$CFLAGS_JDKLIB $STATIC_ANALYZER_CFLAGS"
+ CFLAGS_JDKEXE="$CFLAGS_JDKEXE $STATIC_ANALYZER_CFLAGS"
+ ])
+ AC_SUBST(STATIC_ANALYZER_ENABLED)
+])
+
################################################################################
#
# LeakSanitizer
diff --git a/make/autoconf/lib-tests.m4 b/make/autoconf/lib-tests.m4
index d2a4fcbb191..23f3d443a6c 100644
--- a/make/autoconf/lib-tests.m4
+++ b/make/autoconf/lib-tests.m4
@@ -28,7 +28,7 @@
################################################################################
# Minimum supported versions
-JTREG_MINIMUM_VERSION=7.5.1
+JTREG_MINIMUM_VERSION=8
GTEST_MINIMUM_VERSION=1.14.0
################################################################################
diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4
index bf697928f1b..8dc3d55ed0c 100644
--- a/make/autoconf/libraries.m4
+++ b/make/autoconf/libraries.m4
@@ -136,12 +136,8 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES],
BASIC_JVM_LIBS="$BASIC_JVM_LIBS $LIBPTHREAD"
fi
- # librt for legacy clock_gettime
+ # librt - for timers (timer_* functions)
if test "x$OPENJDK_TARGET_OS" = xlinux; then
- # Hotspot needs to link librt to get the clock_* functions.
- # But once our supported minimum build and runtime platform
- # has glibc 2.17, this can be removed as the functions are
- # in libc.
BASIC_JVM_LIBS="$BASIC_JVM_LIBS -lrt"
fi
diff --git a/make/autoconf/toolchain_microsoft.m4 b/make/autoconf/toolchain_microsoft.m4
index 17ad2666b3a..f577cf1a2a1 100644
--- a/make/autoconf/toolchain_microsoft.m4
+++ b/make/autoconf/toolchain_microsoft.m4
@@ -25,7 +25,7 @@
################################################################################
# The order of these defines the priority by which we try to find them.
-VALID_VS_VERSIONS="2022 2019"
+VALID_VS_VERSIONS="2022 2019 2026"
VS_DESCRIPTION_2019="Microsoft Visual Studio 2019"
VS_VERSION_INTERNAL_2019=142
@@ -57,6 +57,21 @@ VS_SDK_PLATFORM_NAME_2022=
VS_SUPPORTED_2022=true
VS_TOOLSET_SUPPORTED_2022=true
+VS_DESCRIPTION_2026="Microsoft Visual Studio 2026"
+VS_VERSION_INTERNAL_2026=145
+VS_MSVCR_2026=vcruntime140.dll
+VS_VCRUNTIME_1_2026=vcruntime140_1.dll
+VS_MSVCP_2026=msvcp140.dll
+VS_ENVVAR_2026="VS180COMNTOOLS"
+VS_USE_UCRT_2026="true"
+VS_VS_INSTALLDIR_2026="Microsoft Visual Studio/18"
+VS_EDITIONS_2026="BuildTools Community Professional Enterprise"
+VS_SDK_INSTALLDIR_2026=
+VS_VS_PLATFORM_NAME_2026="v145"
+VS_SDK_PLATFORM_NAME_2026=
+VS_SUPPORTED_2026=true
+VS_TOOLSET_SUPPORTED_2026=true
+
################################################################################
AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT],
diff --git a/make/common/Utils.gmk b/make/common/Utils.gmk
index 03009ec3ca4..4868a9f12c0 100644
--- a/make/common/Utils.gmk
+++ b/make/common/Utils.gmk
@@ -78,7 +78,7 @@ EscapeDollar = $(subst $$,\$$,$(subst \$$,$$,$(strip $1)))
################################################################################
# This macro works just like EscapeDollar above, but for #.
-EscapeHash = $(subst \#,\\\#,$(subst \\\#,\#,$(strip $1)))
+EscapeHash = $(subst $(HASH),\$(HASH),$(subst \$(HASH),$(HASH),$(strip $1)))
################################################################################
# This macro translates $ into $$ to protect the string from make itself.
diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf
index 27845ffbd7a..438e4b3ce8d 100644
--- a/make/conf/github-actions.conf
+++ b/make/conf/github-actions.conf
@@ -26,7 +26,7 @@
# Versions and download locations for dependencies used by GitHub Actions (GHA)
GTEST_VERSION=1.14.0
-JTREG_VERSION=7.5.1+1
+JTREG_VERSION=8+2
LINUX_X64_BOOT_JDK_EXT=tar.gz
LINUX_X64_BOOT_JDK_URL=https://download.java.net/java/GA/jdk24/1f9ff9062db4449d8ca828c504ffae90/36/GPL/openjdk-24_linux-x64_bin.tar.gz
diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js
index 9bedc77cc28..4f2bd27d54c 100644
--- a/make/conf/jib-profiles.js
+++ b/make/conf/jib-profiles.js
@@ -1174,9 +1174,9 @@ var getJibProfilesDependencies = function (input, common) {
jtreg: {
server: "jpg",
product: "jtreg",
- version: "7.5.1",
- build_number: "1",
- file: "bundles/jtreg-7.5.1+1.zip",
+ version: "8",
+ build_number: "2",
+ file: "bundles/jtreg-8+2.zip",
environment_name: "JT_HOME",
environment_path: input.get("jtreg", "home_path") + "/bin",
configure_args: "--with-jtreg=" + input.get("jtreg", "home_path"),
diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf
index f5ee4e23c8b..f82c7822485 100644
--- a/make/conf/version-numbers.conf
+++ b/make/conf/version-numbers.conf
@@ -28,15 +28,15 @@
DEFAULT_VERSION_FEATURE=25
DEFAULT_VERSION_INTERIM=0
-DEFAULT_VERSION_UPDATE=0
+DEFAULT_VERSION_UPDATE=3
DEFAULT_VERSION_PATCH=0
DEFAULT_VERSION_EXTRA1=0
DEFAULT_VERSION_EXTRA2=0
DEFAULT_VERSION_EXTRA3=0
-DEFAULT_VERSION_DATE=2025-09-16
+DEFAULT_VERSION_DATE=2026-04-21
DEFAULT_VERSION_CLASSFILE_MAJOR=69 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
DEFAULT_VERSION_CLASSFILE_MINOR=0
DEFAULT_VERSION_DOCS_API_SINCE=11
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="24 25"
DEFAULT_JDK_SOURCE_TARGET_VERSION=25
-DEFAULT_PROMOTED_VERSION_PRE=
+DEFAULT_PROMOTED_VERSION_PRE=ea
diff --git a/make/data/ubsan/ubsan_default_options.c b/make/data/ubsan/ubsan_default_options.c
index 05e4722e45a..5615436e39f 100644
--- a/make/data/ubsan/ubsan_default_options.c
+++ b/make/data/ubsan/ubsan_default_options.c
@@ -62,5 +62,8 @@
// thread so it is easier to track down. You can override these options by setting the environment
// variable UBSAN_OPTIONS.
ATTRIBUTE_DEFAULT_VISIBILITY ATTRIBUTE_USED const char* __ubsan_default_options() {
- return "halt_on_error=1,print_stacktrace=1" _LLVM_SYMBOLIZER(LLVM_SYMBOLIZER);
+ return "halt_on_error=1,"
+ "handle_segv=0,"
+ "handle_sigbus=0,"
+ "print_stacktrace=1" _LLVM_SYMBOLIZER(LLVM_SYMBOLIZER);
}
diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk
index 6b5edc85b23..f7a7732993a 100644
--- a/make/hotspot/lib/CompileJvm.gmk
+++ b/make/hotspot/lib/CompileJvm.gmk
@@ -156,6 +156,10 @@ ifeq ($(call isTargetOs, windows), true)
WIN_EXPORT_FILE := $(JVM_OUTPUTDIR)/win-exports.def
endif
+ ifeq ($(SHIP_DEBUG_SYMBOLS), public)
+ CFLAGS_STRIPPED_DEBUGINFO := -DHAS_STRIPPED_DEBUGINFO
+ endif
+
JVM_LDFLAGS += -def:$(WIN_EXPORT_FILE)
endif
@@ -181,6 +185,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJVM, \
CFLAGS := $(JVM_CFLAGS), \
abstract_vm_version.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \
arguments.cpp_CXXFLAGS := $(CFLAGS_VM_VERSION), \
+ whitebox.cpp_CXXFLAGS := $(CFLAGS_STRIPPED_DEBUGINFO), \
DISABLED_WARNINGS_gcc := $(DISABLED_WARNINGS_gcc), \
DISABLED_WARNINGS_gcc_ad_$(HOTSPOT_TARGET_CPU_ARCH).cpp := nonnull, \
DISABLED_WARNINGS_gcc_bytecodeInterpreter.cpp := unused-label, \
diff --git a/make/ide/vscode/hotspot/CreateVSCodeProject.gmk b/make/ide/vscode/hotspot/CreateVSCodeProject.gmk
index 11f25c152f6..9e5671b8250 100644
--- a/make/ide/vscode/hotspot/CreateVSCodeProject.gmk
+++ b/make/ide/vscode/hotspot/CreateVSCodeProject.gmk
@@ -84,6 +84,8 @@ define CreateFromTemplate
$(SED) -e 's!{{TOPDIR}}!$(call SedEscape,$(call FixPath,$(TOPDIR)))!g' \
-e 's!{{TOPDIR_RELATIVE}}!$(call SedEscape,$(call FixPath,$(strip \
$(call RelativePath,$(OUTPUTDIR),$(TOPDIR)))))!g' \
+ -e 's!{{TOPDIR_BASE}}!$(notdir $(TOPDIR))!g' \
+ -e 's!{{OUTPUT_BASE}}!$(notdir $(OUTPUTDIR))!g' \
-e 's!{{WORKSPACE_ROOT}}!$(call SedEscape,$(call FixPath,$(WORKSPACE_ROOT)))!g' \
-e 's!{{OUTPUTDIR}}!$(call SedEscape,$(call FixPath,$(OUTPUTDIR)))!g' \
-e 's!{{CONF_NAME}}!$(CONF_NAME)!g' \
diff --git a/make/ide/vscode/hotspot/template-workspace.jsonc b/make/ide/vscode/hotspot/template-workspace.jsonc
index 30533c7ce84..af62d151b24 100644
--- a/make/ide/vscode/hotspot/template-workspace.jsonc
+++ b/make/ide/vscode/hotspot/template-workspace.jsonc
@@ -1,12 +1,12 @@
{
"folders": [
{
- "name": "Source root",
+ "name": "Source root ({{TOPDIR_BASE}})",
"path": "{{TOPDIR}}"
},
// {{EXTRA_WORKSPACE_ROOT}}
{
- "name": "Build artifacts",
+ "name": "Build artifacts ({{OUTPUT_BASE}})",
"path": "{{OUTPUTDIR}}"
}
],
diff --git a/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java b/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java
index d8752bca142..cbef22d91c0 100644
--- a/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java
+++ b/make/jdk/src/classes/build/tools/cldrconverter/Bundle.java
@@ -542,10 +542,10 @@ private void handleDateTimeFormatPatterns(String[] patternKeys, Map= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
if (count != 0) {
- converter.convert(calendarType, lastLetter, count, jrePattern);
+ converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
lastLetter = 0;
count = 0;
}
@@ -627,7 +627,7 @@ private String translateDateFormatLetters(CalendarType calendarType, String cldr
count++;
continue;
}
- converter.convert(calendarType, lastLetter, count, jrePattern);
+ converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
lastLetter = c;
count = 1;
}
@@ -637,7 +637,7 @@ private String translateDateFormatLetters(CalendarType calendarType, String cldr
}
if (count != 0) {
- converter.convert(calendarType, lastLetter, count, jrePattern);
+ converter.convert(calendarType, patternKey, lastLetter, count, jrePattern);
}
if (cldrFormat.contentEquals(jrePattern)) {
return cldrFormat;
@@ -661,7 +661,7 @@ private String toMetaZoneKey(String tzKey) {
* on the support given by the SimpleDateFormat and the j.t.f.DateTimeFormatter
* for date-time formatting.
*/
- private void convertDateTimePatternLetter(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) {
+ private void convertDateTimePatternLetter(CalendarType calendarType, String patternKey, char cldrLetter, int count, StringBuilder sb) {
switch (cldrLetter) {
case 'u':
case 'U':
@@ -683,7 +683,7 @@ private void convertDateTimePatternLetter(CalendarType calendarType, char cldrLe
* Perform a conversion of CLDR date-time format pattern letter which is
* specific to the SimpleDateFormat.
*/
- private void convertSDFLetter(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb) {
+ private void convertSDFLetter(CalendarType calendarType, String patternKey, char cldrLetter, int count, StringBuilder sb) {
switch (cldrLetter) {
case 'G':
if (calendarType != CalendarType.GREGORIAN) {
@@ -722,6 +722,17 @@ private void convertSDFLetter(CalendarType calendarType, char cldrLetter, int co
appendN('z', count, sb);
break;
+ case 'y':
+ // If the style is FULL/LONG for a Japanese Calendar, make the
+ // count == 4 for Gan-nen
+ if (calendarType == CalendarType.JAPANESE &&
+ (patternKey.contains("full-") ||
+ patternKey.contains("long-"))) {
+ count = 4;
+ }
+ appendN(cldrLetter, count, sb);
+ break;
+
case 'Z':
if (count == 4 || count == 5) {
sb.append("XXX");
@@ -767,6 +778,7 @@ private void handleSkeletonPatterns(Map myMap, CalendarType cale
.collect(Collectors.toMap(
e -> calendarPrefix + e.getKey(),
e -> translateDateFormatLetters(calendarType,
+ e.getKey(),
(String)e.getValue(),
this::convertDateTimePatternLetter)
))
@@ -775,7 +787,7 @@ private void handleSkeletonPatterns(Map myMap, CalendarType cale
@FunctionalInterface
private interface ConvertDateTimeLetters {
- void convert(CalendarType calendarType, char cldrLetter, int count, StringBuilder sb);
+ void convert(CalendarType calendarType, String patternKey, char cldrLetter, int count, StringBuilder sb);
}
/**
diff --git a/make/modules/java.desktop/lib/ClientLibraries.gmk b/make/modules/java.desktop/lib/ClientLibraries.gmk
index dcb41defba3..5cb7501b6f4 100644
--- a/make/modules/java.desktop/lib/ClientLibraries.gmk
+++ b/make/modules/java.desktop/lib/ClientLibraries.gmk
@@ -177,7 +177,8 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
endif
LIBSPLASHSCREEN_CFLAGS += -DSPLASHSCREEN -DPNG_NO_MMX_CODE \
- -DPNG_ARM_NEON_OPT=0 -DPNG_ARM_NEON_IMPLEMENTATION=0
+ -DPNG_ARM_NEON_OPT=0 -DPNG_ARM_NEON_IMPLEMENTATION=0 \
+ -DPNG_LOONGARCH_LSX_OPT=0
ifeq ($(call isTargetOs, linux)+$(call isTargetCpuArch, ppc), true+true)
LIBSPLASHSCREEN_CFLAGS += -DPNG_POWERPC_VSX_OPT=0
@@ -235,7 +236,7 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
DISABLED_WARNINGS_gcc_dgif_lib.c := sign-compare, \
DISABLED_WARNINGS_gcc_jcmaster.c := implicit-fallthrough, \
DISABLED_WARNINGS_gcc_jdphuff.c := shift-negative-value, \
- DISABLED_WARNINGS_gcc_png.c := maybe-uninitialized unused-function, \
+ DISABLED_WARNINGS_gcc_png.c := maybe-uninitialized, \
DISABLED_WARNINGS_gcc_pngerror.c := maybe-uninitialized, \
DISABLED_WARNINGS_gcc_splashscreen_gfx_impl.c := implicit-fallthrough \
maybe-uninitialized, \
@@ -246,7 +247,6 @@ ifeq ($(ENABLE_HEADLESS_ONLY), false)
DISABLED_WARNINGS_clang := deprecated-non-prototype, \
DISABLED_WARNINGS_clang_dgif_lib.c := sign-compare, \
DISABLED_WARNINGS_clang_gzwrite.c := format-nonliteral, \
- DISABLED_WARNINGS_clang_png.c := unused-function, \
DISABLED_WARNINGS_clang_splashscreen_impl.c := sign-compare \
unused-but-set-variable unused-function, \
DISABLED_WARNINGS_clang_splashscreen_png.c := \
diff --git a/make/modules/jdk.jpackage/Lib.gmk b/make/modules/jdk.jpackage/Lib.gmk
index e9c55548b0d..5b9b6e3fcfe 100644
--- a/make/modules/jdk.jpackage/Lib.gmk
+++ b/make/modules/jdk.jpackage/Lib.gmk
@@ -68,7 +68,7 @@ $(eval $(call SetupJdkExecutable, BUILD_JPACKAGEAPPLAUNCHER, \
-rpath @executable_path/../PlugIns/, \
LIBS_macosx := -framework Cocoa, \
LIBS_windows := msi.lib ole32.lib shell32.lib shlwapi.lib user32.lib, \
- LIBS_linux := $(LIBDL), \
+ LIBS_linux := $(LIBDL) $(LIBPTHREAD), \
MANIFEST := $(JAVA_MANIFEST), \
MANIFEST_VERSION := $(VERSION_NUMBER_FOUR_POSITIONS) \
))
@@ -97,7 +97,7 @@ ifeq ($(call isTargetOs, linux), true)
DISABLED_WARNINGS_clang_JvmLauncherLib.c := format-nonliteral, \
DISABLED_WARNINGS_clang_tstrings.cpp := format-nonliteral, \
LD_SET_ORIGIN := false, \
- LIBS_linux := $(LIBDL), \
+ LIBS_linux := $(LIBDL) $(LIBPTHREAD), \
))
TARGETS += $(BUILD_LIBJPACKAGEAPPLAUNCHERAUX)
diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk
index 60a88ca1c9a..00ef4bece58 100644
--- a/make/test/JtregNativeJdk.gmk
+++ b/make/test/JtregNativeJdk.gmk
@@ -62,7 +62,8 @@ BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libGetXSpace := java.base:libjava
ifeq ($(call isTargetOs, windows), true)
BUILD_JDK_JTREG_EXCLUDE += libDirectIO.c libInheritedChannel.c \
libExplicitAttach.c libImplicitAttach.c \
- exelauncher.c
+ exelauncher.c \
+ libChangeSignalDisposition.c exePrintSignalDisposition.c
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeNullCallerTest := $(LIBCXX)
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib
@@ -137,6 +138,7 @@ ifneq ($(filter build-test-jdk-jtreg-native, $(MAKECMDGOALS)), )
OUTPUT_DIR := $(BUILD_JDK_JTREG_OUTPUT_DIR), \
EXCLUDE := $(BUILD_JDK_JTREG_EXCLUDE), \
EXTRA_FILES := $(BUILD_JDK_JTREG_EXTRA_FILES), \
+ LIBS := $(LIBPTHREAD), \
))
endif
diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad
index f367362b4d8..6899526a282 100644
--- a/src/hotspot/cpu/aarch64/aarch64.ad
+++ b/src/hotspot/cpu/aarch64/aarch64.ad
@@ -3454,10 +3454,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 +4550,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 +6836,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)
diff --git a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp
index a6aab24349a..b1b215a2170 100644
--- a/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp
@@ -310,7 +310,18 @@ static void restore_live_registers(StubAssembler* sasm, bool restore_fpu_registe
__ add(sp, sp, 32 * wordSize);
}
+#ifdef R18_RESERVED
+ /*
+ Do not modify r18_tls when restoring registers if it is a reserved register. On Windows,
+ for example, r18_tls is used to store the pointer to the current thread's TEB (where TLS
+ variables are stored). Therefore, modifying r18_tls would corrupt the TEB pointer.
+ */
+ __ pop(RegSet::range(r0, r17), sp);
+ __ ldp(zr, r19, Address(__ post(sp, 2 * wordSize)));
+ __ pop(RegSet::range(r20, r29), sp);
+#else
__ pop(RegSet::range(r0, r29), sp);
+#endif
}
static void restore_live_registers_except_r0(StubAssembler* sasm, bool restore_fpu_registers = true) {
@@ -323,8 +334,20 @@ static void restore_live_registers_except_r0(StubAssembler* sasm, bool restore_f
__ add(sp, sp, 32 * wordSize);
}
+#ifdef R18_RESERVED
+ /*
+ Do not modify r18_tls when restoring registers if it is a reserved register. On Windows,
+ for example, r18_tls is used to store the pointer to the current thread's TEB (where TLS
+ variables are stored). Therefore, modifying r18_tls would corrupt the TEB pointer.
+ */
+ __ ldp(zr, r1, Address(__ post(sp, 2 * wordSize)));
+ __ pop(RegSet::range(r2, r17), sp);
+ __ ldp(zr, r19, Address(__ post(sp, 2 * wordSize)));
+ __ pop(RegSet::range(r20, r29), sp);
+#else
__ ldp(zr, r1, Address(__ post(sp, 16)));
__ pop(RegSet::range(r2, r29), sp);
+#endif
}
diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
index 869e26d3359..302701e1cad 100644
--- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.cpp
@@ -331,13 +331,7 @@ void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm, Label* slo
__ ldr(rscratch2, thread_disarmed_and_epoch_addr);
__ cmp(rscratch1, rscratch2);
} else {
- assert(patching_type == NMethodPatchingType::conc_data_patch, "must be");
- // Subsequent loads of oops must occur after load of guard value.
- // BarrierSetNMethod::disarm sets guard with release semantics.
- __ membar(__ LoadLoad);
- Address thread_disarmed_addr(rthread, in_bytes(bs_nm->thread_disarmed_guard_value_offset()));
- __ ldrw(rscratch2, thread_disarmed_addr);
- __ cmpw(rscratch1, rscratch2);
+ ShouldNotReachHere();
}
__ br(condition, barrier_target);
diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
index 0d6bfc98a72..fa093a6ef69 100644
--- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp
@@ -39,8 +39,7 @@ class Node;
enum class NMethodPatchingType {
stw_instruction_and_data_patch,
- conc_instruction_and_data_patch,
- conc_data_patch
+ conc_instruction_and_data_patch
};
class BarrierSetAssembler: public CHeapObj {
diff --git a/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp
index c45611c882b..88c90a548d1 100644
--- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetNMethod_aarch64.cpp
@@ -58,8 +58,6 @@ static int entry_barrier_offset(nmethod* nm) {
return -4 * (4 + slow_path_size(nm));
case NMethodPatchingType::conc_instruction_and_data_patch:
return -4 * (10 + slow_path_size(nm));
- case NMethodPatchingType::conc_data_patch:
- return -4 * (5 + slow_path_size(nm));
}
ShouldNotReachHere();
return 0;
diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp
index a12d4e2beec..c89847b9d52 100644
--- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp
@@ -67,7 +67,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler {
Register scratch, RegSet saved_regs);
public:
- virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_data_patch; }
+ virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_instruction_and_data_patch; }
#ifdef COMPILER1
void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub);
diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
index 276fdd013db..c7ad3e73620 100644
--- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
@@ -1824,3 +1824,14 @@ void InterpreterMacroAssembler::load_method_entry(Register cache, Register index
add(cache, cache, Array::base_offset_in_bytes());
lea(cache, Address(cache, index));
}
+
+#ifdef ASSERT
+void InterpreterMacroAssembler::verify_field_offset(Register reg) {
+ // Verify the field offset is not in the header, implicitly checks for 0
+ Label L;
+ subs(zr, reg, oopDesc::base_offset_in_bytes());
+ br(Assembler::GE, L);
+ stop("bad field offset");
+ bind(L);
+}
+#endif
diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
index 447d4f8244e..bbc405e2ea8 100644
--- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
@@ -319,6 +319,8 @@ class InterpreterMacroAssembler: public MacroAssembler {
void load_resolved_indy_entry(Register cache, Register index);
void load_field_entry(Register cache, Register index, int bcp_offset = 1);
void load_method_entry(Register cache, Register index, int bcp_offset = 1);
+
+ void verify_field_offset(Register reg) NOT_DEBUG_RETURN;
};
#endif // CPU_AARCH64_INTERP_MASM_AARCH64_HPP
diff --git a/src/hotspot/cpu/aarch64/javaFrameAnchor_aarch64.hpp b/src/hotspot/cpu/aarch64/javaFrameAnchor_aarch64.hpp
index 8d125d3c027..a6e7d74f528 100644
--- a/src/hotspot/cpu/aarch64/javaFrameAnchor_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/javaFrameAnchor_aarch64.hpp
@@ -39,24 +39,22 @@
// 3 - restoring an old state (javaCalls)
void clear(void) {
+ // No hardware barriers are necessary. All members are volatile and the profiler
+ // is run from a signal handler and only observers the thread its running on.
+
// clearing _last_Java_sp must be first
_last_Java_sp = nullptr;
- OrderAccess::release();
_last_Java_fp = nullptr;
_last_Java_pc = nullptr;
}
void copy(JavaFrameAnchor* src) {
- // In order to make sure the transition state is valid for "this"
+ // No hardware barriers are necessary. All members are volatile and the profiler
+ // is run from a signal handler and only observers the thread its running on.
+
// We must clear _last_Java_sp before copying the rest of the new data
- //
- // Hack Alert: Temporary bugfix for 4717480/4721647
- // To act like previous version (pd_cache_state) don't null _last_Java_sp
- // unless the value is changing
- //
if (_last_Java_sp != src->_last_Java_sp) {
_last_Java_sp = nullptr;
- OrderAccess::release();
}
_last_Java_fp = src->_last_Java_fp;
_last_Java_pc = src->_last_Java_pc;
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
index a277a689280..a424bd7f275 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
@@ -639,12 +639,13 @@ void MacroAssembler::set_last_Java_frame(Register last_java_sp,
last_java_sp = esp;
}
- str(last_java_sp, Address(rthread, JavaThread::last_Java_sp_offset()));
-
// last_java_fp is optional
if (last_java_fp->is_valid()) {
str(last_java_fp, Address(rthread, JavaThread::last_Java_fp_offset()));
}
+
+ // We must set sp last.
+ str(last_java_sp, Address(rthread, JavaThread::last_Java_sp_offset()));
}
void MacroAssembler::set_last_Java_frame(Register last_java_sp,
diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
index fcfe153a9a5..cb588adb6c6 100644
--- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
@@ -168,6 +168,7 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg,
Register temp_reg, bool load_bc_into_bc_reg/*=true*/,
int byte_no)
{
+ assert_different_registers(bc_reg, temp_reg);
if (!RewriteBytecodes) return;
Label L_patch_done;
@@ -231,9 +232,12 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg,
__ stop("patching the wrong bytecode");
__ bind(L_okay);
#endif
-
- // patch bytecode
- __ strb(bc_reg, at_bcp(0));
+ // Patch bytecode with release store to coordinate with ResolvedFieldEntry loads
+ // in fast bytecode codelets. load_field_entry has a memory barrier that gains
+ // the needed ordering, together with control dependency on entering the fast codelet
+ // itself.
+ __ lea(temp_reg, at_bcp(0));
+ __ stlrb(bc_reg, temp_reg);
__ bind(L_patch_done);
}
@@ -3082,6 +3086,7 @@ void TemplateTable::fast_storefield(TosState state)
// R1: field offset, R2: field holder, R5: flags
load_resolved_field_entry(r2, r2, noreg, r1, r5);
+ __ verify_field_offset(r1);
{
Label notVolatile;
@@ -3171,6 +3176,8 @@ void TemplateTable::fast_accessfield(TosState state)
__ load_field_entry(r2, r1);
__ load_sized_value(r1, Address(r2, in_bytes(ResolvedFieldEntry::field_offset_offset())), sizeof(int), true /*is_signed*/);
+ __ verify_field_offset(r1);
+
__ load_unsigned_byte(r3, Address(r2, in_bytes(ResolvedFieldEntry::flags_offset())));
// r0: object
@@ -3237,7 +3244,9 @@ void TemplateTable::fast_xaccess(TosState state)
__ ldr(r0, aaddress(0));
// access constant pool cache
__ load_field_entry(r2, r3, 2);
+
__ load_sized_value(r1, Address(r2, in_bytes(ResolvedFieldEntry::field_offset_offset())), sizeof(int), true /*is_signed*/);
+ __ verify_field_offset(r1);
// 8179954: We need to make sure that the code generated for
// volatile accesses forms a sequentially-consistent set of
diff --git a/src/hotspot/cpu/ppc/abstractInterpreter_ppc.cpp b/src/hotspot/cpu/ppc/abstractInterpreter_ppc.cpp
index beadce33637..9c74e6f53e7 100644
--- a/src/hotspot/cpu/ppc/abstractInterpreter_ppc.cpp
+++ b/src/hotspot/cpu/ppc/abstractInterpreter_ppc.cpp
@@ -100,7 +100,7 @@ int AbstractInterpreter::size_activation(int max_stack,
// It is also guaranteed to be walkable even though it is in a skeletal state
//
// is_top_frame == true:
-// We're processing the *oldest* interpreter frame!
+// We're processing the *youngest* interpreter frame on top of stack!
//
// pop_frame_extra_args:
// If this is != 0 we are returning to a deoptimized frame by popping
@@ -131,8 +131,9 @@ void AbstractInterpreter::layout_activation(Method* method,
#ifdef ASSERT
if (caller->is_interpreted_frame()) {
assert(locals_base <= caller->interpreter_frame_expression_stack(), "bad placement");
- const int caller_abi_bytesize = (is_bottom_frame ? frame::top_ijava_frame_abi_size : frame::parent_ijava_frame_abi_size);
- intptr_t* l2 = caller->sp() + method->max_locals() - 1 + (caller_abi_bytesize / Interpreter::stackElementSize);
+ // If the bottom frame's caller was thawed then it has frame::java_abi (aka parent_ijava_frame_abi).
+ // With an ordinary i2c call it would keep the larger frame::top_ijava_frame_abi
+ intptr_t* l2 = caller->sp() + method->max_locals() - 1 + (frame::parent_ijava_frame_abi_size / Interpreter::stackElementSize);
assert(locals_base >= l2, "bad placement");
}
#endif
diff --git a/src/hotspot/cpu/ppc/atomicAccess_ppc.hpp b/src/hotspot/cpu/ppc/atomicAccess_ppc.hpp
new file mode 100644
index 00000000000..579bfd3cb00
--- /dev/null
+++ b/src/hotspot/cpu/ppc/atomicAccess_ppc.hpp
@@ -0,0 +1,649 @@
+/*
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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 CPU_PPC_ATOMICACCESS_PPC_HPP
+#define CPU_PPC_ATOMICACCESS_PPC_HPP
+
+#ifndef PPC64
+#error "Atomic currently only implemented for PPC64"
+#endif
+
+#include "orderAccess_ppc.hpp"
+#include "utilities/debug.hpp"
+
+// Implementation of class AtomicAccess
+
+//
+// machine barrier instructions:
+//
+// - sync two-way memory barrier, aka fence
+// - lwsync orders Store|Store,
+// Load|Store,
+// Load|Load,
+// but not Store|Load
+// - eieio orders memory accesses for device memory (only)
+// - isync invalidates speculatively executed instructions
+// From the POWER ISA 2.06 documentation:
+// "[...] an isync instruction prevents the execution of
+// instructions following the isync until instructions
+// preceding the isync have completed, [...]"
+// From IBM's AIX assembler reference:
+// "The isync [...] instructions causes the processor to
+// refetch any instructions that might have been fetched
+// prior to the isync instruction. The instruction isync
+// causes the processor to wait for all previous instructions
+// to complete. Then any instructions already fetched are
+// discarded and instruction processing continues in the
+// environment established by the previous instructions."
+//
+// semantic barrier instructions:
+// (as defined in orderAccess.hpp)
+//
+// - release orders Store|Store, (maps to lwsync)
+// Load|Store
+// - acquire orders Load|Store, (maps to lwsync)
+// Load|Load
+// - fence orders Store|Store, (maps to sync)
+// Load|Store,
+// Load|Load,
+// Store|Load
+//
+
+inline void pre_membar(atomic_memory_order order) {
+ switch (order) {
+ case memory_order_relaxed:
+ case memory_order_acquire: break;
+ case memory_order_release:
+ case memory_order_acq_rel: __asm__ __volatile__ ("lwsync" : : : "memory"); break;
+ default /*conservative*/ : __asm__ __volatile__ ("sync" : : : "memory"); break;
+ }
+}
+
+inline void post_membar(atomic_memory_order order) {
+ switch (order) {
+ case memory_order_relaxed:
+ case memory_order_release: break;
+ case memory_order_acquire:
+ case memory_order_acq_rel: __asm__ __volatile__ ("isync" : : : "memory"); break;
+ default /*conservative*/ : __asm__ __volatile__ ("sync" : : : "memory"); break;
+ }
+}
+
+
+
+template
+struct Atomic::PlatformAdd {
+ template
+ D add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const;
+
+ template
+ D fetch_then_add(D volatile* dest, I add_value, atomic_memory_order order) const {
+ return add_then_fetch(dest, add_value, order) - add_value;
+ }
+};
+
+template<>
+template
+inline D Atomic::PlatformAdd<4>::add_then_fetch(D volatile* dest, I add_value,
+ atomic_memory_order order) const {
+ STATIC_ASSERT(4 == sizeof(I));
+ STATIC_ASSERT(4 == sizeof(D));
+
+ D result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: lwarx %[result], 0, %[dest] \n"
+ " add %[result], %[result], %[add_value] \n"
+ " stwcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [result] "=&r" (result)
+ : [add_value] "r" (add_value),
+ [dest] "b" (dest)
+ : "cc", "memory" );
+
+ post_membar(order);
+
+ return result;
+}
+
+
+template<>
+template
+inline D Atomic::PlatformAdd<8>::add_then_fetch(D volatile* dest, I add_value,
+ atomic_memory_order order) const {
+ STATIC_ASSERT(8 == sizeof(I));
+ STATIC_ASSERT(8 == sizeof(D));
+
+ D result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: ldarx %[result], 0, %[dest] \n"
+ " add %[result], %[result], %[add_value] \n"
+ " stdcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [result] "=&r" (result)
+ : [add_value] "r" (add_value),
+ [dest] "b" (dest)
+ : "cc", "memory" );
+
+ post_membar(order);
+
+ return result;
+}
+
+template<>
+template
+inline T Atomic::PlatformXchg<4>::operator()(T volatile* dest,
+ T exchange_value,
+ atomic_memory_order order) const {
+ STATIC_ASSERT(4 == sizeof(T));
+ // Note that xchg doesn't necessarily do an acquire
+ // (see synchronizer.cpp).
+
+ T old_value;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ /* atomic loop */
+ "1: \n"
+ " lwarx %[old_value], 0, %[dest] \n"
+ " stwcx. %[exchange_value], 0, %[dest] \n"
+ " bne- 1b \n"
+ /* exit */
+ "2: \n"
+ /* out */
+ : [old_value] "=&r" (old_value)
+ /* in */
+ : [dest] "b" (dest),
+ [exchange_value] "r" (exchange_value)
+ /* clobber */
+ : "cc",
+ "memory"
+ );
+
+ post_membar(order);
+
+ return old_value;
+}
+
+template<>
+template
+inline T Atomic::PlatformXchg<8>::operator()(T volatile* dest,
+ T exchange_value,
+ atomic_memory_order order) const {
+ STATIC_ASSERT(8 == sizeof(T));
+ // Note that xchg doesn't necessarily do an acquire
+ // (see synchronizer.cpp).
+
+ T old_value;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ /* atomic loop */
+ "1: \n"
+ " ldarx %[old_value], 0, %[dest] \n"
+ " stdcx. %[exchange_value], 0, %[dest] \n"
+ " bne- 1b \n"
+ /* exit */
+ "2: \n"
+ /* out */
+ : [old_value] "=&r" (old_value)
+ /* in */
+ : [dest] "b" (dest),
+ [exchange_value] "r" (exchange_value)
+ /* clobber */
+ : "cc",
+ "memory"
+ );
+
+ post_membar(order);
+
+ return old_value;
+}
+
+template<>
+template
+inline T Atomic::PlatformCmpxchg<1>::operator()(T volatile* dest,
+ T compare_value,
+ T exchange_value,
+ atomic_memory_order order) const {
+ STATIC_ASSERT(1 == sizeof(T));
+
+ // Note that cmpxchg guarantees a two-way memory barrier across
+ // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not
+ // specified otherwise (see atomicAccess.hpp).
+
+ const unsigned int masked_compare_val = (unsigned int)(unsigned char)compare_value;
+
+ unsigned int old_value;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ /* simple guard */
+ " lbz %[old_value], 0(%[dest]) \n"
+ " cmpw %[masked_compare_val], %[old_value] \n"
+ " bne- 2f \n"
+ /* atomic loop */
+ "1: \n"
+ " lbarx %[old_value], 0, %[dest] \n"
+ " cmpw %[masked_compare_val], %[old_value] \n"
+ " bne- 2f \n"
+ " stbcx. %[exchange_value], 0, %[dest] \n"
+ " bne- 1b \n"
+ /* exit */
+ "2: \n"
+ /* out */
+ : [old_value] "=&r" (old_value)
+ /* in */
+ : [dest] "b" (dest),
+ [masked_compare_val] "r" (masked_compare_val),
+ [exchange_value] "r" (exchange_value)
+ /* clobber */
+ : "cc",
+ "memory"
+ );
+
+ post_membar(order);
+
+ return PrimitiveConversions::cast((unsigned char)old_value);
+}
+
+template<>
+template
+inline T Atomic::PlatformCmpxchg<4>::operator()(T volatile* dest,
+ T compare_value,
+ T exchange_value,
+ atomic_memory_order order) const {
+ STATIC_ASSERT(4 == sizeof(T));
+
+ // Note that cmpxchg guarantees a two-way memory barrier across
+ // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not
+ // specified otherwise (see atomicAccess.hpp).
+
+ T old_value;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ /* simple guard */
+ " lwz %[old_value], 0(%[dest]) \n"
+ " cmpw %[compare_value], %[old_value] \n"
+ " bne- 2f \n"
+ /* atomic loop */
+ "1: \n"
+ " lwarx %[old_value], 0, %[dest] \n"
+ " cmpw %[compare_value], %[old_value] \n"
+ " bne- 2f \n"
+ " stwcx. %[exchange_value], 0, %[dest] \n"
+ " bne- 1b \n"
+ /* exit */
+ "2: \n"
+ /* out */
+ : [old_value] "=&r" (old_value)
+ /* in */
+ : [dest] "b" (dest),
+ [compare_value] "r" (compare_value),
+ [exchange_value] "r" (exchange_value)
+ /* clobber */
+ : "cc",
+ "memory"
+ );
+
+ post_membar(order);
+
+ return old_value;
+}
+
+template<>
+template
+inline T Atomic::PlatformCmpxchg<8>::operator()(T volatile* dest,
+ T compare_value,
+ T exchange_value,
+ atomic_memory_order order) const {
+ STATIC_ASSERT(8 == sizeof(T));
+
+ // Note that cmpxchg guarantees a two-way memory barrier across
+ // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not
+ // specified otherwise (see atomicAccess.hpp).
+
+ T old_value;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ /* simple guard */
+ " ld %[old_value], 0(%[dest]) \n"
+ " cmpd %[compare_value], %[old_value] \n"
+ " bne- 2f \n"
+ /* atomic loop */
+ "1: \n"
+ " ldarx %[old_value], 0, %[dest] \n"
+ " cmpd %[compare_value], %[old_value] \n"
+ " bne- 2f \n"
+ " stdcx. %[exchange_value], 0, %[dest] \n"
+ " bne- 1b \n"
+ /* exit */
+ "2: \n"
+ /* out */
+ : [old_value] "=&r" (old_value)
+ /* in */
+ : [dest] "b" (dest),
+ [compare_value] "r" (compare_value),
+ [exchange_value] "r" (exchange_value)
+ /* clobber */
+ : "cc",
+ "memory"
+ );
+
+ post_membar(order);
+
+ return old_value;
+}
+
+template
+struct Atomic::PlatformOrderedLoad
+{
+ template
+ T operator()(const volatile T* p) const {
+ T t = Atomic::load(p);
+ // Use twi-isync for load_acquire (faster than lwsync).
+ __asm__ __volatile__ ("twi 0,%0,0\n isync\n" : : "r" (t) : "memory");
+ return t;
+ }
+};
+
+template<>
+class Atomic::PlatformBitops<4, true> {
+public:
+ template
+ T fetch_then_and(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(4 == sizeof(T));
+ T old_value, result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: lwarx %[old_value], 0, %[dest] \n"
+ " and %[result], %[old_value], %[bits] \n"
+ " stwcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [old_value] "=&r" (old_value),
+ [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return old_value;
+ }
+
+ template
+ T fetch_then_or(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(4 == sizeof(T));
+ T old_value, result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: lwarx %[old_value], 0, %[dest] \n"
+ " or %[result], %[old_value], %[bits] \n"
+ " stwcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [old_value] "=&r" (old_value),
+ [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return old_value;
+ }
+
+ template
+ T fetch_then_xor(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(4 == sizeof(T));
+ T old_value, result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: lwarx %[old_value], 0, %[dest] \n"
+ " xor %[result], %[old_value], %[bits] \n"
+ " stwcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [old_value] "=&r" (old_value),
+ [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return old_value;
+ }
+
+ template
+ T and_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(4 == sizeof(T));
+ T result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: lwarx %[result], 0, %[dest] \n"
+ " and %[result], %[result], %[bits] \n"
+ " stwcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return result;
+ }
+
+ template
+ T or_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(4 == sizeof(T));
+ T result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: lwarx %[result], 0, %[dest] \n"
+ " or %[result], %[result], %[bits] \n"
+ " stwcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return result;
+ }
+
+ template
+ T xor_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(4 == sizeof(T));
+ T result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: lwarx %[result], 0, %[dest] \n"
+ " xor %[result], %[result], %[bits] \n"
+ " stwcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return result;
+ }
+};
+
+template<>
+class Atomic::PlatformBitops<8, true> {
+public:
+ template
+ T fetch_then_and(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(8 == sizeof(T));
+ T old_value, result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: ldarx %[old_value], 0, %[dest] \n"
+ " and %[result], %[old_value], %[bits] \n"
+ " stdcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [old_value] "=&r" (old_value),
+ [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return old_value;
+ }
+
+ template
+ T fetch_then_or(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(8 == sizeof(T));
+ T old_value, result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: ldarx %[old_value], 0, %[dest] \n"
+ " or %[result], %[old_value], %[bits] \n"
+ " stdcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [old_value] "=&r" (old_value),
+ [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return old_value;
+ }
+
+ template
+ T fetch_then_xor(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(8 == sizeof(T));
+ T old_value, result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: ldarx %[old_value], 0, %[dest] \n"
+ " xor %[result], %[old_value], %[bits] \n"
+ " stdcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [old_value] "=&r" (old_value),
+ [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return old_value;
+ }
+
+ template
+ T and_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(8 == sizeof(T));
+ T result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: ldarx %[result], 0, %[dest] \n"
+ " and %[result], %[result], %[bits] \n"
+ " stdcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return result;
+ }
+
+ template
+ T or_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(8 == sizeof(T));
+ T result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: ldarx %[result], 0, %[dest] \n"
+ " or %[result], %[result], %[bits] \n"
+ " stdcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return result;
+ }
+
+ template
+ T xor_then_fetch(T volatile* dest, T bits, atomic_memory_order order) const {
+ STATIC_ASSERT(8 == sizeof(T));
+ T result;
+
+ pre_membar(order);
+
+ __asm__ __volatile__ (
+ "1: ldarx %[result], 0, %[dest] \n"
+ " xor %[result], %[result], %[bits] \n"
+ " stdcx. %[result], 0, %[dest] \n"
+ " bne- 1b \n"
+ : [result] "=&r" (result)
+ : [dest] "b" (dest),
+ [bits] "r" (bits)
+ : "cc", "memory" );
+
+ post_membar(order);
+ return result;
+ }
+};
+#endif // CPU_PPC_ATOMICACCESS_PPC_HPP
diff --git a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp
index f48b1bc5f66..390623f48a1 100644
--- a/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp
+++ b/src/hotspot/cpu/ppc/gc/shared/barrierSetAssembler_ppc.hpp
@@ -40,8 +40,7 @@ class Node;
enum class NMethodPatchingType {
stw_instruction_and_data_patch,
- conc_instruction_and_data_patch,
- conc_data_patch
+ conc_instruction_and_data_patch
};
class BarrierSetAssembler: public CHeapObj {
diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp
index 6ee70b4b4ea..b058dcf1a2e 100644
--- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp
+++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp
@@ -69,7 +69,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler {
Register preserve);
public:
- virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_data_patch; }
+ virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_instruction_and_data_patch; }
/* ==== C1 stubs ==== */
#ifdef COMPILER1
diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp
index d3969427db3..9140dd7ca4e 100644
--- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp
+++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp
@@ -133,8 +133,13 @@ class InterpreterMacroAssembler: public MacroAssembler {
void get_cache_index_at_bcp(Register Rdst, int bcp_offset, size_t index_size);
void load_resolved_indy_entry(Register cache, Register index);
- void load_field_entry(Register cache, Register index, int bcp_offset = 1);
- void load_method_entry(Register cache, Register index, int bcp_offset = 1);
+ void load_field_or_method_entry(bool is_method, Register cache, Register index, int bcp_offset, bool for_fast_bytecode);
+ void load_field_entry(Register cache, Register index, int bcp_offset = 1, bool for_fast_bytecode = false) {
+ load_field_or_method_entry(false, cache, index, bcp_offset, for_fast_bytecode);
+ }
+ void load_method_entry(Register cache, Register index, int bcp_offset = 1, bool for_fast_bytecode = false) {
+ load_field_or_method_entry(true, cache, index, bcp_offset, for_fast_bytecode);
+ }
void get_u4(Register Rdst, Register Rsrc, int offset, signedOrNot is_signed);
diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp
index 29fb54250c2..4fa7b614aca 100644
--- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp
+++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp
@@ -468,33 +468,33 @@ void InterpreterMacroAssembler::load_resolved_indy_entry(Register cache, Registe
add(cache, cache, index);
}
-void InterpreterMacroAssembler::load_field_entry(Register cache, Register index, int bcp_offset) {
+void InterpreterMacroAssembler::load_field_or_method_entry(bool is_method, Register cache, Register index, int bcp_offset, bool for_fast_bytecode) {
+ const int entry_size = is_method ? sizeof(ResolvedMethodEntry) : sizeof(ResolvedFieldEntry),
+ base_offset = is_method ? Array::base_offset_in_bytes() : Array::base_offset_in_bytes(),
+ entries_offset = is_method ? in_bytes(ConstantPoolCache::method_entries_offset()) : in_bytes(ConstantPoolCache::field_entries_offset());
+
// Get index out of bytecode pointer
get_cache_index_at_bcp(index, bcp_offset, sizeof(u2));
// Take shortcut if the size is a power of 2
- if (is_power_of_2(sizeof(ResolvedFieldEntry))) {
+ if (is_power_of_2(entry_size)) {
// Scale index by power of 2
- sldi(index, index, log2i_exact(sizeof(ResolvedFieldEntry)));
+ sldi(index, index, log2i_exact(entry_size));
} else {
// Scale the index to be the entry index * sizeof(ResolvedFieldEntry)
- mulli(index, index, sizeof(ResolvedFieldEntry));
+ mulli(index, index, entry_size);
}
// Get address of field entries array
- ld_ptr(cache, in_bytes(ConstantPoolCache::field_entries_offset()), R27_constPoolCache);
- addi(cache, cache, Array::base_offset_in_bytes());
+ ld_ptr(cache, entries_offset, R27_constPoolCache);
+ addi(cache, cache, base_offset);
add(cache, cache, index);
-}
-void InterpreterMacroAssembler::load_method_entry(Register cache, Register index, int bcp_offset) {
- // Get index out of bytecode pointer
- get_cache_index_at_bcp(index, bcp_offset, sizeof(u2));
- // Scale the index to be the entry index * sizeof(ResolvedMethodEntry)
- mulli(index, index, sizeof(ResolvedMethodEntry));
-
- // Get address of field entries array
- ld_ptr(cache, ConstantPoolCache::method_entries_offset(), R27_constPoolCache);
- addi(cache, cache, Array::base_offset_in_bytes());
- add(cache, cache, index); // method_entries + base_offset + scaled index
+ if (for_fast_bytecode) {
+ // Prevent speculative loading from ResolvedFieldEntry/ResolvedMethodEntry as it can miss the info written by another thread.
+ // TemplateTable::patch_bytecode uses release-store.
+ // We reached here via control dependency (Bytecode dispatch has used the rewritten Bytecode).
+ // So, we can use control-isync based ordering.
+ isync();
+ }
}
// Load object from cpool->resolved_references(index).
@@ -1266,11 +1266,11 @@ void InterpreterMacroAssembler::verify_method_data_pointer() {
lhz(R11_scratch1, in_bytes(DataLayout::bci_offset()), R28_mdx);
ld(R12_scratch2, in_bytes(Method::const_offset()), R19_method);
addi(R11_scratch1, R11_scratch1, in_bytes(ConstMethod::codes_offset()));
- add(R11_scratch1, R12_scratch2, R12_scratch2);
+ add(R11_scratch1, R11_scratch1, R12_scratch2);
cmpd(CR0, R11_scratch1, R14_bcp);
beq(CR0, verify_continue);
- call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp ), R19_method, R14_bcp, R28_mdx);
+ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp), R19_method, R14_bcp, R28_mdx);
bind(verify_continue);
#endif
diff --git a/src/hotspot/cpu/ppc/orderAccess_ppc.hpp b/src/hotspot/cpu/ppc/orderAccess_ppc.hpp
new file mode 100644
index 00000000000..f5fa8da755f
--- /dev/null
+++ b/src/hotspot/cpu/ppc/orderAccess_ppc.hpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * 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 CPU_PPC_ORDERACCESS_PPC_HPP
+#define CPU_PPC_ORDERACCESS_PPC_HPP
+
+#ifndef PPC64
+#error "OrderAccess currently only implemented for PPC64"
+#endif
+
+// Compiler version last used for testing: gcc 4.1.2
+// Please update this information when this file changes
+
+// Implementation of class OrderAccess.
+
+//
+// Machine barrier instructions:
+//
+// - sync Two-way memory barrier, aka fence.
+// - lwsync orders Store|Store,
+// Load|Store,
+// Load|Load,
+// but not Store|Load
+// - eieio orders Store|Store
+// - isync Invalidates speculatively executed instructions,
+// but isync may complete before storage accesses
+// associated with instructions preceding isync have
+// been performed.
+//
+// Semantic barrier instructions:
+// (as defined in orderAccess.hpp)
+//
+// - release orders Store|Store, (maps to lwsync)
+// Load|Store
+// - acquire orders Load|Store, (maps to lwsync)
+// Load|Load
+// - fence orders Store|Store, (maps to sync)
+// Load|Store,
+// Load|Load,
+// Store|Load
+//
+
+#define inlasm_sync() __asm__ __volatile__ ("sync" : : : "memory");
+#define inlasm_lwsync() __asm__ __volatile__ ("lwsync" : : : "memory");
+#define inlasm_eieio() __asm__ __volatile__ ("eieio" : : : "memory");
+#define inlasm_isync() __asm__ __volatile__ ("isync" : : : "memory");
+
+inline void OrderAccess::loadload() { inlasm_lwsync(); }
+inline void OrderAccess::storestore() { inlasm_lwsync(); }
+inline void OrderAccess::loadstore() { inlasm_lwsync(); }
+inline void OrderAccess::storeload() { inlasm_sync(); }
+
+inline void OrderAccess::acquire() { inlasm_lwsync(); }
+inline void OrderAccess::release() { inlasm_lwsync(); }
+inline void OrderAccess::fence() { inlasm_sync(); }
+inline void OrderAccess::cross_modify_fence_impl()
+ { inlasm_isync(); }
+
+#undef inlasm_sync
+#undef inlasm_lwsync
+#undef inlasm_eieio
+#undef inlasm_isync
+
+#endif // CPU_PPC_ORDERACCESS_PPC_HPP
diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad
index 128e566d0f3..a04e397c52b 100644
--- a/src/hotspot/cpu/ppc/ppc.ad
+++ b/src/hotspot/cpu/ppc/ppc.ad
@@ -12583,27 +12583,27 @@ instruct countTrailingZerosL_cnttzd(iRegIdst dst, iRegLsrc src) %{
%}
// Expand nodes for byte_reverse_int.
-instruct insrwi_a(iRegIdst dst, iRegIsrc src, immI16 pos, immI16 shift) %{
- effect(DEF dst, USE src, USE pos, USE shift);
+instruct insrwi_a(iRegIdst dst, iRegIsrc src, immI16 n, immI16 b) %{
+ effect(DEF dst, USE src, USE n, USE b);
predicate(false);
- format %{ "INSRWI $dst, $src, $pos, $shift" %}
+ format %{ "INSRWI $dst, $src, $n, $b" %}
size(4);
ins_encode %{
- __ insrwi($dst$$Register, $src$$Register, $shift$$constant, $pos$$constant);
+ __ insrwi($dst$$Register, $src$$Register, $n$$constant, $b$$constant);
%}
ins_pipe(pipe_class_default);
%}
// As insrwi_a, but with USE_DEF.
-instruct insrwi(iRegIdst dst, iRegIsrc src, immI16 pos, immI16 shift) %{
- effect(USE_DEF dst, USE src, USE pos, USE shift);
+instruct insrwi(iRegIdst dst, iRegIsrc src, immI16 n, immI16 b) %{
+ effect(USE_DEF dst, USE src, USE n, USE b);
predicate(false);
- format %{ "INSRWI $dst, $src, $pos, $shift" %}
+ format %{ "INSRWI $dst, $src, $n, $b" %}
size(4);
ins_encode %{
- __ insrwi($dst$$Register, $src$$Register, $shift$$constant, $pos$$constant);
+ __ insrwi($dst$$Register, $src$$Register, $n$$constant, $b$$constant);
%}
ins_pipe(pipe_class_default);
%}
@@ -12625,12 +12625,12 @@ instruct bytes_reverse_int_Ex(iRegIdst dst, iRegIsrc src) %{
iRegLdst tmpI3;
urShiftI_reg_imm(tmpI1, src, imm24);
- insrwi_a(dst, tmpI1, imm24, imm8);
+ insrwi_a(dst, tmpI1, imm8, imm24);
urShiftI_reg_imm(tmpI2, src, imm16);
- insrwi(dst, tmpI2, imm8, imm16);
+ insrwi(dst, tmpI2, imm16, imm8);
urShiftI_reg_imm(tmpI3, src, imm8);
insrwi(dst, tmpI3, imm8, imm8);
- insrwi(dst, src, imm0, imm8);
+ insrwi(dst, src, imm8, imm0);
%}
%}
@@ -12748,7 +12748,7 @@ instruct bytes_reverse_ushort_Ex(iRegIdst dst, iRegIsrc src) %{
immI16 imm8 %{ (int) 8 %}
urShiftI_reg_imm(dst, src, imm8);
- insrwi(dst, src, imm16, imm8);
+ insrwi(dst, src, imm8, imm16);
%}
%}
@@ -12777,7 +12777,7 @@ instruct bytes_reverse_short_Ex(iRegIdst dst, iRegIsrc src) %{
iRegLdst tmpI1;
urShiftI_reg_imm(tmpI1, src, imm8);
- insrwi(tmpI1, src, imm16, imm8);
+ insrwi(tmpI1, src, imm8, imm16);
extsh(dst, tmpI1);
%}
%}
diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp
index 7431f77aeff..e3a8028f471 100644
--- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp
+++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp
@@ -148,7 +148,9 @@ void TemplateTable::patch_bytecode(Bytecodes::Code new_bc, Register Rnew_bc, Reg
__ bind(L_fast_patch);
}
- // Patch bytecode.
+ // Patch bytecode with release store to coordinate with ResolvedFieldEntry
+ // and ResolvedMethodEntry loads in fast bytecode codelets.
+ __ release();
__ stb(Rnew_bc, 0, R14_bcp);
__ bind(L_patch_done);
@@ -312,6 +314,7 @@ void TemplateTable::fast_aldc(LdcType type) {
// We are resolved if the resolved reference cache entry contains a
// non-null object (CallSite, etc.)
__ get_cache_index_at_bcp(R31, 1, index_size); // Load index.
+ // Only rewritten during link time. So, no need for memory barriers for accessing resolved info.
__ load_resolved_reference_at_index(R17_tos, R31, R11_scratch1, R12_scratch2, &is_null);
// Convert null sentinel to null
@@ -3109,7 +3112,7 @@ void TemplateTable::fast_storefield(TosState state) {
const ConditionRegister CR_is_vol = CR2; // Non-volatile condition register (survives runtime call in do_oop_store).
// Constant pool already resolved => Load flags and offset of field.
- __ load_field_entry(Rcache, Rscratch);
+ __ load_field_entry(Rcache, Rscratch, 1, /* for_fast_bytecode */ true);
jvmti_post_field_mod(Rcache, Rscratch, false /* not static */);
load_resolved_field_entry(noreg, Rcache, noreg, Roffset, Rflags, false); // Uses R11, R12
@@ -3190,7 +3193,7 @@ void TemplateTable::fast_accessfield(TosState state) {
// R12_scratch2 used by load_field_cp_cache_entry
// Constant pool already resolved. Get the field offset.
- __ load_field_entry(Rcache, Rscratch);
+ __ load_field_entry(Rcache, Rscratch, 1, /* for_fast_bytecode */ true);
load_resolved_field_entry(noreg, Rcache, noreg, Roffset, Rflags, false); // Uses R11, R12
// JVMTI support
@@ -3329,7 +3332,7 @@ void TemplateTable::fast_xaccess(TosState state) {
__ ld(Rclass_or_obj, 0, R18_locals);
// Constant pool already resolved. Get the field offset.
- __ load_field_entry(Rcache, Rscratch, 2);
+ __ load_field_entry(Rcache, Rscratch, 2, /* for_fast_bytecode */ true);
load_resolved_field_entry(noreg, Rcache, noreg, Roffset, Rflags, false); // Uses R11, R12
// JVMTI support not needed, since we switch back to single bytecode as soon as debugger attaches.
@@ -3490,7 +3493,7 @@ void TemplateTable::fast_invokevfinal(int byte_no) {
assert(byte_no == f2_byte, "use this argument");
Register Rcache = R31;
- __ load_method_entry(Rcache, R11_scratch1);
+ __ load_method_entry(Rcache, R11_scratch1, 1, /* for_fast_bytecode */ true);
invokevfinal_helper(Rcache, R11_scratch1, R12_scratch2, R22_tmp2, R23_tmp3);
}
diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp
index ec2766ac75b..721364ce412 100644
--- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp
+++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp
@@ -97,6 +97,10 @@ void VM_Version::initialize() {
FLAG_SET_ERGO(TrapBasedRangeChecks, false);
}
+ if (FLAG_IS_DEFAULT(UsePopCountInstruction)) {
+ FLAG_SET_ERGO(UsePopCountInstruction, true);
+ }
+
if (PowerArchitecturePPC64 >= 9) {
// Performance is good since Power9.
if (FLAG_IS_DEFAULT(SuperwordUseVSX)) {
@@ -105,6 +109,10 @@ void VM_Version::initialize() {
}
MaxVectorSize = SuperwordUseVSX ? 16 : 8;
+ if (!SuperwordUseVSX && FLAG_IS_DEFAULT(EnableVectorSupport)) {
+ // VectorSupport intrinsics currently have issues with MaxVectorSize < 16 (JDK-8370803).
+ FLAG_SET_ERGO(EnableVectorSupport, false);
+ }
if (FLAG_IS_DEFAULT(AlignVector)) {
FLAG_SET_ERGO(AlignVector, false);
}
diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp
index 3317ccc3b53..0712b60fb2a 100644
--- a/src/hotspot/cpu/riscv/assembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp
@@ -912,6 +912,43 @@ class Assembler : public AbstractAssembler {
emit(insn);
}
+ public:
+
+ static uint32_t encode_csrrw(Register Rd, const uint32_t csr, Register Rs1) {
+ guarantee(is_uimm12(csr), "csr is invalid");
+ uint32_t insn = 0;
+ patch((address)&insn, 6, 0, 0b1110011);
+ patch((address)&insn, 14, 12, 0b001);
+ patch_reg((address)&insn, 7, Rd);
+ patch_reg((address)&insn, 15, Rs1);
+ patch((address)&insn, 31, 20, csr);
+ return insn;
+ }
+
+ static uint32_t encode_jal(Register Rd, const int32_t offset) {
+ guarantee(is_simm21(offset) && ((offset % 2) == 0), "offset is invalid.");
+ uint32_t insn = 0;
+ patch((address)&insn, 6, 0, 0b1101111);
+ patch_reg((address)&insn, 7, Rd);
+ patch((address)&insn, 19, 12, (uint32_t)((offset >> 12) & 0xff));
+ patch((address)&insn, 20, (uint32_t)((offset >> 11) & 0x1));
+ patch((address)&insn, 30, 21, (uint32_t)((offset >> 1) & 0x3ff));
+ patch((address)&insn, 31, (uint32_t)((offset >> 20) & 0x1));
+ return insn;
+ }
+
+ static uint32_t encode_jalr(Register Rd, Register Rs, const int32_t offset) {
+ guarantee(is_simm12(offset), "offset is invalid.");
+ uint32_t insn = 0;
+ patch((address)&insn, 6, 0, 0b1100111);
+ patch_reg((address)&insn, 7, Rd);
+ patch((address)&insn, 14, 12, 0b000);
+ patch_reg((address)&insn, 15, Rs);
+ int32_t val = offset & 0xfff;
+ patch((address)&insn, 31, 20, val);
+ return insn;
+ }
+
protected:
enum barrier {
@@ -1988,6 +2025,7 @@ enum VectorMask {
// Vector Narrowing Integer Right Shift Instructions
INSN(vnsra_wi, 0b1010111, 0b011, 0b101101);
+ INSN(vnsrl_wi, 0b1010111, 0b011, 0b101100);
#undef INSN
@@ -3666,19 +3704,15 @@ enum Nf {
// --------------------------
// Upper Immediate Instruction
// --------------------------
-#define INSN(NAME) \
- void NAME(Register Rd, int32_t imm) { \
- /* lui -> c.lui */ \
- if (do_compress() && (Rd != x0 && Rd != x2 && imm != 0 && is_simm18(imm))) { \
- c_lui(Rd, imm); \
- return; \
- } \
- _lui(Rd, imm); \
+ void lui(Register Rd, int32_t imm) {
+ /* lui -> c.lui */
+ if (do_compress() && (Rd != x0 && Rd != x2 && imm != 0 && is_simm18(imm))) {
+ c_lui(Rd, imm);
+ return;
+ }
+ _lui(Rd, imm);
}
- INSN(lui);
-
-#undef INSN
// Cache Management Operations
// These instruction may be turned off for user space.
diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
index 08d35c2831a..30148651818 100644
--- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
@@ -401,7 +401,7 @@ void LIR_Assembler::return_op(LIR_Opr result, C1SafepointPollStub* code_stub) {
code_stub->set_safepoint_offset(__ offset());
__ relocate(relocInfo::poll_return_type);
- __ safepoint_poll(*code_stub->entry(), true /* at_return */, false /* acquire */, true /* in_nmethod */);
+ __ safepoint_poll(*code_stub->entry(), true /* at_return */, true /* in_nmethod */);
__ ret();
}
@@ -1354,6 +1354,7 @@ void LIR_Assembler::align_call(LIR_Code code) {
}
void LIR_Assembler::call(LIR_OpJavaCall* op, relocInfo::relocType rtype) {
+ Assembler::IncompressibleScope scope(_masm);
address call = __ reloc_call(Address(op->addr(), rtype));
if (call == nullptr) {
bailout("reloc call address stub overflow");
@@ -1364,6 +1365,7 @@ void LIR_Assembler::call(LIR_OpJavaCall* op, relocInfo::relocType rtype) {
}
void LIR_Assembler::ic_call(LIR_OpJavaCall* op) {
+ Assembler::IncompressibleScope scope(_masm);
address call = __ ic_call(op->addr());
if (call == nullptr) {
bailout("reloc call address stub overflow");
@@ -1856,6 +1858,10 @@ void LIR_Assembler::leal(LIR_Opr addr, LIR_Opr dest, LIR_PatchCode patch_code, C
void LIR_Assembler::rt_call(LIR_Opr result, address dest, const LIR_OprList* args, LIR_Opr tmp, CodeEmitInfo* info) {
assert(!tmp->is_valid(), "don't need temporary");
+ Assembler::IncompressibleScope scope(_masm);
+ // Post call nops must be natural aligned due to cmodx rules.
+ align_call(lir_rtcall);
+
__ rt_call(dest);
if (info != nullptr) {
diff --git a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp
index b9ebd49779e..31925f7f263 100644
--- a/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c1_LIRGenerator_riscv.cpp
@@ -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,
diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
index ce13ebde74f..d8a7fba146b 100644
--- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
@@ -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);
@@ -2393,18 +2391,7 @@ static void float_to_float16_slow_path(C2_MacroAssembler& masm, C2GeneralStub();
__ bind(stub.entry());
- __ fmv_x_w(dst, src);
-
- // preserve the payloads of non-canonical NaNs.
- __ srai(dst, dst, 13);
- // preserve the sign bit.
- __ srai(tmp, dst, 13);
- __ slli(tmp, tmp, 10);
- __ mv(t0, 0x3ff);
- __ orr(tmp, tmp, t0);
-
- // get the result by merging sign bit and payloads of preserved non-canonical NaNs.
- __ andr(dst, dst, tmp);
+ __ float_to_float16_NaN(dst, src, t0, tmp);
__ j(stub.continuation());
#undef __
@@ -2412,7 +2399,7 @@ static void float_to_float16_slow_path(C2_MacroAssembler& masm, C2GeneralStub(dst, src, xtmp, 130, float_to_float16_slow_path);
+ auto stub = C2CodeStub::make(dst, src, xtmp, 64, float_to_float16_slow_path);
// On riscv, NaN needs a special process as fcvt does not work in that case.
@@ -2491,41 +2478,80 @@ static void float_to_float16_v_slow_path(C2_MacroAssembler& masm,
#define __ masm.
VectorRegister dst = stub.data<0>();
VectorRegister src = stub.data<1>();
- VectorRegister tmp = stub.data<2>();
+ VectorRegister vtmp = stub.data<2>();
+ assert_different_registers(dst, src, vtmp);
+
__ bind(stub.entry());
+ // Active elements (NaNs) are marked in v0 mask register.
// mul is already set to mf2 in float_to_float16_v.
- // preserve the payloads of non-canonical NaNs.
- __ vnsra_wi(dst, src, 13, Assembler::v0_t);
-
- // preserve the sign bit.
- __ vnsra_wi(tmp, src, 26, Assembler::v0_t);
- __ vsll_vi(tmp, tmp, 10, Assembler::v0_t);
- __ mv(t0, 0x3ff);
- __ vor_vx(tmp, tmp, t0, Assembler::v0_t);
-
- // get the result by merging sign bit and payloads of preserved non-canonical NaNs.
- __ vand_vv(dst, dst, tmp, Assembler::v0_t);
+ // Float (32 bits)
+ // Bit: 31 30 to 23 22 to 0
+ // +---+------------------+-----------------------------+
+ // | S | Exponent | Mantissa (Fraction) |
+ // +---+------------------+-----------------------------+
+ // 1 bit 8 bits 23 bits
+ //
+ // Float (16 bits)
+ // Bit: 15 14 to 10 9 to 0
+ // +---+----------------+------------------+
+ // | S | Exponent | Mantissa |
+ // +---+----------------+------------------+
+ // 1 bit 5 bits 10 bits
+ const int fp_sign_bits = 1;
+ const int fp32_bits = 32;
+ const int fp32_mantissa_2nd_part_bits = 9;
+ const int fp32_mantissa_3rd_part_bits = 4;
+ const int fp16_exponent_bits = 5;
+ const int fp16_mantissa_bits = 10;
+
+ // preserve the sign bit and exponent, clear mantissa.
+ __ vnsra_wi(dst, src, fp32_bits - fp_sign_bits - fp16_exponent_bits, Assembler::v0_t);
+ __ vsll_vi(dst, dst, fp16_mantissa_bits, Assembler::v0_t);
+
+ // Preserve high order bit of float NaN in the
+ // binary16 result NaN (tenth bit); OR in remaining
+ // bits into lower 9 bits of binary 16 significand.
+ // | (doppel & 0x007f_e000) >> 13 // 10 bits
+ // | (doppel & 0x0000_1ff0) >> 4 // 9 bits
+ // | (doppel & 0x0000_000f)); // 4 bits
+ //
+ // Check j.l.Float.floatToFloat16 for more information.
+ // 10 bits
+ __ vnsrl_wi(vtmp, src, fp32_mantissa_2nd_part_bits + fp32_mantissa_3rd_part_bits, Assembler::v0_t);
+ __ mv(t0, 0x3ff); // retain first part of mantissa in a float 32
+ __ vand_vx(vtmp, vtmp, t0, Assembler::v0_t);
+ __ vor_vv(dst, dst, vtmp, Assembler::v0_t);
+ // 9 bits
+ __ vnsrl_wi(vtmp, src, fp32_mantissa_3rd_part_bits, Assembler::v0_t);
+ __ mv(t0, 0x1ff); // retain second part of mantissa in a float 32
+ __ vand_vx(vtmp, vtmp, t0, Assembler::v0_t);
+ __ vor_vv(dst, dst, vtmp, Assembler::v0_t);
+ // 4 bits
+ // Narrow shift is necessary to move data from 32 bits element to 16 bits element in vector register.
+ __ vnsrl_wi(vtmp, src, 0, Assembler::v0_t);
+ __ vand_vi(vtmp, vtmp, 0xf, Assembler::v0_t);
+ __ vor_vv(dst, dst, vtmp, Assembler::v0_t);
__ j(stub.continuation());
#undef __
}
// j.l.Float.float16ToFloat
-void C2_MacroAssembler::float_to_float16_v(VectorRegister dst, VectorRegister src, VectorRegister vtmp,
- Register tmp, uint vector_length) {
+void C2_MacroAssembler::float_to_float16_v(VectorRegister dst, VectorRegister src,
+ VectorRegister vtmp, Register tmp, uint vector_length) {
assert_different_registers(dst, src, vtmp);
auto stub = C2CodeStub::make
- (dst, src, vtmp, 28, float_to_float16_v_slow_path);
+ (dst, src, vtmp, 56, float_to_float16_v_slow_path);
// On riscv, NaN needs a special process as vfncvt_f_f_w does not work in that case.
vsetvli_helper(BasicType::T_FLOAT, vector_length, Assembler::m1);
// check whether there is a NaN.
- // replace v_fclass with vmseq_vv as performance optimization.
+ // replace v_fclass with vmfne_vv as performance optimization.
vmfne_vv(v0, src, src);
vcpop_m(t0, v0);
diff --git a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp
index d1278c419a0..cc685645ec5 100644
--- a/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp
+++ b/src/hotspot/cpu/riscv/downcallLinker_riscv.cpp
@@ -287,7 +287,7 @@ void DowncallLinker::StubGenerator::generate() {
__ membar(MacroAssembler::AnyAny);
}
- __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, false /* in_nmethod */);
__ lwu(t0, Address(xthread, JavaThread::suspend_flags_offset()));
__ bnez(t0, L_safepoint_poll_slow_path);
diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
index 7e9bea381a5..387db778c1f 100644
--- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
@@ -241,10 +241,6 @@ void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm, Label* slo
__ lwu(t0, *guard);
switch (patching_type) {
- case NMethodPatchingType::conc_data_patch:
- // Subsequent loads of oops must occur after load of guard value.
- // BarrierSetNMethod::disarm sets guard with release semantics.
- __ membar(MacroAssembler::LoadLoad); // fall through to stw_instruction_and_data_patch
case NMethodPatchingType::stw_instruction_and_data_patch:
{
// With STW patching, no data or instructions are updated concurrently,
diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp
index 7061dca738c..63a7032bb84 100644
--- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.hpp
@@ -40,8 +40,7 @@ class Node;
enum class NMethodPatchingType {
stw_instruction_and_data_patch,
- conc_instruction_and_data_patch,
- conc_data_patch
+ conc_instruction_and_data_patch
};
class BarrierSetAssembler: public CHeapObj {
diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetNMethod_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetNMethod_riscv.cpp
index f24e4f789bc..ac619f83f7d 100644
--- a/src/hotspot/cpu/riscv/gc/shared/barrierSetNMethod_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetNMethod_riscv.cpp
@@ -50,8 +50,6 @@ static int entry_barrier_offset(nmethod* nm) {
switch (bs_asm->nmethod_patching_type()) {
case NMethodPatchingType::stw_instruction_and_data_patch:
return -4 * (4 + slow_path_size(nm));
- case NMethodPatchingType::conc_data_patch:
- return -4 * (5 + slow_path_size(nm));
case NMethodPatchingType::conc_instruction_and_data_patch:
return -4 * (15 + slow_path_size(nm));
}
diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp
index 7d12cc8cbb6..3fe7c8d1740 100644
--- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp
@@ -69,7 +69,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler {
public:
- virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_data_patch; }
+ virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_instruction_and_data_patch; }
#ifdef COMPILER1
void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub);
diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
index fae34a9c770..eececc93393 100644
--- a/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
+++ b/src/hotspot/cpu/riscv/interp_masm_riscv.cpp
@@ -645,7 +645,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state,
// the stack, will call InterpreterRuntime::at_unwind.
Label slow_path;
Label fast_path;
- safepoint_poll(slow_path, true /* at_return */, false /* acquire */, false /* in_nmethod */);
+ safepoint_poll(slow_path, true /* at_return */, false /* in_nmethod */);
j(fast_path);
bind(slow_path);
@@ -1937,6 +1937,15 @@ void InterpreterMacroAssembler::load_method_entry(Register cache, Register index
}
#ifdef ASSERT
+void InterpreterMacroAssembler::verify_field_offset(Register reg) {
+ // Verify the field offset is not in the header, implicitly checks for 0
+ Label L;
+ mv(t0, oopDesc::base_offset_in_bytes());
+ bge(reg, t0, L);
+ stop("bad field offset");
+ bind(L);
+}
+
void InterpreterMacroAssembler::verify_access_flags(Register access_flags, uint32_t flag,
const char* msg, bool stop_by_hit) {
Label L;
diff --git a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
index 891db16b243..13e77b1a359 100644
--- a/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
+++ b/src/hotspot/cpu/riscv/interp_masm_riscv.hpp
@@ -300,6 +300,8 @@ class InterpreterMacroAssembler: public MacroAssembler {
void load_field_entry(Register cache, Register index, int bcp_offset = 1);
void load_method_entry(Register cache, Register index, int bcp_offset = 1);
+ void verify_field_offset(Register reg) NOT_DEBUG_RETURN;
+
#ifdef ASSERT
void verify_access_flags(Register access_flags, uint32_t flag,
const char* msg, bool stop_by_hit = true);
diff --git a/src/hotspot/cpu/riscv/javaFrameAnchor_riscv.hpp b/src/hotspot/cpu/riscv/javaFrameAnchor_riscv.hpp
index 1293fae0c0b..6bf1230914a 100644
--- a/src/hotspot/cpu/riscv/javaFrameAnchor_riscv.hpp
+++ b/src/hotspot/cpu/riscv/javaFrameAnchor_riscv.hpp
@@ -39,25 +39,23 @@
// 3 - restoring an old state (javaCalls)
void clear(void) {
+ // No hardware barriers are necessary. All members are volatile and the profiler
+ // is run from a signal handler and the only observer is the thread its running on.
+
// clearing _last_Java_sp must be first
_last_Java_sp = nullptr;
- OrderAccess::release();
_last_Java_fp = nullptr;
_last_Java_pc = nullptr;
}
void copy(JavaFrameAnchor* src) {
- // In order to make sure the transition state is valid for "this"
+ // No hardware barriers are necessary. All members are volatile and the profiler
+ // is run from a signal handler and the only observer is the thread its running on.
+
// We must clear _last_Java_sp before copying the rest of the new data
- //
- // Hack Alert: Temporary bugfix for 4717480/4721647
- // To act like previous version (pd_cache_state) don't null _last_Java_sp
- // unless the value is changing
- //
assert(src != nullptr, "Src should not be null.");
if (_last_Java_sp != src->_last_Java_sp) {
_last_Java_sp = nullptr;
- OrderAccess::release();
}
_last_Java_fp = src->_last_Java_fp;
_last_Java_pc = src->_last_Java_pc;
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
index 55894916527..4e3d30c4081 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
@@ -97,52 +97,52 @@ bool MacroAssembler::is_pc_relative_at(address instr) {
// auipc + load
// auipc + fload_load
return (is_auipc_at(instr)) &&
- (is_addi_at(instr + instruction_size) ||
- is_jalr_at(instr + instruction_size) ||
- is_load_at(instr + instruction_size) ||
- is_float_load_at(instr + instruction_size)) &&
+ (is_addi_at(instr + MacroAssembler::instruction_size) ||
+ is_jalr_at(instr + MacroAssembler::instruction_size) ||
+ is_load_at(instr + MacroAssembler::instruction_size) ||
+ is_float_load_at(instr + MacroAssembler::instruction_size)) &&
check_pc_relative_data_dependency(instr);
}
// ie:ld(Rd, Label)
bool MacroAssembler::is_load_pc_relative_at(address instr) {
return is_auipc_at(instr) && // auipc
- is_ld_at(instr + instruction_size) && // ld
+ is_ld_at(instr + MacroAssembler::instruction_size) && // ld
check_load_pc_relative_data_dependency(instr);
}
bool MacroAssembler::is_movptr1_at(address instr) {
return is_lui_at(instr) && // Lui
- is_addi_at(instr + instruction_size) && // Addi
- is_slli_shift_at(instr + instruction_size * 2, 11) && // Slli Rd, Rs, 11
- is_addi_at(instr + instruction_size * 3) && // Addi
- is_slli_shift_at(instr + instruction_size * 4, 6) && // Slli Rd, Rs, 6
- (is_addi_at(instr + instruction_size * 5) ||
- is_jalr_at(instr + instruction_size * 5) ||
- is_load_at(instr + instruction_size * 5)) && // Addi/Jalr/Load
+ is_addi_at(instr + MacroAssembler::instruction_size) && // Addi
+ is_slli_shift_at(instr + MacroAssembler::instruction_size * 2, 11) && // Slli Rd, Rs, 11
+ is_addi_at(instr + MacroAssembler::instruction_size * 3) && // Addi
+ is_slli_shift_at(instr + MacroAssembler::instruction_size * 4, 6) && // Slli Rd, Rs, 6
+ (is_addi_at(instr + MacroAssembler::instruction_size * 5) ||
+ is_jalr_at(instr + MacroAssembler::instruction_size * 5) ||
+ is_load_at(instr + MacroAssembler::instruction_size * 5)) && // Addi/Jalr/Load
check_movptr1_data_dependency(instr);
}
bool MacroAssembler::is_movptr2_at(address instr) {
return is_lui_at(instr) && // lui
- is_lui_at(instr + instruction_size) && // lui
- is_slli_shift_at(instr + instruction_size * 2, 18) && // slli Rd, Rs, 18
- is_add_at(instr + instruction_size * 3) &&
- (is_addi_at(instr + instruction_size * 4) ||
- is_jalr_at(instr + instruction_size * 4) ||
- is_load_at(instr + instruction_size * 4)) && // Addi/Jalr/Load
+ is_lui_at(instr + MacroAssembler::instruction_size) && // lui
+ is_slli_shift_at(instr + MacroAssembler::instruction_size * 2, 18) && // slli Rd, Rs, 18
+ is_add_at(instr + MacroAssembler::instruction_size * 3) &&
+ (is_addi_at(instr + MacroAssembler::instruction_size * 4) ||
+ is_jalr_at(instr + MacroAssembler::instruction_size * 4) ||
+ is_load_at(instr + MacroAssembler::instruction_size * 4)) && // Addi/Jalr/Load
check_movptr2_data_dependency(instr);
}
bool MacroAssembler::is_li16u_at(address instr) {
return is_lui_at(instr) && // lui
- is_srli_at(instr + instruction_size) && // srli
+ is_srli_at(instr + MacroAssembler::instruction_size) && // srli
check_li16u_data_dependency(instr);
}
bool MacroAssembler::is_li32_at(address instr) {
return is_lui_at(instr) && // lui
- is_addiw_at(instr + instruction_size) && // addiw
+ is_addiw_at(instr + MacroAssembler::instruction_size) && // addiw
check_li32_data_dependency(instr);
}
@@ -355,14 +355,15 @@ void MacroAssembler::call_VM(Register oop_result,
}
void MacroAssembler::post_call_nop() {
+ assert(!in_compressible_scope(), "Must be");
+ assert_alignment(pc());
if (!Continuations::enabled()) {
return;
}
- relocate(post_call_nop_Relocation::spec(), [&] {
- InlineSkippedInstructionsCounter skipCounter(this);
- nop();
- li32(zr, 0);
- });
+ relocate(post_call_nop_Relocation::spec());
+ InlineSkippedInstructionsCounter skipCounter(this);
+ nop();
+ li32(zr, 0);
}
// these are no-ops overridden by InterpreterMacroAssembler
@@ -389,12 +390,14 @@ void MacroAssembler::set_last_Java_frame(Register last_java_sp,
last_java_sp = esp;
}
- sd(last_java_sp, Address(xthread, JavaThread::last_Java_sp_offset()));
-
// last_java_fp is optional
if (last_java_fp->is_valid()) {
sd(last_java_fp, Address(xthread, JavaThread::last_Java_fp_offset()));
}
+
+ // We must set sp last.
+ sd(last_java_sp, Address(xthread, JavaThread::last_Java_sp_offset()));
+
}
void MacroAssembler::set_last_Java_frame(Register last_java_sp,
@@ -3402,6 +3405,8 @@ void MacroAssembler::decode_klass_not_null(Register r, Register tmp) {
void MacroAssembler::decode_klass_not_null(Register dst, Register src, Register tmp) {
assert(UseCompressedClassPointers, "should only be used for compressed headers");
+ assert_different_registers(dst, tmp);
+ assert_different_registers(src, tmp);
if (CompressedKlassPointers::base() == nullptr) {
if (CompressedKlassPointers::shift() != 0) {
@@ -3412,18 +3417,13 @@ void MacroAssembler::decode_klass_not_null(Register dst, Register src, Register
return;
}
- Register xbase = dst;
- if (dst == src) {
- xbase = tmp;
- }
+ Register xbase = tmp;
- assert_different_registers(src, xbase);
mv(xbase, (uintptr_t)CompressedKlassPointers::base());
if (CompressedKlassPointers::shift() != 0) {
- Register t = src == dst ? dst : t0;
- assert_different_registers(t, xbase);
- shadd(dst, src, xbase, t, CompressedKlassPointers::shift());
+ // dst = (src << shift) + xbase
+ shadd(dst, src, xbase, dst /* temporary, dst != xbase */, CompressedKlassPointers::shift());
} else {
add(dst, xbase, src);
}
@@ -3773,11 +3773,8 @@ void MacroAssembler::check_klass_subtype(Register sub_klass,
bind(L_failure);
}
-void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool acquire, bool in_nmethod, Register tmp_reg) {
+void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp_reg) {
ld(tmp_reg, Address(xthread, JavaThread::polling_word_offset()));
- if (acquire) {
- membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore);
- }
if (at_return) {
bgtu(in_nmethod ? sp : fp, tmp_reg, slow_path, /* is_far */ true);
} else {
@@ -5019,7 +5016,7 @@ address MacroAssembler::reloc_call(Address entry, Register tmp) {
address MacroAssembler::ic_call(address entry, jint method_index) {
RelocationHolder rh = virtual_call_Relocation::spec(pc(), method_index);
- IncompressibleScope scope(this); // relocations
+ assert(!in_compressible_scope(), "Must be");
movptr(t0, (address)Universe::non_oop_word(), t1);
assert_cond(entry != nullptr);
return reloc_call(Address(entry, rh));
@@ -5113,7 +5110,7 @@ address MacroAssembler::emit_reloc_call_address_stub(int insts_call_instruction_
int MacroAssembler::max_reloc_call_address_stub_size() {
// Max stub size: alignment nop, target address.
- return 1 * instruction_size + wordSize;
+ return 1 * MacroAssembler::instruction_size + wordSize;
}
int MacroAssembler::static_call_stub_size() {
@@ -5344,42 +5341,6 @@ void MacroAssembler::add2_with_carry(Register final_dest_hi, Register dest_hi, R
add(final_dest_hi, dest_hi, carry);
}
-/**
- * Multiply 32 bit by 32 bit first loop.
- */
-void MacroAssembler::multiply_32_x_32_loop(Register x, Register xstart, Register x_xstart,
- Register y, Register y_idx, Register z,
- Register carry, Register product,
- Register idx, Register kdx) {
- // jlong carry, x[], y[], z[];
- // for (int idx=ystart, kdx=ystart+1+xstart; idx >= 0; idx--, kdx--) {
- // long product = y[idx] * x[xstart] + carry;
- // z[kdx] = (int)product;
- // carry = product >>> 32;
- // }
- // z[xstart] = (int)carry;
-
- Label L_first_loop, L_first_loop_exit;
- blez(idx, L_first_loop_exit);
-
- shadd(t0, xstart, x, t0, LogBytesPerInt);
- lwu(x_xstart, Address(t0, 0));
-
- bind(L_first_loop);
- subiw(idx, idx, 1);
- shadd(t0, idx, y, t0, LogBytesPerInt);
- lwu(y_idx, Address(t0, 0));
- mul(product, x_xstart, y_idx);
- add(product, product, carry);
- srli(carry, product, 32);
- subiw(kdx, kdx, 1);
- shadd(t0, kdx, z, t0, LogBytesPerInt);
- sw(product, Address(t0, 0));
- bgtz(idx, L_first_loop);
-
- bind(L_first_loop_exit);
-}
-
/**
* Multiply 64 bit by 64 bit first loop.
*/
@@ -5596,77 +5557,16 @@ void MacroAssembler::multiply_to_len(Register x, Register xlen, Register y, Regi
const Register carry = tmp5;
const Register product = xlen;
const Register x_xstart = tmp0;
+ const Register jdx = tmp1;
mv(idx, ylen); // idx = ylen;
addw(kdx, xlen, ylen); // kdx = xlen+ylen;
mv(carry, zr); // carry = 0;
- Label L_multiply_64_x_64_loop, L_done;
-
+ Label L_done;
subiw(xstart, xlen, 1);
bltz(xstart, L_done);
- const Register jdx = tmp1;
-
- if (AvoidUnalignedAccesses) {
- int base_offset = arrayOopDesc::base_offset_in_bytes(T_INT);
- assert((base_offset % (UseCompactObjectHeaders ? 4 :
- (UseCompressedClassPointers ? 8 : 4))) == 0, "Must be");
-
- if ((base_offset % 8) == 0) {
- // multiply_64_x_64_loop emits 8-byte load/store to access two elements
- // at a time from int arrays x and y. When base_offset is 8 bytes, these
- // accesses are naturally aligned if both xlen and ylen are even numbers.
- orr(t0, xlen, ylen);
- test_bit(t0, t0, 0);
- beqz(t0, L_multiply_64_x_64_loop);
- }
-
- Label L_second_loop_unaligned, L_third_loop, L_third_loop_exit;
-
- multiply_32_x_32_loop(x, xstart, x_xstart, y, y_idx, z, carry, product, idx, kdx);
- shadd(t0, xstart, z, t0, LogBytesPerInt);
- sw(carry, Address(t0, 0));
-
- bind(L_second_loop_unaligned);
- mv(carry, zr);
- mv(jdx, ylen);
- subiw(xstart, xstart, 1);
- bltz(xstart, L_done);
-
- subi(sp, sp, 2 * wordSize);
- sd(z, Address(sp, 0));
- sd(zr, Address(sp, wordSize));
- shadd(t0, xstart, z, t0, LogBytesPerInt);
- addi(z, t0, 4);
- shadd(t0, xstart, x, t0, LogBytesPerInt);
- lwu(product, Address(t0, 0));
-
- blez(jdx, L_third_loop_exit);
-
- bind(L_third_loop);
- subiw(jdx, jdx, 1);
- shadd(t0, jdx, y, t0, LogBytesPerInt);
- lwu(t0, Address(t0, 0));
- mul(t1, t0, product);
- add(t0, t1, carry);
- shadd(tmp6, jdx, z, t1, LogBytesPerInt);
- lwu(t1, Address(tmp6, 0));
- add(t0, t0, t1);
- sw(t0, Address(tmp6, 0));
- srli(carry, t0, 32);
- bgtz(jdx, L_third_loop);
-
- bind(L_third_loop_exit);
- ld(z, Address(sp, 0));
- addi(sp, sp, 2 * wordSize);
- shadd(t0, xstart, z, t0, LogBytesPerInt);
- sw(carry, Address(t0, 0));
-
- j(L_second_loop_unaligned);
- }
-
- bind(L_multiply_64_x_64_loop);
multiply_64_x_64_loop(x, xstart, x_xstart, y, y_idx, z, carry, product, idx, kdx);
Label L_second_loop_aligned;
@@ -5974,13 +5874,14 @@ void MacroAssembler::fill_words(Register base, Register cnt, Register value) {
// in cnt.
//
// NOTE: This is intended to be used in the zero_blocks() stub. If
-// you want to use it elsewhere, note that cnt must be >= CacheLineSize.
+// you want to use it elsewhere, note that cnt must be >= zicboz_block_size.
void MacroAssembler::zero_dcache_blocks(Register base, Register cnt, Register tmp1, Register tmp2) {
+ int zicboz_block_size = VM_Version::zicboz_block_size.value();
Label initial_table_end, loop;
// Align base with cache line size.
neg(tmp1, base);
- andi(tmp1, tmp1, CacheLineSize - 1);
+ andi(tmp1, tmp1, zicboz_block_size - 1);
// tmp1: the number of bytes to be filled to align the base with cache line size.
add(base, base, tmp1);
@@ -5990,16 +5891,16 @@ void MacroAssembler::zero_dcache_blocks(Register base, Register cnt, Register tm
la(tmp1, initial_table_end);
sub(tmp2, tmp1, tmp2);
jr(tmp2);
- for (int i = -CacheLineSize + wordSize; i < 0; i += wordSize) {
+ for (int i = -zicboz_block_size + wordSize; i < 0; i += wordSize) {
sd(zr, Address(base, i));
}
bind(initial_table_end);
- mv(tmp1, CacheLineSize / wordSize);
+ mv(tmp1, zicboz_block_size / wordSize);
bind(loop);
cbo_zero(base);
sub(cnt, cnt, tmp1);
- addi(base, base, CacheLineSize);
+ addi(base, base, zicboz_block_size);
bge(cnt, tmp1, loop);
}
@@ -6054,6 +5955,62 @@ void MacroAssembler::java_round_double(Register dst, FloatRegister src, FloatReg
bind(done);
}
+// Helper routine processing the slow path of NaN when converting float to float16
+void MacroAssembler::float_to_float16_NaN(Register dst, FloatRegister src,
+ Register tmp1, Register tmp2) {
+ fmv_x_w(dst, src);
+
+ // Float (32 bits)
+ // Bit: 31 30 to 23 22 to 0
+ // +---+------------------+-----------------------------+
+ // | S | Exponent | Mantissa (Fraction) |
+ // +---+------------------+-----------------------------+
+ // 1 bit 8 bits 23 bits
+ //
+ // Float (16 bits)
+ // Bit: 15 14 to 10 9 to 0
+ // +---+----------------+------------------+
+ // | S | Exponent | Mantissa |
+ // +---+----------------+------------------+
+ // 1 bit 5 bits 10 bits
+ const int fp_sign_bits = 1;
+ const int fp32_bits = 32;
+ const int fp32_exponent_bits = 8;
+ const int fp32_mantissa_1st_part_bits = 10;
+ const int fp32_mantissa_2nd_part_bits = 9;
+ const int fp32_mantissa_3rd_part_bits = 4;
+ const int fp16_exponent_bits = 5;
+ const int fp16_mantissa_bits = 10;
+
+ // preserve the sign bit and exponent, clear mantissa.
+ srai(tmp2, dst, fp32_bits - fp_sign_bits - fp16_exponent_bits);
+ slli(tmp2, tmp2, fp16_mantissa_bits);
+
+ // Preserve high order bit of float NaN in the
+ // binary16 result NaN (tenth bit); OR in remaining
+ // bits into lower 9 bits of binary 16 significand.
+ // | (doppel & 0x007f_e000) >> 13 // 10 bits
+ // | (doppel & 0x0000_1ff0) >> 4 // 9 bits
+ // | (doppel & 0x0000_000f)); // 4 bits
+ //
+ // Check j.l.Float.floatToFloat16 for more information.
+ // 10 bits
+ int left_shift = fp_sign_bits + fp32_exponent_bits + 32;
+ int right_shift = left_shift + fp32_mantissa_2nd_part_bits + fp32_mantissa_3rd_part_bits;
+ slli(tmp1, dst, left_shift);
+ srli(tmp1, tmp1, right_shift);
+ orr(tmp2, tmp2, tmp1);
+ // 9 bits
+ left_shift += fp32_mantissa_1st_part_bits;
+ right_shift = left_shift + fp32_mantissa_3rd_part_bits;
+ slli(tmp1, dst, left_shift);
+ srli(tmp1, tmp1, right_shift);
+ orr(tmp2, tmp2, tmp1);
+ // 4 bits
+ andi(tmp1, dst, 0xf);
+ orr(dst, tmp2, tmp1);
+}
+
#define FCVT_SAFE(FLOATCVT, FLOATSIG) \
void MacroAssembler::FLOATCVT##_safe(Register dst, FloatRegister src, Register tmp) { \
Label done; \
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
index 6276663762a..13b70d5dbd7 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
@@ -44,7 +44,7 @@ class MacroAssembler: public Assembler {
MacroAssembler(CodeBuffer* code) : Assembler(code) {}
- void safepoint_poll(Label& slow_path, bool at_return, bool acquire, bool in_nmethod, Register tmp_reg = t0);
+ void safepoint_poll(Label& slow_path, bool at_return, bool in_nmethod, Register tmp_reg = t0);
// Alignment
int align(int modulus, int extra_offset = 0);
@@ -1240,7 +1240,7 @@ class MacroAssembler: public Assembler {
void far_jump(const Address &entry, Register tmp = t1);
static int far_branch_size() {
- return 2 * 4; // auipc + jalr, see far_call() & far_jump()
+ return 2 * MacroAssembler::instruction_size; // auipc + jalr, see far_call() & far_jump()
}
void load_byte_map_base(Register reg);
@@ -1384,10 +1384,6 @@ class MacroAssembler: public Assembler {
void adc(Register dst, Register src1, Register src2, Register carry);
void add2_with_carry(Register final_dest_hi, Register dest_hi, Register dest_lo,
Register src1, Register src2, Register carry);
- void multiply_32_x_32_loop(Register x, Register xstart, Register x_xstart,
- Register y, Register y_idx, Register z,
- Register carry, Register product,
- Register idx, Register kdx);
void multiply_64_x_64_loop(Register x, Register xstart, Register x_xstart,
Register y, Register y_idx, Register z,
Register carry, Register product,
@@ -1435,6 +1431,9 @@ class MacroAssembler: public Assembler {
void java_round_float(Register dst, FloatRegister src, FloatRegister ftmp);
void java_round_double(Register dst, FloatRegister src, FloatRegister ftmp);
+ // Helper routine processing the slow path of NaN when converting float to float16
+ void float_to_float16_NaN(Register dst, FloatRegister src, Register tmp1, Register tmp2);
+
// vector load/store unit-stride instructions
void vlex_v(VectorRegister vd, Register base, Assembler::SEW sew, VectorMask vm = unmasked) {
switch (sew) {
@@ -1648,9 +1647,9 @@ class MacroAssembler: public Assembler {
public:
enum {
// movptr
- movptr1_instruction_size = 6 * instruction_size, // lui, addi, slli, addi, slli, addi. See movptr1().
- movptr2_instruction_size = 5 * instruction_size, // lui, lui, slli, add, addi. See movptr2().
- load_pc_relative_instruction_size = 2 * instruction_size // auipc, ld
+ movptr1_instruction_size = 6 * MacroAssembler::instruction_size, // lui, addi, slli, addi, slli, addi. See movptr1().
+ movptr2_instruction_size = 5 * MacroAssembler::instruction_size, // lui, lui, slli, add, addi. See movptr2().
+ load_pc_relative_instruction_size = 2 * MacroAssembler::instruction_size // auipc, ld
};
static bool is_load_pc_relative_at(address branch);
@@ -1705,11 +1704,11 @@ class MacroAssembler: public Assembler {
// addi/jalr/load
static bool check_movptr1_data_dependency(address instr) {
address lui = instr;
- address addi1 = lui + instruction_size;
- address slli1 = addi1 + instruction_size;
- address addi2 = slli1 + instruction_size;
- address slli2 = addi2 + instruction_size;
- address last_instr = slli2 + instruction_size;
+ address addi1 = lui + MacroAssembler::instruction_size;
+ address slli1 = addi1 + MacroAssembler::instruction_size;
+ address addi2 = slli1 + MacroAssembler::instruction_size;
+ address slli2 = addi2 + MacroAssembler::instruction_size;
+ address last_instr = slli2 + MacroAssembler::instruction_size;
return extract_rs1(addi1) == extract_rd(lui) &&
extract_rs1(addi1) == extract_rd(addi1) &&
extract_rs1(slli1) == extract_rd(addi1) &&
@@ -1729,10 +1728,10 @@ class MacroAssembler: public Assembler {
// addi/jalr/load
static bool check_movptr2_data_dependency(address instr) {
address lui1 = instr;
- address lui2 = lui1 + instruction_size;
- address slli = lui2 + instruction_size;
- address add = slli + instruction_size;
- address last_instr = add + instruction_size;
+ address lui2 = lui1 + MacroAssembler::instruction_size;
+ address slli = lui2 + MacroAssembler::instruction_size;
+ address add = slli + MacroAssembler::instruction_size;
+ address last_instr = add + MacroAssembler::instruction_size;
return extract_rd(add) == extract_rd(lui2) &&
extract_rs1(add) == extract_rd(lui2) &&
extract_rs2(add) == extract_rd(slli) &&
@@ -1746,7 +1745,7 @@ class MacroAssembler: public Assembler {
// srli
static bool check_li16u_data_dependency(address instr) {
address lui = instr;
- address srli = lui + instruction_size;
+ address srli = lui + MacroAssembler::instruction_size;
return extract_rs1(srli) == extract_rd(lui) &&
extract_rs1(srli) == extract_rd(srli);
@@ -1757,7 +1756,7 @@ class MacroAssembler: public Assembler {
// addiw
static bool check_li32_data_dependency(address instr) {
address lui = instr;
- address addiw = lui + instruction_size;
+ address addiw = lui + MacroAssembler::instruction_size;
return extract_rs1(addiw) == extract_rd(lui) &&
extract_rs1(addiw) == extract_rd(addiw);
@@ -1768,7 +1767,7 @@ class MacroAssembler: public Assembler {
// jalr/addi/load/float_load
static bool check_pc_relative_data_dependency(address instr) {
address auipc = instr;
- address last_instr = auipc + instruction_size;
+ address last_instr = auipc + MacroAssembler::instruction_size;
return extract_rs1(last_instr) == extract_rd(auipc);
}
@@ -1778,7 +1777,7 @@ class MacroAssembler: public Assembler {
// load
static bool check_load_pc_relative_data_dependency(address instr) {
address auipc = instr;
- address load = auipc + instruction_size;
+ address load = auipc + MacroAssembler::instruction_size;
return extract_rd(load) == extract_rd(auipc) &&
extract_rs1(load) == extract_rd(load);
diff --git a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp
index 39b6737631d..d770999df96 100644
--- a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp
+++ b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp
@@ -93,14 +93,60 @@ void MethodHandles::verify_klass(MacroAssembler* _masm,
void MethodHandles::verify_ref_kind(MacroAssembler* _masm, int ref_kind, Register member_reg, Register temp) {}
+void MethodHandles::verify_method(MacroAssembler* _masm, Register method, vmIntrinsics::ID iid) {
+ BLOCK_COMMENT("verify_method {");
+ __ verify_method_ptr(method);
+ if (VerifyMethodHandles) {
+ Label L_ok;
+ assert_different_registers(method, t0, t1);
+ const Register method_holder = t1;
+ __ load_method_holder(method_holder, method);
+
+ switch (iid) {
+ case vmIntrinsicID::_invokeBasic:
+ // Require compiled LambdaForm class to be fully initialized.
+ __ lbu(t0, Address(method_holder, InstanceKlass::init_state_offset()));
+ __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore);
+ __ mv(t1, InstanceKlass::fully_initialized);
+ __ beq(t0, t1, L_ok);
+ break;
+ case vmIntrinsicID::_linkToStatic:
+ __ clinit_barrier(method_holder, t0, &L_ok);
+ break;
+
+ case vmIntrinsicID::_linkToVirtual:
+ case vmIntrinsicID::_linkToSpecial:
+ case vmIntrinsicID::_linkToInterface:
+ // Class initialization check is too strong here. Just ensure that class initialization has been initiated.
+ __ lbu(t0, Address(method_holder, InstanceKlass::init_state_offset()));
+ __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore);
+ __ mv(t1, InstanceKlass::being_initialized);
+ __ bge(t0, t1, L_ok);
+
+ // init_state check failed, but it may be an abstract interface method
+ __ lhu(t0, Address(method, Method::access_flags_offset()));
+ __ test_bit(t1, t0, exact_log2(JVM_ACC_ABSTRACT));
+ __ bnez(t1, L_ok);
+ break;
+
+ default:
+ fatal("unexpected intrinsic %d: %s", vmIntrinsics::as_int(iid), vmIntrinsics::name_at(iid));
+ }
+
+ // Method holder init state check failed for a concrete method.
+ __ stop("Method holder klass is not initialized");
+ __ BIND(L_ok);
+ }
+ BLOCK_COMMENT("} verify_method");
+}
#endif //ASSERT
void MethodHandles::jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp,
- bool for_compiler_entry) {
+ bool for_compiler_entry, vmIntrinsics::ID iid) {
assert(method == xmethod, "interpreter calling convention");
Label L_no_such_method;
__ beqz(xmethod, L_no_such_method);
- __ verify_method_ptr(method);
+ verify_method(_masm, method, iid);
if (!for_compiler_entry && JvmtiExport::can_post_interpreter_events()) {
Label run_compiled_code;
@@ -158,7 +204,7 @@ void MethodHandles::jump_to_lambda_form(MacroAssembler* _masm,
__ BIND(L);
}
- jump_from_method_handle(_masm, method_temp, temp2, for_compiler_entry);
+ jump_from_method_handle(_masm, method_temp, temp2, for_compiler_entry, vmIntrinsics::_invokeBasic);
BLOCK_COMMENT("} jump_to_lambda_form");
}
@@ -437,8 +483,7 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm,
// After figuring out which concrete method to call, jump into it.
// Note that this works in the interpreter with no data motion.
// But the compiled version will require that r2_recv be shifted out.
- __ verify_method_ptr(xmethod);
- jump_from_method_handle(_masm, xmethod, temp1, for_compiler_entry);
+ jump_from_method_handle(_masm, xmethod, temp1, for_compiler_entry, iid);
if (iid == vmIntrinsics::_linkToInterface) {
__ bind(L_incompatible_class_change_error);
__ far_jump(RuntimeAddress(SharedRuntime::throw_IncompatibleClassChangeError_entry()));
diff --git a/src/hotspot/cpu/riscv/methodHandles_riscv.hpp b/src/hotspot/cpu/riscv/methodHandles_riscv.hpp
index 6017b26c66d..ffc3b3ab676 100644
--- a/src/hotspot/cpu/riscv/methodHandles_riscv.hpp
+++ b/src/hotspot/cpu/riscv/methodHandles_riscv.hpp
@@ -39,6 +39,8 @@ enum /* platform_dependent_constants */ {
Register obj, vmClassID klass_id,
const char* error_message = "wrong klass") NOT_DEBUG_RETURN;
+ static void verify_method(MacroAssembler* _masm, Register method, vmIntrinsics::ID iid) NOT_DEBUG_RETURN;
+
static void verify_method_handle(MacroAssembler* _masm, Register mh_reg) {
verify_klass(_masm, mh_reg, VM_CLASS_ID(java_lang_invoke_MethodHandle),
"reference is a MH");
@@ -49,7 +51,7 @@ enum /* platform_dependent_constants */ {
// Similar to InterpreterMacroAssembler::jump_from_interpreted.
// Takes care of special dispatch from single stepping too.
static void jump_from_method_handle(MacroAssembler* _masm, Register method, Register temp,
- bool for_compiler_entry);
+ bool for_compiler_entry, vmIntrinsics::ID iid);
static void jump_to_lambda_form(MacroAssembler* _masm,
Register recv, Register method_temp,
diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
index 31947b520d0..50076d7c8ce 100644
--- a/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
+++ b/src/hotspot/cpu/riscv/nativeInst_riscv.cpp
@@ -33,6 +33,7 @@
#include "runtime/safepoint.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
+#include "utilities/align.hpp"
#include "utilities/ostream.hpp"
#ifdef COMPILER1
#include "c1/c1_Runtime1.hpp"
@@ -46,128 +47,111 @@ bool NativeInstruction::is_call_at(address addr) {
}
//-----------------------------------------------------------------------------
-// NativeFarCall
-//
-// Implements direct far calling loading an address from the stub section version of reloc call.
-
-class NativeFarCall: public NativeInstruction {
- public:
- enum RISCV_specific_constants {
- return_address_offset = 3 * NativeInstruction::instruction_size, // auipc + ld + jalr
- };
-
- address instruction_address() const { return addr_at(0); }
- address next_instruction_address() const { return addr_at(return_address_offset); }
- address return_address() const { return addr_at(return_address_offset); }
- address destination() const;
- address reloc_destination(address orig_address);
-
- void set_destination(address dest);
- void verify();
- void print();
-
- bool set_destination_mt_safe(address dest, bool assert_lock = true);
- bool reloc_set_destination(address dest);
-
- private:
- address stub_address();
-
- static void set_stub_address_destination_at(address dest, address value);
- static address stub_address_destination_at(address src);
- public:
-
- static NativeFarCall* at(address addr);
- static bool is_at(address addr);
- static bool is_call_before(address return_address);
-};
+// NativeCall
-address NativeFarCall::destination() const {
+address NativeCall::destination() const {
address addr = instruction_address();
- assert(NativeFarCall::is_at(addr), "unexpected code at call site");
+ assert(NativeCall::is_at(addr), "unexpected code at call site");
- address destination = MacroAssembler::target_addr_for_insn(addr);
+ address stub_addr = MacroAssembler::target_addr_for_insn(addr);
CodeBlob* cb = CodeCache::find_blob(addr);
- assert(cb && cb->is_nmethod(), "sanity");
+ assert(cb != nullptr && cb->is_nmethod(), "nmethod expected");
nmethod *nm = (nmethod *)cb;
- assert(nm != nullptr, "Sanity");
- assert(nm->stub_contains(destination), "Sanity");
- assert(destination != nullptr, "Sanity");
- return stub_address_destination_at(destination);
+ assert(nm->stub_contains(stub_addr), "Sanity");
+ assert(stub_addr != nullptr, "Sanity");
+
+ return stub_address_destination_at(stub_addr);
}
-address NativeFarCall::reloc_destination(address orig_address) {
+address NativeCall::reloc_destination() {
address call_addr = instruction_address();
+ assert(NativeCall::is_at(call_addr), "unexpected code at call site");
CodeBlob *code = CodeCache::find_blob(call_addr);
assert(code != nullptr, "Could not find the containing code blob");
address stub_addr = nullptr;
- if (code != nullptr && code->is_nmethod()) {
- stub_addr = trampoline_stub_Relocation::get_trampoline_for(call_addr, (nmethod*)code);
+ if (code->is_nmethod()) {
+ // TODO: Need to revisit this when porting the AOT features.
+ stub_addr = trampoline_stub_Relocation::get_trampoline_for(call_addr, code->as_nmethod());
+ assert(stub_addr != nullptr, "Sanity");
}
- if (stub_addr != nullptr) {
- stub_addr = MacroAssembler::target_addr_for_insn(call_addr);
- }
return stub_addr;
}
-void NativeFarCall::set_destination(address dest) {
- address addr = instruction_address();
- assert(NativeFarCall::is_at(addr), "unexpected code at call site");
- Unimplemented();
-}
-
-void NativeFarCall::verify() {
- assert(NativeFarCall::is_at(instruction_address()), "unexpected code at call site");
+void NativeCall::verify() {
+ assert(NativeCall::is_at(instruction_address()), "unexpected code at call site");
}
-void NativeFarCall::print() {
- assert(NativeFarCall::is_at(instruction_address()), "unexpected code at call site");
- tty->print_cr(PTR_FORMAT ": auipc,ld,jalr x1, offset/reg, ", p2i(addr_at(0)));
+void NativeCall::print() {
+ assert(NativeCall::is_at(instruction_address()), "unexpected code at call site");
+ tty->print_cr(PTR_FORMAT ": auipc,ld,jalr x1, offset/reg, ", p2i(instruction_address()));
+}
+
+void NativeCall::optimize_call(address dest, bool mt_safe) {
+ // Skip over auipc + ld
+ address jmp_ins_pc = instruction_address() + 2 * NativeInstruction::instruction_size;
+ // Rutime calls may be unaligned, but they are never changed after relocation.
+ assert(!mt_safe || is_aligned(jmp_ins_pc, NativeInstruction::instruction_size), "Must be naturally aligned: %p", jmp_ins_pc);
+ // If reachable use JAL
+ if (Assembler::reachable_from_branch_at(jmp_ins_pc, dest)) {
+ int64_t distance = dest - jmp_ins_pc;
+ uint32_t new_jal = Assembler::encode_jal(ra, distance);
+ Atomic::store((uint32_t *)jmp_ins_pc, new_jal);
+ } else if (!MacroAssembler::is_jalr_at(jmp_ins_pc)) { // The jalr is always identical: jalr ra, 0(t1)
+ uint32_t new_jalr = Assembler::encode_jalr(ra, t1, 0);
+ Atomic::store((uint32_t *)jmp_ins_pc, new_jalr);
+ } else {
+ // No change to instruction stream
+ return;
+ }
+ // We changed instruction stream
+ if (mt_safe) {
+ // IC invalidate provides a leading full fence, it thus happens after we changed the instruction stream.
+ ICache::invalidate_range(jmp_ins_pc, NativeInstruction::instruction_size);
+ }
}
-bool NativeFarCall::set_destination_mt_safe(address dest, bool assert_lock) {
- assert(NativeFarCall::is_at(addr_at(0)), "unexpected code at call site");
- assert(!assert_lock ||
- (CodeCache_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) ||
- CompiledICLocker::is_safe(addr_at(0)),
+bool NativeCall::set_destination_mt_safe(address dest) {
+ assert(NativeCall::is_at(instruction_address()), "unexpected code at call site");
+ assert((CodeCache_lock->is_locked() || SafepointSynchronize::is_at_safepoint()) ||
+ CompiledICLocker::is_safe(instruction_address()),
"concurrent code patching");
- address call_addr = addr_at(0);
- assert(NativeFarCall::is_at(call_addr), "unexpected code at call site");
-
address stub_addr = stub_address();
+ assert(stub_addr != nullptr, "No stub?");
+ set_stub_address_destination_at(stub_addr, dest); // release
+ // optimize_call happens after we stored new address in addr stub.
+ // patches jalr -> jal/jal -> jalr depending on dest
+ optimize_call(dest, true);
- if (stub_addr != nullptr) {
- set_stub_address_destination_at(stub_addr, dest);
- return true;
- }
-
- return false;
+ return true;
}
-bool NativeFarCall::reloc_set_destination(address dest) {
- address call_addr = addr_at(0);
- assert(NativeFarCall::is_at(call_addr), "unexpected code at call site");
+// The argument passed in is the address to the stub containing the destination
+bool NativeCall::reloc_set_destination(address stub_addr) {
+ address call_addr = instruction_address();
+ assert(NativeCall::is_at(call_addr), "unexpected code at call site");
CodeBlob *code = CodeCache::find_blob(call_addr);
assert(code != nullptr, "Could not find the containing code blob");
- address stub_addr = nullptr;
- if (code != nullptr && code->is_nmethod()) {
- stub_addr = trampoline_stub_Relocation::get_trampoline_for(call_addr, (nmethod*)code);
- }
+ if (code->is_nmethod()) {
+ // TODO: Need to revisit this when porting the AOT features.
+ assert(stub_addr != nullptr, "Sanity");
+ assert(stub_addr == trampoline_stub_Relocation::get_trampoline_for(call_addr, code->as_nmethod()), "Sanity");
+ MacroAssembler::pd_patch_instruction_size(call_addr, stub_addr); // patches auipc + ld to stub_addr
- if (stub_addr != nullptr) {
- MacroAssembler::pd_patch_instruction_size(call_addr, stub_addr);
+ address dest = stub_address_destination_at(stub_addr);
+ optimize_call(dest, false); // patches jalr -> jal/jal -> jalr depending on dest
}
return true;
}
-void NativeFarCall::set_stub_address_destination_at(address dest, address value) {
+void NativeCall::set_stub_address_destination_at(address dest, address value) {
assert_cond(dest != nullptr);
assert_cond(value != nullptr);
@@ -175,31 +159,24 @@ void NativeFarCall::set_stub_address_destination_at(address dest, address value)
OrderAccess::release();
}
-address NativeFarCall::stub_address_destination_at(address src) {
+address NativeCall::stub_address_destination_at(address src) {
assert_cond(src != nullptr);
address dest = (address)get_data64_at(src);
return dest;
}
-address NativeFarCall::stub_address() {
- address call_addr = addr_at(0);
+address NativeCall::stub_address() {
+ address call_addr = instruction_address();
CodeBlob *code = CodeCache::find_blob(call_addr);
assert(code != nullptr, "Could not find the containing code blob");
- address dest = MacroAssembler::pd_call_destination(call_addr);
- assert(code->contains(dest), "Sanity");
- return dest;
-}
-
-NativeFarCall* NativeFarCall::at(address addr) {
- assert_cond(addr != nullptr);
- assert(NativeFarCall::is_at(addr), "unexpected code at call site: %p", addr);
- NativeFarCall* call = (NativeFarCall*)(addr);
- return call;
+ address stub_addr = MacroAssembler::target_addr_for_insn(call_addr);
+ assert(code->contains(stub_addr), "Sanity");
+ return stub_addr;
}
-bool NativeFarCall::is_at(address addr) {
+bool NativeCall::is_at(address addr) {
assert_cond(addr != nullptr);
const int instr_size = NativeInstruction::instruction_size;
if (MacroAssembler::is_auipc_at(addr) &&
@@ -209,65 +186,23 @@ bool NativeFarCall::is_at(address addr) {
(MacroAssembler::extract_rd(addr + instr_size) == x6) &&
(MacroAssembler::extract_rs1(addr + instr_size) == x6) &&
(MacroAssembler::extract_rs1(addr + 2 * instr_size) == x6) &&
- (MacroAssembler::extract_rd(addr + 2 * instr_size) == x1)) {
+ (MacroAssembler::extract_rd(addr + 2 * instr_size) == x1)) {
+ return true;
+ }
+ if (MacroAssembler::is_auipc_at(addr) &&
+ MacroAssembler::is_ld_at(addr + instr_size) &&
+ MacroAssembler::is_jal_at(addr + 2 * instr_size) &&
+ (MacroAssembler::extract_rd(addr) == x6) &&
+ (MacroAssembler::extract_rd(addr + instr_size) == x6) &&
+ (MacroAssembler::extract_rs1(addr + instr_size) == x6) &&
+ (MacroAssembler::extract_rd(addr + 2 * instr_size) == x1)) {
return true;
}
return false;
}
-bool NativeFarCall::is_call_before(address return_address) {
- return NativeFarCall::is_at(return_address - return_address_offset);
-}
-
-//-----------------------------------------------------------------------------
-// NativeCall
-
-address NativeCall::instruction_address() const {
- return NativeFarCall::at(addr_at(0))->instruction_address();
-}
-
-address NativeCall::next_instruction_address() const {
- return NativeFarCall::at(addr_at(0))->next_instruction_address();
-}
-
-address NativeCall::return_address() const {
- return NativeFarCall::at(addr_at(0))->return_address();
-}
-
-address NativeCall::destination() const {
- return NativeFarCall::at(addr_at(0))->destination();
-}
-
-address NativeCall::reloc_destination(address orig_address) {
- return NativeFarCall::at(addr_at(0))->reloc_destination(orig_address);
-}
-
-void NativeCall::set_destination(address dest) {
- NativeFarCall::at(addr_at(0))->set_destination(dest);
-}
-
-void NativeCall::verify() {
- NativeFarCall::at(addr_at(0))->verify();;
-}
-
-void NativeCall::print() {
- NativeFarCall::at(addr_at(0))->print();;
-}
-
-bool NativeCall::set_destination_mt_safe(address dest, bool assert_lock) {
- return NativeFarCall::at(addr_at(0))->set_destination_mt_safe(dest, assert_lock);
-}
-
-bool NativeCall::reloc_set_destination(address dest) {
- return NativeFarCall::at(addr_at(0))->reloc_set_destination(dest);
-}
-
-bool NativeCall::is_at(address addr) {
- return NativeFarCall::is_at(addr);
-}
-
bool NativeCall::is_call_before(address return_address) {
- return NativeFarCall::is_call_before(return_address);
+ return NativeCall::is_at(return_address - NativeCall::instruction_size);
}
NativeCall* nativeCall_at(address addr) {
@@ -280,7 +215,7 @@ NativeCall* nativeCall_at(address addr) {
NativeCall* nativeCall_before(address return_address) {
assert_cond(return_address != nullptr);
NativeCall* call = nullptr;
- call = (NativeCall*)(return_address - NativeFarCall::return_address_offset);
+ call = (NativeCall*)(return_address - NativeCall::instruction_size);
DEBUG_ONLY(call->verify());
return call;
}
@@ -339,19 +274,6 @@ void NativeMovConstReg::print() {
p2i(instruction_address()), data());
}
-//-------------------------------------------------------------------
-
-int NativeMovRegMem::offset() const {
- Unimplemented();
- return 0;
-}
-
-void NativeMovRegMem::set_offset(int x) { Unimplemented(); }
-
-void NativeMovRegMem::verify() {
- Unimplemented();
-}
-
//--------------------------------------------------------------------------------
void NativeJump::verify() { }
@@ -432,7 +354,9 @@ void NativeIllegalInstruction::insert(address code_pos) {
}
bool NativeInstruction::is_stop() {
- return uint_at(0) == 0xc0101073; // an illegal instruction, 'csrrw x0, time, x0'
+ // an illegal instruction, 'csrrw x0, time, x0'
+ uint32_t encoded = Assembler::encode_csrrw(x0, Assembler::time, x0);
+ return uint_at(0) == encoded;
}
//-------------------------------------------------------------------
@@ -481,6 +405,8 @@ void NativeGeneralJump::insert_unconditional(address code_pos, address entry) {
MacroAssembler a(&cb);
Assembler::IncompressibleScope scope(&a); // Fixed length: see NativeGeneralJump::get_instruction_size()
+ MacroAssembler::assert_alignment(code_pos);
+
int32_t offset = 0;
a.movptr(t1, entry, offset, t0); // lui, lui, slli, add
a.jr(t1, offset); // jalr
@@ -512,6 +438,7 @@ bool NativePostCallNop::decode(int32_t& oopmap_slot, int32_t& cb_offset) const {
}
bool NativePostCallNop::patch(int32_t oopmap_slot, int32_t cb_offset) {
+ MacroAssembler::assert_alignment(addr_at(4));
if (((oopmap_slot & 0xff) != oopmap_slot) || ((cb_offset & 0xffffff) != cb_offset)) {
return false; // cannot encode
}
@@ -523,14 +450,17 @@ bool NativePostCallNop::patch(int32_t oopmap_slot, int32_t cb_offset) {
return true; // successfully encoded
}
-void NativeDeoptInstruction::verify() {
+bool NativeDeoptInstruction::is_deopt_at(address instr) {
+ assert(instr != nullptr, "Must be");
+ uint32_t value = Assembler::ld_instr(instr);
+ uint32_t encoded = Assembler::encode_csrrw(x0, Assembler::instret, x0);
+ return value == encoded;
}
// Inserts an undefined instruction at a given pc
void NativeDeoptInstruction::insert(address code_pos) {
- // 0xc0201073 encodes CSRRW x0, instret, x0
- uint32_t insn = 0xc0201073;
- uint32_t *pos = (uint32_t *) code_pos;
- *pos = insn;
+ MacroAssembler::assert_alignment(code_pos);
+ uint32_t encoded = Assembler::encode_csrrw(x0, Assembler::instret, x0);
+ Assembler::sd_instr(code_pos, encoded);
ICache::invalidate_range(code_pos, 4);
}
diff --git a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp
index d8f5fa57816..a50f14d7923 100644
--- a/src/hotspot/cpu/riscv/nativeInst_riscv.hpp
+++ b/src/hotspot/cpu/riscv/nativeInst_riscv.hpp
@@ -37,7 +37,7 @@
// - NativeInstruction
// - - NativeCall
// - - NativeMovConstReg
-// - - NativeMovRegMem
+// - - NativeMovRegMem - Unimplemented
// - - NativeJump
// - - NativeGeneralJump
// - - NativeIllegalInstruction
@@ -94,7 +94,6 @@ class NativeInstruction {
static uint64_t get_data64_at(address src) { return Bytes::get_native_u8(src); }
public:
-
inline friend NativeInstruction* nativeInstruction_at(address addr);
static bool maybe_cpool_ref(address instr) {
@@ -112,6 +111,7 @@ NativeCall* nativeCall_before(address return_address);
// The NativeCall is an abstraction for accessing/manipulating native
// call instructions (used to manipulate inline caches, primitive &
// DSO calls, etc.).
+// NativeCall is reloc call on RISC-V. See MacroAssembler::reloc_call.
class NativeCall: private NativeInstruction {
// private: when common code is using byte_size()
private:
@@ -119,34 +119,48 @@ class NativeCall: private NativeInstruction {
// Use byte_size() as it can be changed in runtime
// Since instruction_size exists on NativeInstruction we need
// to overload and hide it.
- instruction_size = 3 * Assembler::instruction_size // auipc + ld + jalr
+ instruction_size = 3 * NativeInstruction::instruction_size // auipc + ld + jalr
};
- public:
+ public:
static int byte_size() {
- return 3 * NativeInstruction::instruction_size; // auipc + ld + jalr
+ return NativeCall::instruction_size; // auipc + ld + jalr
}
// Creation
friend NativeCall* nativeCall_at(address addr);
friend NativeCall* nativeCall_before(address return_address);
- address instruction_address() const;
- address next_instruction_address() const;
- address return_address() const;
+ address instruction_address() const { return addr_at(0); }
+ address next_instruction_address() const { return addr_at(NativeCall::instruction_size); }
+ address return_address() const { return addr_at(NativeCall::instruction_size); }
address destination() const;
- address reloc_destination(address orig_address);
+ address reloc_destination();
void verify_alignment() {} // do nothing on riscv
void verify();
void print();
- void set_destination(address dest);
- bool set_destination_mt_safe(address dest, bool assert_lock = true);
+ void set_destination(address dest) { Unimplemented(); }
+ // patch stub to target address of the reloc call
+ bool set_destination_mt_safe(address dest);
+ // patch reloc call to stub address
bool reloc_set_destination(address dest);
static bool is_at(address addr);
static bool is_call_before(address return_address);
+
+ private:
+ // return stub address, without checking stub address in locs
+ address stub_address();
+ // set target address at stub
+ static void set_stub_address_destination_at(address dest, address value);
+ // return target address at stub
+ static address stub_address_destination_at(address src);
+ // We either have a jalr or jal depending on distance to old destination.
+ // This method emits a new jal if new destination is within jal reach.
+ // Otherwise restores the jalr which can reach any destination.
+ void optimize_call(address dest, bool mt_safe = true);
};
// An interface for accessing/manipulating native mov reg, imm instructions.
@@ -219,38 +233,18 @@ inline NativeMovConstReg* nativeMovConstReg_before(address addr) {
// NativeMovRegMem to keep some compilers happy.
class NativeMovRegMem: public NativeInstruction {
public:
- enum RISCV_specific_constants {
- instruction_size = NativeInstruction::instruction_size,
- instruction_offset = 0,
- data_offset = 0,
- next_instruction_offset = NativeInstruction::instruction_size
- };
-
- int instruction_start() const { return instruction_offset; }
-
- address instruction_address() const { return addr_at(instruction_offset); }
+ int num_bytes_to_end_of_patch() const { Unimplemented(); return 0; }
- int num_bytes_to_end_of_patch() const { return instruction_offset + instruction_size; }
+ int offset() const { Unimplemented(); return 0; }
- int offset() const;
+ void set_offset(int x) { Unimplemented(); }
- void set_offset(int x);
-
- void add_offset_in_bytes(int add_offset) {
- set_offset(offset() + add_offset);
- }
-
- void verify();
- void print();
-
- private:
- inline friend NativeMovRegMem* nativeMovRegMem_at(address addr);
+ void add_offset_in_bytes(int add_offset) { Unimplemented(); }
};
inline NativeMovRegMem* nativeMovRegMem_at(address addr) {
- NativeMovRegMem* test = (NativeMovRegMem*)(addr - NativeMovRegMem::instruction_offset);
- DEBUG_ONLY(test->verify());
- return test;
+ Unimplemented();
+ return (NativeMovRegMem*)nullptr;
}
class NativeJump: public NativeInstruction {
@@ -363,14 +357,7 @@ class NativeDeoptInstruction: public NativeInstruction {
address instruction_address() const { return addr_at(instruction_offset); }
address next_instruction_address() const { return addr_at(instruction_size); }
- void verify();
-
- static bool is_deopt_at(address instr) {
- assert(instr != nullptr, "");
- uint32_t value = Assembler::ld_instr(instr);
- // 0xc0201073 encodes CSRRW x0, instret, x0
- return value == 0xc0201073;
- }
+ static bool is_deopt_at(address instr);
// MT-safe patching
static void insert(address code_pos);
diff --git a/src/hotspot/cpu/riscv/relocInfo_riscv.cpp b/src/hotspot/cpu/riscv/relocInfo_riscv.cpp
index 7bee372b0ef..ccd8b891996 100644
--- a/src/hotspot/cpu/riscv/relocInfo_riscv.cpp
+++ b/src/hotspot/cpu/riscv/relocInfo_riscv.cpp
@@ -72,13 +72,12 @@ void Relocation::pd_set_data_value(address x, bool verify_only) {
}
address Relocation::pd_call_destination(address orig_addr) {
- assert(is_call(), "should be an address instruction here");
+ assert(is_call(), "should be a call here");
if (NativeCall::is_at(addr())) {
- return nativeCall_at(addr())->reloc_destination(orig_addr);
+ return nativeCall_at(addr())->reloc_destination();
}
- // Non call reloc
+
if (orig_addr != nullptr) {
- // the extracted address from the instructions in address orig_addr
address new_addr = MacroAssembler::pd_call_destination(orig_addr);
// If call is branch to self, don't try to relocate it, just leave it
// as branch to self. This happens during code generation if the code
@@ -87,20 +86,19 @@ address Relocation::pd_call_destination(address orig_addr) {
new_addr = (new_addr == orig_addr) ? addr() : new_addr;
return new_addr;
}
+
return MacroAssembler::pd_call_destination(addr());
}
void Relocation::pd_set_call_destination(address x) {
- assert(is_call(), "should be an address instruction here");
+ assert(is_call(), "should be a call here");
if (NativeCall::is_at(addr())) {
- NativeCall* nc = nativeCall_at(addr());
- if (nc->reloc_set_destination(x)) {
- return;
- }
+ NativeCall* call = nativeCall_at(addr());
+ call->reloc_set_destination(x);
+ } else {
+ MacroAssembler::pd_patch_instruction_size(addr(), x);
+ assert(pd_call_destination(addr()) == x, "fail in reloc");
}
- MacroAssembler::pd_patch_instruction_size(addr(), x);
- address pd_call = pd_call_destination(addr());
- assert(pd_call == x, "fail in reloc");
}
address* Relocation::pd_address_in_code() {
diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad
index e838ee184fb..9b7e7138710 100644
--- a/src/hotspot/cpu/riscv/riscv.ad
+++ b/src/hotspot/cpu/riscv/riscv.ad
@@ -1184,6 +1184,8 @@ bool is_CAS(int opcode, bool maybe_volatile)
}
}
+constexpr uint64_t MAJIK_DWORD = 0xabbaabbaabbaabbaull;
+
// predicate controlling translation of CAS
//
// returns true if CAS needs to use an acquiring load otherwise false
@@ -1269,6 +1271,26 @@ int CallDynamicJavaDirectNode::compute_padding(int current_offset) const
return align_up(current_offset, alignment_required()) - current_offset;
}
+int CallRuntimeDirectNode::compute_padding(int current_offset) const
+{
+ return align_up(current_offset, alignment_required()) - current_offset;
+}
+
+int CallLeafDirectNode::compute_padding(int current_offset) const
+{
+ return align_up(current_offset, alignment_required()) - current_offset;
+}
+
+int CallLeafDirectVectorNode::compute_padding(int current_offset) const
+{
+ return align_up(current_offset, alignment_required()) - current_offset;
+}
+
+int CallLeafNoFPDirectNode::compute_padding(int current_offset) const
+{
+ return align_up(current_offset, alignment_required()) - current_offset;
+}
+
//=============================================================================
#ifndef PRODUCT
@@ -1343,10 +1365,15 @@ void MachPrologNode::format(PhaseRegAlloc *ra_, outputStream *st) const {
st->print("# stack bang size=%d\n\t", framesize);
}
- st->print("sd fp, [sp, #%d]\n\t", - 2 * wordSize);
- st->print("sd ra, [sp, #%d]\n\t", - wordSize);
- if (PreserveFramePointer) { st->print("sub fp, sp, #%d\n\t", 2 * wordSize); }
st->print("sub sp, sp, #%d\n\t", framesize);
+ st->print("sd fp, [sp, #%d]\n\t", framesize - 2 * wordSize);
+ st->print("sd ra, [sp, #%d]\n\t", framesize - wordSize);
+ if (PreserveFramePointer) { st->print("add fp, sp, #%d\n\t", framesize); }
+
+ if (VerifyStackAtCalls) {
+ st->print("mv t2, %ld\n\t", MAJIK_DWORD);
+ st->print("sd t2, [sp, #%d]\n\t", framesize - 3 * wordSize);
+ }
if (C->stub_function() == nullptr) {
st->print("ld t0, [guard]\n\t");
@@ -1396,6 +1423,11 @@ void MachPrologNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const {
__ build_frame(framesize);
+ if (VerifyStackAtCalls) {
+ __ mv(t2, MAJIK_DWORD);
+ __ sd(t2, Address(sp, framesize - 3 * wordSize));
+ }
+
if (C->stub_function() == nullptr) {
BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
// Dummy labels for just measuring the code size
@@ -1417,10 +1449,6 @@ void MachPrologNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const {
bs->nmethod_entry_barrier(masm, slow_path, continuation, guard);
}
- if (VerifyStackAtCalls) {
- Unimplemented();
- }
-
C->output()->set_frame_complete(__ offset());
if (C->has_mach_constant_base_node()) {
@@ -1493,7 +1521,7 @@ void MachEpilogNode::emit(C2_MacroAssembler *masm, PhaseRegAlloc *ra_) const {
code_stub = &stub->entry();
}
__ relocate(relocInfo::poll_return_type);
- __ safepoint_poll(*code_stub, true /* at_return */, false /* acquire */, true /* in_nmethod */);
+ __ safepoint_poll(*code_stub, true /* at_return */, true /* in_nmethod */);
}
}
@@ -2271,10 +2299,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;
@@ -2414,7 +2438,13 @@ encode %{
enc_class riscv_enc_call_epilog() %{
if (VerifyStackAtCalls) {
// Check that stack depth is unchanged: find majik cookie on stack
- __ call_Unimplemented();
+ int framesize = ra_->reg2offset_unchecked(OptoReg::add(ra_->_matcher._old_SP, -3 * VMRegImpl::slots_per_word));
+ Label stack_ok;
+ __ ld(t1, Address(sp, framesize));
+ __ mv(t2, MAJIK_DWORD);
+ __ beq(t2, t1, stack_ok);
+ __ stop("MAJIK_DWORD not found");
+ __ bind(stack_ok);
}
%}
@@ -2825,21 +2855,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()
%{
@@ -3843,13 +3858,18 @@ opclass immIorL(immI, immL);
pipeline %{
attributes %{
- // RISC-V instructions are of fixed length
- fixed_size_instructions; // Fixed size instructions TODO does
- max_instructions_per_bundle = 2; // Generic RISC-V 1, Sifive Series 7 2
- // RISC-V instructions come in 32-bit word units
- instruction_unit_size = 4; // An instruction is 4 bytes long
- instruction_fetch_unit_size = 64; // The processor fetches one line
- instruction_fetch_units = 1; // of 64 bytes
+ // RISC-V instructions are of length 2 or 4 bytes.
+ variable_size_instructions;
+ instruction_unit_size = 2;
+
+ // Up to 4 instructions per bundle
+ max_instructions_per_bundle = 4;
+
+ // The RISC-V processor fetches 64 bytes...
+ instruction_fetch_unit_size = 64;
+
+ // ...in one line.
+ instruction_fetch_units = 1;
// List of nop instructions
nops( MachNop );
@@ -4799,18 +4819,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)
%{
@@ -8195,7 +8203,7 @@ instruct unnecessary_membar_volatile_rvtso() %{
ins_cost(0);
size(0);
-
+
format %{ "#@unnecessary_membar_volatile_rvtso (unnecessary so empty encoding)" %}
ins_encode %{
__ block_comment("unnecessary_membar_volatile_rvtso");
@@ -8431,6 +8439,17 @@ instruct castVV(vReg dst)
ins_pipe(pipe_class_empty);
%}
+instruct castVVMask(vRegMask dst)
+%{
+ match(Set dst (CastVV dst));
+
+ size(0);
+ format %{ "# castVV of $dst" %}
+ ins_encode(/* empty encoding */);
+ ins_cost(0);
+ ins_pipe(pipe_class_empty);
+%}
+
// ============================================================================
// Convert Instructions
@@ -8536,7 +8555,7 @@ instruct convD2F_reg(fRegF dst, fRegD src) %{
// single <-> half precision
-instruct convHF2F_reg_reg(fRegF dst, iRegINoSp src, iRegINoSp tmp) %{
+instruct convHF2F_reg_reg(fRegF dst, iRegIorL2I src, iRegINoSp tmp) %{
match(Set dst (ConvHF2F src));
effect(TEMP tmp);
format %{ "fmv.h.x $dst, $src\t# move source from $src to $dst\n\t"
@@ -8565,7 +8584,7 @@ instruct convF2HF_reg_reg(iRegINoSp dst, fRegF src, fRegF ftmp, iRegINoSp xtmp)
instruct reinterpretS2HF(fRegF dst, iRegI src)
%{
match(Set dst (ReinterpretS2HF src));
- format %{ "fmv.h.x $dst, $src" %}
+ format %{ "fmv.h.x $dst, $src\t# reinterpretS2HF" %}
ins_encode %{
__ fmv_h_x($dst$$FloatRegister, $src$$Register);
%}
@@ -8585,7 +8604,7 @@ instruct convF2HFAndS2HF(fRegF dst, fRegF src)
instruct reinterpretHF2S(iRegINoSp dst, fRegF src)
%{
match(Set dst (ReinterpretHF2S src));
- format %{ "fmv.x.h $dst, $src" %}
+ format %{ "fmv.x.h $dst, $src\t# reinterpretHF2S" %}
ins_encode %{
__ fmv_x_h($dst$$Register, $src$$FloatRegister);
%}
@@ -8947,7 +8966,7 @@ instruct encodeKlass_not_null(iRegNNoSp dst, iRegP src) %{
instruct decodeKlass_not_null(iRegPNoSp dst, iRegN src, iRegPNoSp tmp) %{
match(Set dst (DecodeNKlass src));
- effect(TEMP tmp);
+ effect(TEMP_DEF dst, TEMP tmp);
ins_cost(ALU_COST);
format %{ "decode_klass_not_null $dst, $src\t#@decodeKlass_not_null" %}
@@ -10518,6 +10537,7 @@ instruct CallRuntimeDirect(method meth)
ins_encode(riscv_enc_java_to_runtime(meth));
ins_pipe(pipe_class_call);
+ ins_alignment(4);
%}
// Call Runtime Instruction
@@ -10535,6 +10555,7 @@ instruct CallLeafDirect(method meth)
ins_encode(riscv_enc_java_to_runtime(meth));
ins_pipe(pipe_class_call);
+ ins_alignment(4);
%}
// Call Runtime Instruction without safepoint and with vector arguments
@@ -10552,6 +10573,7 @@ instruct CallLeafDirectVector(method meth)
ins_encode(riscv_enc_java_to_runtime(meth));
ins_pipe(pipe_class_call);
+ ins_alignment(4);
%}
// Call Runtime Instruction
@@ -10569,6 +10591,7 @@ instruct CallLeafNoFPDirect(method meth)
ins_encode(riscv_enc_java_to_runtime(meth));
ins_pipe(pipe_class_call);
+ ins_alignment(4);
%}
// ============================================================================
diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad
index 8b5759ce11c..a824f33367c 100644
--- a/src/hotspot/cpu/riscv/riscv_v.ad
+++ b/src/hotspot/cpu/riscv/riscv_v.ad
@@ -110,15 +110,16 @@ source %{
if (vlen < 4) {
return false;
}
+ break;
case Op_VectorCastHF2F:
case Op_VectorCastF2HF:
case Op_AddVHF:
+ case Op_SubVHF:
+ case Op_MulVHF:
case Op_DivVHF:
case Op_MaxVHF:
case Op_MinVHF:
- case Op_MulVHF:
case Op_SqrtVHF:
- case Op_SubVHF:
return UseZvfh;
case Op_FmaVHF:
return UseZvfh && UseFMA;
@@ -146,13 +147,28 @@ source %{
if (!UseRVV) {
return false;
}
+
switch (opcode) {
case Op_SelectFromTwoVector:
// There is no masked version of selectFrom two vector, i.e. selectFrom(av, bv, mask) in vector API.
return false;
+ // Currently, the masked versions of the following 8 Float16 operations are disabled.
+ // When the support for Float16 vector classes is added in VectorAPI and the masked
+ // Float16 IR can be generated, these masked operations will be enabled and relevant
+ // backend support added.
+ case Op_AddVHF:
+ case Op_SubVHF:
+ case Op_MulVHF:
+ case Op_DivVHF:
+ case Op_MaxVHF:
+ case Op_MinVHF:
+ case Op_SqrtVHF:
+ case Op_FmaVHF:
+ return false;
default:
break;
}
+
return match_rule_supported_vector(opcode, vlen, bt);
}
diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
index 391be81c1ae..3b2d7977b7c 100644
--- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
+++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
@@ -213,7 +213,7 @@ void RegisterSaver::restore_live_registers(MacroAssembler* masm) {
// Is vector's size (in bytes) bigger than a size saved by default?
// riscv does not ovlerlay the floating-point registers on vector registers like aarch64.
bool SharedRuntime::is_wide_vector(int size) {
- return UseRVV;
+ return UseRVV && size > 0;
}
// ---------------------------------------------------------------------------
@@ -1004,20 +1004,23 @@ static void gen_continuation_enter(MacroAssembler* masm,
__ bnez(c_rarg2, call_thaw);
- // Make sure the call is patchable
- __ align(NativeInstruction::instruction_size);
-
- const address tr_call = __ reloc_call(resolve);
- if (tr_call == nullptr) {
- fatal("CodeCache is full at gen_continuation_enter");
- }
+ address call_pc;
+ {
+ Assembler::IncompressibleScope scope(masm);
+ // Make sure the call is patchable
+ __ align(NativeInstruction::instruction_size);
- oop_maps->add_gc_map(__ pc() - start, map);
- __ post_call_nop();
+ call_pc = __ reloc_call(resolve);
+ if (call_pc == nullptr) {
+ fatal("CodeCache is full at gen_continuation_enter");
+ }
+ oop_maps->add_gc_map(__ pc() - start, map);
+ __ post_call_nop();
+ }
__ j(exit);
- address stub = CompiledDirectCall::emit_to_interp_stub(masm, tr_call);
+ address stub = CompiledDirectCall::emit_to_interp_stub(masm, call_pc);
if (stub == nullptr) {
fatal("CodeCache is full at gen_continuation_enter");
}
@@ -1036,26 +1039,36 @@ static void gen_continuation_enter(MacroAssembler* masm,
__ bnez(c_rarg2, call_thaw);
- // Make sure the call is patchable
- __ align(NativeInstruction::instruction_size);
+ address call_pc;
+ {
+ Assembler::IncompressibleScope scope(masm);
+ // Make sure the call is patchable
+ __ align(NativeInstruction::instruction_size);
- const address tr_call = __ reloc_call(resolve);
- if (tr_call == nullptr) {
- fatal("CodeCache is full at gen_continuation_enter");
- }
+ call_pc = __ reloc_call(resolve);
+ if (call_pc == nullptr) {
+ fatal("CodeCache is full at gen_continuation_enter");
+ }
- oop_maps->add_gc_map(__ pc() - start, map);
- __ post_call_nop();
+ oop_maps->add_gc_map(__ pc() - start, map);
+ __ post_call_nop();
+ }
__ j(exit);
__ bind(call_thaw);
- ContinuationEntry::_thaw_call_pc_offset = __ pc() - start;
- __ rt_call(CAST_FROM_FN_PTR(address, StubRoutines::cont_thaw()));
- oop_maps->add_gc_map(__ pc() - start, map->deep_copy());
- ContinuationEntry::_return_pc_offset = __ pc() - start;
- __ post_call_nop();
+ // Post call nops must be natural aligned due to cmodx rules.
+ {
+ Assembler::IncompressibleScope scope(masm);
+ __ align(NativeInstruction::instruction_size);
+
+ ContinuationEntry::_thaw_call_pc_offset = __ pc() - start;
+ __ rt_call(CAST_FROM_FN_PTR(address, StubRoutines::cont_thaw()));
+ oop_maps->add_gc_map(__ pc() - start, map->deep_copy());
+ ContinuationEntry::_return_pc_offset = __ pc() - start;
+ __ post_call_nop();
+ }
__ bind(exit);
ContinuationEntry::_cleanup_offset = __ pc() - start;
@@ -1084,7 +1097,7 @@ static void gen_continuation_enter(MacroAssembler* masm,
__ jr(x11); // the exception handler
}
- address stub = CompiledDirectCall::emit_to_interp_stub(masm, tr_call);
+ address stub = CompiledDirectCall::emit_to_interp_stub(masm, call_pc);
if (stub == nullptr) {
fatal("CodeCache is full at gen_continuation_enter");
}
@@ -1117,10 +1130,16 @@ static void gen_continuation_yield(MacroAssembler* masm,
__ mv(c_rarg1, sp);
+ // Post call nops must be natural aligned due to cmodx rules.
+ __ align(NativeInstruction::instruction_size);
+
frame_complete = __ pc() - start;
address the_pc = __ pc();
- __ post_call_nop(); // this must be exactly after the pc value that is pushed into the frame info, we use this nop for fast CodeBlob lookup
+ {
+ Assembler::IncompressibleScope scope(masm);
+ __ post_call_nop(); // this must be exactly after the pc value that is pushed into the frame info, we use this nop for fast CodeBlob lookup
+ }
__ mv(c_rarg0, xthread);
__ set_last_Java_frame(sp, fp, the_pc, t0);
@@ -1777,15 +1796,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
// check for safepoint operation in progress and/or pending suspend requests
{
- // We need an acquire here to ensure that any subsequent load of the
- // global SafepointSynchronize::_state flag is ordered after this load
- // of the thread-local polling word. We don't want this poll to
- // return false (i.e. not safepointing) and a later poll of the global
- // SafepointSynchronize::_state spuriously to return true.
- // This is to avoid a race when we're in a native->Java transition
- // racing the code which wakes up from a safepoint.
-
- __ safepoint_poll(safepoint_in_progress, true /* at_return */, true /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(safepoint_in_progress, true /* at_return */, false /* in_nmethod */);
__ lwu(t0, Address(xthread, JavaThread::suspend_flags_offset()));
__ bnez(t0, safepoint_in_progress);
__ bind(safepoint_in_progress_done);
@@ -2475,7 +2486,7 @@ void SharedRuntime::generate_deopt_blob() {
// EPILOG must remove this many slots.
// RISCV needs two words for RA (return address) and FP (frame pointer).
uint SharedRuntime::in_preserve_stack_slots() {
- return 2 * VMRegImpl::slots_per_word;
+ return 2 * VMRegImpl::slots_per_word + (VerifyStackAtCalls ? 0 : 2) ;
}
uint SharedRuntime::out_preserve_stack_slots() {
diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp
index c58f6bc338d..58796e8892c 100644
--- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp
+++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp
@@ -683,10 +683,11 @@ class StubGenerator: public StubCodeGenerator {
address start = __ pc();
if (UseBlockZeroing) {
- // Ensure count >= 2*CacheLineSize so that it still deserves a cbo.zero
- // after alignment.
+ int zicboz_block_size = VM_Version::zicboz_block_size.value();
+ // Ensure count >= 2 * zicboz_block_size so that it still deserves
+ // a cbo.zero after alignment.
Label small;
- int low_limit = MAX2(2 * CacheLineSize, BlockZeroingLowLimit) / wordSize;
+ int low_limit = MAX2(2 * zicboz_block_size, (int)BlockZeroingLowLimit) / wordSize;
__ mv(tmp1, low_limit);
__ blt(cnt, tmp1, small);
__ zero_dcache_blocks(base, cnt, tmp1, tmp2);
@@ -6354,18 +6355,8 @@ class StubGenerator: public StubCodeGenerator {
__ ret();
__ bind(NaN_SLOW);
- __ fmv_x_w(dst, src);
-
- // preserve the payloads of non-canonical NaNs.
- __ srai(dst, dst, 13);
- // preserve the sign bit.
- __ srai(t1, dst, 13);
- __ slli(t1, t1, 10);
- __ mv(t0, 0x3ff);
- __ orr(t1, t1, t0);
-
- // get the result by merging sign bit and payloads of preserved non-canonical NaNs.
- __ andr(dst, dst, t1);
+
+ __ float_to_float16_NaN(dst, src, t0, t1);
__ ret();
return entry;
diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp
index b8de3547c83..a478cd7b183 100644
--- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp
+++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp
@@ -1148,9 +1148,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
Label L;
__ ld(x28, Address(xmethod, Method::native_function_offset()));
ExternalAddress unsatisfied(SharedRuntime::native_method_throw_unsatisfied_link_error_entry());
- __ la(t, unsatisfied);
- __ load_long_misaligned(t1, Address(t, 0), t0, 2); // 2 bytes aligned, but not 4 or 8
-
+ __ la(t1, unsatisfied);
__ bne(x28, t1, L);
__ call_VM(noreg,
CAST_FROM_FN_PTR(address,
@@ -1229,15 +1227,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
{
Label L, Continue;
- // We need an acquire here to ensure that any subsequent load of the
- // global SafepointSynchronize::_state flag is ordered after this load
- // of the thread-local polling word. We don't want this poll to
- // return false (i.e. not safepointing) and a later poll of the global
- // SafepointSynchronize::_state spuriously to return true.
- //
- // This is to avoid a race when we're in a native->Java transition
- // racing the code which wakes up from a safepoint.
- __ safepoint_poll(L, true /* at_return */, true /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(L, true /* at_return */, false /* in_nmethod */);
__ lwu(t1, Address(xthread, JavaThread::suspend_flags_offset()));
__ beqz(t1, Continue);
__ bind(L);
@@ -1388,7 +1378,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
Label slow_path;
Label fast_path;
- __ safepoint_poll(slow_path, true /* at_return */, false /* acquire */, false /* in_nmethod */);
+ __ safepoint_poll(slow_path, true /* at_return */, false /* in_nmethod */);
__ j(fast_path);
__ bind(slow_path);
diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp
index f6bf1e79f92..2c4ea70b9f8 100644
--- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp
+++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp
@@ -133,6 +133,7 @@ Address TemplateTable::at_bcp(int offset) {
void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg,
Register temp_reg, bool load_bc_into_bc_reg /*=true*/,
int byte_no) {
+ assert_different_registers(bc_reg, temp_reg);
if (!RewriteBytecodes) { return; }
Label L_patch_done;
@@ -196,7 +197,11 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bc, Register bc_reg,
__ bind(L_okay);
#endif
- // patch bytecode
+ // Patch bytecode with release store to coordinate with ResolvedFieldEntry loads
+ // in fast bytecode codelets. load_field_entry has a memory barrier that gains
+ // the needed ordering, together with control dependency on entering the fast codelet
+ // itself.
+ __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
__ sb(bc_reg, at_bcp(0));
__ bind(L_patch_done);
}
@@ -703,7 +708,6 @@ void TemplateTable::index_check(Register array, Register index) {
__ mv(x11, index);
}
Label ok;
- __ sext(index, index, 32);
__ bltu(index, length, ok);
__ mv(x13, array);
__ mv(t1, Interpreter::_throw_ArrayIndexOutOfBoundsException_entry);
@@ -1047,7 +1051,7 @@ void TemplateTable::aastore() {
transition(vtos, vtos);
// stack: ..., array, index, value
__ ld(x10, at_tos()); // value
- __ ld(x12, at_tos_p1()); // index
+ __ lw(x12, at_tos_p1()); // index
__ ld(x13, at_tos_p2()); // array
index_check(x13, x12); // kills x11
@@ -1457,9 +1461,9 @@ void TemplateTable::iinc() {
transition(vtos, vtos);
__ load_signed_byte(x11, at_bcp(2)); // get constant
locals_index(x12);
- __ ld(x10, iaddress(x12, x10, _masm));
+ __ lw(x10, iaddress(x12, x10, _masm));
__ addw(x10, x10, x11);
- __ sd(x10, iaddress(x12, t0, _masm));
+ __ sw(x10, iaddress(x12, t0, _masm));
}
void TemplateTable::wide_iinc() {
@@ -1472,9 +1476,9 @@ void TemplateTable::wide_iinc() {
__ orr(x11, x11, t1);
locals_index_wide(x12);
- __ ld(x10, iaddress(x12, t0, _masm));
+ __ lw(x10, iaddress(x12, t0, _masm));
__ addw(x10, x10, x11);
- __ sd(x10, iaddress(x12, t0, _masm));
+ __ sw(x10, iaddress(x12, t0, _masm));
}
void TemplateTable::convert() {
@@ -3017,6 +3021,7 @@ void TemplateTable::fast_storefield(TosState state) {
// X11: field offset, X12: field holder, X13: flags
load_resolved_field_entry(x12, x12, noreg, x11, x13);
+ __ verify_field_offset(x11);
{
Label notVolatile;
@@ -3104,6 +3109,8 @@ void TemplateTable::fast_accessfield(TosState state) {
__ load_field_entry(x12, x11);
__ load_sized_value(x11, Address(x12, in_bytes(ResolvedFieldEntry::field_offset_offset())), sizeof(int), true /*is_signed*/);
+ __ verify_field_offset(x11);
+
__ load_unsigned_byte(x13, Address(x12, in_bytes(ResolvedFieldEntry::flags_offset())));
// x10: object
@@ -3159,7 +3166,9 @@ void TemplateTable::fast_xaccess(TosState state) {
__ ld(x10, aaddress(0));
// access constant pool cache
__ load_field_entry(x12, x13, 2);
+
__ load_sized_value(x11, Address(x12, in_bytes(ResolvedFieldEntry::field_offset_offset())), sizeof(int), true /*is_signed*/);
+ __ verify_field_offset(x11);
// make sure exception is reported in correct bcp range (getfield is
// next instruction)
diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp
index eca1bb83ab6..fb04c272385 100644
--- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp
+++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp
@@ -147,7 +147,7 @@ void VM_Version::common_initialize() {
if (FLAG_IS_DEFAULT(AvoidUnalignedAccesses)) {
FLAG_SET_DEFAULT(AvoidUnalignedAccesses,
- unaligned_access.value() != MISALIGNED_FAST);
+ unaligned_scalar.value() != MISALIGNED_SCALAR_FAST);
}
if (!AvoidUnalignedAccesses) {
@@ -162,7 +162,7 @@ void VM_Version::common_initialize() {
// This machine has fast unaligned memory accesses
if (FLAG_IS_DEFAULT(UseUnalignedAccesses)) {
FLAG_SET_DEFAULT(UseUnalignedAccesses,
- unaligned_access.value() == MISALIGNED_FAST);
+ (unaligned_scalar.value() == MISALIGNED_SCALAR_FAST));
}
#ifdef __riscv_ztso
@@ -181,12 +181,13 @@ void VM_Version::common_initialize() {
FLAG_SET_DEFAULT(UsePopCountInstruction, false);
}
- if (UseZicboz) {
+ if (UseZicboz && zicboz_block_size.enabled() && zicboz_block_size.value() > 0) {
+ assert(is_power_of_2(zicboz_block_size.value()), "Sanity");
if (FLAG_IS_DEFAULT(UseBlockZeroing)) {
FLAG_SET_DEFAULT(UseBlockZeroing, true);
}
if (FLAG_IS_DEFAULT(BlockZeroingLowLimit)) {
- FLAG_SET_DEFAULT(BlockZeroingLowLimit, 2 * CacheLineSize);
+ FLAG_SET_DEFAULT(BlockZeroingLowLimit, 4 * zicboz_block_size.value());
}
} else if (UseBlockZeroing) {
warning("Block zeroing is not available");
@@ -194,24 +195,19 @@ void VM_Version::common_initialize() {
}
if (UseRVV) {
- if (!ext_V.enabled() && FLAG_IS_DEFAULT(UseRVV)) {
- warning("RVV is not supported on this CPU");
- FLAG_SET_DEFAULT(UseRVV, false);
- } else {
- // read vector length from vector CSR vlenb
- _initial_vector_length = cpu_vector_length();
- }
+ // read vector length from vector CSR vlenb
+ _initial_vector_length = cpu_vector_length();
}
- // Misc Intrinsics could depend on RVV
+ // Misc Intrinsics that could depend on RVV.
- if (UseZba || UseRVV) {
+ if (!AvoidUnalignedAccesses && (UseZba || UseRVV)) {
if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
FLAG_SET_DEFAULT(UseCRC32Intrinsics, true);
}
} else {
if (!FLAG_IS_DEFAULT(UseCRC32Intrinsics)) {
- warning("CRC32 intrinsic requires Zba or RVV instructions (not available on this CPU)");
+ warning("CRC32 intrinsic are not available on this CPU.");
}
FLAG_SET_DEFAULT(UseCRC32Intrinsics, false);
}
@@ -269,6 +265,11 @@ void VM_Version::c2_initialize() {
}
}
+ if (FLAG_IS_DEFAULT(AlignVector)) {
+ FLAG_SET_DEFAULT(AlignVector,
+ unaligned_vector.value() != MISALIGNED_VECTOR_FAST);
+ }
+
// NOTE: Make sure codes dependent on UseRVV are put after MaxVectorSize initialize,
// as there are extra checks inside it which could disable UseRVV
// in some situations.
@@ -325,20 +326,40 @@ void VM_Version::c2_initialize() {
FLAG_SET_DEFAULT(UseMulAddIntrinsic, true);
}
- if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) {
- FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true);
+ if (!AvoidUnalignedAccesses) {
+ if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) {
+ FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true);
+ }
+ } else if (UseMultiplyToLenIntrinsic) {
+ warning("Intrinsics for BigInteger.multiplyToLen() not available on this CPU.");
+ FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, false);
}
- if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) {
- FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, true);
+ if (!AvoidUnalignedAccesses) {
+ if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) {
+ FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, true);
+ }
+ } else if (UseSquareToLenIntrinsic) {
+ warning("Intrinsics for BigInteger.squareToLen() not available on this CPU.");
+ FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, false);
}
- if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) {
- FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, true);
+ if (!AvoidUnalignedAccesses) {
+ if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) {
+ FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, true);
+ }
+ } else if (UseMontgomeryMultiplyIntrinsic) {
+ warning("Intrinsics for BigInteger.montgomeryMultiply() not available on this CPU.");
+ FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, false);
}
- if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) {
- FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, true);
+ if (!AvoidUnalignedAccesses) {
+ if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) {
+ FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, true);
+ }
+ } else if (UseMontgomerySquareIntrinsic) {
+ warning("Intrinsics for BigInteger.montgomerySquare() not available on this CPU.");
+ FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, false);
}
// Adler32
@@ -456,10 +477,6 @@ void VM_Version::c2_initialize() {
warning("AES/CTR intrinsics are not available on this CPU");
FLAG_SET_DEFAULT(UseAESCTRIntrinsics, false);
}
-
- if (FLAG_IS_DEFAULT(AlignVector)) {
- FLAG_SET_DEFAULT(AlignVector, AvoidUnalignedAccesses);
- }
}
#endif // COMPILER2
diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp
index a0a42fb5463..85454593dfe 100644
--- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp
+++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp
@@ -86,25 +86,27 @@ class VM_Version : public Abstract_VM_Version {
} \
} \
- #define UPDATE_DEFAULT_DEP(flag, dep) \
- void update_flag() { \
- assert(enabled(), "Must be."); \
- /* dep must be declared before */ \
- assert((uintptr_t)(this) > \
- (uintptr_t)(&dep), "Invalid");\
- if (FLAG_IS_DEFAULT(flag)) { \
- if (dep.enabled()) { \
- FLAG_SET_DEFAULT(flag, true); \
- } else { \
- FLAG_SET_DEFAULT(flag, false); \
- } \
- } else { \
- /* Sync CPU features with flags */ \
- if (!flag) { \
- disable_feature(); \
- } \
- } \
- } \
+ #define UPDATE_DEFAULT_DEP(flag, dep) \
+ void update_flag() { \
+ assert(enabled(), "Must be."); \
+ /* dep must be declared before */ \
+ assert((uintptr_t)(this) > \
+ (uintptr_t)(&dep), "Invalid"); \
+ if (FLAG_IS_DEFAULT(flag)) { \
+ if (dep.enabled()) { \
+ FLAG_SET_DEFAULT(flag, true); \
+ } else { \
+ FLAG_SET_DEFAULT(flag, false); \
+ /* Sync CPU features with flags */ \
+ disable_feature(); \
+ } \
+ } else { \
+ /* Sync CPU features with flags */ \
+ if (!flag) { \
+ disable_feature(); \
+ } \
+ } \
+ } \
#define NO_UPDATE_DEFAULT \
void update_flag() {} \
@@ -151,7 +153,8 @@ class VM_Version : public Abstract_VM_Version {
// mvendorid Manufactory JEDEC id encoded, ISA vol 2 3.1.2..
// marchid Id for microarch. Mvendorid plus marchid uniquely identify the microarch.
// mimpid A unique encoding of the version of the processor implementation.
- // unaligned_access Unaligned memory accesses (unknown, unspported, emulated, slow, firmware, fast)
+ // unaligned_scalar Performance of misaligned scalar accesses (unknown, emulated, slow, fast, unsupported)
+ // unaligned_vector Performance of misaligned vector accesses (unknown, unspported, slow, fast)
// satp mode SATP bits (number of virtual addr bits) mbare, sv39, sv48, sv57, sv64
public:
@@ -160,45 +163,47 @@ class VM_Version : public Abstract_VM_Version {
// Note: the order matters, depender should be after their dependee. E.g. ext_V before ext_Zvbb.
// declaration name , extension name, bit pos ,in str, mapped flag)
- #define RV_FEATURE_FLAGS(decl) \
- decl(ext_I , "i" , ('I' - 'A'), true , NO_UPDATE_DEFAULT) \
- decl(ext_M , "m" , ('M' - 'A'), true , NO_UPDATE_DEFAULT) \
- decl(ext_A , "a" , ('A' - 'A'), true , NO_UPDATE_DEFAULT) \
- decl(ext_F , "f" , ('F' - 'A'), true , NO_UPDATE_DEFAULT) \
- decl(ext_D , "d" , ('D' - 'A'), true , NO_UPDATE_DEFAULT) \
- decl(ext_C , "c" , ('C' - 'A'), true , UPDATE_DEFAULT(UseRVC)) \
- decl(ext_Q , "q" , ('Q' - 'A'), true , NO_UPDATE_DEFAULT) \
- decl(ext_H , "h" , ('H' - 'A'), true , NO_UPDATE_DEFAULT) \
- decl(ext_V , "v" , ('V' - 'A'), true , UPDATE_DEFAULT(UseRVV)) \
- decl(ext_Zicbom , "Zicbom" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbom)) \
- decl(ext_Zicboz , "Zicboz" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicboz)) \
- decl(ext_Zicbop , "Zicbop" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbop)) \
- decl(ext_Zba , "Zba" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZba)) \
- decl(ext_Zbb , "Zbb" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbb)) \
- decl(ext_Zbc , "Zbc" , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \
- decl(ext_Zbs , "Zbs" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbs)) \
- decl(ext_Zbkb , "Zbkb" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbkb)) \
- decl(ext_Zcb , "Zcb" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZcb)) \
- decl(ext_Zfa , "Zfa" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfa)) \
- decl(ext_Zfh , "Zfh" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfh)) \
- decl(ext_Zfhmin , "Zfhmin" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfhmin)) \
- decl(ext_Zicsr , "Zicsr" , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \
- decl(ext_Zicntr , "Zicntr" , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \
- decl(ext_Zifencei , "Zifencei" , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \
- decl(ext_Zic64b , "Zic64b" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZic64b)) \
- decl(ext_Ztso , "Ztso" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZtso)) \
- decl(ext_Zihintpause , "Zihintpause" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZihintpause)) \
- decl(ext_Zacas , "Zacas" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZacas)) \
- decl(ext_Zvbb , "Zvbb" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbb, ext_V)) \
- decl(ext_Zvbc , "Zvbc" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbc, ext_V)) \
- decl(ext_Zvfh , "Zvfh" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfh, ext_V)) \
- decl(ext_Zvkn , "Zvkn" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkn, ext_V)) \
- decl(ext_Zicond , "Zicond" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicond)) \
- decl(mvendorid , "VendorId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
- decl(marchid , "ArchId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
- decl(mimpid , "ImpId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
- decl(unaligned_access, "Unaligned" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
- decl(satp_mode , "SATP" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
+ #define RV_FEATURE_FLAGS(decl) \
+ decl(ext_I , "i" , ('I' - 'A'), true , NO_UPDATE_DEFAULT) \
+ decl(ext_M , "m" , ('M' - 'A'), true , NO_UPDATE_DEFAULT) \
+ decl(ext_A , "a" , ('A' - 'A'), true , NO_UPDATE_DEFAULT) \
+ decl(ext_F , "f" , ('F' - 'A'), true , NO_UPDATE_DEFAULT) \
+ decl(ext_D , "d" , ('D' - 'A'), true , NO_UPDATE_DEFAULT) \
+ decl(ext_C , "c" , ('C' - 'A'), true , UPDATE_DEFAULT(UseRVC)) \
+ decl(ext_Q , "q" , ('Q' - 'A'), true , NO_UPDATE_DEFAULT) \
+ decl(ext_H , "h" , ('H' - 'A'), true , NO_UPDATE_DEFAULT) \
+ decl(ext_V , "v" , ('V' - 'A'), true , UPDATE_DEFAULT(UseRVV)) \
+ decl(ext_Zicbom , "Zicbom" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbom)) \
+ decl(ext_Zicboz , "Zicboz" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicboz)) \
+ decl(ext_Zicbop , "Zicbop" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicbop)) \
+ decl(ext_Zba , "Zba" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZba)) \
+ decl(ext_Zbb , "Zbb" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbb)) \
+ decl(ext_Zbc , "Zbc" , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \
+ decl(ext_Zbs , "Zbs" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbs)) \
+ decl(ext_Zbkb , "Zbkb" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZbkb)) \
+ decl(ext_Zcb , "Zcb" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZcb)) \
+ decl(ext_Zfa , "Zfa" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfa)) \
+ decl(ext_Zfh , "Zfh" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfh)) \
+ decl(ext_Zfhmin , "Zfhmin" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZfhmin)) \
+ decl(ext_Zicsr , "Zicsr" , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \
+ decl(ext_Zicntr , "Zicntr" , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \
+ decl(ext_Zifencei , "Zifencei" , RV_NO_FLAG_BIT, true , NO_UPDATE_DEFAULT) \
+ decl(ext_Zic64b , "Zic64b" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZic64b)) \
+ decl(ext_Ztso , "Ztso" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZtso)) \
+ decl(ext_Zihintpause , "Zihintpause" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZihintpause)) \
+ decl(ext_Zacas , "Zacas" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZacas)) \
+ decl(ext_Zvbb , "Zvbb" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbb, ext_V)) \
+ decl(ext_Zvbc , "Zvbc" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvbc, ext_V)) \
+ decl(ext_Zvfh , "Zvfh" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvfh, ext_V)) \
+ decl(ext_Zvkn , "Zvkn" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT_DEP(UseZvkn, ext_V)) \
+ decl(ext_Zicond , "Zicond" , RV_NO_FLAG_BIT, true , UPDATE_DEFAULT(UseZicond)) \
+ decl(mvendorid , "VendorId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
+ decl(marchid , "ArchId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
+ decl(mimpid , "ImpId" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
+ decl(satp_mode , "SATP" , RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
+ decl(unaligned_scalar , "UnalignedScalar", RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
+ decl(unaligned_vector , "UnalignedVector", RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
+ decl(zicboz_block_size, "ZicbozBlockSize", RV_NO_FLAG_BIT, false, NO_UPDATE_DEFAULT) \
#define DECLARE_RV_FEATURE(NAME, PRETTY, BIT, FSTRING, FLAGF) \
struct NAME##RVFeatureValue : public RVFeatureValue { \
@@ -273,12 +278,19 @@ class VM_Version : public Abstract_VM_Version {
static VM_MODE parse_satp_mode(const char* vm_mode);
// Values from riscv_hwprobe()
- enum UNALIGNED_ACCESS : int {
- MISALIGNED_UNKNOWN = 0,
- MISALIGNED_EMULATED = 1,
- MISALIGNED_SLOW = 2,
- MISALIGNED_FAST = 3,
- MISALIGNED_UNSUPPORTED = 4
+ enum UNALIGNED_SCALAR_ACCESS : int {
+ MISALIGNED_SCALAR_UNKNOWN = 0,
+ MISALIGNED_SCALAR_EMULATED = 1,
+ MISALIGNED_SCALAR_SLOW = 2,
+ MISALIGNED_SCALAR_FAST = 3,
+ MISALIGNED_SCALAR_UNSUPPORTED = 4
+ };
+
+ enum UNALIGNED_VECTOR_ACCESS : int {
+ MISALIGNED_VECTOR_UNKNOWN = 0,
+ MISALIGNED_VECTOR_SLOW = 2,
+ MISALIGNED_VECTOR_FAST = 3,
+ MISALIGNED_VECTOR_UNSUPPORTED = 4
};
// Null terminated list
diff --git a/src/hotspot/cpu/s390/gc/g1/g1_s390.ad b/src/hotspot/cpu/s390/gc/g1/g1_s390.ad
index 31f60c4aeff..ce8c7463ad4 100644
--- a/src/hotspot/cpu/s390/gc/g1/g1_s390.ad
+++ b/src/hotspot/cpu/s390/gc/g1/g1_s390.ad
@@ -356,7 +356,7 @@ instruct g1CompareAndExchangeP(iRegP mem_ptr, rarg5RegP oldval, iRegP_N2P newval
__ z_lgr($res$$Register, $oldval$$Register); // previous content
- __ z_csg($oldval$$Register, $newval$$Register, 0, $mem_ptr$$reg);
+ __ z_csg($res$$Register, $newval$$Register, 0, $mem_ptr$$reg);
write_barrier_post(masm, this,
$mem_ptr$$Register /* store_addr */,
diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp
index ee0376db078..cd4daf14639 100644
--- a/src/hotspot/cpu/x86/assembler_x86.cpp
+++ b/src/hotspot/cpu/x86/assembler_x86.cpp
@@ -1398,11 +1398,7 @@ void Assembler::addl(Address dst, Register src) {
void Assembler::eaddl(Register dst, Address src1, Register src2, bool no_flags) {
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_32bit);
- eevex_prefix_ndd(src1, dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_int8(0x01);
- emit_operand(src2, src1, 0);
+ emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0x01, no_flags, false /* is_map1 */, true /* is_commutative */);
}
void Assembler::addl(Register dst, int32_t imm32) {
@@ -1432,11 +1428,7 @@ void Assembler::addl(Register dst, Register src) {
}
void Assembler::eaddl(Register dst, Register src1, Register src2, bool no_flags) {
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- // NDD shares its encoding bits with NDS bits for regular EVEX instruction.
- // Therefore, DST is passed as the second argument to minimize changes in the leaf level routine.
- (void)emit_eevex_prefix_or_demote_ndd(src1->encoding(), dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_arith(0x03, 0xC0, src1, src2);
+ emit_eevex_prefix_or_demote_arith_ndd(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0x03, 0xC0, no_flags, true /* is_commutative */);
}
void Assembler::addr_nop_4() {
@@ -1657,17 +1649,18 @@ void Assembler::eandl(Register dst, Register src1, Address src2, bool no_flags)
emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0x23, no_flags);
}
+void Assembler::eandl(Register dst, Address src1, Register src2, bool no_flags) {
+ InstructionMark im(this);
+ emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0x21, no_flags, false /* is_map1 */, true /* is_commutative */);
+}
+
void Assembler::andl(Register dst, Register src) {
(void) prefix_and_encode(dst->encoding(), src->encoding());
emit_arith(0x23, 0xC0, dst, src);
}
void Assembler::eandl(Register dst, Register src1, Register src2, bool no_flags) {
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- // NDD shares its encoding bits with NDS bits for regular EVEX instruction.
- // Therefore, DST is passed as the second argument to minimize changes in the leaf level routine.
- (void) emit_eevex_prefix_or_demote_ndd(src1->encoding(), dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_arith(0x23, 0xC0, src1, src2);
+ emit_eevex_prefix_or_demote_arith_ndd(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0x23, 0xC0, no_flags, true /* is_commutative */);
}
void Assembler::andnl(Register dst, Register src1, Register src2) {
@@ -2519,7 +2512,7 @@ void Assembler::imull(Register dst, Register src) {
}
void Assembler::eimull(Register dst, Register src1, Register src2, bool no_flags) {
- emit_eevex_or_demote(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0xAF, no_flags, true /* is_map1 */, true /* swap */);
+ emit_eevex_or_demote(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0xAF, no_flags, true /* is_map1 */, true /* swap */, true /* is_commutative */);
}
void Assembler::imull(Register dst, Address src, int32_t value) {
@@ -4419,11 +4412,7 @@ void Assembler::enotl(Register dst, Register src) {
}
void Assembler::eorw(Register dst, Register src1, Register src2, bool no_flags) {
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- // NDD shares its encoding bits with NDS bits for regular EVEX instruction.
- // Therefore, DST is passed as the second argument to minimize changes in the leaf level routine.
- (void) emit_eevex_prefix_or_demote_ndd(src1->encoding(), dst->encoding(), src2->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_arith(0x0B, 0xC0, src1, src2);
+ emit_eevex_prefix_or_demote_arith_ndd(dst, src1, src2, VEX_SIMD_66, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_16bit, 0x0B, 0xC0, no_flags, true /* is_commutative */);
}
void Assembler::orl(Address dst, int32_t imm32) {
@@ -4467,11 +4456,7 @@ void Assembler::orl(Register dst, Register src) {
}
void Assembler::eorl(Register dst, Register src1, Register src2, bool no_flags) {
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- // NDD shares its encoding bits with NDS bits for regular EVEX instruction.
- // Therefore, DST is passed as the second argument to minimize changes in the leaf level routine.
- (void) emit_eevex_prefix_or_demote_ndd(src1->encoding(), dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_arith(0x0B, 0xC0, src1, src2);
+ emit_eevex_prefix_or_demote_arith_ndd(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0x0B, 0xC0, no_flags, true /* is_commutative */);
}
void Assembler::orl(Address dst, Register src) {
@@ -4483,11 +4468,7 @@ void Assembler::orl(Address dst, Register src) {
void Assembler::eorl(Register dst, Address src1, Register src2, bool no_flags) {
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_32bit);
- eevex_prefix_ndd(src1, dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_int8(0x09);
- emit_operand(src2, src1, 0);
+ emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0x09, no_flags, false /* is_map1 */, true /* is_commutative */);
}
void Assembler::orb(Address dst, int imm8) {
@@ -4517,11 +4498,7 @@ void Assembler::orb(Address dst, Register src) {
void Assembler::eorb(Register dst, Address src1, Register src2, bool no_flags) {
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_8bit);
- eevex_prefix_ndd(src1, dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_int8(0x08);
- emit_operand(src2, src1, 0);
+ emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_8bit, 0x08, no_flags, false /* is_map1 */, true /* is_commutative */);
}
void Assembler::packsswb(XMMRegister dst, XMMRegister src) {
@@ -7323,11 +7300,7 @@ void Assembler::xorl(Register dst, Register src) {
}
void Assembler::exorl(Register dst, Register src1, Register src2, bool no_flags) {
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- // NDD shares its encoding bits with NDS bits for regular EVEX instruction.
- // Therefore, DST is passed as the second argument to minimize changes in the leaf level routine.
- (void) emit_eevex_prefix_or_demote_ndd(src1->encoding(), dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_arith(0x33, 0xC0, src1, src2);
+ emit_eevex_prefix_or_demote_arith_ndd(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0x33, 0xC0, no_flags, true /* is_commutative */);
}
void Assembler::xorl(Address dst, Register src) {
@@ -7339,11 +7312,7 @@ void Assembler::xorl(Address dst, Register src) {
void Assembler::exorl(Register dst, Address src1, Register src2, bool no_flags) {
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_32bit);
- eevex_prefix_ndd(src1, dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_int8(0x31);
- emit_operand(src2, src1, 0);
+ emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_32bit, 0x31, no_flags, false /* is_map1 */, true /* is_commutative */);
}
void Assembler::xorb(Register dst, Address src) {
@@ -7367,11 +7336,7 @@ void Assembler::xorb(Address dst, Register src) {
void Assembler::exorb(Register dst, Address src1, Register src2, bool no_flags) {
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_8bit);
- eevex_prefix_ndd(src1, dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_int8(0x30);
- emit_operand(src2, src1, 0);
+ emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_8bit, 0x30, no_flags, false /* is_map1 */, true /* is_commutative */);
}
void Assembler::xorw(Register dst, Address src) {
@@ -12891,6 +12856,31 @@ void Assembler::eevex_prefix_ndd(Address adr, int ndd_enc, int xreg_enc, VexSimd
vex_prefix(adr, ndd_enc, xreg_enc, pre, opc, attributes, /* nds_is_ndd */ true, no_flags);
}
+void Assembler::emit_eevex_or_demote(Register dst, Address src1, Register src2, VexSimdPrefix pre, VexOpcode opc,
+ int size, int opcode_byte, bool no_flags, bool is_map1, bool is_commutative) {
+ if (is_commutative && is_demotable(no_flags, dst->encoding(), src2->encoding())) {
+ // Opcode byte adjustment due to mismatch between NDD and equivalent demotable variant
+ opcode_byte += 2;
+ if (size == EVEX_64bit) {
+ emit_prefix_and_int8(get_prefixq(src1, dst, is_map1), opcode_byte);
+ } else {
+ // For 32-bit, 16-bit and 8-bit
+ if (size == EVEX_16bit) {
+ emit_int8(0x66);
+ }
+ prefix(src1, dst, false, is_map1);
+ emit_int8(opcode_byte);
+ }
+ } else {
+ bool vex_w = (size == EVEX_64bit) ? true : false;
+ InstructionAttr attributes(AVX_128bit, vex_w, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, size);
+ eevex_prefix_ndd(src1, dst->encoding(), src2->encoding(), pre, opc, &attributes, no_flags);
+ emit_int8(opcode_byte);
+ }
+ emit_operand(src2, src1, 0);
+}
+
void Assembler::emit_eevex_or_demote(Register dst, Register src1, Address src2, VexSimdPrefix pre, VexOpcode opc,
int size, int opcode_byte, bool no_flags, bool is_map1) {
if (is_demotable(no_flags, dst->encoding(), src1->encoding())) {
@@ -12991,18 +12981,20 @@ void Assembler::emit_eevex_or_demote(int dst_enc, int nds_enc, int src_enc, int8
}
void Assembler::emit_eevex_or_demote(int dst_enc, int nds_enc, int src_enc, VexSimdPrefix pre, VexOpcode opc,
- int size, int opcode_byte, bool no_flags, bool is_map1, bool swap) {
+ int size, int opcode_byte, bool no_flags, bool is_map1, bool swap, bool is_commutative) {
int encode;
bool is_prefixq = (size == EVEX_64bit) ? true : false;
- if (is_demotable(no_flags, dst_enc, nds_enc)) {
+ bool first_operand_demotable = is_demotable(no_flags, dst_enc, nds_enc);
+ bool second_operand_demotable = is_commutative && is_demotable(no_flags, dst_enc, src_enc);
+ if (first_operand_demotable || second_operand_demotable) {
if (size == EVEX_16bit) {
emit_int8(0x66);
}
-
+ int src = first_operand_demotable ? src_enc : nds_enc;
if (swap) {
- encode = is_prefixq ? prefixq_and_encode(dst_enc, src_enc, is_map1) : prefix_and_encode(dst_enc, src_enc, is_map1);
+ encode = is_prefixq ? prefixq_and_encode(dst_enc, src, is_map1) : prefix_and_encode(dst_enc, src, is_map1);
} else {
- encode = is_prefixq ? prefixq_and_encode(src_enc, dst_enc, is_map1) : prefix_and_encode(src_enc, dst_enc, is_map1);
+ encode = is_prefixq ? prefixq_and_encode(src, dst_enc, is_map1) : prefix_and_encode(src, dst_enc, is_map1);
}
emit_opcode_prefix_and_encoding((unsigned char)opcode_byte, 0xC0, encode);
} else {
@@ -13050,6 +13042,26 @@ int Assembler::eevex_prefix_and_encode_nf(int dst_enc, int nds_enc, int src_enc,
return vex_prefix_and_encode(dst_enc, nds_enc, src_enc, pre, opc, attributes, /* src_is_gpr */ true, /* nds_is_ndd */ false, no_flags);
}
+void Assembler::emit_eevex_prefix_or_demote_arith_ndd(Register dst, Register src1, Register src2, VexSimdPrefix pre, VexOpcode opc,
+ int size, int op1, int op2, bool no_flags, bool is_commutative) {
+ bool demotable = is_demotable(no_flags, dst->encoding(), src1->encoding());
+ if (!demotable && is_commutative) {
+ if (is_demotable(no_flags, dst->encoding(), src2->encoding())) {
+ // swap src1 and src2
+ Register tmp = src1;
+ src1 = src2;
+ src2 = tmp;
+ }
+ }
+ bool vex_w = (size == EVEX_64bit) ? true : false;
+ bool use_prefixq = vex_w;
+ InstructionAttr attributes(AVX_128bit, vex_w, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
+ // NDD shares its encoding bits with NDS bits for regular EVEX instruction.
+ // Therefore, DST is passed as the second argument to minimize changes in the leaf level routine.
+ (void)emit_eevex_prefix_or_demote_ndd(src1->encoding(), dst->encoding(), src2->encoding(), pre, opc, &attributes, no_flags, use_prefixq);
+ emit_arith(op1, op2, src1, src2);
+}
+
void Assembler::emit_eevex_prefix_or_demote_arith_ndd(Register dst, Register nds, int32_t imm32, VexSimdPrefix pre, VexOpcode opc,
int size, int op1, int op2, bool no_flags) {
int dst_enc = dst->encoding();
@@ -13060,7 +13072,6 @@ void Assembler::emit_eevex_prefix_or_demote_arith_ndd(Register dst, Register nds
} else {
bool vex_w = (size == EVEX_64bit) ? true : false;
InstructionAttr attributes(AVX_128bit, vex_w, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- //attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, size);
attributes.set_is_evex_instruction();
vex_prefix_and_encode(0, dst_enc, nds_enc, pre, opc, &attributes, /* src_is_gpr */ true, /* nds_is_ndd */ true, no_flags);
@@ -13689,7 +13700,7 @@ void Assembler::pdepq(Register dst, Register src1, Address src2) {
void Assembler::sarxl(Register dst, Register src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes, true);
emit_int16((unsigned char)0xF7, (0xC0 | encode));
}
@@ -13697,7 +13708,7 @@ void Assembler::sarxl(Register dst, Register src1, Register src2) {
void Assembler::sarxl(Register dst, Address src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_32bit);
vex_prefix(src1, src2->encoding(), dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes);
emit_int8((unsigned char)0xF7);
@@ -13706,7 +13717,7 @@ void Assembler::sarxl(Register dst, Address src1, Register src2) {
void Assembler::sarxq(Register dst, Register src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes, true);
emit_int16((unsigned char)0xF7, (0xC0 | encode));
}
@@ -13714,7 +13725,7 @@ void Assembler::sarxq(Register dst, Register src1, Register src2) {
void Assembler::sarxq(Register dst, Address src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_64bit);
vex_prefix(src1, src2->encoding(), dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F_38, &attributes);
emit_int8((unsigned char)0xF7);
@@ -13723,7 +13734,7 @@ void Assembler::sarxq(Register dst, Address src1, Register src2) {
void Assembler::shlxl(Register dst, Register src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes, true);
emit_int16((unsigned char)0xF7, (0xC0 | encode));
}
@@ -13731,7 +13742,7 @@ void Assembler::shlxl(Register dst, Register src1, Register src2) {
void Assembler::shlxl(Register dst, Address src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_32bit);
vex_prefix(src1, src2->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
emit_int8((unsigned char)0xF7);
@@ -13740,7 +13751,7 @@ void Assembler::shlxl(Register dst, Address src1, Register src2) {
void Assembler::shlxq(Register dst, Register src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes, true);
emit_int16((unsigned char)0xF7, (0xC0 | encode));
}
@@ -13748,7 +13759,7 @@ void Assembler::shlxq(Register dst, Register src1, Register src2) {
void Assembler::shlxq(Register dst, Address src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_64bit);
vex_prefix(src1, src2->encoding(), dst->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
emit_int8((unsigned char)0xF7);
@@ -13757,7 +13768,7 @@ void Assembler::shlxq(Register dst, Address src1, Register src2) {
void Assembler::shrxl(Register dst, Register src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_38, &attributes, true);
emit_int16((unsigned char)0xF7, (0xC0 | encode));
}
@@ -13765,7 +13776,7 @@ void Assembler::shrxl(Register dst, Register src1, Register src2) {
void Assembler::shrxl(Register dst, Address src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_32bit);
vex_prefix(src1, src2->encoding(), dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_38, &attributes);
emit_int8((unsigned char)0xF7);
@@ -13774,7 +13785,7 @@ void Assembler::shrxl(Register dst, Address src1, Register src2) {
void Assembler::shrxq(Register dst, Register src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_38, &attributes, true);
emit_int16((unsigned char)0xF7, (0xC0 | encode));
}
@@ -13782,7 +13793,7 @@ void Assembler::shrxq(Register dst, Register src1, Register src2) {
void Assembler::shrxq(Register dst, Address src1, Register src2) {
assert(VM_Version::supports_bmi2(), "");
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
+ InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_64bit);
vex_prefix(src1, src2->encoding(), dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_38, &attributes);
emit_int8((unsigned char)0xF7);
@@ -14543,11 +14554,7 @@ void Assembler::addq(Address dst, Register src) {
void Assembler::eaddq(Register dst, Address src1, Register src2, bool no_flags) {
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_64bit);
- eevex_prefix_ndd(src1, dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_int8(0x01);
- emit_operand(src2, src1, 0);
+ emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_64bit, 0x01, no_flags, false /* is_map1 */, true /* is_commutative */);
}
void Assembler::addq(Register dst, int32_t imm32) {
@@ -14576,11 +14583,7 @@ void Assembler::addq(Register dst, Register src) {
}
void Assembler::eaddq(Register dst, Register src1, Register src2, bool no_flags) {
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- // NDD shares its encoding bits with NDS bits for regular EVEX instruction.
- // Therefore, DST is passed as the second argument to minimize changes in the leaf level routine.
- (void) emit_eevex_prefix_or_demote_ndd(src1->encoding(), dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags, true /* use_prefixq */);
- emit_arith(0x03, 0xC0, src1, src2);
+ emit_eevex_prefix_or_demote_arith_ndd(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_64bit, 0x03, 0xC0, no_flags, true /* is_commutative */);
}
void Assembler::adcxq(Register dst, Register src) {
@@ -14673,11 +14676,7 @@ void Assembler::andq(Register dst, Register src) {
}
void Assembler::eandq(Register dst, Register src1, Register src2, bool no_flags) {
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- // NDD shares its encoding bits with NDS bits for regular EVEX instruction.
- // Therefore, DST is passed as the second argument to minimize changes in the leaf level routine.
- (void) emit_eevex_prefix_or_demote_ndd(src1->encoding(), dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags, true /* use_prefixq */);
- emit_arith(0x23, 0xC0, src1, src2);
+ emit_eevex_prefix_or_demote_arith_ndd(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_64bit, 0x23, 0xC0, no_flags, true /* is_commutative */);
}
void Assembler::andq(Address dst, Register src) {
@@ -14688,11 +14687,7 @@ void Assembler::andq(Address dst, Register src) {
void Assembler::eandq(Register dst, Address src1, Register src2, bool no_flags) {
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_64bit);
- eevex_prefix_ndd(src1, dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_int8(0x21);
- emit_operand(src2, src1, 0);
+ emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_64bit, 0x21, no_flags, false /* is_map1 */, true /* is_commutative */);
}
void Assembler::andnq(Register dst, Register src1, Register src2) {
@@ -15038,7 +15033,7 @@ void Assembler::eimulq(Register dst, Register src, bool no_flags) {
}
void Assembler::eimulq(Register dst, Register src1, Register src2, bool no_flags) {
- emit_eevex_or_demote(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_64bit, 0xAF, no_flags, true /* is_map1 */, true /* swap */);
+ emit_eevex_or_demote(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_64bit, 0xAF, no_flags, true /* is_map1 */, true /* swap */, true /* is_commutative */);
}
void Assembler::imulq(Register src) {
@@ -15500,11 +15495,7 @@ void Assembler::orq(Address dst, Register src) {
void Assembler::eorq(Register dst, Address src1, Register src2, bool no_flags) {
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_64bit);
- eevex_prefix_ndd(src1, dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_int8(0x09);
- emit_operand(src2, src1, 0);
+ emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_64bit, 0x09, no_flags, false /* is_map1 */, true /* is_commutative */);
}
void Assembler::orq(Register dst, int32_t imm32) {
@@ -15544,13 +15535,8 @@ void Assembler::orq(Register dst, Register src) {
}
void Assembler::eorq(Register dst, Register src1, Register src2, bool no_flags) {
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- // NDD shares its encoding bits with NDS bits for regular EVEX instruction.
- // Therefore, DST is passed as the second argument to minimize changes in the leaf level routine.
- (void) emit_eevex_prefix_or_demote_ndd(src1->encoding(), dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags, true /* use_prefixq */);
- emit_arith(0x0B, 0xC0, src1, src2);
+ emit_eevex_prefix_or_demote_arith_ndd(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_64bit, 0x0B, 0xC0, no_flags, true /* is_commutative */);
}
-
void Assembler::popcntq(Register dst, Address src) {
assert(VM_Version::supports_popcnt(), "must support");
InstructionMark im(this);
@@ -16292,11 +16278,7 @@ void Assembler::xorq(Register dst, Register src) {
}
void Assembler::exorq(Register dst, Register src1, Register src2, bool no_flags) {
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- // NDD shares its encoding bits with NDS bits for regular EVEX instruction.
- // Therefore, DST is passed as the second argument to minimize changes in the leaf level routine.
- (void) emit_eevex_prefix_or_demote_ndd(src1->encoding(), dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags, true /* use_prefixq */);
- emit_arith(0x33, 0xC0, src1, src2);
+ emit_eevex_prefix_or_demote_arith_ndd(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_64bit, 0x33, 0xC0, no_flags, true /* is_commutative */);
}
void Assembler::xorq(Register dst, Address src) {
@@ -16350,11 +16332,7 @@ void Assembler::esetzucc(Condition cc, Register dst) {
void Assembler::exorq(Register dst, Address src1, Register src2, bool no_flags) {
InstructionMark im(this);
- InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
- attributes.set_address_attributes(/* tuple_type */ EVEX_NOSCALE, /* input_size_in_bits */ EVEX_64bit);
- eevex_prefix_ndd(src1, dst->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, &attributes, no_flags);
- emit_int8(0x31);
- emit_operand(src2, src1, 0);
+ emit_eevex_or_demote(dst, src1, src2, VEX_SIMD_NONE, VEX_OPCODE_0F_3C /* MAP4 */, EVEX_64bit, 0x31, no_flags, false /* is_map1 */, true /* is_commutative */);
}
void InstructionAttr::set_address_attributes(int tuple_type, int input_size_in_bits) {
diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp
index b1959e23722..28e0cde2157 100644
--- a/src/hotspot/cpu/x86/assembler_x86.hpp
+++ b/src/hotspot/cpu/x86/assembler_x86.hpp
@@ -796,14 +796,20 @@ class Assembler : public AbstractAssembler {
int emit_eevex_prefix_or_demote_ndd(int dst_enc, int nds_enc, VexSimdPrefix pre, VexOpcode opc,
InstructionAttr *attributes, bool no_flags = false, bool use_prefixq = false);
+ void emit_eevex_prefix_or_demote_arith_ndd(Register dst, Register src1, Register src2, VexSimdPrefix pre, VexOpcode opc,
+ int size, int op1, int op2, bool no_flags = false, bool is_commutative = false);
+
void emit_eevex_prefix_or_demote_arith_ndd(Register dst, Register nds, int32_t imm32, VexSimdPrefix pre, VexOpcode opc,
int size, int op1, int op2, bool no_flags);
void emit_eevex_or_demote(Register dst, Register src1, Address src2, VexSimdPrefix pre, VexOpcode opc,
int size, int opcode_byte, bool no_flags = false, bool is_map1 = false);
+ void emit_eevex_or_demote(Register dst, Address src1, Register src2, VexSimdPrefix pre, VexOpcode opc,
+ int size, int opcode_byte, bool no_flags = false, bool is_map1 = false, bool is_commutative = false);
+
void emit_eevex_or_demote(int dst_enc, int nds_enc, int src_enc, VexSimdPrefix pre, VexOpcode opc,
- int size, int opcode_byte, bool no_flags, bool is_map1 = false, bool swap = false);
+ int size, int opcode_byte, bool no_flags, bool is_map1 = false, bool swap = false, bool is_commutative = false);
void emit_eevex_or_demote(int dst_enc, int nds_enc, int src_enc, int8_t imm8, VexSimdPrefix pre, VexOpcode opc,
int size, int opcode_byte, bool no_flags, bool is_map1 = false);
@@ -1138,6 +1144,7 @@ class Assembler : public AbstractAssembler {
void eandl(Register dst, Register src, int32_t imm32, bool no_flags);
void andl(Register dst, Address src);
void eandl(Register dst, Register src1, Address src2, bool no_flags);
+ void eandl(Register dst, Address src1, Register src2, bool no_flags);
void andl(Register dst, Register src);
void eandl(Register dst, Register src1, Register src2, bool no_flags);
void andl(Address dst, Register src);
diff --git a/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp b/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp
index ee2d6d8a0be..962f7866259 100644
--- a/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp
+++ b/src/hotspot/cpu/x86/c2_stubGenerator_x86_64_string.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, Intel Corporation. All rights reserved.
+ * Copyright (c) 2024, 2026, Intel Corporation. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -1330,10 +1330,12 @@ static void big_case_loop_helper(bool sizeKnown, int size, Label &noMatch, Label
// Clarification: The BYTE_K compare above compares haystack[(n-32):(n-1)]. We need to
// compare haystack[(k-1):(k-1+31)]. Subtracting either index gives shift value of
// (k + 31 - n): x = (k-1+31)-(n-1) = k-1+31-n+1 = k+31-n.
+ // When isU is set, similarly, shift is from haystack[(n-32):(n-1)] to [(k-2):(k-2+31)]
+
if (sizeKnown) {
- __ movl(temp2, 31 + size);
+ __ movl(temp2, (isU ? 30 : 31) + size);
} else {
- __ movl(temp2, 31);
+ __ movl(temp2, isU ? 30 : 31);
__ addl(temp2, needleLen);
}
__ subl(temp2, hsLength);
diff --git a/src/hotspot/cpu/x86/methodHandles_x86.cpp b/src/hotspot/cpu/x86/methodHandles_x86.cpp
index f3683e7d09c..030091b3fe2 100644
--- a/src/hotspot/cpu/x86/methodHandles_x86.cpp
+++ b/src/hotspot/cpu/x86/methodHandles_x86.cpp
@@ -137,7 +137,7 @@ void MethodHandles::verify_method(MacroAssembler* _masm, Register method, Regist
case vmIntrinsicID::_invokeBasic:
// Require compiled LambdaForm class to be fully initialized.
__ cmpb(Address(method_holder, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized);
- __ jccb(Assembler::equal, L_ok);
+ __ jcc(Assembler::equal, L_ok);
break;
case vmIntrinsicID::_linkToStatic:
@@ -154,7 +154,7 @@ void MethodHandles::verify_method(MacroAssembler* _masm, Register method, Regist
// init_state check failed, but it may be an abstract interface method
__ load_unsigned_short(temp, Address(method, Method::access_flags_offset()));
__ testl(temp, JVM_ACC_ABSTRACT);
- __ jccb(Assembler::notZero, L_ok);
+ __ jcc(Assembler::notZero, L_ok);
break;
default:
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
index 2f1e46f3132..2ba5fc69e88 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp
@@ -393,6 +393,8 @@ class StubGenerator: public StubCodeGenerator {
XMMRegister xmm5, XMMRegister xmm6, XMMRegister xmm7, XMMRegister xmm8);
void ghash_last_8_avx2(Register subkeyHtbl);
+ void check_key_offset(Register key, int offset, int load_size);
+
// Load key and shuffle operation
void ev_load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask);
void ev_load_key(XMMRegister xmmdst, Register key, int offset, Register rscratch);
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp
index 26bf3f7d725..aa523cba524 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp
@@ -1759,25 +1759,43 @@ void StubGenerator::roundDeclast(XMMRegister xmm_reg) {
__ vaesdeclast(xmm8, xmm8, xmm_reg, Assembler::AVX_512bit);
}
+// Check incoming byte offset against the int[] len. key is the pointer to the int[0].
+// This check happens often, so it is important for it to be very compact.
+void StubGenerator::check_key_offset(Register key, int offset, int load_size) {
+#ifdef ASSERT
+ Address key_length(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT));
+ assert((offset + load_size) % 4 == 0, "Alignment is good: %d + %d", offset, load_size);
+ int end_offset = (offset + load_size) / 4;
+ Label L_good;
+ __ cmpl(key_length, end_offset);
+ __ jccb(Assembler::greaterEqual, L_good);
+ __ hlt();
+ __ bind(L_good);
+#endif
+}
// Utility routine for loading a 128-bit key word in little endian format
void StubGenerator::load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask) {
+ check_key_offset(key, offset, 16);
__ movdqu(xmmdst, Address(key, offset));
__ pshufb(xmmdst, xmm_shuf_mask);
}
void StubGenerator::load_key(XMMRegister xmmdst, Register key, int offset, Register rscratch) {
+ check_key_offset(key, offset, 16);
__ movdqu(xmmdst, Address(key, offset));
__ pshufb(xmmdst, ExternalAddress(key_shuffle_mask_addr()), rscratch);
}
void StubGenerator::ev_load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask) {
+ check_key_offset(key, offset, 16);
__ movdqu(xmmdst, Address(key, offset));
__ pshufb(xmmdst, xmm_shuf_mask);
__ evshufi64x2(xmmdst, xmmdst, xmmdst, 0x0, Assembler::AVX_512bit);
}
void StubGenerator::ev_load_key(XMMRegister xmmdst, Register key, int offset, Register rscratch) {
+ check_key_offset(key, offset, 16);
__ movdqu(xmmdst, Address(key, offset));
__ pshufb(xmmdst, ExternalAddress(key_shuffle_mask_addr()), rscratch);
__ evshufi64x2(xmmdst, xmmdst, xmmdst, 0x0, Assembler::AVX_512bit);
@@ -3205,12 +3223,12 @@ void StubGenerator::ghash16_encrypt_parallel16_avx512(Register in, Register out,
//AES round 9
roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15);
- ev_load_key(AESKEY2, key, 11 * 16, rbx);
//AES rounds up to 11 (AES192) or 13 (AES256)
//AES128 is done
__ cmpl(NROUNDS, 52);
__ jcc(Assembler::less, last_aes_rnd);
__ bind(aes_192);
+ ev_load_key(AESKEY2, key, 11 * 16, rbx);
roundEncode(AESKEY1, B00_03, B04_07, B08_11, B12_15);
ev_load_key(AESKEY1, key, 12 * 16, rbx);
roundEncode(AESKEY2, B00_03, B04_07, B08_11, B12_15);
@@ -3506,10 +3524,10 @@ void StubGenerator::aesgcm_avx512(Register in, Register len, Register ct, Regist
false, true, false, false, false, ghashin_offset, aesout_offset, HashKey_32);
ghash16_avx512(false, true, false, false, true, in, pos, avx512_subkeyHtbl, AAD_HASHx, SHUF_MASK, stack_offset, 16 * 16, 0, HashKey_16);
+ __ addl(pos, 16 * 16);
__ bind(MESG_BELOW_32_BLKS);
__ subl(len, 16 * 16);
- __ addl(pos, 16 * 16);
gcm_enc_dec_last_avx512(len, in, pos, AAD_HASHx, SHUF_MASK, avx512_subkeyHtbl, ghashin_offset, HashKey_16, true, true);
__ bind(GHASH_DONE);
@@ -3998,13 +4016,15 @@ void StubGenerator::aesgcm_avx2(Register in, Register len, Register ct, Register
const Register rounds = r10;
const XMMRegister ctr_blockx = xmm9;
const XMMRegister aad_hashx = xmm8;
- Label encrypt_done, encrypt_by_8_new, encrypt_by_8;
+ Label encrypt_done, encrypt_by_8_new, encrypt_by_8, exit;
//This routine should be called only for message sizes of 128 bytes or more.
//Macro flow:
//process 8 16 byte blocks in initial_num_blocks.
//process 8 16 byte blocks at a time until all are done 'encrypt_by_8_new followed by ghash_last_8'
__ xorl(pos, pos);
+ __ cmpl(len, 128);
+ __ jcc(Assembler::less, exit);
//Generate 8 constants for htbl
generateHtbl_8_block_avx2(subkeyHtbl);
@@ -4072,6 +4092,7 @@ void StubGenerator::aesgcm_avx2(Register in, Register len, Register ct, Register
__ vpxor(xmm0, xmm0, xmm0, Assembler::AVX_128bit);
__ vpxor(xmm13, xmm13, xmm13, Assembler::AVX_128bit);
+ __ bind(exit);
}
#undef __
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp
index b48ed80788b..f59647230ee 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_exp.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2016, 2024, Intel Corporation. All rights reserved.
-* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
+* Copyright (C) 2021, Tencent. All rights reserved.
* Intel Math Library (LIBM) Source Code
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp
index 6aacfaaea03..e6e014fbf1b 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_log.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2016, 2024, Intel Corporation. All rights reserved.
-* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
+* Copyright (C) 2021, Tencent. All rights reserved.
* Intel Math Library (LIBM) Source Code
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp
index 4029e53d1b1..fb83f4f7d42 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_pow.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2016, 2024, Intel Corporation. All rights reserved.
-* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
+* Copyright (C) 2021, Tencent. All rights reserved.
* Intel Math Library (LIBM) Source Code
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
diff --git a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp
index 9ea4aeeccfa..46af93e9760 100644
--- a/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp
+++ b/src/hotspot/cpu/x86/templateInterpreterGenerator_x86_64.cpp
@@ -465,13 +465,19 @@ address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::M
__ call_VM_leaf0(CAST_FROM_FN_PTR(address, SharedRuntime::dtan));
}
} else if (kind == Interpreter::java_lang_math_tanh) {
- assert(StubRoutines::dtanh() != nullptr, "not initialized");
+ if (StubRoutines::dtanh() != nullptr) {
__ movdbl(xmm0, Address(rsp, wordSize));
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dtanh())));
+ } else {
+ return nullptr; // Fallback to default implementation
+ }
} else if (kind == Interpreter::java_lang_math_cbrt) {
- assert(StubRoutines::dcbrt() != nullptr, "not initialized");
- __ movdbl(xmm0, Address(rsp, wordSize));
- __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dcbrt())));
+ if (StubRoutines::dcbrt() != nullptr) {
+ __ movdbl(xmm0, Address(rsp, wordSize));
+ __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::dcbrt())));
+ } else {
+ return nullptr; // Fallback to default implementation
+ }
} else if (kind == Interpreter::java_lang_math_abs) {
assert(StubRoutines::x86::double_sign_mask() != nullptr, "not initialized");
__ movdbl(xmm0, Address(rsp, wordSize));
diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp
index 5011eb45991..8fc9c2f9eaa 100644
--- a/src/hotspot/cpu/x86/vm_version_x86.cpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.cpp
@@ -138,7 +138,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator {
const uint32_t CPU_FAMILY_486 = (4 << CPU_FAMILY_SHIFT);
bool use_evex = FLAG_IS_DEFAULT(UseAVX) || (UseAVX > 2);
- Label detect_486, cpu486, detect_586, std_cpuid1, std_cpuid4, std_cpuid24;
+ Label detect_486, cpu486, detect_586, std_cpuid1, std_cpuid4, std_cpuid24, std_cpuid29;
Label sef_cpuid, sefsl1_cpuid, ext_cpuid, ext_cpuid1, ext_cpuid5, ext_cpuid7;
Label ext_cpuid8, done, wrapup, vector_save_restore, apx_save_restore_warning;
Label legacy_setup, save_restore_except, legacy_save_restore, start_simd_check;
@@ -337,6 +337,16 @@ class VM_Version_StubGenerator: public StubCodeGenerator {
__ movl(Address(rsi, 0), rax);
__ movl(Address(rsi, 4), rdx);
+ //
+ // cpuid(0x29) APX NCI NDD NF (EAX = 29H, ECX = 0).
+ //
+ __ bind(std_cpuid29);
+ __ movl(rax, 0x29);
+ __ movl(rcx, 0);
+ __ cpuid();
+ __ lea(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid29_offset())));
+ __ movl(Address(rsi, 0), rbx);
+
//
// cpuid(0x24) Converged Vector ISA Main Leaf (EAX = 24H, ECX = 0).
//
@@ -1015,16 +1025,6 @@ void VM_Version::get_processor_features() {
_features.clear_feature(CPU_AVX10_2);
}
- // Currently APX support is only enabled for targets supporting AVX512VL feature.
- bool apx_supported = os_supports_apx_egprs() && supports_apx_f() && supports_avx512vl();
- if (UseAPX && !apx_supported) {
- warning("UseAPX is not supported on this CPU, setting it to false");
- FLAG_SET_DEFAULT(UseAPX, false);
- }
-
- if (!UseAPX) {
- _features.clear_feature(CPU_APX_F);
- }
if (UseAVX < 2) {
_features.clear_feature(CPU_AVX2);
@@ -1048,6 +1048,7 @@ void VM_Version::get_processor_features() {
_features.clear_feature(CPU_VZEROUPPER);
_features.clear_feature(CPU_AVX512BW);
_features.clear_feature(CPU_AVX512VL);
+ _features.clear_feature(CPU_APX_F);
_features.clear_feature(CPU_AVX512DQ);
_features.clear_feature(CPU_AVX512_VNNI);
_features.clear_feature(CPU_AVX512_VAES);
@@ -1067,8 +1068,20 @@ void VM_Version::get_processor_features() {
}
}
+ // Currently APX support is only enabled for targets supporting AVX512VL feature.
+ bool apx_supported = os_supports_apx_egprs() && supports_apx_f() && supports_avx512vl();
+ if (UseAPX && !apx_supported) {
+ warning("UseAPX is not supported on this CPU, setting it to false");
+ FLAG_SET_DEFAULT(UseAPX, false);
+ }
+
+ if (!UseAPX) {
+ _features.clear_feature(CPU_APX_F);
+ }
+
if (FLAG_IS_DEFAULT(IntelJccErratumMitigation)) {
_has_intel_jcc_erratum = compute_has_intel_jcc_erratum();
+ FLAG_SET_ERGO(IntelJccErratumMitigation, _has_intel_jcc_erratum);
} else {
_has_intel_jcc_erratum = IntelJccErratumMitigation;
}
@@ -2932,7 +2945,8 @@ VM_Version::VM_Features VM_Version::CpuidInfo::feature_flags() const {
if (std_cpuid1_ecx.bits.popcnt != 0)
vm_features.set_feature(CPU_POPCNT);
if (sefsl1_cpuid7_edx.bits.apx_f != 0 &&
- xem_xcr0_eax.bits.apx_f != 0) {
+ xem_xcr0_eax.bits.apx_f != 0 &&
+ std_cpuid29_ebx.bits.apx_nci_ndd_nf != 0) {
vm_features.set_feature(CPU_APX_F);
}
if (std_cpuid1_ecx.bits.avx != 0 &&
diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp
index 3c8971e474b..78ae45ef4f8 100644
--- a/src/hotspot/cpu/x86/vm_version_x86.hpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.hpp
@@ -303,6 +303,14 @@ class VM_Version : public Abstract_VM_Version {
} bits;
};
+ union StdCpuidEax29Ecx0 {
+ uint32_t value;
+ struct {
+ uint32_t apx_nci_ndd_nf : 1,
+ : 31;
+ } bits;
+ };
+
union StdCpuid24MainLeafEax {
uint32_t value;
struct {
@@ -587,6 +595,10 @@ class VM_Version : public Abstract_VM_Version {
StdCpuid24MainLeafEax std_cpuid24_eax;
StdCpuid24MainLeafEbx std_cpuid24_ebx;
+ // cpuid function 0x29 APX Advanced Performance Extensions Leaf
+ // eax = 0x29, ecx = 0
+ StdCpuidEax29Ecx0 std_cpuid29_ebx;
+
// cpuid function 0xB (processor topology)
// ecx = 0
uint32_t tpl_cpuidB0_eax;
@@ -707,6 +719,7 @@ class VM_Version : public Abstract_VM_Version {
static ByteSize std_cpuid0_offset() { return byte_offset_of(CpuidInfo, std_max_function); }
static ByteSize std_cpuid1_offset() { return byte_offset_of(CpuidInfo, std_cpuid1_eax); }
static ByteSize std_cpuid24_offset() { return byte_offset_of(CpuidInfo, std_cpuid24_eax); }
+ static ByteSize std_cpuid29_offset() { return byte_offset_of(CpuidInfo, std_cpuid29_ebx); }
static ByteSize dcp_cpuid4_offset() { return byte_offset_of(CpuidInfo, dcp_cpuid4_eax); }
static ByteSize sef_cpuid7_offset() { return byte_offset_of(CpuidInfo, sef_cpuid7_eax); }
static ByteSize sefsl1_cpuid7_offset() { return byte_offset_of(CpuidInfo, sefsl1_cpuid7_eax); }
@@ -756,7 +769,9 @@ class VM_Version : public Abstract_VM_Version {
_features.set_feature(CPU_SSE2);
_features.set_feature(CPU_VZEROUPPER);
}
- static void set_apx_cpuFeatures() { _features.set_feature(CPU_APX_F); }
+ static void set_apx_cpuFeatures() {
+ _features.set_feature(CPU_APX_F);
+ }
static void set_bmi_cpuFeatures() {
_features.set_feature(CPU_BMI1);
_features.set_feature(CPU_BMI2);
diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp
index e7b401701ac..50a219f0824 100644
--- a/src/hotspot/os/aix/os_aix.cpp
+++ b/src/hotspot/os/aix/os_aix.cpp
@@ -2504,7 +2504,7 @@ static bool thread_cpu_time_unchecked(Thread* thread, jlong* p_sys_time, jlong*
dummy, &dummy_size) == 0) {
tid = pinfo.__pi_tid;
} else {
- tty->print_cr("pthread_getthrds_np failed.");
+ tty->print_cr("pthread_getthrds_np failed, errno: %d.", errno);
error = true;
}
@@ -2515,7 +2515,7 @@ static bool thread_cpu_time_unchecked(Thread* thread, jlong* p_sys_time, jlong*
sys_time = thrdentry.ti_ru.ru_stime.tv_sec * 1000000000LL + thrdentry.ti_ru.ru_stime.tv_usec * 1000LL;
user_time = thrdentry.ti_ru.ru_utime.tv_sec * 1000000000LL + thrdentry.ti_ru.ru_utime.tv_usec * 1000LL;
} else {
- tty->print_cr("pthread_getthrds_np failed.");
+ tty->print_cr("getthrds64 failed, errno: %d.", errno);
error = true;
}
}
diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
index 3535d027dbc..868725fdb67 100644
--- a/src/hotspot/os/bsd/os_bsd.cpp
+++ b/src/hotspot/os/bsd/os_bsd.cpp
@@ -154,7 +154,8 @@ julong os::Bsd::available_memory() {
assert(kerr == KERN_SUCCESS,
"host_statistics64 failed - check mach_host_self() and count");
if (kerr == KERN_SUCCESS) {
- available = vmstat.free_count * os::vm_page_size();
+ // free_count is just a lowerbound, other page categories can be freed too and make memory available
+ available = (vmstat.free_count + vmstat.inactive_count + vmstat.purgeable_count) * os::vm_page_size();
}
#endif
return available;
diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp
index 068914d7049..a9cabc87335 100644
--- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp
+++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp
@@ -62,7 +62,7 @@ CgroupSubsystem* CgroupSubsystemFactory::create() {
CgroupV1MemoryController* memory = nullptr;
CgroupV1Controller* cpuset = nullptr;
CgroupV1CpuController* cpu = nullptr;
- CgroupV1Controller* cpuacct = nullptr;
+ CgroupV1CpuacctController* cpuacct = nullptr;
CgroupV1Controller* pids = nullptr;
CgroupInfo cg_infos[CG_INFO_LENGTH];
u1 cg_type_flags = INVALID_CGROUPS_GENERIC;
@@ -105,9 +105,10 @@ CgroupSubsystem* CgroupSubsystemFactory::create() {
CgroupV2CpuController* cpu = new CgroupV2CpuController(CgroupV2Controller(cg_infos[CPU_IDX]._mount_path,
cg_infos[CPU_IDX]._cgroup_path,
cg_infos[CPU_IDX]._read_only));
+ CgroupV2CpuacctController* cpuacct = new CgroupV2CpuacctController(cpu);
log_debug(os, container)("Detected cgroups v2 unified hierarchy");
cleanup(cg_infos);
- return new CgroupV2Subsystem(memory, cpu, mem_other);
+ return new CgroupV2Subsystem(memory, cpu, cpuacct, mem_other);
}
/*
@@ -150,7 +151,7 @@ CgroupSubsystem* CgroupSubsystemFactory::create() {
cpu = new CgroupV1CpuController(CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only));
cpu->set_subsystem_path(info._cgroup_path);
} else if (strcmp(info._name, "cpuacct") == 0) {
- cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only);
+ cpuacct = new CgroupV1CpuacctController(CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only));
cpuacct->set_subsystem_path(info._cgroup_path);
} else if (strcmp(info._name, "pids") == 0) {
pids = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only);
@@ -856,6 +857,10 @@ jlong CgroupSubsystem::memory_soft_limit_in_bytes() {
return memory_controller()->controller()->memory_soft_limit_in_bytes(phys_mem);
}
+jlong CgroupSubsystem::memory_throttle_limit_in_bytes() {
+ return memory_controller()->controller()->memory_throttle_limit_in_bytes();
+}
+
jlong CgroupSubsystem::memory_usage_in_bytes() {
return memory_controller()->controller()->memory_usage_in_bytes();
}
@@ -884,6 +889,10 @@ int CgroupSubsystem::cpu_shares() {
return cpu_controller()->controller()->cpu_shares();
}
+jlong CgroupSubsystem::cpu_usage_in_micros() {
+ return cpuacct_controller()->cpu_usage_in_micros();
+}
+
void CgroupSubsystem::print_version_specific_info(outputStream* st) {
julong phys_mem = os::Linux::physical_memory();
memory_controller()->controller()->print_version_specific_info(st, phys_mem);
diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
index 53b178397f8..82fc87ef4f4 100644
--- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
+++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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
@@ -102,6 +102,17 @@
log_trace(os, container)(log_string " is: %s", retval); \
}
+#define CONTAINER_READ_NUMERICAL_KEY_VALUE_CHECKED(controller, filename, key, log_string, retval) \
+{ \
+ bool is_ok; \
+ is_ok = controller->read_numerical_key_value(filename, key, &retval); \
+ if (!is_ok) { \
+ log_trace(os, container)(log_string " failed: %d", OSCONTAINER_ERROR); \
+ return OSCONTAINER_ERROR; \
+ } \
+ log_trace(os, container)(log_string " is: " JULONG_FORMAT, retval); \
+}
+
class CgroupController: public CHeapObj {
protected:
char* _cgroup_path;
@@ -216,6 +227,18 @@ class CgroupCpuController: public CHeapObj {
virtual const char* cgroup_path() = 0;
};
+// Pure virtual class representing version agnostic CPU accounting controllers
+class CgroupCpuacctController: public CHeapObj {
+ public:
+ virtual jlong cpu_usage_in_micros() = 0;
+ virtual bool needs_hierarchy_adjustment() = 0;
+ virtual bool is_read_only() = 0;
+ virtual const char* subsystem_path() = 0;
+ virtual void set_subsystem_path(const char* cgroup_path) = 0;
+ virtual const char* mount_point() = 0;
+ virtual const char* cgroup_path() = 0;
+};
+
// Pure virtual class representing version agnostic memory controllers
class CgroupMemoryController: public CHeapObj {
public:
@@ -224,6 +247,7 @@ class CgroupMemoryController: public CHeapObj {
virtual jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) = 0;
virtual jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap) = 0;
virtual jlong memory_soft_limit_in_bytes(julong upper_bound) = 0;
+ virtual jlong memory_throttle_limit_in_bytes() = 0;
virtual jlong memory_max_usage_in_bytes() = 0;
virtual jlong rss_usage_in_bytes() = 0;
virtual jlong cache_usage_in_bytes() = 0;
@@ -250,15 +274,19 @@ class CgroupSubsystem: public CHeapObj {
virtual const char * container_type() = 0;
virtual CachingCgroupController* memory_controller() = 0;
virtual CachingCgroupController* cpu_controller() = 0;
+ virtual CgroupCpuacctController* cpuacct_controller() = 0;
int cpu_quota();
int cpu_period();
int cpu_shares();
+ jlong cpu_usage_in_micros();
+
jlong memory_usage_in_bytes();
jlong memory_and_swap_limit_in_bytes();
jlong memory_and_swap_usage_in_bytes();
jlong memory_soft_limit_in_bytes();
+ jlong memory_throttle_limit_in_bytes();
jlong memory_max_usage_in_bytes();
jlong rss_usage_in_bytes();
jlong cache_usage_in_bytes();
diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp
index 8d9c3edb72a..ce5ef3614d1 100644
--- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp
+++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp
@@ -123,6 +123,12 @@ void CgroupV1Controller::set_subsystem_path(const char* cgroup_path) {
}
}
+jlong CgroupV1MemoryController::uses_mem_hierarchy() {
+ julong use_hierarchy;
+ CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.use_hierarchy", "Use Hierarchy", use_hierarchy);
+ return (jlong)use_hierarchy;
+}
+
/*
* The common case, containers, we have _root == _cgroup_path, and thus set the
* controller path to the _mount_point. This is where the limits are exposed in
@@ -159,13 +165,13 @@ void verbose_log(julong read_mem_limit, julong host_mem) {
jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) {
julong memlimit;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.limit_in_bytes", "Memory Limit", memlimit);
- if (memlimit >= phys_mem) {
- verbose_log(memlimit, phys_mem);
- return (jlong)-1;
- } else {
- verbose_log(memlimit, phys_mem);
- return (jlong)memlimit;
+ if (memlimit >= phys_mem && uses_mem_hierarchy()) {
+ CONTAINER_READ_NUMERICAL_KEY_VALUE_CHECKED(reader(), "/memory.stat",
+ "hierarchical_memory_limit", "Hierarchical Memory Limit",
+ memlimit);
}
+ verbose_log(memlimit, phys_mem);
+ return (jlong)((memlimit < phys_mem) ? memlimit : -1);
}
/* read_mem_swap
@@ -183,12 +189,13 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) {
jlong CgroupV1MemoryController::read_mem_swap(julong host_total_memsw) {
julong memswlimit;
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.memsw.limit_in_bytes", "Memory and Swap Limit", memswlimit);
- if (memswlimit >= host_total_memsw) {
- log_trace(os, container)("Memory and Swap Limit is: Unlimited");
- return (jlong)-1;
- } else {
- return (jlong)memswlimit;
+ if (memswlimit >= host_total_memsw && uses_mem_hierarchy()) {
+ CONTAINER_READ_NUMERICAL_KEY_VALUE_CHECKED(reader(), "/memory.stat",
+ "hierarchical_memsw_limit", "Hierarchical Memory and Swap Limit",
+ memswlimit);
}
+ verbose_log(memswlimit, host_total_memsw);
+ return (jlong)((memswlimit < host_total_memsw) ? memswlimit : -1);
}
jlong CgroupV1MemoryController::memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) {
@@ -248,10 +255,16 @@ jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) {
}
}
+jlong CgroupV1MemoryController::memory_throttle_limit_in_bytes() {
+ // Log this string at trace level so as to make tests happy.
+ log_trace(os, container)("Memory Throttle Limit is not supported.");
+ return OSCONTAINER_ERROR; // not supported
+}
+
// Constructor
CgroupV1Subsystem::CgroupV1Subsystem(CgroupV1Controller* cpuset,
CgroupV1CpuController* cpu,
- CgroupV1Controller* cpuacct,
+ CgroupV1CpuacctController* cpuacct,
CgroupV1Controller* pids,
CgroupV1MemoryController* memory) :
_cpuset(cpuset),
@@ -416,6 +429,13 @@ int CgroupV1CpuController::cpu_shares() {
return shares_int;
}
+jlong CgroupV1CpuacctController::cpu_usage_in_micros() {
+ julong cpu_usage;
+ CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpuacct.usage", "CPU Usage", cpu_usage);
+ // Output is in nanoseconds, convert to microseconds.
+ return (jlong)cpu_usage / 1000;
+}
+
/* pids_max
*
* Return the maximum number of tasks available to the process
diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp
index 0c191ab91c7..a986178f5bb 100644
--- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp
+++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 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
@@ -82,6 +82,7 @@ class CgroupV1MemoryController final : public CgroupMemoryController {
jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) override;
jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap) override;
jlong memory_soft_limit_in_bytes(julong upper_bound) override;
+ jlong memory_throttle_limit_in_bytes() override;
jlong memory_max_usage_in_bytes() override;
jlong rss_usage_in_bytes() override;
jlong cache_usage_in_bytes() override;
@@ -99,6 +100,7 @@ class CgroupV1MemoryController final : public CgroupMemoryController {
const char* mount_point() override { return reader()->mount_point(); }
const char* cgroup_path() override { return reader()->cgroup_path(); }
private:
+ jlong uses_mem_hierarchy();
jlong read_mem_swappiness();
jlong read_mem_swap(julong host_total_memsw);
@@ -140,12 +142,41 @@ class CgroupV1CpuController final : public CgroupCpuController {
}
};
+class CgroupV1CpuacctController final : public CgroupCpuacctController {
+
+ private:
+ CgroupV1Controller _reader;
+ CgroupV1Controller* reader() { return &_reader; }
+ public:
+ jlong cpu_usage_in_micros() override;
+ void set_subsystem_path(const char *cgroup_path) override {
+ reader()->set_subsystem_path(cgroup_path);
+ }
+ bool is_read_only() override {
+ return reader()->is_read_only();
+ }
+ const char* subsystem_path() override {
+ return reader()->subsystem_path();
+ }
+ const char* mount_point() override {
+ return reader()->mount_point();
+ }
+ bool needs_hierarchy_adjustment() override {
+ return reader()->needs_hierarchy_adjustment();
+ }
+ const char* cgroup_path() override { return reader()->cgroup_path(); }
+
+ public:
+ CgroupV1CpuacctController(const CgroupV1Controller& reader) : _reader(reader) {
+ }
+};
+
class CgroupV1Subsystem: public CgroupSubsystem {
public:
CgroupV1Subsystem(CgroupV1Controller* cpuset,
CgroupV1CpuController* cpu,
- CgroupV1Controller* cpuacct,
+ CgroupV1CpuacctController* cpuacct,
CgroupV1Controller* pids,
CgroupV1MemoryController* memory);
@@ -165,13 +196,14 @@ class CgroupV1Subsystem: public CgroupSubsystem {
}
CachingCgroupController* memory_controller() { return _memory; }
CachingCgroupController* cpu_controller() { return _cpu; }
+ CgroupCpuacctController* cpuacct_controller() { return _cpuacct; }
private:
/* controllers */
CachingCgroupController* _memory = nullptr;
CgroupV1Controller* _cpuset = nullptr;
CachingCgroupController* _cpu = nullptr;
- CgroupV1Controller* _cpuacct = nullptr;
+ CgroupV1CpuacctController* _cpuacct = nullptr;
CgroupV1Controller* _pids = nullptr;
};
diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp
index cbadbb9db02..6472fdfccc5 100644
--- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp
+++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, 2025, Red Hat Inc.
+ * Copyright (c) 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
@@ -114,12 +115,14 @@ int CgroupV2CpuController::cpu_quota() {
// Constructor
CgroupV2Subsystem::CgroupV2Subsystem(CgroupV2MemoryController * memory,
CgroupV2CpuController* cpu,
+ CgroupV2CpuacctController* cpuacct,
CgroupV2Controller unified) :
_unified(unified) {
CgroupUtil::adjust_controller(memory);
CgroupUtil::adjust_controller(cpu);
_memory = new CachingCgroupController(memory);
_cpu = new CachingCgroupController(cpu);
+ _cpuacct = cpuacct;
}
bool CgroupV2Subsystem::is_containerized() {
@@ -152,6 +155,17 @@ int CgroupV2CpuController::cpu_period() {
return period;
}
+jlong CgroupV2CpuController::cpu_usage_in_micros() {
+ julong cpu_usage;
+ bool is_ok = reader()->read_numerical_key_value("/cpu.stat", "usage_usec", &cpu_usage);
+ if (!is_ok) {
+ log_trace(os, container)("CPU Usage failed: %d", OSCONTAINER_ERROR);
+ return OSCONTAINER_ERROR;
+ }
+ log_trace(os, container)("CPU Usage is: " JULONG_FORMAT, cpu_usage);
+ return (jlong)cpu_usage;
+}
+
/* memory_usage_in_bytes
*
* Return the amount of used memory used by this cgroup and descendents
@@ -173,10 +187,16 @@ jlong CgroupV2MemoryController::memory_soft_limit_in_bytes(julong phys_mem) {
return mem_soft_limit;
}
+jlong CgroupV2MemoryController::memory_throttle_limit_in_bytes() {
+ jlong mem_throttle_limit;
+ CONTAINER_READ_NUMBER_CHECKED_MAX(reader(), "/memory.high", "Memory Throttle Limit", mem_throttle_limit);
+ return mem_throttle_limit;
+}
+
jlong CgroupV2MemoryController::memory_max_usage_in_bytes() {
- // Log this string at trace level so as to make tests happy.
- log_trace(os, container)("Maximum Memory Usage is not supported.");
- return OSCONTAINER_ERROR; // not supported
+ julong mem_max_usage;
+ CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.peak", "Maximum Memory Usage", mem_max_usage);
+ return mem_max_usage;
}
jlong CgroupV2MemoryController::rss_usage_in_bytes() {
diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp
index 56dcadd670f..e26f37925ca 100644
--- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp
+++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, 2024, Red Hat Inc.
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -61,6 +62,34 @@ class CgroupV2CpuController: public CgroupCpuController {
int cpu_quota() override;
int cpu_period() override;
int cpu_shares() override;
+ jlong cpu_usage_in_micros();
+ bool is_read_only() override {
+ return reader()->is_read_only();
+ }
+ const char* subsystem_path() override {
+ return reader()->subsystem_path();
+ }
+ bool needs_hierarchy_adjustment() override {
+ return reader()->needs_hierarchy_adjustment();
+ }
+ void set_subsystem_path(const char* cgroup_path) override {
+ reader()->set_subsystem_path(cgroup_path);
+ }
+ const char* mount_point() override { return reader()->mount_point(); }
+ const char* cgroup_path() override { return reader()->cgroup_path(); }
+};
+
+class CgroupV2CpuacctController: public CgroupCpuacctController {
+ private:
+ CgroupV2CpuController* _reader;
+ CgroupV2CpuController* reader() { return _reader; }
+ public:
+ CgroupV2CpuacctController(CgroupV2CpuController* reader) : _reader(reader) {
+ }
+ // In cgroup v2, cpu usage is a part of the cpu controller.
+ jlong cpu_usage_in_micros() override {
+ return reader()->cpu_usage_in_micros();
+ }
bool is_read_only() override {
return reader()->is_read_only();
}
@@ -89,6 +118,7 @@ class CgroupV2MemoryController final: public CgroupMemoryController {
jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swp) override;
jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swp) override;
jlong memory_soft_limit_in_bytes(julong upper_bound) override;
+ jlong memory_throttle_limit_in_bytes() override;
jlong memory_usage_in_bytes() override;
jlong memory_max_usage_in_bytes() override;
jlong rss_usage_in_bytes() override;
@@ -118,11 +148,14 @@ class CgroupV2Subsystem: public CgroupSubsystem {
CachingCgroupController* _memory = nullptr;
CachingCgroupController* _cpu = nullptr;
+ CgroupCpuacctController* _cpuacct = nullptr;
+
CgroupV2Controller* unified() { return &_unified; }
public:
CgroupV2Subsystem(CgroupV2MemoryController * memory,
CgroupV2CpuController* cpu,
+ CgroupV2CpuacctController* cpuacct,
CgroupV2Controller unified);
char * cpu_cpuset_cpus() override;
@@ -137,6 +170,7 @@ class CgroupV2Subsystem: public CgroupSubsystem {
}
CachingCgroupController* memory_controller() override { return _memory; }
CachingCgroupController* cpu_controller() override { return _cpu; }
+ CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; };
};
#endif // CGROUP_V2_SUBSYSTEM_LINUX_HPP
diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp
index ecac7307c30..2ba4c252659 100644
--- a/src/hotspot/os/linux/osContainer_linux.cpp
+++ b/src/hotspot/os/linux/osContainer_linux.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -122,6 +122,11 @@ jlong OSContainer::memory_soft_limit_in_bytes() {
return cgroup_subsystem->memory_soft_limit_in_bytes();
}
+jlong OSContainer::memory_throttle_limit_in_bytes() {
+ assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
+ return cgroup_subsystem->memory_throttle_limit_in_bytes();
+}
+
jlong OSContainer::memory_usage_in_bytes() {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->memory_usage_in_bytes();
@@ -177,6 +182,11 @@ int OSContainer::cpu_shares() {
return cgroup_subsystem->cpu_shares();
}
+jlong OSContainer::cpu_usage_in_micros() {
+ assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
+ return cgroup_subsystem->cpu_usage_in_micros();
+}
+
jlong OSContainer::pids_max() {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->pids_max();
diff --git a/src/hotspot/os/linux/osContainer_linux.hpp b/src/hotspot/os/linux/osContainer_linux.hpp
index dd29c7a4769..3c270e8ea50 100644
--- a/src/hotspot/os/linux/osContainer_linux.hpp
+++ b/src/hotspot/os/linux/osContainer_linux.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -54,6 +54,7 @@ class OSContainer: AllStatic {
static jlong memory_and_swap_limit_in_bytes();
static jlong memory_and_swap_usage_in_bytes();
static jlong memory_soft_limit_in_bytes();
+ static jlong memory_throttle_limit_in_bytes();
static jlong memory_usage_in_bytes();
static jlong memory_max_usage_in_bytes();
static jlong rss_usage_in_bytes();
@@ -69,6 +70,8 @@ class OSContainer: AllStatic {
static int cpu_shares();
+ static jlong cpu_usage_in_micros();
+
static jlong pids_max();
static jlong pids_current();
};
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index 807014e7b0c..fc0578f4d9b 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -349,11 +349,20 @@ julong os::physical_memory() {
return phys_mem;
}
+// Returns the resident set size (RSS) of the process.
+// Falls back to using VmRSS from /proc/self/status if /proc/self/smaps_rollup is unavailable.
+// Note: On kernels with memory cgroups or shared memory, VmRSS may underreport RSS.
+// Users requiring accurate RSS values should be aware of this limitation.
size_t os::rss() {
size_t size = 0;
- os::Linux::meminfo_t info;
- if (os::Linux::query_process_memory_info(&info)) {
- size = info.vmrss * K;
+ os::Linux::accurate_meminfo_t accurate_info;
+ if (os::Linux::query_accurate_process_memory_info(&accurate_info) && accurate_info.rss != -1) {
+ size = accurate_info.rss * K;
+ } else {
+ os::Linux::meminfo_t info;
+ if (os::Linux::query_process_memory_info(&info)) {
+ size = info.vmrss * K;
+ }
}
return size;
}
@@ -469,13 +478,11 @@ bool os::Linux::get_tick_information(CPUPerfTicks* pticks, int which_logical_cpu
}
#ifndef SYS_gettid
-// i386: 224, amd64: 186, sparc: 143
+// i386: 224, amd64: 186
#if defined(__i386__)
#define SYS_gettid 224
#elif defined(__amd64__)
#define SYS_gettid 186
- #elif defined(__sparc__)
- #define SYS_gettid 143
#else
#error "Define SYS_gettid for this architecture"
#endif
@@ -2346,6 +2353,37 @@ bool os::Linux::query_process_memory_info(os::Linux::meminfo_t* info) {
return false;
}
+// Accurate memory information need Linux 4.14 or newer
+bool os::Linux::query_accurate_process_memory_info(os::Linux::accurate_meminfo_t* info) {
+ FILE* f = os::fopen("/proc/self/smaps_rollup", "r");
+ if (f == nullptr) {
+ return false;
+ }
+
+ const size_t num_values = sizeof(os::Linux::accurate_meminfo_t) / sizeof(size_t);
+ size_t num_found = 0;
+ char buf[256];
+ info->rss = info->pss = info->pssdirty = info->pssanon =
+ info->pssfile = info->pssshmem = info->swap = info->swappss = -1;
+
+ while (::fgets(buf, sizeof(buf), f) != nullptr && num_found < num_values) {
+ if ( (info->rss == -1 && sscanf(buf, "Rss: %zd kB", &info->rss) == 1) ||
+ (info->pss == -1 && sscanf(buf, "Pss: %zd kB", &info->pss) == 1) ||
+ (info->pssdirty == -1 && sscanf(buf, "Pss_Dirty: %zd kB", &info->pssdirty) == 1) ||
+ (info->pssanon == -1 && sscanf(buf, "Pss_Anon: %zd kB", &info->pssanon) == 1) ||
+ (info->pssfile == -1 && sscanf(buf, "Pss_File: %zd kB", &info->pssfile) == 1) ||
+ (info->pssshmem == -1 && sscanf(buf, "Pss_Shmem: %zd kB", &info->pssshmem) == 1) ||
+ (info->swap == -1 && sscanf(buf, "Swap: %zd kB", &info->swap) == 1) ||
+ (info->swappss == -1 && sscanf(buf, "SwapPss: %zd kB", &info->swappss) == 1)
+ )
+ {
+ num_found ++;
+ }
+ }
+ fclose(f);
+ return true;
+}
+
#ifdef __GLIBC__
// For Glibc, print a one-liner with the malloc tunables.
// Most important and popular is MALLOC_ARENA_MAX, but we are
@@ -2430,6 +2468,7 @@ void os::Linux::print_uptime_info(outputStream* st) {
if (ret == 0) {
os::print_dhm(st, "OS uptime:", (long) sinfo.uptime);
}
+ assert(ret == 0, "sysinfo failed: %s", os::strerror(errno));
}
bool os::Linux::print_container_info(outputStream* st) {
@@ -2487,9 +2526,18 @@ bool os::Linux::print_container_info(outputStream* st) {
st->print_cr("%s", i == OSCONTAINER_ERROR ? "not supported" : "no shares");
}
+ jlong j = OSContainer::cpu_usage_in_micros();
+ st->print("cpu_usage_in_micros: ");
+ if (j >= 0) {
+ st->print_cr(JLONG_FORMAT, j);
+ } else {
+ st->print_cr("%s", j == OSCONTAINER_ERROR ? "not supported" : "no usage");
+ }
+
OSContainer::print_container_helper(st, OSContainer::memory_limit_in_bytes(), "memory_limit_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_and_swap_limit_in_bytes(), "memory_and_swap_limit_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_soft_limit_in_bytes(), "memory_soft_limit_in_bytes");
+ OSContainer::print_container_helper(st, OSContainer::memory_throttle_limit_in_bytes(), "memory_throttle_limit_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_usage_in_bytes(), "memory_usage_in_bytes");
OSContainer::print_container_helper(st, OSContainer::memory_max_usage_in_bytes(), "memory_max_usage_in_bytes");
OSContainer::print_container_helper(st, OSContainer::rss_usage_in_bytes(), "rss_usage_in_bytes");
@@ -2497,7 +2545,7 @@ bool os::Linux::print_container_info(outputStream* st) {
OSContainer::print_version_specific_info(st);
- jlong j = OSContainer::pids_max();
+ j = OSContainer::pids_max();
st->print("maximum number of tasks: ");
if (j > 0) {
st->print_cr(JLONG_FORMAT, j);
@@ -2543,16 +2591,18 @@ void os::print_memory_info(outputStream* st) {
// values in struct sysinfo are "unsigned long"
struct sysinfo si;
- sysinfo(&si);
-
+ int ret = sysinfo(&si);
+ assert(ret == 0, "sysinfo failed: %s", os::strerror(errno));
st->print(", physical " UINT64_FORMAT "k",
os::physical_memory() >> 10);
st->print("(" UINT64_FORMAT "k free)",
os::available_memory() >> 10);
- st->print(", swap " UINT64_FORMAT "k",
- ((jlong)si.totalswap * si.mem_unit) >> 10);
- st->print("(" UINT64_FORMAT "k free)",
- ((jlong)si.freeswap * si.mem_unit) >> 10);
+ if (ret == 0) {
+ st->print(", swap " UINT64_FORMAT "k",
+ ((jlong)si.totalswap * si.mem_unit) >> 10);
+ st->print("(" UINT64_FORMAT "k free)",
+ ((jlong)si.freeswap * si.mem_unit) >> 10);
+ }
st->cr();
st->print("Page Sizes: ");
_page_sizes.print_on(st);
@@ -2684,8 +2734,6 @@ const char* search_string = "CPU";
const char* search_string = "cpu";
#elif defined(S390)
const char* search_string = "machine =";
-#elif defined(SPARC)
-const char* search_string = "cpu";
#else
const char* search_string = "Processor";
#endif
@@ -2737,8 +2785,6 @@ void os::get_summary_cpu_info(char* cpuinfo, size_t length) {
strncpy(cpuinfo, LP64_ONLY("RISCV64") NOT_LP64("RISCV32"), length);
#elif defined(S390)
strncpy(cpuinfo, "S390", length);
-#elif defined(SPARC)
- strncpy(cpuinfo, "sparcv9", length);
#elif defined(ZERO_LIBARCH)
strncpy(cpuinfo, ZERO_LIBARCH, length);
#else
@@ -5221,7 +5267,7 @@ int os::get_core_path(char* buffer, size_t bufferSize) {
if (core_pattern[0] == '|') {
written = jio_snprintf(buffer, bufferSize,
- "\"%s\" (or dumping to %s/core.%d)",
+ "\"%s\" (alternatively, falling back to %s/core.%d)",
&core_pattern[1], p, current_process_id());
} else if (pid_pos != nullptr) {
*pid_pos = '\0';
diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp
index bd2e1ea3230..4e208a11300 100644
--- a/src/hotspot/os/linux/os_linux.hpp
+++ b/src/hotspot/os/linux/os_linux.hpp
@@ -181,6 +181,23 @@ class os::Linux {
// fields will contain -1.
static bool query_process_memory_info(meminfo_t* info);
+ // Output structure for query_accurate_process_memory_info() (all values in KB)
+ struct accurate_meminfo_t {
+ ssize_t rss; // current resident set size
+ ssize_t pss; // current proportional set size
+ ssize_t pssdirty; // proportional set size (dirty)
+ ssize_t pssanon; // proportional set size (anonymous mappings)
+ ssize_t pssfile; // proportional set size (file mappings)
+ ssize_t pssshmem; // proportional set size (shared mappings)
+ ssize_t swap; // swapped out
+ ssize_t swappss; // proportional set size (swapped out)
+ };
+
+ // Attempts to query accurate memory information from /proc/self/smaps_rollup and return it in the output structure.
+ // May fail (returns false) or succeed (returns true) but not all output fields are available; unavailable
+ // fields will contain -1.
+ static bool query_accurate_process_memory_info(accurate_meminfo_t* info);
+
// Tells if the user asked for transparent huge pages.
static bool _thp_requested;
diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp
index 448ebce620a..d0c82135797 100644
--- a/src/hotspot/os/posix/os_posix.cpp
+++ b/src/hotspot/os/posix/os_posix.cpp
@@ -107,41 +107,60 @@ size_t os::_os_min_stack_allowed = PTHREAD_STACK_MIN;
// Check core dump limit and report possible place where core can be found
void os::check_core_dump_prerequisites(char* buffer, size_t bufferSize, bool check_only) {
+ stringStream buf(buffer, bufferSize);
if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) {
- jio_snprintf(buffer, bufferSize, "CreateCoredumpOnCrash is disabled from command line");
- VMError::record_coredump_status(buffer, false);
+ buf.print("CreateCoredumpOnCrash is disabled from command line");
+ VMError::record_coredump_status(buf.freeze(), false);
} else {
struct rlimit rlim;
bool success = true;
bool warn = true;
char core_path[PATH_MAX];
if (get_core_path(core_path, PATH_MAX) <= 0) {
- jio_snprintf(buffer, bufferSize, "core.%d (may not exist)", current_process_id());
+ // In the warning message, let the user know.
+ if (check_only) {
+ buf.print("the core path couldn't be determined. It commonly defaults to ");
+ }
+ buf.print("core.%d%s", current_process_id(), check_only ? "" : " (may not exist)");
#ifdef LINUX
} else if (core_path[0] == '"') { // redirect to user process
- jio_snprintf(buffer, bufferSize, "Core dumps may be processed with %s", core_path);
+ if (check_only) {
+ buf.print("core dumps may be further processed by the following: ");
+ } else {
+ buf.print("Determined by the following: ");
+ }
+ buf.print("%s", core_path);
#endif
} else if (getrlimit(RLIMIT_CORE, &rlim) != 0) {
- jio_snprintf(buffer, bufferSize, "%s (may not exist)", core_path);
+ if (check_only) {
+ buf.print("the rlimit couldn't be determined. If resource limits permit, the core dump will be located at ");
+ }
+ buf.print("%s%s", core_path, check_only ? "" : " (may not exist)");
} else {
switch(rlim.rlim_cur) {
case RLIM_INFINITY:
- jio_snprintf(buffer, bufferSize, "%s", core_path);
+ buf.print("%s", core_path);
warn = false;
break;
case 0:
- jio_snprintf(buffer, bufferSize, "Core dumps have been disabled. To enable core dumping, try \"ulimit -c unlimited\" before starting Java again");
+ buf.print("%s dumps have been disabled. To enable core dumping, try \"ulimit -c unlimited\" before starting Java again", check_only ? "core" : "Core");
success = false;
break;
default:
- jio_snprintf(buffer, bufferSize, "%s (max size " UINT64_FORMAT " k). To ensure a full core dump, try \"ulimit -c unlimited\" before starting Java again", core_path, uint64_t(rlim.rlim_cur) / K);
+ if (check_only) {
+ buf.print("core dumps are constrained ");
+ } else {
+ buf.print( "%s ", core_path);
+ }
+ buf.print( "(max size " UINT64_FORMAT " k). To ensure a full core dump, try \"ulimit -c unlimited\" before starting Java again", uint64_t(rlim.rlim_cur) / K);
break;
}
}
+ const char* result = buf.freeze();
if (!check_only) {
- VMError::record_coredump_status(buffer, success);
+ VMError::record_coredump_status(result, success);
} else if (warn) {
- warning("CreateCoredumpOnCrash specified, but %s", buffer);
+ warning("CreateCoredumpOnCrash specified, but %s", result);
}
}
}
diff --git a/src/hotspot/os/posix/perfMemory_posix.cpp b/src/hotspot/os/posix/perfMemory_posix.cpp
index cbbecea3a6a..e3483781794 100644
--- a/src/hotspot/os/posix/perfMemory_posix.cpp
+++ b/src/hotspot/os/posix/perfMemory_posix.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2021 SAP SE. All rights reserved.
+ * Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -950,7 +950,7 @@ static int create_sharedmem_file(const char* dirname, const char* filename, size
if (result == -1 ) break;
if (!os::write(fd, &zero_int, 1)) {
if (errno == ENOSPC) {
- warning("Insufficient space for shared memory file:\n %s\nTry using the -Djava.io.tmpdir= option to select an alternate temp location.\n", filename);
+ warning("Insufficient space for shared memory file: %s/%s\n", dirname, filename);
}
result = OS_ERR;
break;
diff --git a/src/hotspot/os_cpu/aix_ppc/atomic_aix_ppc.hpp b/src/hotspot/os_cpu/aix_ppc/atomic_aix_ppc.hpp
index 722dffc150d..9a314667fcb 100644
--- a/src/hotspot/os_cpu/aix_ppc/atomic_aix_ppc.hpp
+++ b/src/hotspot/os_cpu/aix_ppc/atomic_aix_ppc.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2019 SAP SE. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,395 +23,5 @@
*
*/
-#ifndef OS_CPU_AIX_PPC_ATOMIC_AIX_PPC_HPP
-#define OS_CPU_AIX_PPC_ATOMIC_AIX_PPC_HPP
-
-#ifndef PPC64
-#error "Atomic currently only implemented for PPC64"
-#endif
-
-#include "orderAccess_aix_ppc.hpp"
-#include "utilities/debug.hpp"
-
-// Implementation of class atomic
-
-//
-// machine barrier instructions:
-//
-// - sync two-way memory barrier, aka fence
-// - lwsync orders Store|Store,
-// Load|Store,
-// Load|Load,
-// but not Store|Load
-// - eieio orders memory accesses for device memory (only)
-// - isync invalidates speculatively executed instructions
-// From the POWER ISA 2.06 documentation:
-// "[...] an isync instruction prevents the execution of
-// instructions following the isync until instructions
-// preceding the isync have completed, [...]"
-// From IBM's AIX assembler reference:
-// "The isync [...] instructions causes the processor to
-// refetch any instructions that might have been fetched
-// prior to the isync instruction. The instruction isync
-// causes the processor to wait for all previous instructions
-// to complete. Then any instructions already fetched are
-// discarded and instruction processing continues in the
-// environment established by the previous instructions."
-//
-// semantic barrier instructions:
-// (as defined in orderAccess.hpp)
-//
-// - release orders Store|Store, (maps to lwsync)
-// Load|Store
-// - acquire orders Load|Store, (maps to lwsync)
-// Load|Load
-// - fence orders Store|Store, (maps to sync)
-// Load|Store,
-// Load|Load,
-// Store|Load
-//
-
-inline void pre_membar(atomic_memory_order order) {
- switch (order) {
- case memory_order_relaxed:
- case memory_order_acquire: break;
- case memory_order_release:
- case memory_order_acq_rel: __asm__ __volatile__ ("lwsync" : : : "memory"); break;
- default /*conservative*/ : __asm__ __volatile__ ("sync" : : : "memory"); break;
- }
-}
-
-inline void post_membar(atomic_memory_order order) {
- switch (order) {
- case memory_order_relaxed:
- case memory_order_release: break;
- case memory_order_acquire:
- case memory_order_acq_rel: __asm__ __volatile__ ("isync" : : : "memory"); break;
- default /*conservative*/ : __asm__ __volatile__ ("sync" : : : "memory"); break;
- }
-}
-
-
-template
-struct Atomic::PlatformAdd {
- template
- D add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const;
-
- template
- D fetch_then_add(D volatile* dest, I add_value, atomic_memory_order order) const {
- return add_then_fetch(dest, add_value, order) - add_value;
- }
-};
-
-template<>
-template
-inline D Atomic::PlatformAdd<4>::add_then_fetch(D volatile* dest, I add_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(4 == sizeof(I));
- STATIC_ASSERT(4 == sizeof(D));
-
- D result;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- "1: lwarx %0, 0, %2 \n"
- " add %0, %0, %1 \n"
- " stwcx. %0, 0, %2 \n"
- " bne- 1b \n"
- : /*%0*/"=&r" (result)
- : /*%1*/"r" (add_value), /*%2*/"r" (dest)
- : "cc", "memory" );
-
- post_membar(order);
-
- return result;
-}
-
-
-template<>
-template
-inline D Atomic::PlatformAdd<8>::add_then_fetch(D volatile* dest, I add_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(8 == sizeof(I));
- STATIC_ASSERT(8 == sizeof(D));
-
- D result;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- "1: ldarx %0, 0, %2 \n"
- " add %0, %0, %1 \n"
- " stdcx. %0, 0, %2 \n"
- " bne- 1b \n"
- : /*%0*/"=&r" (result)
- : /*%1*/"r" (add_value), /*%2*/"r" (dest)
- : "cc", "memory" );
-
- post_membar(order);
-
- return result;
-}
-
-template<>
-template
-inline T Atomic::PlatformXchg<4>::operator()(T volatile* dest,
- T exchange_value,
- atomic_memory_order order) const {
- // Note that xchg doesn't necessarily do an acquire
- // (see synchronizer.cpp).
-
- T old_value;
- const uint64_t zero = 0;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- /* atomic loop */
- "1: \n"
- " lwarx %[old_value], %[dest], %[zero] \n"
- " stwcx. %[exchange_value], %[dest], %[zero] \n"
- " bne- 1b \n"
- /* exit */
- "2: \n"
- /* out */
- : [old_value] "=&r" (old_value),
- "=m" (*dest)
- /* in */
- : [dest] "b" (dest),
- [zero] "r" (zero),
- [exchange_value] "r" (exchange_value),
- "m" (*dest)
- /* clobber */
- : "cc",
- "memory"
- );
-
- post_membar(order);
-
- return old_value;
-}
-
-template<>
-template
-inline T Atomic::PlatformXchg<8>::operator()(T volatile* dest,
- T exchange_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(8 == sizeof(T));
- // Note that xchg doesn't necessarily do an acquire
- // (see synchronizer.cpp).
-
- T old_value;
- const uint64_t zero = 0;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- /* atomic loop */
- "1: \n"
- " ldarx %[old_value], %[dest], %[zero] \n"
- " stdcx. %[exchange_value], %[dest], %[zero] \n"
- " bne- 1b \n"
- /* exit */
- "2: \n"
- /* out */
- : [old_value] "=&r" (old_value),
- "=m" (*dest)
- /* in */
- : [dest] "b" (dest),
- [zero] "r" (zero),
- [exchange_value] "r" (exchange_value),
- "m" (*dest)
- /* clobber */
- : "cc",
- "memory"
- );
-
- post_membar(order);
-
- return old_value;
-}
-
-template<>
-template
-inline T Atomic::PlatformCmpxchg<1>::operator()(T volatile* dest,
- T compare_value,
- T exchange_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(1 == sizeof(T));
-
- // Note that cmpxchg guarantees a two-way memory barrier across
- // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not
- // specified otherwise (see atomic.hpp).
-
- // Using 32 bit internally.
- volatile int *dest_base = (volatile int*)((uintptr_t)dest & ~3);
-
-#ifdef VM_LITTLE_ENDIAN
- const unsigned int shift_amount = ((uintptr_t)dest & 3) * 8;
-#else
- const unsigned int shift_amount = ((~(uintptr_t)dest) & 3) * 8;
-#endif
- const unsigned int masked_compare_val = ((unsigned int)(unsigned char)compare_value),
- masked_exchange_val = ((unsigned int)(unsigned char)exchange_value),
- xor_value = (masked_compare_val ^ masked_exchange_val) << shift_amount;
-
- unsigned int old_value, value32;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- /* simple guard */
- " lbz %[old_value], 0(%[dest]) \n"
- " cmpw %[masked_compare_val], %[old_value] \n"
- " bne- 2f \n"
- /* atomic loop */
- "1: \n"
- " lwarx %[value32], 0, %[dest_base] \n"
- /* extract byte and compare */
- " srd %[old_value], %[value32], %[shift_amount] \n"
- " clrldi %[old_value], %[old_value], 56 \n"
- " cmpw %[masked_compare_val], %[old_value] \n"
- " bne- 2f \n"
- /* replace byte and try to store */
- " xor %[value32], %[xor_value], %[value32] \n"
- " stwcx. %[value32], 0, %[dest_base] \n"
- " bne- 1b \n"
- /* exit */
- "2: \n"
- /* out */
- : [old_value] "=&r" (old_value),
- [value32] "=&r" (value32),
- "=m" (*dest),
- "=m" (*dest_base)
- /* in */
- : [dest] "b" (dest),
- [dest_base] "b" (dest_base),
- [shift_amount] "r" (shift_amount),
- [masked_compare_val] "r" (masked_compare_val),
- [xor_value] "r" (xor_value),
- "m" (*dest),
- "m" (*dest_base)
- /* clobber */
- : "cc",
- "memory"
- );
-
- post_membar(order);
-
- return PrimitiveConversions::cast((unsigned char)old_value);
-}
-
-template<>
-template
-inline T Atomic::PlatformCmpxchg<4>::operator()(T volatile* dest,
- T compare_value,
- T exchange_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(4 == sizeof(T));
-
- // Note that cmpxchg guarantees a two-way memory barrier across
- // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not
- // specified otherwise (see atomic.hpp).
-
- T old_value;
- const uint64_t zero = 0;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- /* simple guard */
- " lwz %[old_value], 0(%[dest]) \n"
- " cmpw %[compare_value], %[old_value] \n"
- " bne- 2f \n"
- /* atomic loop */
- "1: \n"
- " lwarx %[old_value], %[dest], %[zero] \n"
- " cmpw %[compare_value], %[old_value] \n"
- " bne- 2f \n"
- " stwcx. %[exchange_value], %[dest], %[zero] \n"
- " bne- 1b \n"
- /* exit */
- "2: \n"
- /* out */
- : [old_value] "=&r" (old_value),
- "=m" (*dest)
- /* in */
- : [dest] "b" (dest),
- [zero] "r" (zero),
- [compare_value] "r" (compare_value),
- [exchange_value] "r" (exchange_value),
- "m" (*dest)
- /* clobber */
- : "cc",
- "memory"
- );
-
- post_membar(order);
-
- return old_value;
-}
-
-template<>
-template
-inline T Atomic::PlatformCmpxchg<8>::operator()(T volatile* dest,
- T compare_value,
- T exchange_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(8 == sizeof(T));
-
- // Note that cmpxchg guarantees a two-way memory barrier across
- // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not
- // specified otherwise (see atomic.hpp).
-
- T old_value;
- const uint64_t zero = 0;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- /* simple guard */
- " ld %[old_value], 0(%[dest]) \n"
- " cmpd %[compare_value], %[old_value] \n"
- " bne- 2f \n"
- /* atomic loop */
- "1: \n"
- " ldarx %[old_value], %[dest], %[zero] \n"
- " cmpd %[compare_value], %[old_value] \n"
- " bne- 2f \n"
- " stdcx. %[exchange_value], %[dest], %[zero] \n"
- " bne- 1b \n"
- /* exit */
- "2: \n"
- /* out */
- : [old_value] "=&r" (old_value),
- "=m" (*dest)
- /* in */
- : [dest] "b" (dest),
- [zero] "r" (zero),
- [compare_value] "r" (compare_value),
- [exchange_value] "r" (exchange_value),
- "m" (*dest)
- /* clobber */
- : "cc",
- "memory"
- );
-
- post_membar(order);
-
- return old_value;
-}
-
-template
-struct Atomic::PlatformOrderedLoad {
- template
- T operator()(const volatile T* p) const {
- T t = Atomic::load(p);
- // Use twi-isync for load_acquire (faster than lwsync).
- __asm__ __volatile__ ("twi 0,%0,0\n isync\n" : : "r" (t) : "memory");
- return t;
- }
-};
-
-#endif // OS_CPU_AIX_PPC_ATOMIC_AIX_PPC_HPP
+// Including inline assembler functions that are shared between multiple PPC64 platforms.
+#include "atomicAccess_ppc.hpp"
diff --git a/src/hotspot/os_cpu/aix_ppc/orderAccess_aix_ppc.hpp b/src/hotspot/os_cpu/aix_ppc/orderAccess_aix_ppc.hpp
index 9ca6c18d5bb..d395add6d69 100644
--- a/src/hotspot/os_cpu/aix_ppc/orderAccess_aix_ppc.hpp
+++ b/src/hotspot/os_cpu/aix_ppc/orderAccess_aix_ppc.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2019 SAP SE. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,62 +23,5 @@
*
*/
-#ifndef OS_CPU_AIX_PPC_ORDERACCESS_AIX_PPC_HPP
-#define OS_CPU_AIX_PPC_ORDERACCESS_AIX_PPC_HPP
-
-// Included in orderAccess.hpp header file.
-
-// Compiler version last used for testing: xlc 12
-// Please update this information when this file changes
-
-// Implementation of class OrderAccess.
-
-//
-// Machine barrier instructions:
-//
-// - sync Two-way memory barrier, aka fence.
-// - lwsync orders Store|Store,
-// Load|Store,
-// Load|Load,
-// but not Store|Load
-// - eieio orders Store|Store
-// - isync Invalidates speculatively executed instructions,
-// but isync may complete before storage accesses
-// associated with instructions preceding isync have
-// been performed.
-//
-// Semantic barrier instructions:
-// (as defined in orderAccess.hpp)
-//
-// - release orders Store|Store, (maps to lwsync)
-// Load|Store
-// - acquire orders Load|Store, (maps to lwsync)
-// Load|Load
-// - fence orders Store|Store, (maps to sync)
-// Load|Store,
-// Load|Load,
-// Store|Load
-//
-
-#define inlasm_sync() __asm__ __volatile__ ("sync" : : : "memory");
-#define inlasm_lwsync() __asm__ __volatile__ ("lwsync" : : : "memory");
-#define inlasm_eieio() __asm__ __volatile__ ("eieio" : : : "memory");
-#define inlasm_isync() __asm__ __volatile__ ("isync" : : : "memory");
-
-inline void OrderAccess::loadload() { inlasm_lwsync(); }
-inline void OrderAccess::storestore() { inlasm_lwsync(); }
-inline void OrderAccess::loadstore() { inlasm_lwsync(); }
-inline void OrderAccess::storeload() { inlasm_sync(); }
-
-inline void OrderAccess::acquire() { inlasm_lwsync(); }
-inline void OrderAccess::release() { inlasm_lwsync(); }
-inline void OrderAccess::fence() { inlasm_sync(); }
-inline void OrderAccess::cross_modify_fence_impl()
- { inlasm_isync(); }
-
-#undef inlasm_sync
-#undef inlasm_lwsync
-#undef inlasm_eieio
-#undef inlasm_isync
-
-#endif // OS_CPU_AIX_PPC_ORDERACCESS_AIX_PPC_HPP
+// Including inline assembler functions that are shared between multiple PPC64 platforms.
+#include "orderAccess_ppc.hpp"
diff --git a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp
index 6c245f8f1a6..677e90883dc 100644
--- a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp
+++ b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp
@@ -209,8 +209,16 @@ frame os::fetch_compiled_frame_from_context(const void* ucVoid) {
}
intptr_t* os::fetch_bcp_from_context(const void* ucVoid) {
- Unimplemented();
- return nullptr;
+ assert(ucVoid != nullptr, "invariant");
+ const ucontext_t* uc = (const ucontext_t*)ucVoid;
+ assert(os::Posix::ucontext_is_interpreter(uc), "invariant");
+#if (FP_REG_NUM == 11)
+ assert(Rbcp == R7, "expected FP=R11, Rbcp=R7");
+ return (intptr_t*)uc->uc_mcontext.arm_r7;
+#else
+ assert(Rbcp == R11, "expected FP=R7, Rbcp=R11");
+ return (intptr_t*)uc->uc_mcontext.arm_fp; // r11
+#endif
}
frame os::get_sender_for_C_frame(frame* fr) {
diff --git a/src/hotspot/os_cpu/linux_ppc/atomic_linux_ppc.hpp b/src/hotspot/os_cpu/linux_ppc/atomic_linux_ppc.hpp
index 1e4eb37cdac..9a314667fcb 100644
--- a/src/hotspot/os_cpu/linux_ppc/atomic_linux_ppc.hpp
+++ b/src/hotspot/os_cpu/linux_ppc/atomic_linux_ppc.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2019 SAP SE. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,373 +23,5 @@
*
*/
-#ifndef OS_CPU_LINUX_PPC_ATOMIC_LINUX_PPC_HPP
-#define OS_CPU_LINUX_PPC_ATOMIC_LINUX_PPC_HPP
-
-#ifndef PPC64
-#error "Atomic currently only implemented for PPC64"
-#endif
-
-#include "orderAccess_linux_ppc.hpp"
-#include "utilities/debug.hpp"
-
-// Implementation of class atomic
-
-//
-// machine barrier instructions:
-//
-// - sync two-way memory barrier, aka fence
-// - lwsync orders Store|Store,
-// Load|Store,
-// Load|Load,
-// but not Store|Load
-// - eieio orders memory accesses for device memory (only)
-// - isync invalidates speculatively executed instructions
-// From the POWER ISA 2.06 documentation:
-// "[...] an isync instruction prevents the execution of
-// instructions following the isync until instructions
-// preceding the isync have completed, [...]"
-// From IBM's AIX assembler reference:
-// "The isync [...] instructions causes the processor to
-// refetch any instructions that might have been fetched
-// prior to the isync instruction. The instruction isync
-// causes the processor to wait for all previous instructions
-// to complete. Then any instructions already fetched are
-// discarded and instruction processing continues in the
-// environment established by the previous instructions."
-//
-// semantic barrier instructions:
-// (as defined in orderAccess.hpp)
-//
-// - release orders Store|Store, (maps to lwsync)
-// Load|Store
-// - acquire orders Load|Store, (maps to lwsync)
-// Load|Load
-// - fence orders Store|Store, (maps to sync)
-// Load|Store,
-// Load|Load,
-// Store|Load
-//
-
-inline void pre_membar(atomic_memory_order order) {
- switch (order) {
- case memory_order_relaxed:
- case memory_order_acquire: break;
- case memory_order_release:
- case memory_order_acq_rel: __asm__ __volatile__ ("lwsync" : : : "memory"); break;
- default /*conservative*/ : __asm__ __volatile__ ("sync" : : : "memory"); break;
- }
-}
-
-inline void post_membar(atomic_memory_order order) {
- switch (order) {
- case memory_order_relaxed:
- case memory_order_release: break;
- case memory_order_acquire:
- case memory_order_acq_rel: __asm__ __volatile__ ("isync" : : : "memory"); break;
- default /*conservative*/ : __asm__ __volatile__ ("sync" : : : "memory"); break;
- }
-}
-
-
-template
-struct Atomic::PlatformAdd {
- template
- D add_then_fetch(D volatile* dest, I add_value, atomic_memory_order order) const;
-
- template
- D fetch_then_add(D volatile* dest, I add_value, atomic_memory_order order) const {
- return add_then_fetch(dest, add_value, order) - add_value;
- }
-};
-
-template<>
-template
-inline D Atomic::PlatformAdd<4>::add_then_fetch(D volatile* dest, I add_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(4 == sizeof(I));
- STATIC_ASSERT(4 == sizeof(D));
-
- D result;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- "1: lwarx %0, 0, %2 \n"
- " add %0, %0, %1 \n"
- " stwcx. %0, 0, %2 \n"
- " bne- 1b \n"
- : /*%0*/"=&r" (result)
- : /*%1*/"r" (add_value), /*%2*/"r" (dest)
- : "cc", "memory" );
-
- post_membar(order);
-
- return result;
-}
-
-
-template<>
-template
-inline D Atomic::PlatformAdd<8>::add_then_fetch(D volatile* dest, I add_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(8 == sizeof(I));
- STATIC_ASSERT(8 == sizeof(D));
-
- D result;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- "1: ldarx %0, 0, %2 \n"
- " add %0, %0, %1 \n"
- " stdcx. %0, 0, %2 \n"
- " bne- 1b \n"
- : /*%0*/"=&r" (result)
- : /*%1*/"r" (add_value), /*%2*/"r" (dest)
- : "cc", "memory" );
-
- post_membar(order);
-
- return result;
-}
-
-template<>
-template
-inline T Atomic::PlatformXchg<4>::operator()(T volatile* dest,
- T exchange_value,
- atomic_memory_order order) const {
- // Note that xchg doesn't necessarily do an acquire
- // (see synchronizer.cpp).
-
- T old_value;
- const uint64_t zero = 0;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- /* atomic loop */
- "1: \n"
- " lwarx %[old_value], %[dest], %[zero] \n"
- " stwcx. %[exchange_value], %[dest], %[zero] \n"
- " bne- 1b \n"
- /* exit */
- "2: \n"
- /* out */
- : [old_value] "=&r" (old_value),
- "=m" (*dest)
- /* in */
- : [dest] "b" (dest),
- [zero] "r" (zero),
- [exchange_value] "r" (exchange_value),
- "m" (*dest)
- /* clobber */
- : "cc",
- "memory"
- );
-
- post_membar(order);
-
- return old_value;
-}
-
-template<>
-template
-inline T Atomic::PlatformXchg<8>::operator()(T volatile* dest,
- T exchange_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(8 == sizeof(T));
- // Note that xchg doesn't necessarily do an acquire
- // (see synchronizer.cpp).
-
- T old_value;
- const uint64_t zero = 0;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- /* atomic loop */
- "1: \n"
- " ldarx %[old_value], %[dest], %[zero] \n"
- " stdcx. %[exchange_value], %[dest], %[zero] \n"
- " bne- 1b \n"
- /* exit */
- "2: \n"
- /* out */
- : [old_value] "=&r" (old_value),
- "=m" (*dest)
- /* in */
- : [dest] "b" (dest),
- [zero] "r" (zero),
- [exchange_value] "r" (exchange_value),
- "m" (*dest)
- /* clobber */
- : "cc",
- "memory"
- );
-
- post_membar(order);
-
- return old_value;
-}
-
-template<>
-template
-inline T Atomic::PlatformCmpxchg<1>::operator()(T volatile* dest,
- T compare_value,
- T exchange_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(1 == sizeof(T));
-
- // Note that cmpxchg guarantees a two-way memory barrier across
- // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not
- // specified otherwise (see atomic.hpp).
-
- // Using 32 bit internally.
- unsigned int old_value, loaded_value;
- pre_membar(order);
-
- __asm__ __volatile__ (
- /* atomic loop */
- "1: \n"
- " lbarx %[old_value], 0, %[dest] \n"
- /* extract byte and compare */
- " cmpw %[compare_value], %[old_value] \n"
- " bne- 2f \n"
- /* replace byte and try to store */
- " stbcx. %[exchange_value], 0, %[dest] \n"
- " bne- 1b \n"
- /* exit */
- "2: \n"
- /* out */
- : [old_value] "=&r" (old_value),
- [loaded_value] "=&r" (loaded_value),
- "=m" (*dest)
- /* in */
- : [dest] "b" (dest),
- [compare_value] "r" (compare_value),
- [exchange_value] "r" (exchange_value),
- "m" (*dest)
- /* clobber */
- : "cc",
- "memory"
- );
-
- post_membar(order);
-
- return PrimitiveConversions::cast((unsigned char)old_value);
-}
-
-template<>
-template
-inline T Atomic::PlatformCmpxchg<4>::operator()(T volatile* dest,
- T compare_value,
- T exchange_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(4 == sizeof(T));
-
- // Note that cmpxchg guarantees a two-way memory barrier across
- // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not
- // specified otherwise (see atomic.hpp).
-
- T old_value;
- const uint64_t zero = 0;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- /* simple guard */
- " lwz %[old_value], 0(%[dest]) \n"
- " cmpw %[compare_value], %[old_value] \n"
- " bne- 2f \n"
- /* atomic loop */
- "1: \n"
- " lwarx %[old_value], %[dest], %[zero] \n"
- " cmpw %[compare_value], %[old_value] \n"
- " bne- 2f \n"
- " stwcx. %[exchange_value], %[dest], %[zero] \n"
- " bne- 1b \n"
- /* exit */
- "2: \n"
- /* out */
- : [old_value] "=&r" (old_value),
- "=m" (*dest)
- /* in */
- : [dest] "b" (dest),
- [zero] "r" (zero),
- [compare_value] "r" (compare_value),
- [exchange_value] "r" (exchange_value),
- "m" (*dest)
- /* clobber */
- : "cc",
- "memory"
- );
-
- post_membar(order);
-
- return old_value;
-}
-
-template<>
-template
-inline T Atomic::PlatformCmpxchg<8>::operator()(T volatile* dest,
- T compare_value,
- T exchange_value,
- atomic_memory_order order) const {
- STATIC_ASSERT(8 == sizeof(T));
-
- // Note that cmpxchg guarantees a two-way memory barrier across
- // the cmpxchg, so it's really a 'fence_cmpxchg_fence' if not
- // specified otherwise (see atomic.hpp).
-
- T old_value;
- const uint64_t zero = 0;
-
- pre_membar(order);
-
- __asm__ __volatile__ (
- /* simple guard */
- " ld %[old_value], 0(%[dest]) \n"
- " cmpd %[compare_value], %[old_value] \n"
- " bne- 2f \n"
- /* atomic loop */
- "1: \n"
- " ldarx %[old_value], %[dest], %[zero] \n"
- " cmpd %[compare_value], %[old_value] \n"
- " bne- 2f \n"
- " stdcx. %[exchange_value], %[dest], %[zero] \n"
- " bne- 1b \n"
- /* exit */
- "2: \n"
- /* out */
- : [old_value] "=&r" (old_value),
- "=m" (*dest)
- /* in */
- : [dest] "b" (dest),
- [zero] "r" (zero),
- [compare_value] "r" (compare_value),
- [exchange_value] "r" (exchange_value),
- "m" (*dest)
- /* clobber */
- : "cc",
- "memory"
- );
-
- post_membar(order);
-
- return old_value;
-}
-
-template
-struct Atomic::PlatformOrderedLoad
-{
- template
- T operator()(const volatile T* p) const {
- T t = Atomic::load(p);
- // Use twi-isync for load_acquire (faster than lwsync).
- __asm__ __volatile__ ("twi 0,%0,0\n isync\n" : : "r" (t) : "memory");
- return t;
- }
-};
-
-#endif // OS_CPU_LINUX_PPC_ATOMIC_LINUX_PPC_HPP
+// Including inline assembler functions that are shared between multiple PPC64 platforms.
+#include "atomicAccess_ppc.hpp"
diff --git a/src/hotspot/os_cpu/linux_ppc/orderAccess_linux_ppc.hpp b/src/hotspot/os_cpu/linux_ppc/orderAccess_linux_ppc.hpp
index 2e6eb1f3878..d395add6d69 100644
--- a/src/hotspot/os_cpu/linux_ppc/orderAccess_linux_ppc.hpp
+++ b/src/hotspot/os_cpu/linux_ppc/orderAccess_linux_ppc.hpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2014 SAP SE. All rights reserved.
+ * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2025 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,66 +23,5 @@
*
*/
-#ifndef OS_CPU_LINUX_PPC_ORDERACCESS_LINUX_PPC_HPP
-#define OS_CPU_LINUX_PPC_ORDERACCESS_LINUX_PPC_HPP
-
-// Included in orderAccess.hpp header file.
-
-#ifndef PPC64
-#error "OrderAccess currently only implemented for PPC64"
-#endif
-
-// Compiler version last used for testing: gcc 4.1.2
-// Please update this information when this file changes
-
-// Implementation of class OrderAccess.
-
-//
-// Machine barrier instructions:
-//
-// - sync Two-way memory barrier, aka fence.
-// - lwsync orders Store|Store,
-// Load|Store,
-// Load|Load,
-// but not Store|Load
-// - eieio orders Store|Store
-// - isync Invalidates speculatively executed instructions,
-// but isync may complete before storage accesses
-// associated with instructions preceding isync have
-// been performed.
-//
-// Semantic barrier instructions:
-// (as defined in orderAccess.hpp)
-//
-// - release orders Store|Store, (maps to lwsync)
-// Load|Store
-// - acquire orders Load|Store, (maps to lwsync)
-// Load|Load
-// - fence orders Store|Store, (maps to sync)
-// Load|Store,
-// Load|Load,
-// Store|Load
-//
-
-#define inlasm_sync() __asm__ __volatile__ ("sync" : : : "memory");
-#define inlasm_lwsync() __asm__ __volatile__ ("lwsync" : : : "memory");
-#define inlasm_eieio() __asm__ __volatile__ ("eieio" : : : "memory");
-#define inlasm_isync() __asm__ __volatile__ ("isync" : : : "memory");
-
-inline void OrderAccess::loadload() { inlasm_lwsync(); }
-inline void OrderAccess::storestore() { inlasm_lwsync(); }
-inline void OrderAccess::loadstore() { inlasm_lwsync(); }
-inline void OrderAccess::storeload() { inlasm_sync(); }
-
-inline void OrderAccess::acquire() { inlasm_lwsync(); }
-inline void OrderAccess::release() { inlasm_lwsync(); }
-inline void OrderAccess::fence() { inlasm_sync(); }
-inline void OrderAccess::cross_modify_fence_impl()
- { inlasm_isync(); }
-
-#undef inlasm_sync
-#undef inlasm_lwsync
-#undef inlasm_eieio
-#undef inlasm_isync
-
-#endif // OS_CPU_LINUX_PPC_ORDERACCESS_LINUX_PPC_HPP
+// Including inline assembler functions that are shared between multiple PPC64 platforms.
+#include "orderAccess_ppc.hpp"
diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp
index d19128cafc2..a95bfb4ff96 100644
--- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp
+++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp
@@ -89,6 +89,25 @@
#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)
+#define RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE 6
+
+#define RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS 7
+
+#define RISCV_HWPROBE_KEY_TIME_CSR_FREQ 8
+
+#define RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF 9
+#define RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN 0
+#define RISCV_HWPROBE_MISALIGNED_SCALAR_EMULATED 1
+#define RISCV_HWPROBE_MISALIGNED_SCALAR_SLOW 2
+#define RISCV_HWPROBE_MISALIGNED_SCALAR_FAST 3
+#define RISCV_HWPROBE_MISALIGNED_SCALAR_UNSUPPORTED 4
+
+#define RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF 10
+#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN 0
+#define RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW 2
+#define RISCV_HWPROBE_MISALIGNED_VECTOR_FAST 3
+#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED 4
+
#ifndef NR_riscv_hwprobe
#ifndef NR_arch_specific_syscall
#define NR_arch_specific_syscall 244
@@ -114,7 +133,12 @@ static struct riscv_hwprobe query[] = {{RISCV_HWPROBE_KEY_MVENDORID, 0},
{RISCV_HWPROBE_KEY_MIMPID, 0},
{RISCV_HWPROBE_KEY_BASE_BEHAVIOR, 0},
{RISCV_HWPROBE_KEY_IMA_EXT_0, 0},
- {RISCV_HWPROBE_KEY_CPUPERF_0, 0}};
+ {RISCV_HWPROBE_KEY_CPUPERF_0, 0},
+ {RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE, 0},
+ {RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS, 0},
+ {RISCV_HWPROBE_KEY_TIME_CSR_FREQ, 0},
+ {RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF, 0},
+ {RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF, 0}};
bool RiscvHwprobe::probe_features() {
assert(!rw_hwprobe_completed, "Called twice.");
@@ -188,6 +212,9 @@ void RiscvHwprobe::add_features_from_query_result() {
VM_Version::ext_Zbs.enable_feature();
}
#ifndef PRODUCT
+ if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZICBOZ)) {
+ VM_Version::ext_Zicboz.enable_feature();
+ }
if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZBKB)) {
VM_Version::ext_Zbkb.enable_feature();
}
@@ -240,8 +267,22 @@ void RiscvHwprobe::add_features_from_query_result() {
VM_Version::ext_Zicond.enable_feature();
}
#endif
+ // RISCV_HWPROBE_KEY_CPUPERF_0 is deprecated and returns similar values
+ // to RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF. Keep it there for backward
+ // compatibility with old kernels.
if (is_valid(RISCV_HWPROBE_KEY_CPUPERF_0)) {
- VM_Version::unaligned_access.enable_feature(
+ VM_Version::unaligned_scalar.enable_feature(
query[RISCV_HWPROBE_KEY_CPUPERF_0].value & RISCV_HWPROBE_MISALIGNED_MASK);
+ } else if (is_valid(RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF)) {
+ VM_Version::unaligned_scalar.enable_feature(
+ query[RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF].value);
+ }
+
+ if (is_valid(RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF)) {
+ VM_Version::unaligned_vector.enable_feature(
+ query[RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF].value);
+ }
+ if (is_valid(RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE)) {
+ VM_Version::zicboz_block_size.enable_feature(query[RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE].value);
}
}
diff --git a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp
index 506c78cacca..3c49e11c145 100644
--- a/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp
+++ b/src/hotspot/os_cpu/linux_riscv/vm_version_linux_riscv.cpp
@@ -100,7 +100,6 @@
#endif
uint32_t VM_Version::cpu_vector_length() {
- assert(ext_V.enabled(), "should not call this");
return (uint32_t)read_csr(CSR_VLENB);
}
@@ -303,7 +302,7 @@ void VM_Version::rivos_features() {
ext_Zvfh.enable_feature();
- unaligned_access.enable_feature(MISALIGNED_FAST);
+ unaligned_scalar.enable_feature(MISALIGNED_SCALAR_FAST);
satp_mode.enable_feature(VM_SV48);
// Features dependent on march/mimpid.
diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp
index b5fa7dcf247..cce2b29d277 100644
--- a/src/hotspot/share/c1/c1_Compiler.cpp
+++ b/src/hotspot/share/c1/c1_Compiler.cpp
@@ -238,7 +238,7 @@ bool Compiler::is_intrinsic_supported(vmIntrinsics::ID id) {
case vmIntrinsics::_counterTime:
#endif
case vmIntrinsics::_getObjectSize:
-#if defined(X86) || defined(AARCH64) || defined(S390) || defined(RISCV) || defined(PPC64)
+#if defined(X86) || defined(AARCH64) || defined(S390) || defined(RISCV64) || defined(PPC64)
case vmIntrinsics::_clone:
#endif
break;
diff --git a/src/hotspot/share/c1/c1_LIR.cpp b/src/hotspot/share/c1/c1_LIR.cpp
index c1c94244fcc..4c8ebd5a09d 100644
--- a/src/hotspot/share/c1/c1_LIR.cpp
+++ b/src/hotspot/share/c1/c1_LIR.cpp
@@ -350,8 +350,9 @@ LIR_OpArrayCopy::LIR_OpArrayCopy(LIR_Opr src, LIR_Opr src_pos, LIR_Opr dst, LIR_
, _tmp(tmp)
, _expected_type(expected_type)
, _flags(flags) {
-#if defined(X86) || defined(AARCH64) || defined(S390) || defined(RISCV) || defined(PPC64)
- if (expected_type != nullptr && flags == 0) {
+#if defined(X86) || defined(AARCH64) || defined(S390) || defined(RISCV64) || defined(PPC64)
+ if (expected_type != nullptr &&
+ ((flags & ~LIR_OpArrayCopy::get_initial_copy_flags()) == 0)) {
_stub = nullptr;
} else {
_stub = new ArrayCopyStub(this);
diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp
index 0de69e658a3..c7726bf5c3f 100644
--- a/src/hotspot/share/c1/c1_LIR.hpp
+++ b/src/hotspot/share/c1/c1_LIR.hpp
@@ -1282,6 +1282,8 @@ class LIR_OpArrayCopy: public LIR_Op {
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; }
diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp
index 7c41c09d378..57d22a38324 100644
--- a/src/hotspot/share/c1/c1_Runtime1.cpp
+++ b/src/hotspot/share/c1/c1_Runtime1.cpp
@@ -818,7 +818,7 @@ JRT_ENTRY(void, Runtime1::deoptimize(JavaThread* current, jint trap_request))
Deoptimization::DeoptReason reason = Deoptimization::trap_request_reason(trap_request);
if (action == Deoptimization::Action_make_not_entrant) {
- if (nm->make_not_entrant("C1 deoptimize")) {
+ if (nm->make_not_entrant(nmethod::InvalidationReason::C1_DEOPTIMIZE)) {
if (reason == Deoptimization::Reason_tenured) {
MethodData* trap_mdo = Deoptimization::get_method_data(current, method, true /*create_if_missing*/);
if (trap_mdo != nullptr) {
@@ -1110,7 +1110,7 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* current, C1StubId stub_id ))
// safepoint, but if it's still alive then make it not_entrant.
nmethod* nm = CodeCache::find_nmethod(caller_frame.pc());
if (nm != nullptr) {
- nm->make_not_entrant("C1 code patch");
+ nm->make_not_entrant(nmethod::InvalidationReason::C1_CODEPATCH);
}
Deoptimization::deoptimize_frame(current, caller_frame.id());
@@ -1358,7 +1358,7 @@ void Runtime1::patch_code(JavaThread* current, C1StubId stub_id) {
// Make sure the nmethod is invalidated, i.e. made not entrant.
nmethod* nm = CodeCache::find_nmethod(caller_frame.pc());
if (nm != nullptr) {
- nm->make_not_entrant("C1 deoptimize for patching");
+ nm->make_not_entrant(nmethod::InvalidationReason::C1_DEOPTIMIZE_FOR_PATCHING);
}
}
@@ -1486,7 +1486,7 @@ JRT_ENTRY(void, Runtime1::predicate_failed_trap(JavaThread* current))
nmethod* nm = CodeCache::find_nmethod(caller_frame.pc());
assert (nm != nullptr, "no more nmethod?");
- nm->make_not_entrant("C1 predicate failed trap");
+ nm->make_not_entrant(nmethod::InvalidationReason::C1_PREDICATE_FAILED_TRAP);
methodHandle m(current, nm->method());
MethodData* mdo = m->method_data();
diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp
index 730902207f0..f4094aec1ac 100644
--- a/src/hotspot/share/cds/cds_globals.hpp
+++ b/src/hotspot/share/cds/cds_globals.hpp
@@ -121,6 +121,7 @@
\
product(ccstr, AOTCacheOutput, nullptr, \
"Specifies the file name for writing the AOT cache") \
+ constraint(AOTCacheOutputConstraintFunc, AtParse) \
\
product(bool, AOTInvokeDynamicLinking, false, DIAGNOSTIC, \
"AOT-link JVM_CONSTANT_InvokeDynamic entries in cached " \
diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp
index b1072dbce9b..498ce6255e4 100644
--- a/src/hotspot/share/cds/dynamicArchive.cpp
+++ b/src/hotspot/share/cds/dynamicArchive.cpp
@@ -348,6 +348,7 @@ void DynamicArchiveBuilder::write_archive(char* serialized_data, AOTClassLocatio
assert(dynamic_info != nullptr, "Sanity");
dynamic_info->open_as_output();
+ dynamic_info->prepare_for_writing();
ArchiveHeapInfo no_heap_for_dynamic_dump;
ArchiveBuilder::write_archive(dynamic_info, &no_heap_for_dynamic_dump);
diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp
index a413aa2d8e8..80dfa5f19f3 100644
--- a/src/hotspot/share/cds/filemap.cpp
+++ b/src/hotspot/share/cds/filemap.cpp
@@ -766,7 +766,9 @@ void FileMapInfo::open_as_output() {
}
_fd = fd;
_file_open = true;
+}
+void FileMapInfo::prepare_for_writing() {
// Seek past the header. We will write the header after all regions are written
// and their CRCs computed.
size_t header_bytes = header()->header_size();
diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp
index e0b33fc8245..a567d59a7cd 100644
--- a/src/hotspot/share/cds/filemap.hpp
+++ b/src/hotspot/share/cds/filemap.hpp
@@ -359,6 +359,7 @@ class FileMapInfo : public CHeapObj {
// File manipulation.
bool open_as_input() NOT_CDS_RETURN_(false);
void open_as_output();
+ void prepare_for_writing();
void write_header();
void write_region(int region, char* base, size_t size,
bool read_only, bool allow_exec);
diff --git a/src/hotspot/share/cds/lambdaFormInvokers.cpp b/src/hotspot/share/cds/lambdaFormInvokers.cpp
index d6a51c87513..acaa05794a3 100644
--- a/src/hotspot/share/cds/lambdaFormInvokers.cpp
+++ b/src/hotspot/share/cds/lambdaFormInvokers.cpp
@@ -191,7 +191,7 @@ void LambdaFormInvokers::regenerate_holder_classes(TRAPS) {
// make a copy of class bytes so GC will not affect us.
char *buf = NEW_RESOURCE_ARRAY(char, len);
memcpy(buf, (char*)h_bytes->byte_at_addr(0), len);
- ClassFileStream st((u1*)buf, len, nullptr);
+ ClassFileStream st((u1*)buf, len, "jrt:/java.base");
regenerate_class(class_name, st, CHECK);
}
}
diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp
index 6e58b8543de..2db7a3116a6 100644
--- a/src/hotspot/share/cds/metaspaceShared.cpp
+++ b/src/hotspot/share/cds/metaspaceShared.cpp
@@ -113,6 +113,7 @@ intx MetaspaceShared::_relocation_delta;
char* MetaspaceShared::_requested_base_address;
Array* MetaspaceShared::_archived_method_handle_intrinsics = nullptr;
bool MetaspaceShared::_use_optimized_module_handling = true;
+FileMapInfo* MetaspaceShared::_output_mapinfo = nullptr;
// The CDS archive is divided into the following regions:
// rw - read-write metadata
@@ -321,6 +322,24 @@ void MetaspaceShared::initialize_for_static_dump() {
MetaspaceShared::unrecoverable_writing_error();
}
_symbol_region.init(&_symbol_rs, &_symbol_vs);
+ if (CDSConfig::is_dumping_preimage_static_archive()) {
+ // We are in the AOT training run. User code is executed.
+ //
+ // On Windows, if the user code closes System.out and we open the AOT config file for output
+ // only at VM exit, we might get back the same file HANDLE as stdout, and the AOT config
+ // file may get corrupted by UL logs. By opening early, we ensure that the output
+ // HANDLE is different than stdout so we can avoid such corruption.
+ open_output_mapinfo();
+ } else {
+ // No need for the above as we won't execute any user code.
+ }
+}
+
+void MetaspaceShared::open_output_mapinfo() {
+ const char* static_archive = CDSConfig::output_archive_path();
+ assert(static_archive != nullptr, "sanity");
+ _output_mapinfo = new FileMapInfo(static_archive, true);
+ _output_mapinfo->open_as_output();
}
// Called by universe_post_init()
@@ -551,14 +570,13 @@ class VM_PopulateDumpSharedSpace : public VM_Operation {
public:
- VM_PopulateDumpSharedSpace(StaticArchiveBuilder& b) :
- VM_Operation(), _heap_info(), _map_info(nullptr), _builder(b) {}
+ VM_PopulateDumpSharedSpace(StaticArchiveBuilder& b, FileMapInfo* map_info) :
+ VM_Operation(), _heap_info(), _map_info(map_info), _builder(b) {}
bool skip_operation() const { return false; }
VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
ArchiveHeapInfo* heap_info() { return &_heap_info; }
- FileMapInfo* map_info() const { return _map_info; }
void doit(); // outline because gdb sucks
bool allow_nested_vm_operations() const { return true; }
}; // class VM_PopulateDumpSharedSpace
@@ -688,12 +706,6 @@ void VM_PopulateDumpSharedSpace::doit() {
CppVtables::zero_archived_vtables();
// Write the archive file
- if (CDSConfig::is_dumping_final_static_archive()) {
- FileMapInfo::free_current_info(); // FIXME: should not free current info
- }
- const char* static_archive = CDSConfig::output_archive_path();
- assert(static_archive != nullptr, "sanity");
- _map_info = new FileMapInfo(static_archive, true);
_map_info->populate_header(MetaspaceShared::core_region_alignment());
_map_info->set_early_serialized_data(early_serialized_data);
_map_info->set_serialized_data(serialized_data);
@@ -1012,7 +1024,14 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS
}
#endif
- VM_PopulateDumpSharedSpace op(builder);
+ if (!CDSConfig::is_dumping_preimage_static_archive()) {
+ if (CDSConfig::is_dumping_final_static_archive()) {
+ FileMapInfo::free_current_info(); // FIXME: should not free current info
+ }
+ open_output_mapinfo();
+ }
+
+ VM_PopulateDumpSharedSpace op(builder, _output_mapinfo);
VMThread::execute(&op);
if (AOTCodeCache::is_on_for_dump() && CDSConfig::is_dumping_final_static_archive()) {
@@ -1026,7 +1045,9 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS
CDSConfig::disable_dumping_aot_code();
}
- bool status = write_static_archive(&builder, op.map_info(), op.heap_info());
+ bool status = write_static_archive(&builder, _output_mapinfo, op.heap_info());
+ assert(!_output_mapinfo->is_open(), "Must be closed already");
+ _output_mapinfo = nullptr;
if (status && CDSConfig::is_dumping_preimage_static_archive()) {
tty->print_cr("%s AOTConfiguration recorded: %s",
CDSConfig::has_temp_aot_config_file() ? "Temporary" : "", AOTConfiguration);
@@ -1044,11 +1065,10 @@ bool MetaspaceShared::write_static_archive(ArchiveBuilder* builder, FileMapInfo*
// relocate the data so that it can be mapped to MetaspaceShared::requested_base_address()
// without runtime relocation.
builder->relocate_to_requested();
-
- map_info->open_as_output();
if (!map_info->is_open()) {
return false;
}
+ map_info->prepare_for_writing();
builder->write_archive(map_info, heap_info);
if (AllowArchivingWithJavaAgent) {
@@ -1270,7 +1290,7 @@ void MetaspaceShared::unrecoverable_loading_error(const char* message) {
} else if (CDSConfig::new_aot_flags_used()) {
vm_exit_during_initialization("Unable to use AOT cache.", nullptr);
} else {
- vm_exit_during_initialization("Unable to use shared archive.", nullptr);
+ vm_exit_during_initialization("Unable to use shared archive. Unrecoverable archive loading error (run with -Xlog:aot,cds for details)", message);
}
}
@@ -1287,6 +1307,10 @@ void MetaspaceShared::report_loading_error(const char* format, ...) {
LogStream ls_cds(level, LogTagSetMapping::tagset());
LogStream& ls = CDSConfig::new_aot_flags_used() ? ls_aot : ls_cds;
+ if (!ls.is_enabled()) {
+ return;
+ }
+
va_list ap;
va_start(ap, format);
@@ -1394,6 +1418,7 @@ FileMapInfo* MetaspaceShared::open_static_archive() {
FileMapInfo* mapinfo = new FileMapInfo(static_archive, true);
if (!mapinfo->open_as_input()) {
delete(mapinfo);
+ log_info(cds)("Opening of static archive %s failed", static_archive);
return nullptr;
}
return mapinfo;
diff --git a/src/hotspot/share/cds/metaspaceShared.hpp b/src/hotspot/share/cds/metaspaceShared.hpp
index 130e7fe4484..f7746129d16 100644
--- a/src/hotspot/share/cds/metaspaceShared.hpp
+++ b/src/hotspot/share/cds/metaspaceShared.hpp
@@ -59,6 +59,7 @@ class MetaspaceShared : AllStatic {
static char* _requested_base_address;
static bool _use_optimized_module_handling;
static Array* _archived_method_handle_intrinsics;
+ static FileMapInfo* _output_mapinfo;
public:
enum {
@@ -180,6 +181,7 @@ class MetaspaceShared : AllStatic {
private:
static void read_extra_data(JavaThread* current, const char* filename) NOT_CDS_RETURN;
static void fork_and_dump_final_static_archive(TRAPS);
+ static void open_output_mapinfo();
static bool write_static_archive(ArchiveBuilder* builder, FileMapInfo* map_info, ArchiveHeapInfo* heap_info);
static FileMapInfo* open_static_archive();
static FileMapInfo* open_dynamic_archive();
diff --git a/src/hotspot/share/ci/ciClassList.hpp b/src/hotspot/share/ci/ciClassList.hpp
index 618a052765e..bce1e52e80b 100644
--- a/src/hotspot/share/ci/ciClassList.hpp
+++ b/src/hotspot/share/ci/ciClassList.hpp
@@ -80,6 +80,7 @@ friend class ciObjectFactory; \
// Any more access must be given explicitly.
#define CI_PACKAGE_ACCESS_TO \
friend class ciObjectFactory; \
+friend class VMStructs; \
friend class ciCallSite; \
friend class ciConstantPoolCache; \
friend class ciField; \
diff --git a/src/hotspot/share/ci/ciKlass.hpp b/src/hotspot/share/ci/ciKlass.hpp
index 37091471a2a..8d03b910de5 100644
--- a/src/hotspot/share/ci/ciKlass.hpp
+++ b/src/hotspot/share/ci/ciKlass.hpp
@@ -107,7 +107,7 @@ class ciKlass : public ciType {
bool is_in_encoding_range() {
Klass* k = get_Klass();
bool is_in_encoding_range = CompressedKlassPointers::is_encodable(k);
- assert(is_in_encoding_range || k->is_interface() || k->is_abstract(), "sanity");
+ assert(is_in_encoding_range, "sanity");
return is_in_encoding_range;
}
diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp
index 5ea4336f35e..72ec2866c6e 100644
--- a/src/hotspot/share/ci/ciReplay.cpp
+++ b/src/hotspot/share/ci/ciReplay.cpp
@@ -802,7 +802,7 @@ class CompileReplay : public StackObj {
// Make sure the existence of a prior compile doesn't stop this one
nmethod* nm = (entry_bci != InvocationEntryBci) ? method->lookup_osr_nmethod_for(entry_bci, comp_level, true) : method->code();
if (nm != nullptr) {
- nm->make_not_entrant("CI replay");
+ nm->make_not_entrant(nmethod::InvalidationReason::CI_REPLAY);
}
replay_state = this;
CompileBroker::compile_method(methodHandle(THREAD, method), entry_bci, comp_level,
diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index 9c41ab44f82..5e28f3ec641 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -5841,15 +5841,6 @@ bool ClassFileParser::is_java_lang_ref_Reference_subclass() const {
return _super_klass->reference_type() != REF_NONE;
}
-// Returns true if the future Klass will need to be addressable with a narrow Klass ID.
-bool ClassFileParser::klass_needs_narrow_id() const {
- // Classes that are never instantiated need no narrow Klass Id, since the
- // only point of having a narrow id is to put it into an object header. Keeping
- // never instantiated classes out of class space lessens the class space pressure.
- // For more details, see JDK-8338526.
- return !is_interface() && !is_abstract();
-}
-
// ----------------------------------------------------------------------------
// debugging
diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp
index 707fbf6985f..9667c5c2a01 100644
--- a/src/hotspot/share/classfile/classFileParser.hpp
+++ b/src/hotspot/share/classfile/classFileParser.hpp
@@ -513,11 +513,6 @@ class ClassFileParser {
bool is_hidden() const { return _is_hidden; }
bool is_interface() const { return _access_flags.is_interface(); }
- bool is_abstract() const { return _access_flags.is_abstract(); }
-
- // Returns true if the Klass to be generated will need to be addressable
- // with a narrow Klass ID.
- bool klass_needs_narrow_id() const;
ClassLoaderData* loader_data() const { return _loader_data; }
const Symbol* class_name() const { return _class_name; }
diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp
index e6c10859371..b8734dcea25 100644
--- a/src/hotspot/share/classfile/classLoader.cpp
+++ b/src/hotspot/share/classfile/classLoader.cpp
@@ -1192,10 +1192,7 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik,
oop loader = ik->class_loader();
char* src = (char*)stream->source();
if (src == nullptr) {
- if (loader == nullptr) {
- // JFR classes
- ik->set_shared_classpath_index(0);
- }
+ ik->set_shared_classpath_index(-1); // unsupported location
return;
}
diff --git a/src/hotspot/share/classfile/resolutionErrors.cpp b/src/hotspot/share/classfile/resolutionErrors.cpp
index 03af71bc26f..506f8891043 100644
--- a/src/hotspot/share/classfile/resolutionErrors.cpp
+++ b/src/hotspot/share/classfile/resolutionErrors.cpp
@@ -73,7 +73,7 @@ void ResolutionErrorTable::add_entry(const constantPoolHandle& pool, int cp_inde
ResolutionErrorKey key(pool(), cp_index);
ResolutionErrorEntry *entry = new ResolutionErrorEntry(error, message, cause, cause_msg);
- _resolution_error_table->put(key, entry);
+ _resolution_error_table->put_when_absent(key, entry);
}
// create new nest host error entry
@@ -85,7 +85,7 @@ void ResolutionErrorTable::add_entry(const constantPoolHandle& pool, int cp_inde
ResolutionErrorKey key(pool(), cp_index);
ResolutionErrorEntry *entry = new ResolutionErrorEntry(message);
- _resolution_error_table->put(key, entry);
+ _resolution_error_table->put_when_absent(key, entry);
}
// find entry in the table
@@ -126,6 +126,13 @@ ResolutionErrorEntry::~ResolutionErrorEntry() {
}
}
+void ResolutionErrorEntry::set_nest_host_error(const char* message) {
+ assert(_nest_host_error == nullptr, "caller should have checked");
+ assert_lock_strong(SystemDictionary_lock);
+ _nest_host_error = message;
+}
+
+
class ResolutionErrorDeleteIterate : StackObj {
ConstantPool* p;
diff --git a/src/hotspot/share/classfile/resolutionErrors.hpp b/src/hotspot/share/classfile/resolutionErrors.hpp
index 60f8aea68ef..39859ad2b70 100644
--- a/src/hotspot/share/classfile/resolutionErrors.hpp
+++ b/src/hotspot/share/classfile/resolutionErrors.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -91,10 +91,7 @@ class ResolutionErrorEntry : public CHeapObj {
~ResolutionErrorEntry();
// The incoming nest host error message is already in the C-Heap.
- void set_nest_host_error(const char* message) {
- _nest_host_error = message;
- }
-
+ void set_nest_host_error(const char* message);
Symbol* error() const { return _error; }
const char* message() const { return _message; }
diff --git a/src/hotspot/share/classfile/stackMapTable.cpp b/src/hotspot/share/classfile/stackMapTable.cpp
index 1ed72e998fb..0664491950b 100644
--- a/src/hotspot/share/classfile/stackMapTable.cpp
+++ b/src/hotspot/share/classfile/stackMapTable.cpp
@@ -132,8 +132,16 @@ bool StackMapTable::match_stackmap(
}
void StackMapTable::check_jump_target(
- StackMapFrame* frame, int32_t target, TRAPS) const {
+ StackMapFrame* frame, int bci, int offset, TRAPS) const {
ErrorContext ctx;
+ // Jump targets must be within the method and the method size is limited. See JVMS 4.11
+ int min_offset = -1 * max_method_code_size;
+ if (offset < min_offset || offset > max_method_code_size) {
+ frame->verifier()->verify_error(ErrorContext::bad_stackmap(bci, frame),
+ "Illegal target of jump or branch (bci %d + offset %d)", bci, offset);
+ return;
+ }
+ int target = bci + offset;
bool match = match_stackmap(
frame, target, true, false, &ctx, CHECK_VERIFY(frame->verifier()));
if (!match || (target < 0 || target >= _code_length)) {
diff --git a/src/hotspot/share/classfile/stackMapTable.hpp b/src/hotspot/share/classfile/stackMapTable.hpp
index cc4202f3280..0ec7af9d0c9 100644
--- a/src/hotspot/share/classfile/stackMapTable.hpp
+++ b/src/hotspot/share/classfile/stackMapTable.hpp
@@ -67,7 +67,7 @@ class StackMapTable : public StackObj {
// Check jump instructions. Make sure there are no uninitialized
// instances on backward branch.
- void check_jump_target(StackMapFrame* frame, int32_t target, TRAPS) const;
+ void check_jump_target(StackMapFrame* frame, int bci, int offset, TRAPS) const;
// The following methods are only used inside this class.
diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp
index b1abb2ab0fe..ee526e9bb07 100644
--- a/src/hotspot/share/classfile/systemDictionary.cpp
+++ b/src/hotspot/share/classfile/systemDictionary.cpp
@@ -55,6 +55,7 @@
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/access.inline.hpp"
+#include "oops/constantPool.inline.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/klass.inline.hpp"
#include "oops/method.inline.hpp"
@@ -1806,18 +1807,28 @@ Symbol* SystemDictionary::find_resolution_error(const constantPoolHandle& pool,
void SystemDictionary::add_nest_host_error(const constantPoolHandle& pool,
int which,
- const char* message) {
+ const stringStream& message) {
{
MutexLocker ml(Thread::current(), SystemDictionary_lock);
ResolutionErrorEntry* entry = ResolutionErrorTable::find_entry(pool, which);
- if (entry != nullptr && entry->nest_host_error() == nullptr) {
+ if (entry == nullptr) {
+ // Only add a new entry to the resolution error table if one hasn't been found for this
+ // constant pool index. In this case resolution succeeded but there's an error in this nest host
+ // that we use the table to record.
+ assert(pool->resolved_klass_at(which) != nullptr, "klass should be resolved if there is no entry");
+ ResolutionErrorTable::add_entry(pool, which, message.as_string(true /* on C-heap */));
+ } else {
// An existing entry means we had a true resolution failure (LinkageError) with our nest host, but we
// still want to add the error message for the higher-level access checks to report. We should
// only reach here under the same error condition, so we can ignore the potential race with setting
- // the message. If we see it is already set then we can ignore it.
- entry->set_nest_host_error(message);
- } else {
- ResolutionErrorTable::add_entry(pool, which, message);
+ // the message.
+ const char* nhe = entry->nest_host_error();
+ if (nhe == nullptr) {
+ entry->set_nest_host_error(message.as_string(true /* on C-heap */));
+ } else {
+ DEBUG_ONLY(const char* msg = message.base();)
+ assert(strcmp(nhe, msg) == 0, "New message %s, differs from original %s", msg, nhe);
+ }
}
}
}
diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp
index 8cf2cd83b82..b8efef8edcb 100644
--- a/src/hotspot/share/classfile/systemDictionary.hpp
+++ b/src/hotspot/share/classfile/systemDictionary.hpp
@@ -280,7 +280,7 @@ class SystemDictionary : AllStatic {
// Record a nest host resolution/validation error
static void add_nest_host_error(const constantPoolHandle& pool, int which,
- const char* message);
+ const stringStream& message);
static const char* find_nest_host_error(const constantPoolHandle& pool, int which);
static void add_to_initiating_loader(JavaThread* current, InstanceKlass* k,
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp
index 8bd09a0d947..bdf558b4a48 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.cpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp
@@ -90,7 +90,7 @@ DEBUG_ONLY(bool SystemDictionaryShared::_class_loading_may_happen = true;)
#ifdef ASSERT
static void check_klass_after_loading(const Klass* k) {
#ifdef _LP64
- if (k != nullptr && UseCompressedClassPointers && k->needs_narrow_id()) {
+ if (k != nullptr && UseCompressedClassPointers) {
CompressedKlassPointers::check_encodable(k);
}
#endif
diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp
index 0f1468f0309..f2eda58e660 100644
--- a/src/hotspot/share/classfile/verifier.cpp
+++ b/src/hotspot/share/classfile/verifier.cpp
@@ -781,7 +781,6 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) {
// Merge with the next instruction
{
- int target;
VerificationType type, type2;
VerificationType atype;
@@ -1606,9 +1605,8 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) {
case Bytecodes::_ifle:
current_frame.pop_stack(
VerificationType::integer_type(), CHECK_VERIFY(this));
- target = bcs.dest();
stackmap_table.check_jump_target(
- ¤t_frame, target, CHECK_VERIFY(this));
+ ¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this));
no_control_flow = false; break;
case Bytecodes::_if_acmpeq :
case Bytecodes::_if_acmpne :
@@ -1619,19 +1617,16 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) {
case Bytecodes::_ifnonnull :
current_frame.pop_stack(
VerificationType::reference_check(), CHECK_VERIFY(this));
- target = bcs.dest();
stackmap_table.check_jump_target
- (¤t_frame, target, CHECK_VERIFY(this));
+ (¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this));
no_control_flow = false; break;
case Bytecodes::_goto :
- target = bcs.dest();
stackmap_table.check_jump_target(
- ¤t_frame, target, CHECK_VERIFY(this));
+ ¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this));
no_control_flow = true; break;
case Bytecodes::_goto_w :
- target = bcs.dest_w();
stackmap_table.check_jump_target(
- ¤t_frame, target, CHECK_VERIFY(this));
+ ¤t_frame, bcs.bci(), bcs.get_offset_s4(), CHECK_VERIFY(this));
no_control_flow = true; break;
case Bytecodes::_tableswitch :
case Bytecodes::_lookupswitch :
@@ -2280,15 +2275,14 @@ void ClassVerifier::verify_switch(
}
}
}
- int target = bci + default_offset;
- stackmap_table->check_jump_target(current_frame, target, CHECK_VERIFY(this));
+ stackmap_table->check_jump_target(current_frame, bci, default_offset, CHECK_VERIFY(this));
for (int i = 0; i < keys; i++) {
// Because check_jump_target() may safepoint, the bytecode could have
// moved, which means 'aligned_bcp' is no good and needs to be recalculated.
aligned_bcp = align_up(bcs->bcp() + 1, jintSize);
- target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize);
+ int offset = (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize);
stackmap_table->check_jump_target(
- current_frame, target, CHECK_VERIFY(this));
+ current_frame, bci, offset, CHECK_VERIFY(this));
}
NOT_PRODUCT(aligned_bcp = nullptr); // no longer valid at this point
}
@@ -2442,209 +2436,6 @@ void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs,
}
}
-// Look at the method's handlers. If the bci is in the handler's try block
-// then check if the handler_pc is already on the stack. If not, push it
-// unless the handler has already been scanned.
-void ClassVerifier::push_handlers(ExceptionTable* exhandlers,
- GrowableArray* handler_list,
- GrowableArray* handler_stack,
- u4 bci) {
- int exlength = exhandlers->length();
- for(int x = 0; x < exlength; x++) {
- if (bci >= exhandlers->start_pc(x) && bci < exhandlers->end_pc(x)) {
- u4 exhandler_pc = exhandlers->handler_pc(x);
- if (!handler_list->contains(exhandler_pc)) {
- handler_stack->append_if_missing(exhandler_pc);
- handler_list->append(exhandler_pc);
- }
- }
- }
-}
-
-// Return TRUE if all code paths starting with start_bc_offset end in
-// bytecode athrow or loop.
-bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
- ResourceMark rm;
- // Create bytecode stream.
- RawBytecodeStream bcs(method());
- int code_length = method()->code_size();
- bcs.set_start(start_bc_offset);
-
- // Create stack for storing bytecode start offsets for if* and *switch.
- GrowableArray* bci_stack = new GrowableArray(30);
- // Create stack for handlers for try blocks containing this handler.
- GrowableArray* handler_stack = new GrowableArray(30);
- // Create list of handlers that have been pushed onto the handler_stack
- // so that handlers embedded inside of their own TRY blocks only get
- // scanned once.
- GrowableArray* handler_list = new GrowableArray(30);
- // Create list of visited branch opcodes (goto* and if*).
- GrowableArray* visited_branches = new GrowableArray(30);
- ExceptionTable exhandlers(_method());
-
- while (true) {
- if (bcs.is_last_bytecode()) {
- // if no more starting offsets to parse or if at the end of the
- // method then return false.
- if ((bci_stack->is_empty()) || (bcs.end_bci() == code_length))
- return false;
- // Pop a bytecode starting offset and scan from there.
- bcs.set_start(bci_stack->pop());
- }
- Bytecodes::Code opcode = bcs.raw_next();
- int bci = bcs.bci();
-
- // If the bytecode is in a TRY block, push its handlers so they
- // will get parsed.
- push_handlers(&exhandlers, handler_list, handler_stack, bci);
-
- switch (opcode) {
- case Bytecodes::_if_icmpeq:
- case Bytecodes::_if_icmpne:
- case Bytecodes::_if_icmplt:
- case Bytecodes::_if_icmpge:
- case Bytecodes::_if_icmpgt:
- case Bytecodes::_if_icmple:
- case Bytecodes::_ifeq:
- case Bytecodes::_ifne:
- case Bytecodes::_iflt:
- case Bytecodes::_ifge:
- case Bytecodes::_ifgt:
- case Bytecodes::_ifle:
- case Bytecodes::_if_acmpeq:
- case Bytecodes::_if_acmpne:
- case Bytecodes::_ifnull:
- case Bytecodes::_ifnonnull: {
- int target = bcs.dest();
- if (visited_branches->contains(bci)) {
- if (bci_stack->is_empty()) {
- if (handler_stack->is_empty()) {
- return true;
- } else {
- // Parse the catch handlers for try blocks containing athrow.
- bcs.set_start(handler_stack->pop());
- }
- } else {
- // Pop a bytecode starting offset and scan from there.
- bcs.set_start(bci_stack->pop());
- }
- } else {
- if (target > bci) { // forward branch
- if (target >= code_length) return false;
- // Push the branch target onto the stack.
- bci_stack->push(target);
- // then, scan bytecodes starting with next.
- bcs.set_start(bcs.next_bci());
- } else { // backward branch
- // Push bytecode offset following backward branch onto the stack.
- bci_stack->push(bcs.next_bci());
- // Check bytecodes starting with branch target.
- bcs.set_start(target);
- }
- // Record target so we don't branch here again.
- visited_branches->append(bci);
- }
- break;
- }
-
- case Bytecodes::_goto:
- case Bytecodes::_goto_w: {
- int target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w());
- if (visited_branches->contains(bci)) {
- if (bci_stack->is_empty()) {
- if (handler_stack->is_empty()) {
- return true;
- } else {
- // Parse the catch handlers for try blocks containing athrow.
- bcs.set_start(handler_stack->pop());
- }
- } else {
- // Been here before, pop new starting offset from stack.
- bcs.set_start(bci_stack->pop());
- }
- } else {
- if (target >= code_length) return false;
- // Continue scanning from the target onward.
- bcs.set_start(target);
- // Record target so we don't branch here again.
- visited_branches->append(bci);
- }
- break;
- }
-
- // Check that all switch alternatives end in 'athrow' bytecodes. Since it
- // is difficult to determine where each switch alternative ends, parse
- // each switch alternative until either hit a 'return', 'athrow', or reach
- // the end of the method's bytecodes. This is gross but should be okay
- // because:
- // 1. tableswitch and lookupswitch byte codes in handlers for ctor explicit
- // constructor invocations should be rare.
- // 2. if each switch alternative ends in an athrow then the parsing should be
- // short. If there is no athrow then it is bogus code, anyway.
- case Bytecodes::_lookupswitch:
- case Bytecodes::_tableswitch:
- {
- address aligned_bcp = align_up(bcs.bcp() + 1, jintSize);
- int default_offset = Bytes::get_Java_u4(aligned_bcp) + bci;
- int keys, delta;
- if (opcode == Bytecodes::_tableswitch) {
- jint low = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize);
- jint high = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize);
- // This is invalid, but let the regular bytecode verifier
- // report this because the user will get a better error message.
- if (low > high) return true;
- keys = high - low + 1;
- delta = 1;
- } else {
- keys = (int)Bytes::get_Java_u4(aligned_bcp + jintSize);
- delta = 2;
- }
- // Invalid, let the regular bytecode verifier deal with it.
- if (keys < 0) return true;
-
- // Push the offset of the next bytecode onto the stack.
- bci_stack->push(bcs.next_bci());
-
- // Push the switch alternatives onto the stack.
- for (int i = 0; i < keys; i++) {
- int target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize);
- if (target > code_length) return false;
- bci_stack->push(target);
- }
-
- // Start bytecode parsing for the switch at the default alternative.
- if (default_offset > code_length) return false;
- bcs.set_start(default_offset);
- break;
- }
-
- case Bytecodes::_return:
- return false;
-
- case Bytecodes::_athrow:
- {
- if (bci_stack->is_empty()) {
- if (handler_stack->is_empty()) {
- return true;
- } else {
- // Parse the catch handlers for try blocks containing athrow.
- bcs.set_start(handler_stack->pop());
- }
- } else {
- // Pop a bytecode offset and starting scanning from there.
- bcs.set_start(bci_stack->pop());
- }
- }
- break;
-
- default:
- ;
- } // end switch
- } // end while loop
-
- return false;
-}
-
void ClassVerifier::verify_invoke_init(
RawBytecodeStream* bcs, u2 ref_class_index, VerificationType ref_class_type,
StackMapFrame* current_frame, u4 code_length, bool in_try_block,
@@ -2669,25 +2460,6 @@ void ClassVerifier::verify_invoke_init(
// sure that all catch clause paths end in a throw. Otherwise, this can
// result in returning an incomplete object.
if (in_try_block) {
- ExceptionTable exhandlers(_method());
- int exlength = exhandlers.length();
- for(int i = 0; i < exlength; i++) {
- u2 start_pc = exhandlers.start_pc(i);
- u2 end_pc = exhandlers.end_pc(i);
-
- if (bci >= start_pc && bci < end_pc) {
- if (!ends_in_athrow(exhandlers.handler_pc(i))) {
- verify_error(ErrorContext::bad_code(bci),
- "Bad method call from after the start of a try block");
- return;
- } else if (log_is_enabled(Debug, verification)) {
- ResourceMark rm(THREAD);
- log_debug(verification)("Survived call to ends_in_athrow(): %s",
- current_class()->name()->as_C_string());
- }
- }
- }
-
// Check the exception handler target stackmaps with the locals from the
// incoming stackmap (before initialize_object() changes them to outgoing
// state).
diff --git a/src/hotspot/share/classfile/verifier.hpp b/src/hotspot/share/classfile/verifier.hpp
index 7857d472705..290800be715 100644
--- a/src/hotspot/share/classfile/verifier.hpp
+++ b/src/hotspot/share/classfile/verifier.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 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
@@ -334,17 +334,6 @@ class ClassVerifier : public StackObj {
bool* this_uninit, const constantPoolHandle& cp, StackMapTable* stackmap_table,
TRAPS);
- // Used by ends_in_athrow() to push all handlers that contain bci onto the
- // handler_stack, if the handler has not already been pushed on the stack.
- void push_handlers(ExceptionTable* exhandlers,
- GrowableArray* handler_list,
- GrowableArray* handler_stack,
- u4 bci);
-
- // Returns true if all paths starting with start_bc_offset end in athrow
- // bytecode or loop.
- bool ends_in_athrow(u4 start_bc_offset);
-
void verify_invoke_instructions(
RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame,
bool in_try_block, bool* this_uninit, VerificationType return_type,
diff --git a/src/hotspot/share/classfile/vmClasses.cpp b/src/hotspot/share/classfile/vmClasses.cpp
index 813926e51a2..b5a19ce6391 100644
--- a/src/hotspot/share/classfile/vmClasses.cpp
+++ b/src/hotspot/share/classfile/vmClasses.cpp
@@ -210,9 +210,11 @@ void vmClasses::resolve_all(TRAPS) {
#endif
InstanceStackChunkKlass::init_offset_of_stack();
+#if INCLUDE_CDS
if (CDSConfig::is_using_aot_linked_classes()) {
AOTLinkedClassBulkLoader::load_javabase_classes(THREAD);
}
+#endif
}
#if INCLUDE_CDS
diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp
index 6fd5c07e137..baa945cdddf 100644
--- a/src/hotspot/share/classfile/vmIntrinsics.cpp
+++ b/src/hotspot/share/classfile/vmIntrinsics.cpp
@@ -289,8 +289,6 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_dsin:
case vmIntrinsics::_dcos:
case vmIntrinsics::_dtan:
- case vmIntrinsics::_dtanh:
- case vmIntrinsics::_dcbrt:
case vmIntrinsics::_dlog:
case vmIntrinsics::_dexp:
case vmIntrinsics::_dpow:
@@ -316,6 +314,13 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_fmaF:
if (!InlineMathNatives || !UseFMA) return true;
break;
+ case vmIntrinsics::_dtanh:
+ case vmIntrinsics::_dcbrt:
+ if (!InlineMathNatives || !InlineIntrinsics) return true;
+#if defined(AMD64) && (defined(COMPILER1) || defined(COMPILER2))
+ if (!UseLibmIntrinsic) return true;
+#endif
+ break;
case vmIntrinsics::_floatToFloat16:
case vmIntrinsics::_float16ToFloat:
if (!InlineIntrinsics) return true;
diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp
index eaced55cc50..2446b37469b 100644
--- a/src/hotspot/share/code/aotCodeCache.hpp
+++ b/src/hotspot/share/code/aotCodeCache.hpp
@@ -362,8 +362,8 @@ class AOTCodeCache : public CHeapObj {
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_adapter() NOT_CDS_RETURN_(false);
static bool is_using_adapter() NOT_CDS_RETURN_(false);
diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp
index cf21f1f89a4..96b9d8c0e0d 100644
--- a/src/hotspot/share/code/codeBlob.cpp
+++ b/src/hotspot/share/code/codeBlob.cpp
@@ -897,6 +897,7 @@ void CodeBlob::dump_for_addr(address addr, outputStream* st, bool verbose) const
nm->print_nmethod(true);
} else {
nm->print_on(st);
+ nm->print_code_snippet(st, addr);
}
return;
}
diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp
index 902d4345622..169e938fd9d 100644
--- a/src/hotspot/share/code/codeCache.cpp
+++ b/src/hotspot/share/code/codeCache.cpp
@@ -227,11 +227,6 @@ void CodeCache::initialize_heaps() {
if (!non_nmethod.set) {
non_nmethod.size += compiler_buffer_size;
- // Further down, just before FLAG_SET_ERGO(), all segment sizes are
- // aligned down to the next lower multiple of min_size. For large page
- // sizes, this may result in (non_nmethod.size == 0) which is not acceptable.
- // Therefore, force non_nmethod.size to at least min_size.
- non_nmethod.size = MAX2(non_nmethod.size, min_size);
}
if (!profiled.set && !non_profiled.set) {
@@ -307,11 +302,10 @@ void CodeCache::initialize_heaps() {
// Note: if large page support is enabled, min_size is at least the large
// page size. This ensures that the code cache is covered by large pages.
- non_profiled.size += non_nmethod.size & alignment_mask(min_size);
- non_profiled.size += profiled.size & alignment_mask(min_size);
- non_nmethod.size = align_down(non_nmethod.size, min_size);
- profiled.size = align_down(profiled.size, min_size);
- non_profiled.size = align_down(non_profiled.size, min_size);
+ non_nmethod.size = align_up(non_nmethod.size, min_size);
+ profiled.size = align_up(profiled.size, min_size);
+ non_profiled.size = align_up(non_profiled.size, min_size);
+ cache_size = non_nmethod.size + profiled.size + non_profiled.size;
FLAG_SET_ERGO(NonNMethodCodeHeapSize, non_nmethod.size);
FLAG_SET_ERGO(ProfiledCodeHeapSize, profiled.size);
@@ -882,6 +876,7 @@ void CodeCache::do_unloading(bool unloading_occurred) {
void CodeCache::verify_clean_inline_caches() {
#ifdef ASSERT
+ if (!VerifyInlineCaches) return;
NMethodIterator iter(NMethodIterator::not_unloading);
while(iter.next()) {
nmethod* nm = iter.method();
@@ -1361,7 +1356,7 @@ void CodeCache::make_marked_nmethods_deoptimized() {
while(iter.next()) {
nmethod* nm = iter.method();
if (nm->is_marked_for_deoptimization() && !nm->has_been_deoptimized() && nm->can_be_deoptimized()) {
- nm->make_not_entrant("marked for deoptimization");
+ nm->make_not_entrant(nmethod::InvalidationReason::MARKED_FOR_DEOPTIMIZATION);
nm->make_deoptimized();
}
}
diff --git a/src/hotspot/share/code/compiledIC.hpp b/src/hotspot/share/code/compiledIC.hpp
index 37ca090fa9c..624c1b428de 100644
--- a/src/hotspot/share/code/compiledIC.hpp
+++ b/src/hotspot/share/code/compiledIC.hpp
@@ -192,13 +192,13 @@ class CompiledDirectCall : public ResourceObj {
static inline CompiledDirectCall* before(address return_addr) {
CompiledDirectCall* st = new CompiledDirectCall(nativeCall_before(return_addr));
- st->verify();
+ if (VerifyInlineCaches) st->verify();
return st;
}
static inline CompiledDirectCall* at(address native_call) {
CompiledDirectCall* st = new CompiledDirectCall(nativeCall_at(native_call));
- st->verify();
+ if (VerifyInlineCaches) st->verify();
return st;
}
diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp
index 412cf222997..4d39b715f25 100644
--- a/src/hotspot/share/code/nmethod.cpp
+++ b/src/hotspot/share/code/nmethod.cpp
@@ -1970,14 +1970,12 @@ void nmethod::invalidate_osr_method() {
}
}
-void nmethod::log_state_change(const char* reason) const {
- assert(reason != nullptr, "Must provide a reason");
-
+void nmethod::log_state_change(InvalidationReason invalidation_reason) const {
if (LogCompilation) {
if (xtty != nullptr) {
ttyLocker ttyl; // keep the following output all in one block
xtty->begin_elem("make_not_entrant thread='%zu' reason='%s'",
- os::current_thread_id(), reason);
+ os::current_thread_id(), invalidation_reason_to_string(invalidation_reason));
log_identity(xtty);
xtty->stamp();
xtty->end_elem();
@@ -1986,7 +1984,7 @@ void nmethod::log_state_change(const char* reason) const {
ResourceMark rm;
stringStream ss(NEW_RESOURCE_ARRAY(char, 256), 256);
- ss.print("made not entrant: %s", reason);
+ ss.print("made not entrant: %s", invalidation_reason_to_string(invalidation_reason));
CompileTask::print_ul(this, ss.freeze());
if (PrintCompilation) {
@@ -2001,9 +1999,7 @@ void nmethod::unlink_from_method() {
}
// Invalidate code
-bool nmethod::make_not_entrant(const char* reason) {
- assert(reason != nullptr, "Must provide a reason");
-
+bool nmethod::make_not_entrant(InvalidationReason invalidation_reason) {
// This can be called while the system is already at a safepoint which is ok
NoSafepointVerifier nsv;
@@ -2061,7 +2057,7 @@ bool nmethod::make_not_entrant(const char* reason) {
assert(success, "Transition can't fail");
// Log the transition once
- log_state_change(reason);
+ log_state_change(invalidation_reason);
// Remove nmethod from method.
unlink_from_method();
@@ -2072,7 +2068,7 @@ bool nmethod::make_not_entrant(const char* reason) {
// Invalidate can't occur while holding the NMethodState_lock
JVMCINMethodData* nmethod_data = jvmci_nmethod_data();
if (nmethod_data != nullptr) {
- nmethod_data->invalidate_nmethod_mirror(this);
+ nmethod_data->invalidate_nmethod_mirror(this, invalidation_reason);
}
#endif
@@ -2110,7 +2106,9 @@ void nmethod::unlink() {
// Clear the link between this nmethod and a HotSpotNmethod mirror
JVMCINMethodData* nmethod_data = jvmci_nmethod_data();
if (nmethod_data != nullptr) {
- nmethod_data->invalidate_nmethod_mirror(this);
+ nmethod_data->invalidate_nmethod_mirror(this, is_cold() ?
+ nmethod::InvalidationReason::UNLOADING_COLD :
+ nmethod::InvalidationReason::UNLOADING);
}
#endif
@@ -4003,6 +4001,46 @@ void nmethod::print_value_on_impl(outputStream* st) const {
#endif
}
+void nmethod::print_code_snippet(outputStream* st, address addr) const {
+ if (entry_point() <= addr && addr < code_end()) {
+ // Pointing into the nmethod's code. Try to disassemble some instructions around addr.
+ // Determine conservative start and end points.
+ address start;
+ if (frame_complete_offset() != CodeOffsets::frame_never_safe &&
+ addr >= code_begin() + frame_complete_offset()) {
+ start = code_begin() + frame_complete_offset();
+ } else {
+ start = (addr < verified_entry_point()) ? entry_point() : verified_entry_point();
+ }
+ address start_for_hex_dump = start; // We can choose a different starting point for hex dump, below.
+ address end = code_end();
+
+ // Try using relocations to find closer instruction start and end points.
+ // (Some platforms have variable length instructions and can only
+ // disassemble correctly at instruction start addresses.)
+ RelocIterator iter((nmethod*)this, start);
+ while (iter.next() && iter.addr() < addr) { // find relocation before addr
+ // Note: There's a relocation which doesn't point to an instruction start:
+ // ZBarrierRelocationFormatStoreGoodAfterMov with ZGC on x86_64
+ // We could detect and skip it, but hex dump is still usable when
+ // disassembler produces garbage in such a very rare case.
+ start = iter.addr();
+ // We want at least 64 Bytes ahead in hex dump.
+ if (iter.addr() <= (addr - 64)) start_for_hex_dump = iter.addr();
+ }
+ if (iter.has_current()) {
+ if (iter.addr() == addr) iter.next(); // find relocation after addr
+ if (iter.has_current()) end = iter.addr();
+ }
+
+ // Always print hex. Disassembler may still have problems when hitting an incorrect instruction start.
+ os::print_hex_dump(st, start_for_hex_dump, end, 1, /* print_ascii=*/false);
+ if (!Disassembler::is_abstract()) {
+ Disassembler::decode(start, end, st);
+ }
+ }
+}
+
#ifndef PRODUCT
void nmethod::print_calls(outputStream* st) {
diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp
index 2ce6e5cd361..35adb84733a 100644
--- a/src/hotspot/share/code/nmethod.hpp
+++ b/src/hotspot/share/code/nmethod.hpp
@@ -471,6 +471,82 @@ class nmethod : public CodeBlob {
void oops_do_set_strong_done(nmethod* old_head);
public:
+ // If you change anything in this enum please patch
+ // vmStructs_jvmci.cpp accordingly.
+ enum class InvalidationReason : s1 {
+ NOT_INVALIDATED = -1,
+ C1_CODEPATCH,
+ C1_DEOPTIMIZE,
+ C1_DEOPTIMIZE_FOR_PATCHING,
+ C1_PREDICATE_FAILED_TRAP,
+ CI_REPLAY,
+ UNLOADING,
+ UNLOADING_COLD,
+ JVMCI_INVALIDATE,
+ JVMCI_MATERIALIZE_VIRTUAL_OBJECT,
+ JVMCI_REPLACED_WITH_NEW_CODE,
+ JVMCI_REPROFILE,
+ MARKED_FOR_DEOPTIMIZATION,
+ MISSING_EXCEPTION_HANDLER,
+ NOT_USED,
+ OSR_INVALIDATION_BACK_BRANCH,
+ OSR_INVALIDATION_FOR_COMPILING_WITH_C1,
+ OSR_INVALIDATION_OF_LOWER_LEVEL,
+ SET_NATIVE_FUNCTION,
+ UNCOMMON_TRAP,
+ WHITEBOX_DEOPTIMIZATION,
+ ZOMBIE,
+ INVALIDATION_REASONS_COUNT
+ };
+
+
+ static const char* invalidation_reason_to_string(InvalidationReason invalidation_reason) {
+ switch (invalidation_reason) {
+ case InvalidationReason::C1_CODEPATCH:
+ return "C1 code patch";
+ case InvalidationReason::C1_DEOPTIMIZE:
+ return "C1 deoptimized";
+ case InvalidationReason::C1_DEOPTIMIZE_FOR_PATCHING:
+ return "C1 deoptimize for patching";
+ case InvalidationReason::C1_PREDICATE_FAILED_TRAP:
+ return "C1 predicate failed trap";
+ case InvalidationReason::CI_REPLAY:
+ return "CI replay";
+ case InvalidationReason::JVMCI_INVALIDATE:
+ return "JVMCI invalidate";
+ case InvalidationReason::JVMCI_MATERIALIZE_VIRTUAL_OBJECT:
+ return "JVMCI materialize virtual object";
+ case InvalidationReason::JVMCI_REPLACED_WITH_NEW_CODE:
+ return "JVMCI replaced with new code";
+ case InvalidationReason::JVMCI_REPROFILE:
+ return "JVMCI reprofile";
+ case InvalidationReason::MARKED_FOR_DEOPTIMIZATION:
+ return "marked for deoptimization";
+ case InvalidationReason::MISSING_EXCEPTION_HANDLER:
+ return "missing exception handler";
+ case InvalidationReason::NOT_USED:
+ return "not used";
+ case InvalidationReason::OSR_INVALIDATION_BACK_BRANCH:
+ return "OSR invalidation back branch";
+ case InvalidationReason::OSR_INVALIDATION_FOR_COMPILING_WITH_C1:
+ return "OSR invalidation for compiling with C1";
+ case InvalidationReason::OSR_INVALIDATION_OF_LOWER_LEVEL:
+ return "OSR invalidation of lower level";
+ case InvalidationReason::SET_NATIVE_FUNCTION:
+ return "set native function";
+ case InvalidationReason::UNCOMMON_TRAP:
+ return "uncommon trap";
+ case InvalidationReason::WHITEBOX_DEOPTIMIZATION:
+ return "whitebox deoptimization";
+ case InvalidationReason::ZOMBIE:
+ return "zombie";
+ default: {
+ assert(false, "Unhandled reason");
+ return "Unknown";
+ }
+ }
+ }
+
// create nmethod with entry_bci
static nmethod* new_nmethod(const methodHandle& method,
int compile_id,
@@ -633,8 +709,8 @@ class nmethod : public CodeBlob {
// alive. It is used when an uncommon trap happens. Returns true
// if this thread changed the state of the nmethod or false if
// another thread performed the transition.
- bool make_not_entrant(const char* reason);
- bool make_not_used() { return make_not_entrant("not used"); }
+ bool make_not_entrant(InvalidationReason invalidation_reason);
+ bool make_not_used() { return make_not_entrant(InvalidationReason::NOT_USED); }
bool is_marked_for_deoptimization() const { return deoptimization_status() != not_marked; }
bool has_been_deoptimized() const { return deoptimization_status() == deoptimize_done; }
@@ -917,6 +993,7 @@ class nmethod : public CodeBlob {
void print_on_impl(outputStream* st) const;
void print_code();
void print_value_on_impl(outputStream* st) const;
+ void print_code_snippet(outputStream* st, address addr) const;
#if defined(SUPPORT_DATA_STRUCTS)
// print output in opt build for disassembler library
@@ -947,7 +1024,7 @@ class nmethod : public CodeBlob {
// Logging
void log_identity(xmlStream* log) const;
void log_new_nmethod() const;
- void log_state_change(const char* reason) const;
+ void log_state_change(InvalidationReason invalidation_reason) const;
// Prints block-level comments, including nmethod specific block labels:
void print_nmethod_labels(outputStream* stream, address block_begin, bool print_section_labels=true) const;
diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp
index 39c90281c79..6f84dbeb40c 100644
--- a/src/hotspot/share/compiler/compilationPolicy.cpp
+++ b/src/hotspot/share/compiler/compilationPolicy.cpp
@@ -404,7 +404,7 @@ double CompilationPolicy::threshold_scale(CompLevel level, int feedback_k) {
return 1;
}
-void CompilationPolicy::print_counters(const char* prefix, Method* m) {
+void CompilationPolicy::print_counters_on(outputStream* st, const char* prefix, Method* m) {
int invocation_count = m->invocation_count();
int backedge_count = m->backedge_count();
MethodData* mdh = m->method_data();
@@ -416,133 +416,140 @@ void CompilationPolicy::print_counters(const char* prefix, Method* m) {
mdo_invocations_start = mdh->invocation_count_start();
mdo_backedges_start = mdh->backedge_count_start();
}
- tty->print(" %stotal=%d,%d %smdo=%d(%d),%d(%d)", prefix,
- invocation_count, backedge_count, prefix,
- mdo_invocations, mdo_invocations_start,
- mdo_backedges, mdo_backedges_start);
- tty->print(" %smax levels=%d,%d", prefix,
- m->highest_comp_level(), m->highest_osr_comp_level());
+ st->print(" %stotal=%d,%d %smdo=%d(%d),%d(%d)", prefix,
+ invocation_count, backedge_count, prefix,
+ mdo_invocations, mdo_invocations_start,
+ mdo_backedges, mdo_backedges_start);
+ st->print(" %smax levels=%d,%d", prefix, m->highest_comp_level(), m->highest_osr_comp_level());
}
-void CompilationPolicy::print_training_data(const char* prefix, Method* method) {
+void CompilationPolicy::print_training_data_on(outputStream* st, const char* prefix, Method* method) {
methodHandle m(Thread::current(), method);
- tty->print(" %smtd: ", prefix);
+ st->print(" %smtd: ", prefix);
MethodTrainingData* mtd = MethodTrainingData::find(m);
if (mtd == nullptr) {
- tty->print("null");
+ st->print("null");
} else {
MethodData* md = mtd->final_profile();
- tty->print("mdo=");
+ st->print("mdo=");
if (md == nullptr) {
- tty->print("null");
+ st->print("null");
} else {
int mdo_invocations = md->invocation_count();
int mdo_backedges = md->backedge_count();
int mdo_invocations_start = md->invocation_count_start();
int mdo_backedges_start = md->backedge_count_start();
- tty->print("%d(%d), %d(%d)", mdo_invocations, mdo_invocations_start, mdo_backedges, mdo_backedges_start);
+ st->print("%d(%d), %d(%d)", mdo_invocations, mdo_invocations_start, mdo_backedges, mdo_backedges_start);
}
CompileTrainingData* ctd = mtd->last_toplevel_compile(CompLevel_full_optimization);
- tty->print(", deps=");
+ st->print(", deps=");
if (ctd == nullptr) {
- tty->print("null");
+ st->print("null");
} else {
- tty->print("%d", ctd->init_deps_left());
+ st->print("%d", ctd->init_deps_left());
}
}
}
// Print an event.
-void CompilationPolicy::print_event(EventType type, Method* m, Method* im, int bci, CompLevel level) {
+void CompilationPolicy::print_event_on(outputStream *st, EventType type, Method* m, Method* im, int bci, CompLevel level) {
bool inlinee_event = m != im;
- ttyLocker tty_lock;
- tty->print("%lf: [", os::elapsedTime());
+ st->print("%lf: [", os::elapsedTime());
switch(type) {
case CALL:
- tty->print("call");
+ st->print("call");
break;
case LOOP:
- tty->print("loop");
+ st->print("loop");
break;
case COMPILE:
- tty->print("compile");
+ st->print("compile");
break;
case FORCE_COMPILE:
- tty->print("force-compile");
+ st->print("force-compile");
break;
case REMOVE_FROM_QUEUE:
- tty->print("remove-from-queue");
+ st->print("remove-from-queue");
break;
case UPDATE_IN_QUEUE:
- tty->print("update-in-queue");
+ st->print("update-in-queue");
break;
case REPROFILE:
- tty->print("reprofile");
+ st->print("reprofile");
break;
case MAKE_NOT_ENTRANT:
- tty->print("make-not-entrant");
+ st->print("make-not-entrant");
break;
default:
- tty->print("unknown");
+ st->print("unknown");
}
- tty->print(" level=%d ", level);
+ st->print(" level=%d ", level);
ResourceMark rm;
char *method_name = m->name_and_sig_as_C_string();
- tty->print("[%s", method_name);
+ st->print("[%s", method_name);
if (inlinee_event) {
char *inlinee_name = im->name_and_sig_as_C_string();
- tty->print(" [%s]] ", inlinee_name);
+ st->print(" [%s]] ", inlinee_name);
}
- else tty->print("] ");
- tty->print("@%d queues=%d,%d", bci, CompileBroker::queue_size(CompLevel_full_profile),
- CompileBroker::queue_size(CompLevel_full_optimization));
+ else st->print("] ");
+ st->print("@%d queues=%d,%d", bci, CompileBroker::queue_size(CompLevel_full_profile),
+ CompileBroker::queue_size(CompLevel_full_optimization));
- tty->print(" rate=");
- if (m->prev_time() == 0) tty->print("n/a");
- else tty->print("%f", m->rate());
+ st->print(" rate=");
+ if (m->prev_time() == 0) st->print("n/a");
+ else st->print("%f", m->rate());
- tty->print(" k=%.2lf,%.2lf", threshold_scale(CompLevel_full_profile, Tier3LoadFeedback),
- threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback));
+ st->print(" k=%.2lf,%.2lf", threshold_scale(CompLevel_full_profile, Tier3LoadFeedback),
+ threshold_scale(CompLevel_full_optimization, Tier4LoadFeedback));
if (type != COMPILE) {
- print_counters("", m);
+ print_counters_on(st, "", m);
if (inlinee_event) {
- print_counters("inlinee ", im);
+ print_counters_on(st, "inlinee ", im);
}
- tty->print(" compilable=");
+ st->print(" compilable=");
bool need_comma = false;
if (!m->is_not_compilable(CompLevel_full_profile)) {
- tty->print("c1");
+ st->print("c1");
need_comma = true;
}
if (!m->is_not_osr_compilable(CompLevel_full_profile)) {
- if (need_comma) tty->print(",");
- tty->print("c1-osr");
+ if (need_comma) st->print(",");
+ st->print("c1-osr");
need_comma = true;
}
if (!m->is_not_compilable(CompLevel_full_optimization)) {
- if (need_comma) tty->print(",");
- tty->print("c2");
+ if (need_comma) st->print(",");
+ st->print("c2");
need_comma = true;
}
if (!m->is_not_osr_compilable(CompLevel_full_optimization)) {
- if (need_comma) tty->print(",");
- tty->print("c2-osr");
+ if (need_comma) st->print(",");
+ st->print("c2-osr");
}
- tty->print(" status=");
+ st->print(" status=");
if (m->queued_for_compilation()) {
- tty->print("in-queue");
- } else tty->print("idle");
- print_training_data("", m);
+ st->print("in-queue");
+ } else st->print("idle");
+
+ print_training_data_on(st, "", m);
if (inlinee_event) {
- print_training_data("inlinee ", im);
+ print_training_data_on(st, "inlinee ", im);
}
}
- tty->print_cr("]");
+ st->print_cr("]");
+
+}
+
+void CompilationPolicy::print_event(EventType type, Method* m, Method* im, int bci, CompLevel level) {
+ stringStream s;
+ print_event_on(&s, type, m, im, bci, level);
+ ResourceMark rm;
+ tty->print("%s", s.as_string());
}
void CompilationPolicy::initialize() {
@@ -924,7 +931,7 @@ void CompilationPolicy::compile(const methodHandle& mh, int bci, CompLevel level
nmethod* osr_nm = mh->lookup_osr_nmethod_for(bci, CompLevel_simple, false);
if (osr_nm != nullptr && osr_nm->comp_level() > CompLevel_simple) {
// Invalidate the existing OSR nmethod so that a compile at CompLevel_simple is permitted.
- osr_nm->make_not_entrant("OSR invalidation for compiling with C1");
+ osr_nm->make_not_entrant(nmethod::InvalidationReason::OSR_INVALIDATION_FOR_COMPILING_WITH_C1);
}
compile(mh, bci, CompLevel_simple, THREAD);
}
@@ -1335,17 +1342,24 @@ CompLevel CompilationPolicy::standard_transition(const methodHandle& method, Com
return next_level;
}
+template static inline bool apply_predicate(const methodHandle& method, CompLevel cur_level, int i, int b, bool delay_profiling, double delay_profiling_scale) {
+ if (delay_profiling) {
+ return Predicate::apply_scaled(method, cur_level, i, b, delay_profiling_scale);
+ } else {
+ return Predicate::apply(method, cur_level, i, b);
+ }
+}
+
template
CompLevel CompilationPolicy::transition_from_none(const methodHandle& method, CompLevel cur_level, bool delay_profiling, bool disable_feedback) {
precond(cur_level == CompLevel_none);
CompLevel next_level = cur_level;
int i = method->invocation_count();
int b = method->backedge_count();
- double scale = delay_profiling ? Tier0ProfileDelayFactor : 1.0;
// If we were at full profile level, would we switch to full opt?
if (transition_from_full_profile(method, CompLevel_full_profile) == CompLevel_full_optimization) {
next_level = CompLevel_full_optimization;
- } else if (!CompilationModeFlag::disable_intermediate() && Predicate::apply_scaled(method, cur_level, i, b, scale)) {
+ } else if (!CompilationModeFlag::disable_intermediate() && apply_predicate(method, cur_level, i, b, delay_profiling, Tier0ProfileDelayFactor)) {
// C1-generated fully profiled code is about 30% slower than the limited profile
// code that has only invocation and backedge counters. The observation is that
// if C2 queue is large enough we can spend too much time in the fully profiled code
@@ -1387,13 +1401,12 @@ CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle&
CompLevel next_level = cur_level;
int i = method->invocation_count();
int b = method->backedge_count();
- double scale = delay_profiling ? Tier2ProfileDelayFactor : 1.0;
MethodData* mdo = method->method_data();
if (mdo != nullptr) {
if (mdo->would_profile()) {
if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
- Predicate::apply_scaled(method, cur_level, i, b, scale))) {
+ apply_predicate(method, cur_level, i, b, delay_profiling, Tier2ProfileDelayFactor))) {
next_level = CompLevel_full_profile;
}
} else {
@@ -1403,7 +1416,7 @@ CompLevel CompilationPolicy::transition_from_limited_profile(const methodHandle&
// If there is no MDO we need to profile
if (disable_feedback || (CompileBroker::queue_size(CompLevel_full_optimization) <=
Tier3DelayOff * compiler_count(CompLevel_full_optimization) &&
- Predicate::apply_scaled(method, cur_level, i, b, scale))) {
+ apply_predicate(method, cur_level, i, b, delay_profiling, Tier2ProfileDelayFactor))) {
next_level = CompLevel_full_profile;
}
}
@@ -1516,7 +1529,7 @@ void CompilationPolicy::method_back_branch_event(const methodHandle& mh, const m
int osr_bci = nm->is_osr_method() ? nm->osr_entry_bci() : InvocationEntryBci;
print_event(MAKE_NOT_ENTRANT, mh(), mh(), osr_bci, level);
}
- nm->make_not_entrant("OSR invalidation, back branch");
+ nm->make_not_entrant(nmethod::InvalidationReason::OSR_INVALIDATION_BACK_BRANCH);
}
}
// Fix up next_level if necessary to avoid deopts
diff --git a/src/hotspot/share/compiler/compilationPolicy.hpp b/src/hotspot/share/compiler/compilationPolicy.hpp
index 75374a2f830..f4a7c4c249b 100644
--- a/src/hotspot/share/compiler/compilationPolicy.hpp
+++ b/src/hotspot/share/compiler/compilationPolicy.hpp
@@ -287,8 +287,8 @@ class CompilationPolicy : AllStatic {
// loop_event checks if a method should be OSR compiled at a different
// level.
static CompLevel loop_event(const methodHandle& method, CompLevel cur_level, JavaThread* THREAD);
- static void print_counters(const char* prefix, Method* m);
- static void print_training_data(const char* prefix, Method* method);
+ static void print_counters_on(outputStream* st, const char* prefix, Method* m);
+ static void print_training_data_on(outputStream* st, const char* prefix, Method* method);
// Has a method been long around?
// We don't remove old methods from the compile queue even if they have
// very low activity (see select_task()).
@@ -318,6 +318,7 @@ class CompilationPolicy : AllStatic {
static void set_c2_count(int x) { _c2_count = x; }
enum EventType { CALL, LOOP, COMPILE, FORCE_COMPILE, FORCE_RECOMPILE, REMOVE_FROM_QUEUE, UPDATE_IN_QUEUE, REPROFILE, MAKE_NOT_ENTRANT };
+ static void print_event_on(outputStream *st, EventType type, Method* m, Method* im, int bci, CompLevel level);
static void print_event(EventType type, Method* m, Method* im, int bci, CompLevel level);
// Check if the method can be compiled, change level if necessary
static void compile(const methodHandle& mh, int bci, CompLevel level, TRAPS);
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
index 705dcd7895b..e96fbeba964 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
@@ -28,6 +28,7 @@
#include "code/codeCache.hpp"
#include "compiler/oopMap.hpp"
#include "gc/g1/g1Allocator.inline.hpp"
+#include "gc/g1/g1Analytics.hpp"
#include "gc/g1/g1Arguments.hpp"
#include "gc/g1/g1BarrierSet.hpp"
#include "gc/g1/g1BatchedTask.hpp"
@@ -387,22 +388,26 @@ HeapWord* G1CollectedHeap::allocate_new_tlab(size_t min_size,
assert_heap_not_locked_and_not_at_safepoint();
assert(!is_humongous(requested_size), "we do not allow humongous TLABs");
- return attempt_allocation(min_size, requested_size, actual_size);
+ // Do not allow a GC because we are allocating a new TLAB to avoid an issue
+ // with UseGCOverheadLimit: although this GC would return null if the overhead
+ // limit would be exceeded, but it would likely free at least some space.
+ // So the subsequent outside-TLAB allocation could be successful anyway and
+ // the indication that the overhead limit had been exceeded swallowed.
+ return attempt_allocation(min_size, requested_size, actual_size, false /* allow_gc */);
}
-HeapWord*
-G1CollectedHeap::mem_allocate(size_t word_size,
- bool* gc_overhead_limit_was_exceeded) {
+HeapWord* G1CollectedHeap::mem_allocate(size_t word_size,
+ bool* gc_overhead_limit_was_exceeded) {
assert_heap_not_locked_and_not_at_safepoint();
if (is_humongous(word_size)) {
return attempt_allocation_humongous(word_size);
}
size_t dummy = 0;
- return attempt_allocation(word_size, word_size, &dummy);
+ return attempt_allocation(word_size, word_size, &dummy, true /* allow_gc */);
}
-HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_size) {
+HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_size, bool allow_gc) {
ResourceMark rm; // For retrieving the thread names in log messages.
// Make sure you read the note in attempt_allocation_humongous().
@@ -429,6 +434,8 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_
result = _allocator->attempt_allocation_locked(node_index, word_size);
if (result != nullptr) {
return result;
+ } else if (!allow_gc) {
+ return nullptr;
}
// Read the GC count while still holding the Heap_lock.
@@ -446,8 +453,15 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(uint node_index, size_t word_
log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu words",
Thread::current()->name(), word_size);
+ // Has the gc overhead limit been reached in the meantime? If so, this mutator
+ // should receive null even when unsuccessfully scheduling a collection as well
+ // for global consistency.
+ if (gc_overhead_limit_exceeded()) {
+ return nullptr;
+ }
+
// We can reach here if we were unsuccessful in scheduling a collection (because
- // another thread beat us to it). In this case immeditealy retry the allocation
+ // another thread beat us to it). In this case immediately retry the allocation
// attempt because another thread successfully performed a collection and possibly
// reclaimed enough space. The first attempt (without holding the Heap_lock) is
// here and the follow-on attempt will be at the start of the next loop
@@ -592,7 +606,8 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion range) {
inline HeapWord* G1CollectedHeap::attempt_allocation(size_t min_word_size,
size_t desired_word_size,
- size_t* actual_word_size) {
+ size_t* actual_word_size,
+ bool allow_gc) {
assert_heap_not_locked_and_not_at_safepoint();
assert(!is_humongous(desired_word_size), "attempt_allocation() should not "
"be called for humongous allocation requests");
@@ -604,7 +619,7 @@ inline HeapWord* G1CollectedHeap::attempt_allocation(size_t min_word_size,
if (result == nullptr) {
*actual_word_size = desired_word_size;
- result = attempt_allocation_slow(node_index, desired_word_size);
+ result = attempt_allocation_slow(node_index, desired_word_size, allow_gc);
}
assert_heap_not_locked();
@@ -688,6 +703,13 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {
log_trace(gc, alloc)("%s: Unsuccessfully scheduled collection allocating %zu",
Thread::current()->name(), word_size);
+ // Has the gc overhead limit been reached in the meantime? If so, this mutator
+ // should receive null even when unsuccessfully scheduling a collection as well
+ // for global consistency.
+ if (gc_overhead_limit_exceeded()) {
+ return nullptr;
+ }
+
// We can reach here if we were unsuccessful in scheduling a collection (because
// another thread beat us to it).
// Humongous object allocation always needs a lock, so we wait for the retry
@@ -890,25 +912,62 @@ void G1CollectedHeap::resize_heap_if_necessary(size_t allocation_word_size) {
}
}
+void G1CollectedHeap::update_gc_overhead_counter() {
+ assert(SafepointSynchronize::is_at_safepoint(), "precondition");
+
+ if (!UseGCOverheadLimit) {
+ return;
+ }
+
+ bool gc_time_over_limit = (_policy->analytics()->long_term_pause_time_ratio() * 100) >= GCTimeLimit;
+ double free_space_percent = percent_of(num_available_regions() * G1HeapRegion::GrainBytes, max_capacity());
+ bool free_space_below_limit = free_space_percent < GCHeapFreeLimit;
+
+ log_debug(gc)("GC Overhead Limit: GC Time %f Free Space %f Counter %zu",
+ (_policy->analytics()->long_term_pause_time_ratio() * 100),
+ free_space_percent,
+ _gc_overhead_counter);
+
+ if (gc_time_over_limit && free_space_below_limit) {
+ _gc_overhead_counter++;
+ } else {
+ _gc_overhead_counter = 0;
+ }
+}
+
+bool G1CollectedHeap::gc_overhead_limit_exceeded() {
+ return _gc_overhead_counter >= GCOverheadLimitThreshold;
+}
+
HeapWord* G1CollectedHeap::satisfy_failed_allocation_helper(size_t word_size,
bool do_gc,
bool maximal_compaction,
bool expect_null_mutator_alloc_region) {
- // Let's attempt the allocation first.
- HeapWord* result =
- attempt_allocation_at_safepoint(word_size,
- expect_null_mutator_alloc_region);
- if (result != nullptr) {
- return result;
- }
+ // Skip allocation if GC overhead limit has been exceeded to let the mutator run
+ // into an OOME. It can either exit "gracefully" or try to free up memory asap.
+ // For the latter situation, keep running GCs. If the mutator frees up enough
+ // memory quickly enough, the overhead(s) will go below the threshold(s) again
+ // and the VM may continue running.
+ // If we did not continue garbage collections, the (gc overhead) limit may decrease
+ // enough by itself to not count as exceeding the limit any more, in the worst
+ // case bouncing back-and-forth all the time.
+ if (!gc_overhead_limit_exceeded()) {
+ // Let's attempt the allocation first.
+ HeapWord* result =
+ attempt_allocation_at_safepoint(word_size,
+ expect_null_mutator_alloc_region);
+ if (result != nullptr) {
+ return result;
+ }
- // In a G1 heap, we're supposed to keep allocation from failing by
- // incremental pauses. Therefore, at least for now, we'll favor
- // expansion over collection. (This might change in the future if we can
- // do something smarter than full collection to satisfy a failed alloc.)
- result = expand_and_allocate(word_size);
- if (result != nullptr) {
- return result;
+ // In a G1 heap, we're supposed to keep allocation from failing by
+ // incremental pauses. Therefore, at least for now, we'll favor
+ // expansion over collection. (This might change in the future if we can
+ // do something smarter than full collection to satisfy a failed alloc.)
+ result = expand_and_allocate(word_size);
+ if (result != nullptr) {
+ return result;
+ }
}
if (do_gc) {
@@ -932,6 +991,10 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation_helper(size_t word_size,
HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) {
assert_at_safepoint_on_vm_thread();
+ // Update GC overhead limits after the initial garbage collection leading to this
+ // allocation attempt.
+ update_gc_overhead_counter();
+
// Attempts to allocate followed by Full GC.
HeapWord* result =
satisfy_failed_allocation_helper(word_size,
@@ -966,6 +1029,10 @@ HeapWord* G1CollectedHeap::satisfy_failed_allocation(size_t word_size) {
assert(!soft_ref_policy()->should_clear_all_soft_refs(),
"Flag should have been handled and cleared prior to this point");
+ if (gc_overhead_limit_exceeded()) {
+ log_info(gc)("GC Overhead Limit exceeded too often (%zu).", GCOverheadLimitThreshold);
+ }
+
// 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
@@ -1131,6 +1198,7 @@ class HumongousRegionSetChecker : public G1HeapRegionSetChecker {
G1CollectedHeap::G1CollectedHeap() :
CollectedHeap(),
+ _gc_overhead_counter(0),
_service_thread(nullptr),
_periodic_gc_task(nullptr),
_free_arena_memory_task(nullptr),
@@ -1690,6 +1758,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) {
@@ -1750,7 +1878,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.
//
@@ -1762,11 +1928,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() ||
@@ -1790,56 +1951,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;
}
}
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
index fbd1fe6165f..c74d77aefce 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
@@ -169,6 +169,17 @@ class G1CollectedHeap : public CollectedHeap {
friend class G1CheckRegionAttrTableClosure;
private:
+ // GC Overhead Limit functionality related members.
+ //
+ // The goal is to return null for allocations prematurely (before really going
+ // OOME) in case both GC CPU usage (>= GCTimeLimit) and not much available free
+ // memory (<= GCHeapFreeLimit) so that applications can exit gracefully or try
+ // to keep running by easing off memory.
+ uintx _gc_overhead_counter; // The number of consecutive garbage collections we were over the limits.
+
+ void update_gc_overhead_counter();
+ bool gc_overhead_limit_exceeded();
+
G1ServiceThread* _service_thread;
G1ServiceTask* _periodic_gc_task;
G1MonotonicArenaFreeMemoryTask* _free_arena_memory_task;
@@ -274,6 +285,14 @@ class G1CollectedHeap : public CollectedHeap {
// (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,
@@ -417,18 +436,14 @@ class G1CollectedHeap : public CollectedHeap {
//
// * If either call cannot satisfy the allocation request using the
// current allocating region, they will try to get a new one. If
- // this fails, they will attempt to do an evacuation pause and
- // retry the allocation.
- //
- // * If all allocation attempts fail, even after trying to schedule
- // an evacuation pause, allocate_new_tlab() will return null,
- // whereas mem_allocate() will attempt a heap expansion and/or
- // schedule a Full GC.
+ // this fails, (only) mem_allocate() will attempt to do an evacuation
+ // pause and retry the allocation. Allocate_new_tlab() will return null,
+ // deferring to the following mem_allocate().
//
// * We do not allow humongous-sized TLABs. So, allocate_new_tlab
// should never be called with word_size being humongous. All
// humongous allocation requests should go to mem_allocate() which
- // will satisfy them with a special path.
+ // will satisfy them in a special path.
HeapWord* allocate_new_tlab(size_t min_size,
size_t requested_size,
@@ -442,12 +457,13 @@ class G1CollectedHeap : public CollectedHeap {
// should only be used for non-humongous allocations.
inline HeapWord* attempt_allocation(size_t min_word_size,
size_t desired_word_size,
- size_t* actual_word_size);
-
+ size_t* actual_word_size,
+ bool allow_gc);
// Second-level mutator allocation attempt: take the Heap_lock and
// retry the allocation attempt, potentially scheduling a GC
- // pause. This should only be used for non-humongous allocations.
- HeapWord* attempt_allocation_slow(uint node_index, size_t word_size);
+ // pause if allow_gc is set. This should only be used for non-humongous
+ // allocations.
+ HeapWord* attempt_allocation_slow(uint node_index, size_t word_size, bool allow_gc);
// Takes the Heap_lock and attempts a humongous allocation. It can
// potentially schedule a GC pause.
diff --git a/src/hotspot/share/gc/g1/g1CollectorState.hpp b/src/hotspot/share/gc/g1/g1CollectorState.hpp
index f9f1839c7be..54b85543449 100644
--- a/src/hotspot/share/gc/g1/g1CollectorState.hpp
+++ b/src/hotspot/share/gc/g1/g1CollectorState.hpp
@@ -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 @@ class G1CollectorState {
_in_concurrent_start_gc(false),
_initiate_conc_mark_if_possible(false),
+ _mark_in_progress(false),
_mark_or_rebuild_in_progress(false),
_clearing_bitmap(false),
_in_full_gc(false) { }
@@ -92,6 +96,7 @@ class G1CollectorState {
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_clearing_bitmap(bool v) { _clearing_bitmap = v; }
@@ -106,6 +111,7 @@ class G1CollectorState {
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 clearing_bitmap() const { return _clearing_bitmap; }
diff --git a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp
index f7b0cb23b65..18e0e40c54a 100644
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp
@@ -570,8 +570,8 @@ void G1GCPhaseTimes::print(bool evacuation_failed) {
accounted_ms += print_evacuate_optional_collection_set();
accounted_ms += print_post_evacuate_collection_set(evacuation_failed);
- assert(_gc_pause_time_ms >= accounted_ms, "GC pause time(%.3lfms) cannot be "
- "smaller than the sum of each phase(%.3lfms).", _gc_pause_time_ms, accounted_ms);
+ assert(_gc_pause_time_ms >= accounted_ms, "GC pause time(%.15lf ms) cannot be "
+ "smaller than the sum of each phase(%.15lf ms).", _gc_pause_time_ms, accounted_ms);
print_other(accounted_ms);
diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp
index 92b56953067..36cd8a7fcda 100644
--- a/src/hotspot/share/gc/g1/g1Policy.cpp
+++ b/src/hotspot/share/gc/g1/g1Policy.cpp
@@ -593,6 +593,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_clearing_bitmap(false);
@@ -704,6 +705,7 @@ void G1Policy::record_concurrent_mark_remark_end() {
double elapsed_time_ms = (end_time_sec - _mark_remark_start_sec)*1000.0;
_analytics->report_concurrent_mark_remark_times_ms(elapsed_time_ms);
record_pause(G1GCPauseType::Remark, _mark_remark_start_sec, end_time_sec);
+ collector_state()->set_mark_in_progress(false);
}
void G1Policy::record_concurrent_mark_cleanup_start() {
@@ -941,6 +943,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);
}
@@ -1227,6 +1230,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.
@@ -1259,8 +1273,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.
@@ -1275,7 +1288,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
diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp
index 69cdd8d5ca6..f57ce756443 100644
--- a/src/hotspot/share/gc/g1/g1VMOperations.cpp
+++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp
@@ -59,6 +59,7 @@ VM_G1TryInitiateConcMark::VM_G1TryInitiateConcMark(uint gc_count_before,
GCCause::Cause gc_cause) :
VM_GC_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
diff --git a/src/hotspot/share/gc/g1/g1VMOperations.hpp b/src/hotspot/share/gc/g1/g1VMOperations.hpp
index a201d57db76..8bac6263265 100644
--- a/src/hotspot/share/gc/g1/g1VMOperations.hpp
+++ b/src/hotspot/share/gc/g1/g1VMOperations.hpp
@@ -45,6 +45,7 @@ class VM_G1CollectFull : public VM_GC_Operation {
class VM_G1TryInitiateConcMark : public VM_GC_Operation {
bool _transient_failure;
+ bool _mark_in_progress;
bool _cycle_already_in_progress;
bool _whitebox_attached;
bool _terminating;
@@ -59,6 +60,7 @@ class VM_G1TryInitiateConcMark : public VM_GC_Operation {
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; }
diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp
index 2d23dce9488..5e83c8b76d8 100644
--- a/src/hotspot/share/gc/shared/collectedHeap.cpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.cpp
@@ -67,7 +67,7 @@ Klass* CollectedHeap::_filler_object_klass = nullptr;
size_t CollectedHeap::_filler_array_max_size = 0;
size_t CollectedHeap::_stack_chunk_max_size = 0;
-class GCLogMessage : public FormatBuffer<512> {};
+class GCLogMessage : public FormatBuffer<1024> {};
template <>
void EventLogBase::print(outputStream* st, GCLogMessage& m) {
diff --git a/src/hotspot/share/gc/shared/gcCause.hpp b/src/hotspot/share/gc/shared/gcCause.hpp
index ef96bf21567..9a918243db4 100644
--- a/src/hotspot/share/gc/shared/gcCause.hpp
+++ b/src/hotspot/share/gc/shared/gcCause.hpp
@@ -106,6 +106,11 @@ class GCCause : public AllStatic {
cause == GCCause::_heap_dump);
}
+ inline static bool is_codecache_requested_gc(GCCause::Cause cause) {
+ return (cause == _codecache_GC_threshold ||
+ cause == _codecache_GC_aggressive);
+ }
+
// Causes for collection of the tenured gernation
inline static bool is_tenured_allocation_failure_gc(GCCause::Cause cause) {
// _allocation_failure is the generic cause a collection which could result
diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp
index f693ab910ba..4b3cbf04b40 100644
--- a/src/hotspot/share/gc/shared/gc_globals.hpp
+++ b/src/hotspot/share/gc/shared/gc_globals.hpp
@@ -414,7 +414,7 @@
"Initial ratio of young generation/survivor space size") \
range(3, max_uintx) \
\
- product(bool, UseGCOverheadLimit, true, \
+ product(bool, UseGCOverheadLimit, falseInDebug, \
"Use policy to limit of proportion of time spent in GC " \
"before an OutOfMemory error is thrown") \
\
diff --git a/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp
index 28e5e9adf20..9da11518c3e 100644
--- a/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedupStat.cpp
@@ -100,13 +100,17 @@ void StringDedup::Stat::log_summary(const Stat* last_stat, const Stat* total_sta
log_info(stringdedup)(
"Concurrent String Deduplication "
- "%zu/" STRDEDUP_BYTES_FORMAT_NS " (new), "
+ "%zu (inspected), "
+ "%zu/" STRDEDUP_BYTES_FORMAT_NS " (new unknown), "
"%zu/" STRDEDUP_BYTES_FORMAT_NS " (deduped), "
- "avg " STRDEDUP_PERCENT_FORMAT_NS ", "
+ "total avg deduped/new unknown bytes " STRDEDUP_PERCENT_FORMAT_NS ", "
+ STRDEDUP_BYTES_FORMAT_NS " (total deduped)," STRDEDUP_BYTES_FORMAT_NS " (total new unknown), "
STRDEDUP_ELAPSED_FORMAT_MS " of " STRDEDUP_ELAPSED_FORMAT_MS,
+ last_stat->_inspected,
last_stat->_new, STRDEDUP_BYTES_PARAM(last_stat->_new_bytes),
last_stat->_deduped, STRDEDUP_BYTES_PARAM(last_stat->_deduped_bytes),
total_deduped_bytes_percent,
+ STRDEDUP_BYTES_PARAM(total_stat->_deduped_bytes), STRDEDUP_BYTES_PARAM(total_stat->_new_bytes),
strdedup_elapsed_param_ms(last_stat->_process_elapsed),
strdedup_elapsed_param_ms(last_stat->_active_elapsed));
}
@@ -217,14 +221,14 @@ void StringDedup::Stat::log_statistics(bool total) const {
double replaced_percent = percent_of(_replaced, _new);
double deleted_percent = percent_of(_deleted, _new);
log_times(total ? "Total" : "Last");
- log_debug(stringdedup)(" Inspected: %12zu", _inspected);
- log_debug(stringdedup)(" Known: %12zu(%5.1f%%)", _known, known_percent);
- log_debug(stringdedup)(" Shared: %12zu(%5.1f%%)", _known_shared, known_shared_percent);
- log_debug(stringdedup)(" New: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT,
+ log_debug(stringdedup)(" Inspected: %12zu", _inspected);
+ log_debug(stringdedup)(" Known: %12zu(%5.1f%%)", _known, known_percent);
+ log_debug(stringdedup)(" Shared: %12zu(%5.1f%%)", _known_shared, known_shared_percent);
+ log_debug(stringdedup)(" New unknown: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT,
_new, new_percent, STRDEDUP_BYTES_PARAM(_new_bytes));
- log_debug(stringdedup)(" Replaced: %12zu(%5.1f%%)", _replaced, replaced_percent);
- log_debug(stringdedup)(" Deleted: %12zu(%5.1f%%)", _deleted, deleted_percent);
- log_debug(stringdedup)(" Deduplicated: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT "(%5.1f%%)",
+ log_debug(stringdedup)(" Replaced: %12zu(%5.1f%%)", _replaced, replaced_percent);
+ log_debug(stringdedup)(" Deleted: %12zu(%5.1f%%)", _deleted, deleted_percent);
+ log_debug(stringdedup)(" Deduplicated: %12zu(%5.1f%%)" STRDEDUP_BYTES_FORMAT "(%5.1f%%)",
_deduped, deduped_percent, STRDEDUP_BYTES_PARAM(_deduped_bytes), deduped_bytes_percent);
log_debug(stringdedup)(" Skipped: %zu (dead), %zu (incomplete), %zu (shared)",
_skipped_dead, _skipped_incomplete, _skipped_shared);
diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp
index 807d4197b12..8210718126b 100644
--- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp
+++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2015, 2021, Red Hat, Inc. All rights reserved.
- * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved.
+ * Copyright (C) 2022, Tencent. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp
index eb87a9dc4c1..3ec42478282 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp
@@ -91,7 +91,7 @@ void ShenandoahAdaptiveHeuristics::choose_collection_set_from_regiondata(Shenand
// we hit max_cset. When max_cset is hit, we terminate the cset selection. Note that in this scheme,
// ShenandoahGarbageThreshold is the soft threshold which would be ignored until min_garbage is hit.
- size_t capacity = _space_info->soft_max_capacity();
+ size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
size_t max_cset = (size_t)((1.0 * capacity / 100 * ShenandoahEvacReserve) / ShenandoahEvacWaste);
size_t free_target = (capacity / 100 * ShenandoahMinFreeThreshold) + max_cset;
size_t min_garbage = (free_target > actual_free ? (free_target - actual_free) : 0);
@@ -233,7 +233,7 @@ static double saturate(double value, double min, double max) {
// in operation mode. We want some way to decide that the average rate has changed, while keeping average
// allocation rate computation independent.
bool ShenandoahAdaptiveHeuristics::should_start_gc() {
- size_t capacity = _space_info->soft_max_capacity();
+ size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
size_t available = _space_info->soft_available();
size_t allocated = _space_info->bytes_allocated_since_gc_start();
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp
index 403405b984d..592bba67757 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahCompactHeuristics.cpp
@@ -47,7 +47,7 @@ ShenandoahCompactHeuristics::ShenandoahCompactHeuristics(ShenandoahSpaceInfo* sp
bool ShenandoahCompactHeuristics::should_start_gc() {
size_t max_capacity = _space_info->max_capacity();
- size_t capacity = _space_info->soft_max_capacity();
+ size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
size_t available = _space_info->available();
// Make sure the code below treats available without the soft tail.
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp
index 08fd4599346..dfae9040242 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGenerationalHeuristics.cpp
@@ -28,7 +28,7 @@
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
#include "gc/shenandoah/shenandoahEvacInfo.hpp"
#include "gc/shenandoah/shenandoahGeneration.hpp"
-#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
+#include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
#include "gc/shenandoah/shenandoahOldGeneration.hpp"
#include "gc/shenandoah/shenandoahTrace.hpp"
@@ -65,8 +65,6 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio
size_t free = 0;
size_t free_regions = 0;
- const uint tenuring_threshold = heap->age_census()->tenuring_threshold();
-
// This counts number of humongous regions that we intend to promote in this cycle.
size_t humongous_regions_promoted = 0;
// This counts number of regular regions that will be promoted in place.
@@ -98,12 +96,12 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio
bool is_candidate;
// This is our candidate for later consideration.
if (collection_set->is_preselected(i)) {
- assert(region->age() >= tenuring_threshold, "Preselection filter");
+ assert(heap->is_tenurable(region), "Preselection filter");
is_candidate = true;
preselected_candidates++;
// Set garbage value to maximum value to force this into the sorted collection set.
garbage = region_size_bytes;
- } else if (region->is_young() && (region->age() >= tenuring_threshold)) {
+ } else if (region->is_young() && heap->is_tenurable(region)) {
// Note that for GLOBAL GC, region may be OLD, and OLD regions do not qualify for pre-selection
// This region is old enough to be promoted but it was not preselected, either because its garbage is below
@@ -142,7 +140,7 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio
immediate_regions++;
immediate_garbage += garbage;
} else {
- if (region->is_young() && region->age() >= tenuring_threshold) {
+ if (region->is_young() && heap->is_tenurable(region)) {
oop obj = cast_to_oop(region->bottom());
size_t humongous_regions = ShenandoahHeapRegion::required_regions(obj->size() * HeapWordSize);
humongous_regions_promoted += humongous_regions;
@@ -246,10 +244,6 @@ void ShenandoahGenerationalHeuristics::choose_collection_set(ShenandoahCollectio
size_t ShenandoahGenerationalHeuristics::add_preselected_regions_to_collection_set(ShenandoahCollectionSet* cset,
const RegionData* data,
size_t size) const {
-#ifdef ASSERT
- const uint tenuring_threshold = ShenandoahGenerationalHeap::heap()->age_census()->tenuring_threshold();
-#endif
-
// cur_young_garbage represents the amount of memory to be reclaimed from young-gen. In the case that live objects
// are known to be promoted out of young-gen, we count this as cur_young_garbage because this memory is reclaimed
// from young-gen and becomes available to serve future young-gen allocation requests.
@@ -257,7 +251,7 @@ size_t ShenandoahGenerationalHeuristics::add_preselected_regions_to_collection_s
for (size_t idx = 0; idx < size; idx++) {
ShenandoahHeapRegion* r = data[idx].get_region();
if (cset->is_preselected(r->index())) {
- assert(r->age() >= tenuring_threshold, "Preselected regions must have tenure age");
+ assert(ShenandoahGenerationalHeap::heap()->is_tenurable(r), "Preselected regions must have tenure age");
// Entire region will be promoted, This region does not impact young-gen or old-gen evacuation reserve.
// This region has been pre-selected and its impact on promotion reserve is already accounted for.
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp
index b8d85de0487..b4f46955730 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahGlobalHeuristics.cpp
@@ -25,7 +25,7 @@
#include "gc/shenandoah/heuristics/shenandoahGlobalHeuristics.hpp"
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
-#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
+#include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp"
#include "gc/shenandoah/shenandoahGlobalGeneration.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
#include "utilities/quickSort.hpp"
@@ -56,7 +56,6 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti
size_t capacity = heap->young_generation()->max_capacity();
size_t garbage_threshold = region_size_bytes * ShenandoahGarbageThreshold / 100;
size_t ignore_threshold = region_size_bytes * ShenandoahIgnoreGarbageThreshold / 100;
- const uint tenuring_threshold = heap->age_census()->tenuring_threshold();
size_t young_evac_reserve = heap->young_generation()->get_evacuation_reserve();
size_t old_evac_reserve = heap->old_generation()->get_evacuation_reserve();
@@ -100,7 +99,7 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti
ShenandoahHeapRegion* r = data[idx].get_region();
assert(!cset->is_preselected(r->index()), "There should be no preselected regions during GLOBAL GC");
bool add_region = false;
- if (r->is_old() || (r->age() >= tenuring_threshold)) {
+ if (r->is_old() || heap->is_tenurable(r)) {
size_t new_cset = old_cur_cset + r->get_live_data_bytes();
if ((r->garbage() > garbage_threshold)) {
while ((new_cset > max_old_cset) && (unaffiliated_young_regions > 0)) {
@@ -114,7 +113,7 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti
old_cur_cset = new_cset;
}
} else {
- assert(r->is_young() && (r->age() < tenuring_threshold), "DeMorgan's law (assuming r->is_affiliated)");
+ assert(r->is_young() && !heap->is_tenurable(r), "DeMorgan's law (assuming r->is_affiliated)");
size_t new_cset = young_cur_cset + r->get_live_data_bytes();
size_t region_garbage = r->garbage();
size_t new_garbage = cur_young_garbage + region_garbage;
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp
index 29b94e2f68f..2131f95b413 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahSpaceInfo.hpp
@@ -37,7 +37,6 @@
class ShenandoahSpaceInfo {
public:
virtual const char* name() const = 0;
- virtual size_t soft_max_capacity() const = 0;
virtual size_t max_capacity() const = 0;
virtual size_t soft_available() const = 0;
virtual size_t available() const = 0;
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp
index e6f60dc1c83..205135751aa 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahStaticHeuristics.cpp
@@ -42,7 +42,7 @@ ShenandoahStaticHeuristics::~ShenandoahStaticHeuristics() {}
bool ShenandoahStaticHeuristics::should_start_gc() {
size_t max_capacity = _space_info->max_capacity();
- size_t capacity = _space_info->soft_max_capacity();
+ size_t capacity = ShenandoahHeap::heap()->soft_max_capacity();
size_t available = _space_info->available();
// Make sure the code below treats available without the soft tail.
diff --git a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp
index 3aca436104b..d236be8c9e6 100644
--- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp
+++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahYoungHeuristics.cpp
@@ -26,7 +26,7 @@
#include "gc/shenandoah/heuristics/shenandoahOldHeuristics.hpp"
#include "gc/shenandoah/heuristics/shenandoahYoungHeuristics.hpp"
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
-#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
+#include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
#include "gc/shenandoah/shenandoahOldGeneration.hpp"
#include "gc/shenandoah/shenandoahYoungGeneration.hpp"
@@ -64,19 +64,18 @@ void ShenandoahYoungHeuristics::choose_young_collection_set(ShenandoahCollection
size_t size, size_t actual_free,
size_t cur_young_garbage) const {
- auto heap = ShenandoahGenerationalHeap::heap();
+ const auto heap = ShenandoahGenerationalHeap::heap();
- size_t capacity = heap->young_generation()->max_capacity();
- size_t garbage_threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahGarbageThreshold / 100;
- size_t ignore_threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahIgnoreGarbageThreshold / 100;
- const uint tenuring_threshold = heap->age_census()->tenuring_threshold();
+ const size_t capacity = heap->soft_max_capacity();
+ const size_t garbage_threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahGarbageThreshold / 100;
+ const size_t ignore_threshold = ShenandoahHeapRegion::region_size_bytes() * ShenandoahIgnoreGarbageThreshold / 100;
// This is young-gen collection or a mixed evacuation.
// If this is mixed evacuation, the old-gen candidate regions have already been added.
- size_t max_cset = (size_t) (heap->young_generation()->get_evacuation_reserve() / ShenandoahEvacWaste);
size_t cur_cset = 0;
- size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + max_cset;
- size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0;
+ const size_t max_cset = (size_t) (heap->young_generation()->get_evacuation_reserve() / ShenandoahEvacWaste);
+ const size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + max_cset;
+ const size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0;
log_info(gc, ergo)(
@@ -89,11 +88,15 @@ void ShenandoahYoungHeuristics::choose_young_collection_set(ShenandoahCollection
if (cset->is_preselected(r->index())) {
continue;
}
- if (r->age() < tenuring_threshold) {
- size_t new_cset = cur_cset + r->get_live_data_bytes();
- size_t region_garbage = r->garbage();
- size_t new_garbage = cur_young_garbage + region_garbage;
- bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage);
+
+ // Note that we do not add tenurable regions if they were not pre-selected. They were not preselected
+ // because there is insufficient room in old-gen to hold their to-be-promoted live objects or because
+ // they are to be promoted in place.
+ if (!heap->is_tenurable(r)) {
+ const size_t new_cset = cur_cset + r->get_live_data_bytes();
+ const size_t region_garbage = r->garbage();
+ const size_t new_garbage = cur_young_garbage + region_garbage;
+ const bool add_regardless = (region_garbage > ignore_threshold) && (new_garbage < min_garbage);
assert(r->is_young(), "Only young candidates expected in the data array");
if ((new_cset <= max_cset) && (add_regardless || (region_garbage > garbage_threshold))) {
cur_cset = new_cset;
@@ -101,9 +104,6 @@ void ShenandoahYoungHeuristics::choose_young_collection_set(ShenandoahCollection
cset->add_region(r);
}
}
- // Note that we do not add aged regions if they were not pre-selected. The reason they were not preselected
- // is because there is not sufficient room in old-gen to hold their to-be-promoted live objects or because
- // they are to be promoted in place.
}
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp
index 94c98b78f1b..bd66f55bd8f 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.cpp
@@ -27,8 +27,15 @@
#include "gc/shenandoah/shenandoahAgeCensus.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
-ShenandoahAgeCensus::ShenandoahAgeCensus() {
+ShenandoahAgeCensus::ShenandoahAgeCensus()
+ : ShenandoahAgeCensus(ShenandoahHeap::heap()->max_workers())
+{
assert(ShenandoahHeap::heap()->mode()->is_generational(), "Only in generational mode");
+}
+
+ShenandoahAgeCensus::ShenandoahAgeCensus(uint max_workers)
+ : _max_workers(max_workers)
+{
if (ShenandoahGenerationalMinTenuringAge > ShenandoahGenerationalMaxTenuringAge) {
vm_exit_during_initialization(
err_msg("ShenandoahGenerationalMinTenuringAge=%zu"
@@ -39,6 +46,9 @@ ShenandoahAgeCensus::ShenandoahAgeCensus() {
_global_age_table = NEW_C_HEAP_ARRAY(AgeTable*, MAX_SNAPSHOTS, mtGC);
CENSUS_NOISE(_global_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, MAX_SNAPSHOTS, mtGC);)
_tenuring_threshold = NEW_C_HEAP_ARRAY(uint, MAX_SNAPSHOTS, mtGC);
+ CENSUS_NOISE(_skipped = 0);
+ NOT_PRODUCT(_counted = 0);
+ NOT_PRODUCT(_total = 0);
for (int i = 0; i < MAX_SNAPSHOTS; i++) {
// Note that we don't now get perfdata from age_table
@@ -48,10 +58,9 @@ ShenandoahAgeCensus::ShenandoahAgeCensus() {
_tenuring_threshold[i] = MAX_COHORTS;
}
if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) {
- size_t max_workers = ShenandoahHeap::heap()->max_workers();
- _local_age_table = NEW_C_HEAP_ARRAY(AgeTable*, max_workers, mtGC);
+ _local_age_table = NEW_C_HEAP_ARRAY(AgeTable*, _max_workers, mtGC);
CENSUS_NOISE(_local_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, max_workers, mtGC);)
- for (uint i = 0; i < max_workers; i++) {
+ for (uint i = 0; i < _max_workers; i++) {
_local_age_table[i] = new AgeTable(false);
CENSUS_NOISE(_local_noise[i].clear();)
}
@@ -61,6 +70,22 @@ ShenandoahAgeCensus::ShenandoahAgeCensus() {
_epoch = MAX_SNAPSHOTS - 1; // see update_epoch()
}
+ShenandoahAgeCensus::~ShenandoahAgeCensus() {
+ for (uint i = 0; i < MAX_SNAPSHOTS; i++) {
+ delete _global_age_table[i];
+ }
+ FREE_C_HEAP_ARRAY(AgeTable*, _global_age_table);
+ FREE_C_HEAP_ARRAY(uint, _tenuring_threshold);
+ CENSUS_NOISE(FREE_C_HEAP_ARRAY(ShenandoahNoiseStats, _global_noise));
+ if (_local_age_table) {
+ for (uint i = 0; i < _max_workers; i++) {
+ delete _local_age_table[i];
+ }
+ FREE_C_HEAP_ARRAY(AgeTable*, _local_age_table);
+ CENSUS_NOISE(FREE_C_HEAP_ARRAY(ShenandoahNoiseStats, _local_noise));
+ }
+}
+
CENSUS_NOISE(void ShenandoahAgeCensus::add(uint obj_age, uint region_age, uint region_youth, size_t size, uint worker_id) {)
NO_CENSUS_NOISE(void ShenandoahAgeCensus::add(uint obj_age, uint region_age, size_t size, uint worker_id) {)
if (obj_age <= markWord::max_age) {
@@ -131,12 +156,11 @@ void ShenandoahAgeCensus::update_census(size_t age0_pop, AgeTable* pv1, AgeTable
assert(pv1 == nullptr && pv2 == nullptr, "Error, check caller");
// Seed cohort 0 with population that may have been missed during
// regular census.
- _global_age_table[_epoch]->add((uint)0, age0_pop);
+ _global_age_table[_epoch]->add(0u, age0_pop);
- size_t max_workers = ShenandoahHeap::heap()->max_workers();
// Merge data from local age tables into the global age table for the epoch,
// clearing the local tables.
- for (uint i = 0; i < max_workers; i++) {
+ for (uint i = 0; i < _max_workers; i++) {
// age stats
_global_age_table[_epoch]->merge(_local_age_table[i]);
_local_age_table[i]->clear(); // clear for next census
@@ -177,8 +201,7 @@ void ShenandoahAgeCensus::reset_local() {
assert(_local_age_table == nullptr, "Error");
return;
}
- size_t max_workers = ShenandoahHeap::heap()->max_workers();
- for (uint i = 0; i < max_workers; i++) {
+ for (uint i = 0; i < _max_workers; i++) {
_local_age_table[i]->clear();
CENSUS_NOISE(_local_noise[i].clear();)
}
@@ -204,8 +227,7 @@ bool ShenandoahAgeCensus::is_clear_local() {
assert(_local_age_table == nullptr, "Error");
return true;
}
- size_t max_workers = ShenandoahHeap::heap()->max_workers();
- for (uint i = 0; i < max_workers; i++) {
+ for (uint i = 0; i < _max_workers; i++) {
bool clear = _local_age_table[i]->is_clear();
CENSUS_NOISE(clear |= _local_noise[i].is_clear();)
if (!clear) {
@@ -246,7 +268,7 @@ void ShenandoahAgeCensus::update_tenuring_threshold() {
_tenuring_threshold[_epoch] = tt;
}
print();
- log_trace(gc, age)("New tenuring threshold %zu (min %zu, max %zu)",
+ log_info(gc, age)("New tenuring threshold %zu (min %zu, max %zu)",
(uintx) _tenuring_threshold[_epoch], ShenandoahGenerationalMinTenuringAge, ShenandoahGenerationalMaxTenuringAge);
}
@@ -279,13 +301,14 @@ uint ShenandoahAgeCensus::compute_tenuring_threshold() {
uint upper_bound = ShenandoahGenerationalMaxTenuringAge;
const uint prev_tt = previous_tenuring_threshold();
if (ShenandoahGenerationalCensusIgnoreOlderCohorts && prev_tt > 0) {
- // We stay below the computed tenuring threshold for the last cycle plus 1,
- // ignoring the mortality rates of any older cohorts.
- upper_bound = MIN2(upper_bound, prev_tt + 1);
+ // We stay below the computed tenuring threshold for the last cycle,
+ // ignoring the mortality rates of any older cohorts (which may see
+ // higher mortality rates due to promotions).
+ upper_bound = MIN2(upper_bound, prev_tt);
}
upper_bound = MIN2(upper_bound, markWord::max_age);
- const uint lower_bound = MAX2((uint)ShenandoahGenerationalMinTenuringAge, (uint)1);
+ const uint lower_bound = MAX2((uint)ShenandoahGenerationalMinTenuringAge, 1u);
uint tenuring_threshold = upper_bound;
for (uint i = upper_bound; i >= lower_bound; i--) {
@@ -303,9 +326,9 @@ uint ShenandoahAgeCensus::compute_tenuring_threshold() {
// cohorts are considered eligible for tenuring when all older
// cohorts are. We return the next higher age as the tenuring threshold
// so that we do not prematurely promote objects of this age.
- assert(tenuring_threshold == i+1 || tenuring_threshold == upper_bound, "Error");
+ assert(tenuring_threshold == i + 1 || tenuring_threshold == upper_bound, "Error");
assert(tenuring_threshold >= lower_bound && tenuring_threshold <= upper_bound, "Error");
- return tenuring_threshold;
+ return i + 1;
}
// Remember that we passed over this cohort, looking for younger cohorts
// showing high mortality. We want to tenure cohorts of this age.
@@ -335,6 +358,14 @@ double ShenandoahAgeCensus::mortality_rate(size_t prev_pop, size_t cur_pop) {
}
void ShenandoahAgeCensus::print() {
+
+ const LogTarget(Debug, gc, age) lt;
+ if (!lt.is_enabled()) {
+ return;
+ }
+
+ LogStream ls(lt);
+
// Print the population vector for the current epoch, and
// for the previous epoch, as well as the computed mortality
// ratio for each extant cohort.
@@ -350,33 +381,32 @@ void ShenandoahAgeCensus::print() {
for (uint i = 1; i < MAX_COHORTS; i++) {
const size_t prev_pop = prev_pv->sizes[i-1]; // (i-1) OK because i >= 1
const size_t cur_pop = cur_pv->sizes[i];
- double mr = mortality_rate(prev_pop, cur_pop);
+ const double mr = mortality_rate(prev_pop, cur_pop);
// Suppress printing when everything is zero
if (prev_pop + cur_pop > 0) {
- log_info(gc, age)
- (" - age %3u: prev %10zu bytes, curr %10zu bytes, mortality %.2f ",
- i, prev_pop*oopSize, cur_pop*oopSize, mr);
+ ls.print_cr(" - age %3u: prev %10zu bytes, curr %10zu bytes, mortality %.2f ",
+ i, prev_pop * oopSize, cur_pop * oopSize, mr);
}
total += cur_pop;
if (i == tt) {
// Underline the cohort for tenuring threshold (if < MAX_COHORTS)
- log_info(gc, age)("----------------------------------------------------------------------------");
+ ls.print_cr("----------------------------------------------------------------------------");
}
}
- CENSUS_NOISE(_global_noise[cur_epoch].print(total);)
+ CENSUS_NOISE(_global_noise[cur_epoch].print(ls, total);)
}
#ifdef SHENANDOAH_CENSUS_NOISE
-void ShenandoahNoiseStats::print(size_t total) {
+void ShenandoahNoiseStats::print(LogStream& ls, const size_t total) {
if (total > 0) {
- float f_skipped = (float)skipped/(float)total;
- float f_aged = (float)aged/(float)total;
- float f_clamped = (float)clamped/(float)total;
- float f_young = (float)young/(float)total;
- log_info(gc, age)("Skipped: %10zu (%.2f), R-Aged: %10zu (%.2f), "
- "Clamped: %10zu (%.2f), R-Young: %10zu (%.2f)",
- skipped*oopSize, f_skipped, aged*oopSize, f_aged,
- clamped*oopSize, f_clamped, young*oopSize, f_young);
+ const float f_skipped = (float)skipped/(float)total;
+ const float f_aged = (float)aged/(float)total;
+ const float f_clamped = (float)clamped/(float)total;
+ const float f_young = (float)young/(float)total;
+ ls.print_cr("Skipped: %10zu (%.2f), R-Aged: %10zu (%.2f), "
+ "Clamped: %10zu (%.2f), R-Young: %10zu (%.2f)",
+ skipped*oopSize, f_skipped, aged*oopSize, f_aged,
+ clamped*oopSize, f_clamped, young*oopSize, f_young);
}
}
#endif // SHENANDOAH_CENSUS_NOISE
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp
index 89c68f7120b..90d188e1fca 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahAgeCensus.hpp
@@ -37,6 +37,8 @@
#define CENSUS_NOISE(x) x
#define NO_CENSUS_NOISE(x)
+class LogStream;
+
struct ShenandoahNoiseStats {
size_t skipped; // Volume of objects skipped
size_t aged; // Volume of objects from aged regions
@@ -67,7 +69,7 @@ struct ShenandoahNoiseStats {
young += other.young;
}
- void print(size_t total);
+ void print(LogStream& ls, size_t total);
};
#else // SHENANDOAH_CENSUS_NOISE
#define CENSUS_NOISE(x)
@@ -91,7 +93,7 @@ struct ShenandoahNoiseStats {
//
// In addition, this class also maintains per worker population vectors into which
// census for the current minor GC is accumulated (during marking or, optionally, during
-// evacuation). These are cleared after each marking (resectively, evacuation) cycle,
+// evacuation). These are cleared after each marking (respectively, evacuation) cycle,
// once the per-worker data is consolidated into the appropriate population vector
// per minor collection. The _local_age_table is thus C x N, for N GC workers.
class ShenandoahAgeCensus: public CHeapObj {
@@ -111,10 +113,12 @@ class ShenandoahAgeCensus: public CHeapObj {
size_t _total; // net size of objects encountered (counted or skipped) in census
#endif
- uint _epoch; // Current epoch (modulo max age)
- uint *_tenuring_threshold; // An array of the last N tenuring threshold values we
+ uint _epoch; // Current epoch (modulo max age)
+ uint* _tenuring_threshold; // An array of the last N tenuring threshold values we
// computed.
+ uint _max_workers; // Maximum number of workers for parallel tasks
+
// Mortality rate of a cohort, given its population in
// previous and current epochs
double mortality_rate(size_t prev_pop, size_t cur_pop);
@@ -165,11 +169,22 @@ class ShenandoahAgeCensus: public CHeapObj {
};
ShenandoahAgeCensus();
+ ShenandoahAgeCensus(uint max_workers);
+ ~ShenandoahAgeCensus();
// Return the local age table (population vector) for worker_id.
// Only used in the case of (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac)
- AgeTable* get_local_age_table(uint worker_id) {
- return (AgeTable*) _local_age_table[worker_id];
+ AgeTable* get_local_age_table(uint worker_id) const {
+ return _local_age_table[worker_id];
+ }
+
+ // Return the most recently computed tenuring threshold.
+ // Visible for testing. Use is_tenurable for consistent tenuring comparisons.
+ uint tenuring_threshold() const { return _tenuring_threshold[_epoch]; }
+
+ // Return true if this age is at or above the tenuring threshold.
+ bool is_tenurable(uint age) const {
+ return age >= tenuring_threshold();
}
// Update the local age table for worker_id by size for
@@ -201,9 +216,6 @@ class ShenandoahAgeCensus: public CHeapObj {
// is 0, because the evacuated objects have all had their ages incremented.
void update_census(size_t age0_pop, AgeTable* pv1 = nullptr, AgeTable* pv2 = nullptr);
- // Return the most recently computed tenuring threshold
- uint tenuring_threshold() const { return _tenuring_threshold[_epoch]; }
-
// Reset the epoch, clearing accumulated census history
// Note: this isn't currently used, but reserved for planned
// future usage.
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp b/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp
index 6ac62e8e9ed..78ae78f4c24 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahAllocRequest.hpp
@@ -34,6 +34,7 @@ class ShenandoahAllocRequest : StackObj {
enum Type {
_alloc_shared, // Allocate common, outside of TLAB
_alloc_shared_gc, // Allocate common, outside of GCLAB/PLAB
+ _alloc_cds, // Allocate for CDS
_alloc_tlab, // Allocate TLAB
_alloc_gclab, // Allocate GCLAB
_alloc_plab, // Allocate PLAB
@@ -46,6 +47,8 @@ class ShenandoahAllocRequest : StackObj {
return "Shared";
case _alloc_shared_gc:
return "Shared GC";
+ case _alloc_cds:
+ return "CDS";
case _alloc_tlab:
return "TLAB";
case _alloc_gclab:
@@ -121,6 +124,10 @@ class ShenandoahAllocRequest : StackObj {
return ShenandoahAllocRequest(0, requested_size, _alloc_shared, ShenandoahAffiliation::YOUNG_GENERATION);
}
+ static inline ShenandoahAllocRequest for_cds(size_t requested_size) {
+ return ShenandoahAllocRequest(0, requested_size, _alloc_cds, ShenandoahAffiliation::YOUNG_GENERATION);
+ }
+
inline size_t size() const {
return _requested_size;
}
@@ -163,6 +170,7 @@ class ShenandoahAllocRequest : StackObj {
switch (_alloc_type) {
case _alloc_tlab:
case _alloc_shared:
+ case _alloc_cds:
return true;
case _alloc_gclab:
case _alloc_plab:
@@ -178,6 +186,7 @@ class ShenandoahAllocRequest : StackObj {
switch (_alloc_type) {
case _alloc_tlab:
case _alloc_shared:
+ case _alloc_cds:
return false;
case _alloc_gclab:
case _alloc_plab:
@@ -197,6 +206,7 @@ class ShenandoahAllocRequest : StackObj {
return true;
case _alloc_shared:
case _alloc_shared_gc:
+ case _alloc_cds:
return false;
default:
ShouldNotReachHere();
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp
index ffafcc5840d..c23f30a55e9 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp
@@ -64,7 +64,9 @@ void ShenandoahAsserts::print_obj(ShenandoahMessageBuffer& msg, oop obj) {
ShenandoahMarkingContext* const ctx = heap->marking_context();
- msg.append(" " PTR_FORMAT " - klass " PTR_FORMAT " %s\n", p2i(obj), p2i(obj->klass()), obj->klass()->external_name());
+ Klass* obj_klass = ShenandoahForwarding::klass(obj);
+
+ msg.append(" " PTR_FORMAT " - klass " PTR_FORMAT " %s\n", p2i(obj), p2i(obj_klass), obj_klass->external_name());
msg.append(" %3s allocated after mark start\n", ctx->allocated_after_mark_start(obj) ? "" : "not");
msg.append(" %3s after update watermark\n", cast_from_oop(obj) >= r->get_update_watermark() ? "" : "not");
msg.append(" %3s marked strong\n", ctx->is_marked_strong(obj) ? "" : "not");
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp
index 25b900f8d77..35faa40af77 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectionSet.cpp
@@ -27,6 +27,7 @@
#include "gc/shenandoah/shenandoahAgeCensus.hpp"
#include "gc/shenandoah/shenandoahCollectionSet.hpp"
+#include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
#include "gc/shenandoah/shenandoahHeapRegionSet.hpp"
@@ -98,7 +99,7 @@ void ShenandoahCollectionSet::add_region(ShenandoahHeapRegion* r) {
if (r->is_young()) {
_young_bytes_to_evacuate += live;
_young_available_bytes_collected += free;
- if (ShenandoahHeap::heap()->mode()->is_generational() && r->age() >= ShenandoahGenerationalHeap::heap()->age_census()->tenuring_threshold()) {
+ if (ShenandoahHeap::heap()->mode()->is_generational() && ShenandoahGenerationalHeap::heap()->is_tenurable(r)) {
_young_bytes_to_promote += live;
}
} else if (r->is_old()) {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp
index 0169795d6f6..d79e7dcefb9 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.cpp
@@ -37,6 +37,7 @@ ShenandoahCollectorPolicy::ShenandoahCollectorPolicy() :
_abbreviated_degenerated_gcs(0),
_success_full_gcs(0),
_consecutive_degenerated_gcs(0),
+ _consecutive_degenerated_gcs_without_progress(0),
_consecutive_young_gcs(0),
_mixed_gcs(0),
_success_old_gcs(0),
@@ -67,14 +68,14 @@ void ShenandoahCollectorPolicy::record_alloc_failure_to_degenerated(ShenandoahGC
}
void ShenandoahCollectorPolicy::record_degenerated_upgrade_to_full() {
- _consecutive_degenerated_gcs = 0;
+ reset_consecutive_degenerated_gcs();
_alloc_failure_degenerated_upgrade_to_full++;
}
void ShenandoahCollectorPolicy::record_success_concurrent(bool is_young, bool is_abbreviated) {
update_young(is_young);
- _consecutive_degenerated_gcs = 0;
+ reset_consecutive_degenerated_gcs();
_success_concurrent_gcs++;
if (is_abbreviated) {
_abbreviated_concurrent_gcs++;
@@ -95,11 +96,18 @@ void ShenandoahCollectorPolicy::record_interrupted_old() {
_interrupted_old_gcs++;
}
-void ShenandoahCollectorPolicy::record_success_degenerated(bool is_young, bool is_abbreviated) {
+void ShenandoahCollectorPolicy::record_degenerated(bool is_young, bool is_abbreviated, bool progress) {
update_young(is_young);
_success_degenerated_gcs++;
_consecutive_degenerated_gcs++;
+
+ if (progress) {
+ _consecutive_degenerated_gcs_without_progress = 0;
+ } else {
+ _consecutive_degenerated_gcs_without_progress++;
+ }
+
if (is_abbreviated) {
_abbreviated_degenerated_gcs++;
}
@@ -114,7 +122,7 @@ void ShenandoahCollectorPolicy::update_young(bool is_young) {
}
void ShenandoahCollectorPolicy::record_success_full() {
- _consecutive_degenerated_gcs = 0;
+ reset_consecutive_degenerated_gcs();
_consecutive_young_gcs = 0;
_success_full_gcs++;
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp
index 68579508de5..5fe90f64f98 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahCollectorPolicy.hpp
@@ -42,6 +42,7 @@ class ShenandoahCollectorPolicy : public CHeapObj {
// Written by control thread, read by mutators
volatile size_t _success_full_gcs;
uint _consecutive_degenerated_gcs;
+ uint _consecutive_degenerated_gcs_without_progress;
volatile size_t _consecutive_young_gcs;
size_t _mixed_gcs;
size_t _success_old_gcs;
@@ -55,8 +56,25 @@ class ShenandoahCollectorPolicy : public CHeapObj {
ShenandoahSharedFlag _in_shutdown;
ShenandoahTracer* _tracer;
+ void reset_consecutive_degenerated_gcs() {
+ _consecutive_degenerated_gcs = 0;
+ _consecutive_degenerated_gcs_without_progress = 0;
+ }
public:
+ // The most common scenario for lack of good progress following a degenerated GC is an accumulation of floating
+ // garbage during the most recently aborted concurrent GC effort. With generational GC, it is far more effective to
+ // reclaim this floating garbage with another degenerated cycle (which focuses on young generation and might require
+ // a pause of 200 ms) rather than a full GC cycle (which may require over 2 seconds with a 10 GB old generation).
+ //
+ // In generational mode, we'll only upgrade to full GC if we've done two degen cycles in a row and both indicated
+ // bad progress. In non-generational mode, we'll preserve the original behavior, which is to upgrade to full
+ // immediately following a degenerated cycle with bad progress. This preserves original behavior of non-generational
+ // Shenandoah to avoid introducing "surprising new behavior." It also makes less sense with non-generational
+ // Shenandoah to replace a full GC with a degenerated GC, because both have similar pause times in non-generational
+ // mode.
+ static constexpr size_t GENERATIONAL_CONSECUTIVE_BAD_DEGEN_PROGRESS_THRESHOLD = 2;
+
ShenandoahCollectorPolicy();
void record_mixed_cycle();
@@ -69,7 +87,12 @@ class ShenandoahCollectorPolicy : public CHeapObj {
// cycles are very efficient and are worth tracking. Note that both degenerated and
// concurrent cycles can be abbreviated.
void record_success_concurrent(bool is_young, bool is_abbreviated);
- void record_success_degenerated(bool is_young, bool is_abbreviated);
+
+ // Record that a degenerated cycle has been completed. Note that such a cycle may or
+ // may not make "progress". We separately track the total number of degenerated cycles,
+ // the number of consecutive degenerated cycles and the number of consecutive cycles that
+ // fail to make good progress.
+ void record_degenerated(bool is_young, bool is_abbreviated, bool progress);
void record_success_full();
void record_alloc_failure_to_degenerated(ShenandoahGC::ShenandoahDegenPoint point);
void record_alloc_failure_to_full();
@@ -94,6 +117,11 @@ class ShenandoahCollectorPolicy : public CHeapObj {
return _consecutive_degenerated_gcs;
}
+ // Genshen will only upgrade to a full gc after the configured number of futile degenerated cycles.
+ bool generational_should_upgrade_degenerated_gc() const {
+ return _consecutive_degenerated_gcs_without_progress >= GENERATIONAL_CONSECUTIVE_BAD_DEGEN_PROGRESS_THRESHOLD;
+ }
+
static bool is_allocation_failure(GCCause::Cause cause);
static bool is_shenandoah_gc(GCCause::Cause cause);
static bool is_requested_gc(GCCause::Cause cause);
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
index fda97c4836e..64093a7c4bf 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
@@ -831,7 +831,6 @@ bool ShenandoahConcurrentGC::has_in_place_promotions(ShenandoahHeap* heap) {
return heap->mode()->is_generational() && heap->old_generation()->has_in_place_promotions();
}
-template
class ShenandoahConcurrentEvacThreadClosure : public ThreadClosure {
private:
OopClosure* const _oops;
@@ -841,13 +840,9 @@ class ShenandoahConcurrentEvacThreadClosure : public ThreadClosure {
void do_thread(Thread* thread) override {
JavaThread* const jt = JavaThread::cast(thread);
StackWatermarkSet::finish_processing(jt, _oops, StackWatermarkKind::gc);
- if (GENERATIONAL) {
- ShenandoahThreadLocalData::enable_plab_promotions(thread);
- }
}
};
-template
class ShenandoahConcurrentEvacUpdateThreadTask : public WorkerTask {
private:
ShenandoahJavaThreadsIterator _java_threads;
@@ -859,30 +854,20 @@ class ShenandoahConcurrentEvacUpdateThreadTask : public WorkerTask {
}
void work(uint worker_id) override {
- if (GENERATIONAL) {
- Thread* worker_thread = Thread::current();
- ShenandoahThreadLocalData::enable_plab_promotions(worker_thread);
- }
-
// ShenandoahEvacOOMScope has to be setup by ShenandoahContextEvacuateUpdateRootsClosure.
// Otherwise, may deadlock with watermark lock
ShenandoahContextEvacuateUpdateRootsClosure oops_cl;
- ShenandoahConcurrentEvacThreadClosure thr_cl(&oops_cl);
+ ShenandoahConcurrentEvacThreadClosure thr_cl(&oops_cl);
_java_threads.threads_do(&thr_cl, worker_id);
}
};
void ShenandoahConcurrentGC::op_thread_roots() {
- ShenandoahHeap* const heap = ShenandoahHeap::heap();
+ const ShenandoahHeap* const heap = ShenandoahHeap::heap();
assert(heap->is_evacuation_in_progress(), "Checked by caller");
ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::conc_thread_roots);
- if (heap->mode()->is_generational()) {
- ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers());
- heap->workers()->run_task(&task);
- } else {
- ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers());
- heap->workers()->run_task(&task);
- }
+ ShenandoahConcurrentEvacUpdateThreadTask task(heap->workers()->active_workers());
+ heap->workers()->run_task(&task);
}
void ShenandoahConcurrentGC::op_weak_refs() {
@@ -1213,6 +1198,7 @@ void ShenandoahConcurrentGC::op_final_update_refs() {
// We are not concerned about skipping this step in abbreviated cycles because regions
// with no live objects cannot have been written to and so cannot have entries in the SATB
// buffers.
+ ShenandoahGCPhase phase(ShenandoahPhaseTimings::final_update_refs_transfer_satb);
heap->old_generation()->transfer_pointers_from_satb();
// Aging_cycle is only relevant during evacuation cycle for individual objects and during final mark for
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
index d8eb8e0a4e1..9cab6807bd0 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved.
- * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved.
+ * Copyright (C) 2022, Tencent. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
index c941379d576..8a0eba5cd9f 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
@@ -49,8 +49,7 @@ ShenandoahDegenGC::ShenandoahDegenGC(ShenandoahDegenPoint degen_point, Shenandoa
ShenandoahGC(),
_degen_point(degen_point),
_generation(generation),
- _abbreviated(false),
- _consecutive_degen_with_bad_progress(0) {
+ _abbreviated(false) {
}
bool ShenandoahDegenGC::collect(GCCause::Cause cause) {
@@ -247,7 +246,6 @@ void ShenandoahDegenGC::op_degenerated() {
ShenandoahHeapRegion* r;
while ((r = heap->collection_set()->next()) != nullptr) {
if (r->is_pinned()) {
- heap->cancel_gc(GCCause::_shenandoah_upgrade_to_full_gc);
op_degenerated_fail();
return;
}
@@ -312,30 +310,14 @@ void ShenandoahDegenGC::op_degenerated() {
metrics.snap_after();
- // The most common scenario for lack of good progress following a degenerated GC is an accumulation of floating
- // garbage during the most recently aborted concurrent GC effort. With generational GC, it is far more effective to
- // reclaim this floating garbage with another degenerated cycle (which focuses on young generation and might require
- // a pause of 200 ms) rather than a full GC cycle (which may require over 2 seconds with a 10 GB old generation).
- //
- // In generational mode, we'll only upgrade to full GC if we've done two degen cycles in a row and both indicated
- // bad progress. In non-generational mode, we'll preserve the original behavior, which is to upgrade to full
- // immediately following a degenerated cycle with bad progress. This preserves original behavior of non-generational
- // Shenandoah so as to avoid introducing "surprising new behavior." It also makes less sense with non-generational
- // Shenandoah to replace a full GC with a degenerated GC, because both have similar pause times in non-generational
- // mode.
- if (!metrics.is_good_progress(_generation)) {
- _consecutive_degen_with_bad_progress++;
- } else {
- _consecutive_degen_with_bad_progress = 0;
- }
- if (!heap->mode()->is_generational() ||
- ((heap->shenandoah_policy()->consecutive_degenerated_gc_count() > 1) && (_consecutive_degen_with_bad_progress >= 2))) {
- heap->cancel_gc(GCCause::_shenandoah_upgrade_to_full_gc);
- op_degenerated_futile();
- } else {
+ // Decide if this cycle made good progress, and, if not, should it upgrade to a full GC.
+ const bool progress = metrics.is_good_progress(_generation);
+ ShenandoahCollectorPolicy* policy = heap->shenandoah_policy();
+ policy->record_degenerated(_generation->is_young(), _abbreviated, progress);
+ if (progress) {
heap->notify_gc_progress();
- heap->shenandoah_policy()->record_success_degenerated(_generation->is_young(), _abbreviated);
- _generation->heuristics()->record_success_degenerated();
+ } else if (!heap->mode()->is_generational() || policy->generational_should_upgrade_degenerated_gc()) {
+ op_degenerated_futile();
}
}
@@ -482,7 +464,9 @@ const char* ShenandoahDegenGC::degen_event_message(ShenandoahDegenPoint point) c
void ShenandoahDegenGC::upgrade_to_full() {
log_info(gc)("Degenerated GC upgrading to Full GC");
- ShenandoahHeap::heap()->shenandoah_policy()->record_degenerated_upgrade_to_full();
+ ShenandoahHeap* heap = ShenandoahHeap::heap();
+ heap->cancel_gc(GCCause::_shenandoah_upgrade_to_full_gc);
+ heap->shenandoah_policy()->record_degenerated_upgrade_to_full();
ShenandoahFullGC full_gc;
full_gc.op_full(GCCause::_shenandoah_upgrade_to_full_gc);
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp
index a2598fe4e8e..971bd67eb0d 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.hpp
@@ -36,7 +36,6 @@ class ShenandoahDegenGC : public ShenandoahGC {
const ShenandoahDegenPoint _degen_point;
ShenandoahGeneration* _generation;
bool _abbreviated;
- size_t _consecutive_degen_with_bad_progress;
public:
ShenandoahDegenGC(ShenandoahDegenPoint degen_point, ShenandoahGeneration* generation);
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp
index 1acb6a23e7a..87c4943b238 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp
@@ -816,6 +816,7 @@ HeapWord* ShenandoahFreeSet::allocate_single(ShenandoahAllocRequest& req, bool&
switch (req.type()) {
case ShenandoahAllocRequest::_alloc_tlab:
case ShenandoahAllocRequest::_alloc_shared:
+ case ShenandoahAllocRequest::_alloc_cds:
return allocate_for_mutator(req, in_new_region);
case ShenandoahAllocRequest::_alloc_gclab:
case ShenandoahAllocRequest::_alloc_plab:
@@ -880,7 +881,7 @@ HeapWord* ShenandoahFreeSet::allocate_from_regions(Iter& iterator, ShenandoahAll
for (idx_t idx = iterator.current(); iterator.has_next(); idx = iterator.next()) {
ShenandoahHeapRegion* r = _heap->get_region(idx);
size_t min_size = (req.type() == ShenandoahAllocRequest::_alloc_tlab) ? req.min_size() : req.size();
- if (alloc_capacity(r) >= min_size) {
+ if (alloc_capacity(r) >= min_size * HeapWordSize) {
HeapWord* result = try_allocate_in(r, req, in_new_region);
if (result != nullptr) {
return result;
@@ -1169,8 +1170,8 @@ HeapWord* ShenandoahFreeSet::try_allocate_in(ShenandoahHeapRegion* r, Shenandoah
return result;
}
-HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) {
- assert(req.is_mutator_alloc(), "All humongous allocations are performed by mutator");
+HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req, bool is_humongous) {
+ assert(req.is_mutator_alloc(), "All contiguous allocations are performed by mutator");
shenandoah_assert_heaplocked();
size_t words_size = req.size();
@@ -1244,10 +1245,16 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) {
assert(i == beg || _heap->get_region(i - 1)->index() + 1 == r->index(), "Should be contiguous");
assert(r->is_empty(), "Should be empty");
- if (i == beg) {
- r->make_humongous_start();
+ r->set_affiliation(req.affiliation());
+
+ if (is_humongous) {
+ if (i == beg) {
+ r->make_humongous_start();
+ } else {
+ r->make_humongous_cont();
+ }
} else {
- r->make_humongous_cont();
+ r->make_regular_allocation(req.affiliation());
}
// Trailing region may be non-full, record the remainder there
@@ -1257,8 +1264,6 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) {
} else {
used_words = ShenandoahHeapRegion::region_size_words();
}
-
- r->set_affiliation(req.affiliation());
r->set_update_watermark(r->bottom());
r->set_top(r->bottom() + used_words);
}
@@ -1268,14 +1273,26 @@ HeapWord* ShenandoahFreeSet::allocate_contiguous(ShenandoahAllocRequest& req) {
_heap->notify_mutator_alloc_words(ShenandoahHeapRegion::region_size_words() - remainder, true);
}
- // retire_range_from_partition() will adjust bounds on Mutator free set if appropriate
- _partitions.retire_range_from_partition(ShenandoahFreeSetPartitionId::Mutator, beg, end);
-
- size_t total_humongous_size = ShenandoahHeapRegion::region_size_bytes() * num;
- _partitions.increase_used(ShenandoahFreeSetPartitionId::Mutator, total_humongous_size);
+ size_t total_used = 0;
+ if (is_humongous) {
+ // Humongous allocation retires all regions at once: no allocation is possible anymore.
+ _partitions.retire_range_from_partition(ShenandoahFreeSetPartitionId::Mutator, beg, end);
+ total_used = ShenandoahHeapRegion::region_size_bytes() * num;
+ } else {
+ // Non-humongous allocation retires only the regions that cannot be used for allocation anymore.
+ for (idx_t i = beg; i <= end; i++) {
+ ShenandoahHeapRegion* r = _heap->get_region(i);
+ if (r->free() < PLAB::min_size() * HeapWordSize) {
+ _partitions.retire_from_partition(ShenandoahFreeSetPartitionId::Mutator, i, r->used());
+ }
+ total_used += r->used();
+ }
+ }
+ _partitions.increase_used(ShenandoahFreeSetPartitionId::Mutator, total_used);
_partitions.assert_bounds();
+
req.set_actual_size(words_size);
- if (remainder != 0) {
+ if (remainder != 0 && is_humongous) {
req.set_waste(ShenandoahHeapRegion::region_size_words() - remainder);
}
return _heap->get_region(beg)->bottom();
@@ -2064,7 +2081,10 @@ HeapWord* ShenandoahFreeSet::allocate(ShenandoahAllocRequest& req, bool& in_new_
case ShenandoahAllocRequest::_alloc_shared:
case ShenandoahAllocRequest::_alloc_shared_gc:
in_new_region = true;
- return allocate_contiguous(req);
+ return allocate_contiguous(req, /* is_humongous = */ true);
+ case ShenandoahAllocRequest::_alloc_cds:
+ in_new_region = true;
+ return allocate_contiguous(req, /* is_humongous = */ false);
case ShenandoahAllocRequest::_alloc_plab:
case ShenandoahAllocRequest::_alloc_gclab:
case ShenandoahAllocRequest::_alloc_tlab:
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp
index 55f23480618..8ad7055b3d6 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.hpp
@@ -345,7 +345,7 @@ class ShenandoahFreeSet : public CHeapObj {
// object. No other objects are packed into these regions.
//
// Precondition: ShenandoahHeapRegion::requires_humongous(req.size())
- HeapWord* allocate_contiguous(ShenandoahAllocRequest& req);
+ HeapWord* allocate_contiguous(ShenandoahAllocRequest& req, bool is_humongous);
// Change region r from the Mutator partition to the GC's Collector or OldCollector partition. This requires that the
// region is entirely empty.
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp
index 9a511de939c..7b3839dc198 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.cpp
@@ -28,9 +28,8 @@
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
#include "gc/shenandoah/shenandoahFreeSet.hpp"
#include "gc/shenandoah/shenandoahGeneration.hpp"
-#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
+#include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeapRegionClosures.hpp"
-#include "gc/shenandoah/shenandoahMonitoringSupport.hpp"
#include "gc/shenandoah/shenandoahOldGeneration.hpp"
#include "gc/shenandoah/shenandoahReferenceProcessor.hpp"
#include "gc/shenandoah/shenandoahScanRemembered.inline.hpp"
@@ -183,7 +182,7 @@ void ShenandoahGeneration::log_status(const char *msg) const {
// byte size in proper unit and proper unit for byte size are consistent.
const size_t v_used = used();
const size_t v_used_regions = used_regions_size();
- const size_t v_soft_max_capacity = soft_max_capacity();
+ const size_t v_soft_max_capacity = ShenandoahHeap::heap()->soft_max_capacity();
const size_t v_max_capacity = max_capacity();
const size_t v_available = available();
const size_t v_humongous_waste = get_humongous_waste();
@@ -534,7 +533,6 @@ size_t ShenandoahGeneration::select_aged_regions(size_t old_available) {
bool* const candidate_regions_for_promotion_by_copy = heap->collection_set()->preselected_regions();
ShenandoahMarkingContext* const ctx = heap->marking_context();
- const uint tenuring_threshold = heap->age_census()->tenuring_threshold();
const size_t old_garbage_threshold = (ShenandoahHeapRegion::region_size_bytes() * ShenandoahOldGarbageThreshold) / 100;
size_t old_consumed = 0;
@@ -558,7 +556,7 @@ size_t ShenandoahGeneration::select_aged_regions(size_t old_available) {
// skip over regions that aren't regular young with some live data
continue;
}
- if (r->age() >= tenuring_threshold) {
+ if (heap->is_tenurable(r)) {
if ((r->garbage() < old_garbage_threshold)) {
// This tenure-worthy region has too little garbage, so we do not want to expend the copying effort to
// reclaim the garbage; instead this region may be eligible for promotion-in-place to the
@@ -613,7 +611,7 @@ size_t ShenandoahGeneration::select_aged_regions(size_t old_available) {
// these regions. The likely outcome is that these regions will not be selected for evacuation or promotion
// in the current cycle and we will anticipate that they will be promoted in the next cycle. This will cause
// us to reserve more old-gen memory so that these objects can be promoted in the subsequent cycle.
- if (heap->is_aging_cycle() && (r->age() + 1 == tenuring_threshold)) {
+ if (heap->is_aging_cycle() && heap->age_census()->is_tenurable(r->age() + 1)) {
if (r->garbage() >= old_garbage_threshold) {
promo_potential += r->get_live_data_bytes();
}
@@ -799,14 +797,13 @@ void ShenandoahGeneration::cancel_marking() {
ShenandoahGeneration::ShenandoahGeneration(ShenandoahGenerationType type,
uint max_workers,
- size_t max_capacity,
- size_t soft_max_capacity) :
+ size_t max_capacity) :
_type(type),
_task_queues(new ShenandoahObjToScanQueueSet(max_workers)),
_ref_processor(new ShenandoahReferenceProcessor(MAX2(max_workers, 1U))),
_affiliated_region_count(0), _humongous_waste(0), _evacuation_reserve(0),
_used(0), _bytes_allocated_since_gc_start(0),
- _max_capacity(max_capacity), _soft_max_capacity(soft_max_capacity),
+ _max_capacity(max_capacity),
_heuristics(nullptr)
{
_is_marking_complete.set();
@@ -952,7 +949,7 @@ size_t ShenandoahGeneration::available_with_reserve() const {
}
size_t ShenandoahGeneration::soft_available() const {
- return available(soft_max_capacity());
+ return available(ShenandoahHeap::heap()->soft_max_capacity());
}
size_t ShenandoahGeneration::available(size_t capacity) const {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp
index 242acbdea8c..2b7aca342da 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGeneration.hpp
@@ -71,7 +71,6 @@ class ShenandoahGeneration : public CHeapObj, public ShenandoahSpaceInfo {
volatile size_t _used;
volatile size_t _bytes_allocated_since_gc_start;
size_t _max_capacity;
- size_t _soft_max_capacity;
ShenandoahHeuristics* _heuristics;
@@ -105,8 +104,7 @@ class ShenandoahGeneration : public CHeapObj, public ShenandoahSpaceInfo {
public:
ShenandoahGeneration(ShenandoahGenerationType type,
uint max_workers,
- size_t max_capacity,
- size_t soft_max_capacity);
+ size_t max_capacity);
~ShenandoahGeneration();
bool is_young() const { return _type == YOUNG; }
@@ -126,7 +124,6 @@ class ShenandoahGeneration : public CHeapObj, public ShenandoahSpaceInfo {
virtual ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode);
- size_t soft_max_capacity() const override { return _soft_max_capacity; }
size_t max_capacity() const override { return _max_capacity; }
virtual size_t used_regions() const;
virtual size_t used_regions_size() const;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp
index 6b33d5207d0..2555f73d018 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalControlThread.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2013, 2021, Red Hat, Inc. All rights reserved.
- * Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved.
+ * Copyright (C) 2022, Tencent. All rights reserved.
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp
index ba9ef5979a8..b538f7b1417 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp
@@ -26,7 +26,7 @@
#include "gc/shenandoah/shenandoahAsserts.hpp"
#include "gc/shenandoah/shenandoahFreeSet.hpp"
#include "gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp"
-#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
+#include "gc/shenandoah/shenandoahGenerationalHeap.inline.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahOldGeneration.hpp"
#include "gc/shenandoah/shenandoahPacer.hpp"
@@ -57,11 +57,9 @@ ShenandoahGenerationalEvacuationTask::ShenandoahGenerationalEvacuationTask(Shena
_heap(heap),
_regions(iterator),
_concurrent(concurrent),
- _only_promote_regions(only_promote_regions),
- _tenuring_threshold(0)
+ _only_promote_regions(only_promote_regions)
{
shenandoah_assert_generational();
- _tenuring_threshold = _heap->age_census()->tenuring_threshold();
}
void ShenandoahGenerationalEvacuationTask::work(uint worker_id) {
@@ -142,7 +140,7 @@ void ShenandoahGenerationalEvacuationTask::evacuate_and_promote_regions() {
void ShenandoahGenerationalEvacuationTask::maybe_promote_region(ShenandoahHeapRegion* r) {
- if (r->is_young() && r->is_active() && (r->age() >= _tenuring_threshold)) {
+ if (r->is_young() && r->is_active() && _heap->is_tenurable(r)) {
if (r->is_humongous_start()) {
// We promote humongous_start regions along with their affiliated continuations during evacuation rather than
// doing this work during a safepoint. We cannot put humongous regions into the collection set because that
@@ -180,7 +178,7 @@ void ShenandoahGenerationalEvacuationTask::promote_in_place(ShenandoahHeapRegion
assert(region->garbage_before_padded_for_promote() < old_garbage_threshold, "Region %zu has too much garbage for promotion", region->index());
assert(region->is_young(), "Only young regions can be promoted");
assert(region->is_regular(), "Use different service to promote humongous regions");
- assert(region->age() >= _tenuring_threshold, "Only promote regions that are sufficiently aged");
+ assert(_heap->is_tenurable(region), "Only promote regions that are sufficiently aged");
assert(region->get_top_before_promote() == tams, "Region %zu has been used for allocations before promotion", region->index());
}
@@ -263,7 +261,7 @@ void ShenandoahGenerationalEvacuationTask::promote_humongous(ShenandoahHeapRegio
shenandoah_assert_generations_reconciled();
assert(region->is_young(), "Only young regions can be promoted");
assert(region->is_humongous_start(), "Should not promote humongous continuation in isolation");
- assert(region->age() >= _tenuring_threshold, "Only promote regions that are sufficiently aged");
+ assert(_heap->is_tenurable(region), "Only promote regions that are sufficiently aged");
assert(marking_context->is_marked(obj), "promoted humongous object should be alive");
const size_t used_bytes = obj->size() * HeapWordSize;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp
index abe2fc0110c..0c402d6c90a 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.hpp
@@ -39,7 +39,6 @@ class ShenandoahGenerationalEvacuationTask : public WorkerTask {
ShenandoahRegionIterator* _regions;
bool _concurrent;
bool _only_promote_regions;
- uint _tenuring_threshold;
public:
ShenandoahGenerationalEvacuationTask(ShenandoahGenerationalHeap* sh,
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp
index e2e3f0a4677..c4a7408e032 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.cpp
@@ -193,7 +193,6 @@ ShenandoahPrepareForGenerationalCompactionObjectClosure::ShenandoahPrepareForGen
ShenandoahHeapRegion* from_region, uint worker_id) :
_preserved_marks(preserved_marks),
_heap(ShenandoahGenerationalHeap::heap()),
- _tenuring_threshold(0),
_empty_regions(empty_regions),
_empty_regions_pos(0),
_old_to_region(nullptr),
@@ -212,8 +211,6 @@ ShenandoahPrepareForGenerationalCompactionObjectClosure::ShenandoahPrepareForGen
_young_to_region = from_region;
_young_compact_point = from_region->bottom();
}
-
- _tenuring_threshold = _heap->age_census()->tenuring_threshold();
}
void ShenandoahPrepareForGenerationalCompactionObjectClosure::set_from_region(ShenandoahHeapRegion* from_region) {
@@ -279,7 +276,7 @@ void ShenandoahPrepareForGenerationalCompactionObjectClosure::do_object(oop p) {
bool promote_object = false;
if ((_from_affiliation == ShenandoahAffiliation::YOUNG_GENERATION) &&
- (from_region_age + object_age >= _tenuring_threshold)) {
+ _heap->age_census()->is_tenurable(from_region_age + object_age)) {
if ((_old_to_region != nullptr) && (_old_compact_point + obj_size > _old_to_region->end())) {
finish_old_region();
_old_to_region = nullptr;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.hpp
index 9240a056105..06080286f22 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalFullGC.hpp
@@ -90,7 +90,6 @@ class ShenandoahPrepareForGenerationalCompactionObjectClosure : public ObjectClo
private:
PreservedMarks* const _preserved_marks;
ShenandoahGenerationalHeap* const _heap;
- uint _tenuring_threshold;
// _empty_regions is a thread-local list of heap regions that have been completely emptied by this worker thread's
// compaction efforts. The worker thread that drives these efforts adds compacted regions to this list if the
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
index 731868310f4..feb82dd0527 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp
@@ -53,21 +53,6 @@ class ShenandoahGenerationalInitLogger : public ShenandoahInitLogger {
ShenandoahGenerationalInitLogger logger;
logger.print_all();
}
-
- void print_heap() override {
- ShenandoahInitLogger::print_heap();
-
- ShenandoahGenerationalHeap* heap = ShenandoahGenerationalHeap::heap();
-
- ShenandoahYoungGeneration* young = heap->young_generation();
- log_info(gc, init)("Young Generation Soft Size: " EXACTFMT, EXACTFMTARGS(young->soft_max_capacity()));
- log_info(gc, init)("Young Generation Max: " EXACTFMT, EXACTFMTARGS(young->max_capacity()));
-
- ShenandoahOldGeneration* old = heap->old_generation();
- log_info(gc, init)("Old Generation Soft Size: " EXACTFMT, EXACTFMTARGS(old->soft_max_capacity()));
- log_info(gc, init)("Old Generation Max: " EXACTFMT, EXACTFMTARGS(old->max_capacity()));
- }
-
protected:
void print_gc_specific() override {
ShenandoahInitLogger::print_gc_specific();
@@ -141,8 +126,8 @@ void ShenandoahGenerationalHeap::initialize_heuristics() {
size_t initial_capacity_old = max_capacity() - max_capacity_young;
size_t max_capacity_old = max_capacity() - initial_capacity_young;
- _young_generation = new ShenandoahYoungGeneration(max_workers(), max_capacity_young, initial_capacity_young);
- _old_generation = new ShenandoahOldGeneration(max_workers(), max_capacity_old, initial_capacity_old);
+ _young_generation = new ShenandoahYoungGeneration(max_workers(), max_capacity_young);
+ _old_generation = new ShenandoahOldGeneration(max_workers(), max_capacity_old);
_young_generation->initialize_heuristics(mode());
_old_generation->initialize_heuristics(mode());
}
@@ -244,7 +229,7 @@ oop ShenandoahGenerationalHeap::evacuate_object(oop p, Thread* thread) {
if (mark.has_displaced_mark_helper()) {
// We don't want to deal with MT here just to ensure we read the right mark word.
// Skip the potential promotion attempt for this one.
- } else if (r->age() + mark.age() >= age_census()->tenuring_threshold()) {
+ } else if (age_census()->is_tenurable(r->age() + mark.age())) {
oop result = try_evacuate_object(p, thread, r, OLD_GENERATION);
if (result != nullptr) {
return result;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
index ed5a6f3d9a5..fb356873356 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.hpp
@@ -81,6 +81,8 @@ class ShenandoahGenerationalHeap : public ShenandoahHeap {
return _age_census;
}
+ inline bool is_tenurable(const ShenandoahHeapRegion* r) const;
+
ShenandoahEvacuationTracker* evac_tracker() const {
return _evac_tracker;
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.inline.hpp
new file mode 100644
index 00000000000..8289b48185b
--- /dev/null
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.inline.hpp
@@ -0,0 +1,37 @@
+/*
+ * 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
+ * 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_SHENANDOAH_SHENANDOAHGENERATIONALHEAP_INLINE_HPP
+#define SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP_INLINE_HPP
+
+#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
+
+#include "gc/shenandoah/shenandoahAgeCensus.hpp"
+#include "gc/shenandoah/shenandoahHeapRegion.hpp"
+
+inline bool ShenandoahGenerationalHeap::is_tenurable(const ShenandoahHeapRegion* r) const {
+ return _age_census->is_tenurable(r->age());
+}
+
+#endif // SHARE_GC_SHENANDOAH_SHENANDOAHGENERATIONALHEAP_INLINE_HPP
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp
index c6277e3898e..6099c41f262 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.cpp
@@ -50,10 +50,6 @@ size_t ShenandoahGlobalGeneration::used_regions_size() const {
return ShenandoahHeap::heap()->capacity();
}
-size_t ShenandoahGlobalGeneration::soft_max_capacity() const {
- return ShenandoahHeap::heap()->soft_max_capacity();
-}
-
size_t ShenandoahGlobalGeneration::available() const {
// The collector reserve may eat into what the mutator is allowed to use. Make sure we are looking
// at what is available to the mutator when reporting how much memory is available.
@@ -65,8 +61,8 @@ size_t ShenandoahGlobalGeneration::soft_available() const {
size_t available = this->available();
// Make sure the code below treats available without the soft tail.
- assert(max_capacity() >= soft_max_capacity(), "Max capacity must be greater than soft max capacity.");
- size_t soft_tail = max_capacity() - soft_max_capacity();
+ assert(max_capacity() >= ShenandoahHeap::heap()->soft_max_capacity(), "Max capacity must be greater than soft max capacity.");
+ size_t soft_tail = max_capacity() - ShenandoahHeap::heap()->soft_max_capacity();
return (available > soft_tail) ? (available - soft_tail) : 0;
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp
index 5857170d4cc..a823784a459 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahGlobalGeneration.hpp
@@ -32,14 +32,13 @@
// A "generation" that represents the whole heap.
class ShenandoahGlobalGeneration : public ShenandoahGeneration {
public:
- ShenandoahGlobalGeneration(bool generational, uint max_queues, size_t max_capacity, size_t soft_max_capacity)
- : ShenandoahGeneration(generational ? GLOBAL : NON_GEN, max_queues, max_capacity, soft_max_capacity) { }
+ ShenandoahGlobalGeneration(bool generational, uint max_queues, size_t max_capacity)
+ : ShenandoahGeneration(generational ? GLOBAL : NON_GEN, max_queues, max_capacity) { }
public:
const char* name() const override;
size_t max_capacity() const override;
- size_t soft_max_capacity() const override;
size_t used_regions() const override;
size_t used_regions_size() const override;
size_t available() const override;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
index 0fc7ba39cfa..a56ada222d7 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
@@ -32,6 +32,7 @@
#include "gc/shared/gcArguments.hpp"
#include "gc/shared/gcTimer.hpp"
#include "gc/shared/gcTraceTime.inline.hpp"
+#include "gc/shared/gc_globals.hpp"
#include "gc/shared/locationPrinter.inline.hpp"
#include "gc/shared/memAllocator.hpp"
#include "gc/shared/plab.hpp"
@@ -201,8 +202,7 @@ jint ShenandoahHeap::initialize() {
assert(num_min_regions <= _num_regions, "sanity");
_minimum_size = num_min_regions * reg_size_bytes;
- // Default to max heap size.
- _soft_max_size = _num_regions * reg_size_bytes;
+ _soft_max_size = SoftMaxHeapSize;
_committed = _initial_size;
@@ -524,7 +524,7 @@ void ShenandoahHeap::initialize_mode() {
}
void ShenandoahHeap::initialize_heuristics() {
- _global_generation = new ShenandoahGlobalGeneration(mode()->is_generational(), max_workers(), max_capacity(), max_capacity());
+ _global_generation = new ShenandoahGlobalGeneration(mode()->is_generational(), max_workers(), max_capacity());
_global_generation->initialize_heuristics(mode());
}
@@ -1238,6 +1238,11 @@ class ShenandoahRetireGCLABClosure : public ThreadClosure {
// 1. We need to make the plab memory parsable by remembered-set scanning.
// 2. We need to establish a trustworthy UpdateWaterMark value within each old-gen heap region
ShenandoahGenerationalHeap::heap()->retire_plab(plab, thread);
+
+ // Re-enable promotions for the next evacuation phase.
+ ShenandoahThreadLocalData::enable_plab_promotions(thread);
+
+ // Reset the fill size for next evacuation phase.
if (_resize && ShenandoahThreadLocalData::plab_size(thread) > 0) {
ShenandoahThreadLocalData::set_plab_size(thread, 0);
}
@@ -2791,41 +2796,15 @@ bool ShenandoahHeap::requires_barriers(stackChunkOop obj) const {
HeapWord* ShenandoahHeap::allocate_loaded_archive_space(size_t size) {
#if INCLUDE_CDS_JAVA_HEAP
- // CDS wants a continuous memory range to load a bunch of objects.
- // This effectively bypasses normal allocation paths, and requires
- // a bit of massaging to unbreak GC invariants.
-
- ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared(size);
-
- // Easy case: a single regular region, no further adjustments needed.
- if (!ShenandoahHeapRegion::requires_humongous(size)) {
- return allocate_memory(req);
- }
-
- // Hard case: the requested size would cause a humongous allocation.
- // We need to make sure it looks like regular allocation to the rest of GC.
-
- // CDS code would guarantee no objects straddle multiple regions, as long as
- // regions are as large as MIN_GC_REGION_ALIGNMENT. It is impractical at this
- // point to deal with case when Shenandoah runs with smaller regions.
- // TODO: This check can be dropped once MIN_GC_REGION_ALIGNMENT agrees more with Shenandoah.
- if (ShenandoahHeapRegion::region_size_bytes() < ArchiveHeapWriter::MIN_GC_REGION_ALIGNMENT) {
- return nullptr;
- }
-
- HeapWord* mem = allocate_memory(req);
- size_t start_idx = heap_region_index_containing(mem);
- size_t num_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize);
-
- // Flip humongous -> regular.
- {
- ShenandoahHeapLocker locker(lock(), false);
- for (size_t c = start_idx; c < start_idx + num_regions; c++) {
- get_region(c)->make_regular_bypass();
- }
- }
+ // CDS wants a raw continuous memory range to load a bunch of objects itself.
+ // This is an unusual request, since all requested regions should be regular, not humongous.
+ //
+ // CDS would guarantee no objects straddle multiple regions, as long as regions are as large
+ // as MIN_GC_REGION_ALIGNMENT.
+ guarantee(ShenandoahHeapRegion::region_size_bytes() >= ArchiveHeapWriter::MIN_GC_REGION_ALIGNMENT, "Must be");
- return mem;
+ ShenandoahAllocRequest req = ShenandoahAllocRequest::for_cds(size);
+ return allocate_memory(req);
#else
assert(false, "Archive heap loader should not be available, should not be here");
return nullptr;
@@ -2852,17 +2831,25 @@ void ShenandoahHeap::complete_loaded_archive_space(MemRegion archive_space) {
"Archive space should be fully used: " PTR_FORMAT " " PTR_FORMAT,
p2i(cur), p2i(end));
- // Region bounds are good.
- ShenandoahHeapRegion* begin_reg = heap_region_containing(start);
- ShenandoahHeapRegion* end_reg = heap_region_containing(end);
- assert(begin_reg->is_regular(), "Must be");
- assert(end_reg->is_regular(), "Must be");
- assert(begin_reg->bottom() == start,
- "Must agree: archive-space-start: " PTR_FORMAT ", begin-region-bottom: " PTR_FORMAT,
- p2i(start), p2i(begin_reg->bottom()));
- assert(end_reg->top() == end,
- "Must agree: archive-space-end: " PTR_FORMAT ", end-region-top: " PTR_FORMAT,
- p2i(end), p2i(end_reg->top()));
+ // All regions in contiguous space have good state.
+ size_t begin_reg_idx = heap_region_index_containing(start);
+ size_t end_reg_idx = heap_region_index_containing(end);
+
+ for (size_t idx = begin_reg_idx; idx <= end_reg_idx; idx++) {
+ ShenandoahHeapRegion* r = get_region(idx);
+ assert(r->is_regular(), "Must be regular");
+ assert(r->is_young(), "Must be young");
+ assert(idx == end_reg_idx || r->top() == r->end(),
+ "All regions except the last one should be full: " PTR_FORMAT " " PTR_FORMAT,
+ p2i(r->top()), p2i(r->end()));
+ assert(idx != begin_reg_idx || r->bottom() == start,
+ "Archive space start should be at the bottom of first region: " PTR_FORMAT " " PTR_FORMAT,
+ p2i(r->bottom()), p2i(start));
+ assert(idx != end_reg_idx || r->top() == end,
+ "Archive space end should be at the top of last region: " PTR_FORMAT " " PTR_FORMAT,
+ p2i(r->top()), p2i(end));
+ }
+
#endif
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
index 9f5f384cc29..509ba1db9c2 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp
@@ -293,6 +293,7 @@ class ShenandoahHeap : public CollectedHeap {
public:
inline HeapWord* base() const { return _heap_region.start(); }
+ inline HeapWord* end() const { return _heap_region.end(); }
inline size_t num_regions() const { return _num_regions; }
inline bool is_heap_region_special() { return _heap_region_special; }
@@ -680,7 +681,7 @@ class ShenandoahHeap : public CollectedHeap {
// ---------- CDS archive support
- bool can_load_archived_objects() const override { return !ShenandoahCardBarrier; }
+ bool can_load_archived_objects() const override { return true; }
HeapWord* allocate_loaded_archive_space(size_t size) override;
void complete_loaded_archive_space(MemRegion archive_space) override;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
index d00a99ee728..b959494ae99 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
@@ -142,27 +142,17 @@ void ShenandoahHeapRegion::make_affiliated_maybe() {
void ShenandoahHeapRegion::make_regular_bypass() {
shenandoah_assert_heaplocked();
- assert (!Universe::is_fully_initialized() ||
- ShenandoahHeap::heap()->is_full_gc_in_progress() ||
+ assert (ShenandoahHeap::heap()->is_full_gc_in_progress() ||
ShenandoahHeap::heap()->is_degenerated_gc_in_progress(),
- "Only for STW GC or when Universe is initializing (CDS)");
+ "Only for STW GC");
reset_age();
- auto cur_state = state();
- switch (cur_state) {
+ switch (state()) {
case _empty_uncommitted:
do_commit();
case _empty_committed:
case _cset:
case _humongous_start:
case _humongous_cont:
- if (cur_state == _humongous_start || cur_state == _humongous_cont) {
- // CDS allocates chunks of the heap to fill with regular objects. The allocator
- // will dutifully track any waste in the unused portion of the last region. Once
- // CDS has finished initializing the objects, it will convert these regions to
- // regular regions. The 'waste' in the last region is no longer wasted at this point,
- // so we must stop treating it as such.
- decrement_humongous_waste();
- }
set_state(_regular);
return;
case _pinned_cset:
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp
index 0df482c1e2d..57a5391ffe9 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.inline.hpp
@@ -113,6 +113,7 @@ inline void ShenandoahHeapRegion::adjust_alloc_metadata(ShenandoahAllocRequest::
switch (type) {
case ShenandoahAllocRequest::_alloc_shared:
case ShenandoahAllocRequest::_alloc_shared_gc:
+ case ShenandoahAllocRequest::_alloc_cds:
// Counted implicitly by tlab/gclab allocs
break;
case ShenandoahAllocRequest::_alloc_tlab:
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp b/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp
index b5e5e6fd698..421c001b510 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahInitLogger.cpp
@@ -43,6 +43,7 @@ void ShenandoahInitLogger::print_heap() {
log_info(gc, init)("Heap Region Count: %zu", ShenandoahHeapRegion::region_count());
log_info(gc, init)("Heap Region Size: " EXACTFMT, EXACTFMTARGS(ShenandoahHeapRegion::region_size_bytes()));
log_info(gc, init)("TLAB Size Max: " EXACTFMT, EXACTFMTARGS(ShenandoahHeapRegion::max_tlab_size_bytes()));
+ log_info(gc, init)("Soft Max Heap Size: " EXACTFMT, EXACTFMTARGS(ShenandoahHeap::heap()->soft_max_capacity()));
}
void ShenandoahInitLogger::print_gc_specific() {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp
index 34e6af41b42..5318f38d8ef 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.cpp
@@ -57,6 +57,26 @@ bool ShenandoahMarkBitMap::is_bitmap_clear_range(const HeapWord* start, const He
return (result == end);
}
+HeapWord* ShenandoahMarkBitMap::get_prev_marked_addr(const HeapWord* limit,
+ const HeapWord* addr) const {
+#ifdef ASSERT
+ ShenandoahHeap* heap = ShenandoahHeap::heap();
+ ShenandoahHeapRegion* r = heap->heap_region_containing(addr);
+ ShenandoahMarkingContext* ctx = heap->marking_context();
+ HeapWord* tams = ctx->top_at_mark_start(r);
+ assert(limit != nullptr, "limit must not be null");
+ assert(limit >= r->bottom(), "limit must be more than bottom");
+ assert(addr <= tams, "addr must be less than TAMS");
+#endif
+
+ // Round addr down to a possible object boundary to be safe.
+ size_t const addr_offset = address_to_index(align_down(addr, HeapWordSize << LogMinObjAlignment));
+ size_t const limit_offset = address_to_index(limit);
+ size_t const last_offset = get_prev_one_offset(limit_offset, addr_offset);
+
+ // cast required to remove const-ness of the value pointed to. We won't modify that object, but my caller might.
+ return (last_offset > addr_offset)? (HeapWord*) addr + 1: index_to_address(last_offset);
+}
HeapWord* ShenandoahMarkBitMap::get_next_marked_addr(const HeapWord* addr,
const HeapWord* limit) const {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp
index 56daf4c5956..4c3177d0e81 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp
@@ -119,9 +119,21 @@ class ShenandoahMarkBitMap {
template
inline idx_t get_next_bit_impl(idx_t l_index, idx_t r_index) const;
- inline idx_t get_next_one_offset (idx_t l_index, idx_t r_index) const;
+ // Helper for get_prev_{zero,one}_bit variants.
+ // - flip designates whether searching for 1s or 0s. Must be one of
+ // find_{zeros,ones}_flip.
+ // - aligned_left is true if l_index is a priori on a bm_word_t boundary.
+ template
+ inline idx_t get_prev_bit_impl(idx_t l_index, idx_t r_index) const;
+
+ // Search for the first marked address in the range [l_index, r_index), or r_index if none found.
+ inline idx_t get_next_one_offset(idx_t l_index, idx_t r_index) const;
- void clear_large_range (idx_t beg, idx_t end);
+ // Search for last one in the range [l_index, r_index). Return r_index if not found.
+ inline idx_t get_prev_one_offset(idx_t l_index, idx_t r_index) const;
+
+ // Clear the strong and weak mark bits for all index positions >= l_index and < r_index.
+ void clear_large_range(idx_t beg, idx_t end);
// Verify bit is less than size().
void verify_index(idx_t bit) const NOT_DEBUG_RETURN;
@@ -162,12 +174,14 @@ class ShenandoahMarkBitMap {
bool is_bitmap_clear_range(const HeapWord* start, const HeapWord* end) const;
- // Return the address corresponding to the next marked bit at or after
- // "addr", and before "limit", if "limit" is non-null. If there is no
- // such bit, returns "limit" if that is non-null, or else "endWord()".
+ // Return the first marked address in the range [addr, limit), or limit if none found.
HeapWord* get_next_marked_addr(const HeapWord* addr,
const HeapWord* limit) const;
+ // Return the last marked address in the range [limit, addr], or addr+1 if none found.
+ HeapWord* get_prev_marked_addr(const HeapWord* limit,
+ const HeapWord* addr) const;
+
bm_word_t inverted_bit_mask_for_range(idx_t beg, idx_t end) const;
void clear_range_within_word (idx_t beg, idx_t end);
void clear_range (idx_t beg, idx_t end);
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp
index f0a9752b614..21b526f9995 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp
@@ -29,6 +29,7 @@
#include "gc/shenandoah/shenandoahMarkBitMap.hpp"
#include "runtime/atomic.hpp"
+#include "utilities/count_leading_zeros.hpp"
#include "utilities/count_trailing_zeros.hpp"
inline size_t ShenandoahMarkBitMap::address_to_index(const HeapWord* addr) const {
@@ -171,10 +172,99 @@ inline ShenandoahMarkBitMap::idx_t ShenandoahMarkBitMap::get_next_bit_impl(idx_t
return r_index;
}
+template
+inline ShenandoahMarkBitMap::idx_t ShenandoahMarkBitMap::get_prev_bit_impl(idx_t l_index, idx_t r_index) const {
+ STATIC_ASSERT(flip == find_ones_flip || flip == find_zeros_flip);
+ verify_range(l_index, r_index);
+ assert(!aligned_left || is_aligned(l_index, BitsPerWord), "l_index not aligned");
+
+ // The first word often contains an interesting bit, either due to
+ // density or because of features of the calling algorithm. So it's
+ // important to examine that first word with a minimum of fuss,
+ // minimizing setup time for later words that will be wasted if the
+ // first word is indeed interesting.
+
+ // The benefit from aligned_left being true is relatively small.
+ // It saves an operation in the setup for the word search loop.
+ // It also eliminates the range check on the final result.
+ // However, callers often have a comparison with l_index, and
+ // inlining often allows the two comparisons to be combined; it is
+ // important when !aligned_left that return paths either return
+ // l_index or a value dominating a comparison with l_index.
+ // aligned_left is still helpful when the caller doesn't have a
+ // range check because features of the calling algorithm guarantee
+ // an interesting bit will be present.
+
+ if (l_index < r_index) {
+ // Get the word containing r_index, and shift out the high-order bits (representing objects that come after r_index)
+ idx_t index = to_words_align_down(r_index);
+ assert(BitsPerWord - 2 >= bit_in_word(r_index), "sanity");
+ size_t shift = BitsPerWord - 2 - bit_in_word(r_index);
+ bm_word_t cword = (map(index) ^ flip) << shift;
+ // After this shift, the highest order bits correspond to r_index.
+
+ // We give special handling if either of the two most significant bits (Weak or Strong) is set. With 64-bit
+ // words, the mask of interest is 0xc000_0000_0000_0000. Symbolically, this constant is represented by:
+ const bm_word_t first_object_mask = ((bm_word_t) 0x3) << (BitsPerWord - 2);
+ if ((cword & first_object_mask) != 0) {
+ // The first object is similarly often interesting. When it matters
+ // (density or features of the calling algorithm make it likely
+ // the first bit is set), going straight to the next clause compares
+ // poorly with doing this check first; count_leading_zeros can be
+ // relatively expensive, plus there is the additional range check.
+ // But when the first bit isn't set, the cost of having tested for
+ // it is relatively small compared to the rest of the search.
+ return r_index;
+ } else if (cword != 0) {
+ // Note that there are 2 bits corresponding to every index value (Weak and Strong), and every odd index value
+ // corresponds to the same object as index-1
+ // Flipped and shifted first word is non-zero. If leading_zeros is 0 or 1, we return r_index (above).
+ // if leading zeros is 2 or 3, we return (r_index - 1) or (r_index - 2), and so forth
+ idx_t result = r_index + 1 - count_leading_zeros(cword);
+ if (aligned_left || (result >= l_index)) return result;
+ else {
+ // Sentinel value means no object found within specified range.
+ return r_index + 2;
+ }
+ } else {
+ // Flipped and shifted first word is zero. Word search through
+ // aligned up r_index for a non-zero flipped word.
+ idx_t limit = aligned_left
+ ? to_words_align_down(l_index) // Minuscule savings when aligned.
+ : to_words_align_up(l_index);
+ // Unsigned index is always >= unsigned limit if limit equals zero, so test for strictly greater than before decrement.
+ while (index-- > limit) {
+ cword = map(index) ^ flip;
+ if (cword != 0) {
+ // cword hods bits:
+ // 0x03 for the object corresponding to index (and index+1) (count_leading_zeros is 62 or 63)
+ // 0x0c for the object corresponding to index + 2 (and index+3) (count_leading_zeros is 60 or 61)
+ // and so on.
+ idx_t result = bit_index(index + 1) - (count_leading_zeros(cword) + 1);
+ if (aligned_left || (result >= l_index)) return result;
+ else {
+ // Sentinel value means no object found within specified range.
+ return r_index + 2;
+ }
+ }
+ }
+ // No bits in range; return r_index+2.
+ return r_index + 2;
+ }
+ }
+ else {
+ return r_index + 2;
+ }
+}
+
inline ShenandoahMarkBitMap::idx_t ShenandoahMarkBitMap::get_next_one_offset(idx_t l_offset, idx_t r_offset) const {
return get_next_bit_impl(l_offset, r_offset);
}
+inline ShenandoahMarkBitMap::idx_t ShenandoahMarkBitMap::get_prev_one_offset(idx_t l_offset, idx_t r_offset) const {
+ return get_prev_bit_impl(l_offset, r_offset);
+}
+
// Returns a bit mask for a range of bits [beg, end) within a single word. Each
// bit in the mask is 0 if the bit is in the range, 1 if not in the range. The
// returned mask can be used directly to clear the range, or inverted to set the
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp
index 8a52042e513..d8e0c74ea4e 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp
@@ -67,8 +67,12 @@ class ShenandoahMarkingContext : public CHeapObj {
inline bool is_marked_or_old(oop obj) const;
inline bool is_marked_strong_or_old(oop obj) const;
+ // Return address of the first marked address in the range [addr,limit), or limit if no marked object found
inline HeapWord* get_next_marked_addr(const HeapWord* addr, const HeapWord* limit) const;
+ // Return address of the last marked object in range [limit, start], returning start+1 if no marked object found
+ inline HeapWord* get_prev_marked_addr(const HeapWord* limit, const HeapWord* start) const;
+
inline bool allocated_after_mark_start(const oop obj) const;
inline bool allocated_after_mark_start(const HeapWord* addr) const;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp
index e3ba774283c..fe98413a8cc 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.inline.hpp
@@ -72,6 +72,10 @@ inline HeapWord* ShenandoahMarkingContext::get_next_marked_addr(const HeapWord*
return _mark_bit_map.get_next_marked_addr(start, limit);
}
+inline HeapWord* ShenandoahMarkingContext::get_prev_marked_addr(const HeapWord* limit, const HeapWord* start) const {
+ return _mark_bit_map.get_prev_marked_addr(limit, start);
+}
+
inline bool ShenandoahMarkingContext::allocated_after_mark_start(oop obj) const {
const HeapWord* addr = cast_from_oop(obj);
return allocated_after_mark_start(addr);
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp
index 35d963f1801..5cccd395d38 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp
@@ -196,8 +196,8 @@ class ShenandoahConcurrentCoalesceAndFillTask : public WorkerTask {
}
};
-ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues, size_t max_capacity, size_t soft_max_capacity)
- : ShenandoahGeneration(OLD, max_queues, max_capacity, soft_max_capacity),
+ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues, size_t max_capacity)
+ : ShenandoahGeneration(OLD, max_queues, max_capacity),
_coalesce_and_fill_region_array(NEW_C_HEAP_ARRAY(ShenandoahHeapRegion*, ShenandoahHeap::heap()->num_regions(), mtGC)),
_old_heuristics(nullptr),
_region_balance(0),
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp
index b70a8d33b95..abc865c31cd 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp
@@ -88,7 +88,7 @@ class ShenandoahOldGeneration : public ShenandoahGeneration {
bool coalesce_and_fill();
public:
- ShenandoahOldGeneration(uint max_queues, size_t max_capacity, size_t soft_max_capacity);
+ ShenandoahOldGeneration(uint max_queues, size_t max_capacity);
ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode) override;
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp
index 0a456151318..c68f0b78dab 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp
@@ -126,6 +126,7 @@ class outputStream;
f(final_update_refs, "Pause Final Update Refs (N)") \
f(final_update_refs_verify, " Verify") \
f(final_update_refs_update_region_states, " Update Region States") \
+ f(final_update_refs_transfer_satb, " Transfer Old From SATB") \
f(final_update_refs_trash_cset, " Trash Collection Set") \
f(final_update_refs_rebuild_freeset, " Rebuild Free Set") \
f(final_update_refs_propagate_gc_state, " Propagate GC State") \
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp
index 23c705348c4..ea94c2926e8 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp
@@ -125,9 +125,8 @@ void ShenandoahDirectCardMarkRememberedSet::mark_read_table_as_clean() {
// No lock required because arguments align with card boundaries.
void ShenandoahCardCluster::reset_object_range(HeapWord* from, HeapWord* to) {
- assert(((((unsigned long long) from) & (CardTable::card_size() - 1)) == 0) &&
- ((((unsigned long long) to) & (CardTable::card_size() - 1)) == 0),
- "reset_object_range bounds must align with card boundaries");
+ assert(CardTable::is_card_aligned(from) && CardTable::is_card_aligned(to),
+ "Must align with card boundaries");
size_t card_at_start = _rs->card_index_for_addr(from);
size_t num_cards = (to - from) / CardTable::card_size_in_words();
@@ -222,31 +221,88 @@ size_t ShenandoahCardCluster::get_last_start(size_t card_index) const {
return _object_starts[card_index].offsets.last;
}
-// Given a card_index, return the starting address of the first block in the heap
-// that straddles into this card. If this card is co-initial with an object, then
-// this would return the first address of the range that this card covers, which is
-// where the card's first object also begins.
-HeapWord* ShenandoahCardCluster::block_start(const size_t card_index) const {
+HeapWord* ShenandoahCardCluster::first_object_start(const size_t card_index, const ShenandoahMarkingContext* const ctx,
+ HeapWord* tams, HeapWord* end_range_of_interest) const {
HeapWord* left = _rs->addr_for_card_index(card_index);
+ assert(left < end_range_of_interest, "No meaningful work to do");
+ ShenandoahHeapRegion* region = ShenandoahHeap::heap()->heap_region_containing(left);
#ifdef ASSERT
assert(ShenandoahHeap::heap()->mode()->is_generational(), "Do not use in non-generational mode");
- ShenandoahHeapRegion* region = ShenandoahHeap::heap()->heap_region_containing(left);
assert(region->is_old(), "Do not use for young regions");
// For HumongousRegion:s it's more efficient to jump directly to the
// start region.
assert(!region->is_humongous(), "Use region->humongous_start_region() instead");
#endif
+
+ HeapWord* right = MIN2(region->top(), end_range_of_interest);
+ HeapWord* end_of_search_next = MIN2(right, tams);
+ // Since end_range_of_interest may not align on a card boundary, last_relevant_card_index is conservative. Not all of the
+ // memory within the last relevant card's span is < right.
+ size_t last_relevant_card_index;
+ if (end_range_of_interest == _end_of_heap) {
+ last_relevant_card_index = _rs->card_index_for_addr(end_range_of_interest - 1);
+ } else {
+ last_relevant_card_index = _rs->card_index_for_addr(end_range_of_interest);
+ if (_rs->addr_for_card_index(last_relevant_card_index) == end_range_of_interest) {
+ last_relevant_card_index--;
+ }
+ }
+ assert(card_index <= last_relevant_card_index, "sanity: card_index: %zu, last_relevant: %zu, left: " PTR_FORMAT
+ ", end_of_range: " PTR_FORMAT, card_index, last_relevant_card_index, p2i(left), p2i(end_range_of_interest));
+
+ // if marking context is valid and we are below tams, we use the marking bit map to find the first marked object that
+ // intersects with this card. If no such object exists, we return the first marked object that follows the start
+ // of this card's memory range if such an object is found at or before last_relevant_card_index. If there are no
+ // marked objects in this range, we return nullptr.
+ if ((ctx != nullptr) && (left < tams)) {
+ if (ctx->is_marked(left)) {
+ oop obj = cast_to_oop(left);
+ assert(oopDesc::is_oop(obj), "Should be an object");
+ return left;
+ }
+ // get the previous marked object, if any
+ if (region->bottom() < left) {
+ // In the case that this region was most recently marked as young, the fact that this region has been promoted in place
+ // denotes that final mark (Young) has completed. In the case that this region was most recently marked as old, the
+ // fact that (ctx != nullptr) denotes that old marking has completed. Otherwise, ctx would equal null.
+ HeapWord* prev = ctx->get_prev_marked_addr(region->bottom(), left - 1);
+ if (prev < left) {
+ oop obj = cast_to_oop(prev);
+ assert(oopDesc::is_oop(obj), "Should be an object");
+ HeapWord* obj_end = prev + obj->size();
+ if (obj_end > left) {
+ return prev;
+ }
+ }
+ }
+ // Either prev >= left (no previous object found), or the previous object that was found ends before my card range begins.
+ // In eiher case, find the next marked object if any on this or a following card
+ assert(!ctx->is_marked(left), "Was dealt with above");
+ assert(right > left, "We don't expect to be examining cards above the smaller of TAMS or top");
+ HeapWord* next = ctx->get_next_marked_addr(left, end_of_search_next);
+ // If end_of_search_next < right, we may return tams here, which is "marked" by default
+ if (next < right) {
+ oop obj = cast_to_oop(next);
+ assert(oopDesc::is_oop(obj), "Should be an object");
+ return next;
+ } else {
+ return nullptr;
+ }
+ }
+
+ assert((ctx == nullptr) || (left >= tams), "Should have returned above");
+
+ // The following code assumes that all data in region at or above left holds parsable objects
+ assert((left >= tams) || ShenandoahGenerationalHeap::heap()->old_generation()->is_parsable(),
+ "The code that follows expects a parsable heap");
if (starts_object(card_index) && get_first_start(card_index) == 0) {
- // This card contains a co-initial object; a fortiori, it covers
- // also the case of a card being the first in a region.
+ // This card contains a co-initial object; a fortiori, it covers also the case of a card being the first in a region.
assert(oopDesc::is_oop(cast_to_oop(left)), "Should be an object");
return left;
}
- HeapWord* p = nullptr;
- oop obj = cast_to_oop(p);
ssize_t cur_index = (ssize_t)card_index;
assert(cur_index >= 0, "Overflow");
assert(cur_index > 0, "Should have returned above");
@@ -256,37 +312,75 @@ HeapWord* ShenandoahCardCluster::block_start(const size_t card_index) const {
}
// cur_index should start an object: we should not have walked
// past the left end of the region.
- assert(cur_index >= 0 && (cur_index <= (ssize_t)card_index), "Error");
+ assert(cur_index >= 0 && (cur_index <= (ssize_t) card_index), "Error");
assert(region->bottom() <= _rs->addr_for_card_index(cur_index),
"Fell off the bottom of containing region");
assert(starts_object(cur_index), "Error");
size_t offset = get_last_start(cur_index);
// can avoid call via card size arithmetic below instead
- p = _rs->addr_for_card_index(cur_index) + offset;
+ HeapWord* p = _rs->addr_for_card_index(cur_index) + offset;
+ if ((ctx != nullptr) && (p < tams)) {
+ if (ctx->is_marked(p)) {
+ oop obj = cast_to_oop(p);
+ assert(oopDesc::is_oop(obj), "Should be an object");
+ assert(Klass::is_valid(obj->klass()), "Not a valid klass ptr");
+ assert(p + obj->size() > left, "This object should span start of card");
+ assert(p < right, "Result must precede right");
+ return p;
+ } else {
+ // Object that spans start of card is dead, so should not be scanned
+ assert((ctx == nullptr) || (left + get_first_start(card_index) >= tams), "Should have handled this case above");
+ if (starts_object(card_index)) {
+ assert(left + get_first_start(card_index) < right, "Result must precede right");
+ return left + get_first_start(card_index);
+ } else {
+ // Spanning object is dead and this card does not start an object, so the start object is in some card that follows
+ size_t following_card_index = card_index;
+ do {
+ following_card_index++;
+ if (following_card_index > last_relevant_card_index) {
+ return nullptr;
+ }
+ } while (!starts_object(following_card_index));
+ HeapWord* result_candidate = _rs->addr_for_card_index(following_card_index) + get_first_start(following_card_index);
+ return (result_candidate >= right)? nullptr: result_candidate;
+ }
+ }
+ }
+
// Recall that we already dealt with the co-initial object case above
assert(p < left, "obj should start before left");
// While it is safe to ask an object its size in the loop that
// follows, the (ifdef'd out) loop should never be needed.
- // 1. we ask this question only for regions in the old generation
+ // 1. we ask this question only for regions in the old generation, and those
+ // that are not humongous regions
// 2. there is no direct allocation ever by mutators in old generation
- // regions. Only GC will ever allocate in old regions, and then
- // too only during promotion/evacuation phases. Thus there is no danger
+ // regions walked by this code. Only GC will ever allocate in old regions,
+ // and then too only during promotion/evacuation phases. Thus there is no danger
// of races between reading from and writing to the object start array,
// or of asking partially initialized objects their size (in the loop below).
+ // Furthermore, humongous regions (and their dirty cards) are never processed
+ // by this code.
// 3. only GC asks this question during phases when it is not concurrently
// evacuating/promoting, viz. during concurrent root scanning (before
// the evacuation phase) and during concurrent update refs (after the
// evacuation phase) of young collections. This is never called
- // during old or global collections.
+ // during global collections during marking or update refs..
// 4. Every allocation under TAMS updates the object start array.
- NOT_PRODUCT(obj = cast_to_oop(p);)
+#ifdef ASSERT
+ oop obj = cast_to_oop(p);
assert(oopDesc::is_oop(obj), "Should be an object");
-#define WALK_FORWARD_IN_BLOCK_START false
- while (WALK_FORWARD_IN_BLOCK_START && p + obj->size() < left) {
+ while (p + obj->size() < left) {
p += obj->size();
+ obj = cast_to_oop(p);
+ assert(oopDesc::is_oop(obj), "Should be an object");
+ assert(Klass::is_valid(obj->klass()), "Not a valid klass ptr");
+ // Check assumptions in previous block comment if this assert fires
+ fatal("Should never need forward walk in block start");
}
-#undef WALK_FORWARD_IN_BLOCK_START // false
- assert(p + obj->size() > left, "obj should end after left");
+ assert(p <= left, "p should start at or before left end of card");
+ assert(p + obj->size() > left, "obj should end after left end of card");
+#endif // ASSERT
return p;
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp
index 5df34159c0f..08fcd74a460 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp
@@ -351,6 +351,7 @@ class ShenandoahCardCluster: public CHeapObj {
private:
ShenandoahDirectCardMarkRememberedSet* _rs;
+ const HeapWord* _end_of_heap;
public:
static const size_t CardsPerCluster = 64;
@@ -404,6 +405,7 @@ class ShenandoahCardCluster: public CHeapObj {
ShenandoahCardCluster(ShenandoahDirectCardMarkRememberedSet* rs) {
_rs = rs;
+ _end_of_heap = ShenandoahHeap::heap()->end();
_object_starts = NEW_C_HEAP_ARRAY(crossing_info, rs->total_cards() + 1, mtGC); // the +1 is to account for card table guarding entry
for (size_t i = 0; i < rs->total_cards(); i++) {
_object_starts[i].short_word = 0;
@@ -650,12 +652,31 @@ class ShenandoahCardCluster: public CHeapObj {
size_t get_last_start(size_t card_index) const;
- // Given a card_index, return the starting address of the first block in the heap
- // that straddles into the card. If the card is co-initial with an object, then
- // this would return the starting address of the heap that this card covers.
- // Expects to be called for a card affiliated with the old generation in
- // generational mode.
- HeapWord* block_start(size_t card_index) const;
+ // Given a card_index, return the starting address of the first live object in the heap
+ // that intersects with or follows this card. This must be a valid, parsable object, and must
+ // be the first such object that intersects with this card. The object may start before,
+ // at, or after the start of the card identified by card_index, and may end in or after the card.
+ //
+ // The tams argument represents top for the enclosing region at the start of the most recently
+ // initiated concurrent old marking effort. If ctx is non-null, we use the marking context to identify
+ // marked objects below tams. Above tams, we know that every object is marked and that the memory is
+ // parsable (so we can add an object's size to its address to find the next object). If ctx is null,
+ // we use crossing maps to find where object's start, and use object sizes to walk individual objects.
+ // The region must be parsable if ctx is null.
+ //
+ // The end_range_of_interest pointer argument represents an upper bound on how far we look in the forward direction
+ // for the first object in the heap that intersects or follows this card. If there are no live objects found at
+ // an address less than end_range_of_interest returns nullptr.
+ //
+ // Expects to be called for a card in a region affiliated with the old generation of the
+ // generational heap, otherwise behavior is undefined.
+ //
+ // If not null, ctx holds the complete marking context of the old generation. If null,
+ // we expect that the marking context isn't available and the crossing maps are valid.
+ // Note that crossing maps may be invalid following class unloading and before dead
+ // or unloaded objects have been coalesced and filled. Coalesce and fill updates the crossing maps.
+ HeapWord* first_object_start(size_t card_index, const ShenandoahMarkingContext* const ctx,
+ HeapWord* tams, HeapWord* end_range_of_interest) const;
};
// ShenandoahScanRemembered is a concrete class representing the
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp
index 68bec5c2071..82022420a2a 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.inline.hpp
@@ -50,7 +50,7 @@
// degenerated execution, leading to dangling references.
template
void ShenandoahScanRemembered::process_clusters(size_t first_cluster, size_t count, HeapWord* end_of_range,
- ClosureType* cl, bool use_write_table, uint worker_id) {
+ ClosureType* cl, bool use_write_table, uint worker_id) {
assert(ShenandoahHeap::heap()->old_generation()->is_parsable(), "Old generation regions must be parsable for remembered set scan");
// If old-gen evacuation is active, then MarkingContext for old-gen heap regions is valid. We use the MarkingContext
@@ -102,7 +102,7 @@ void ShenandoahScanRemembered::process_clusters(size_t first_cluster, size_t cou
// tams and ctx below are for old generation marking. As such, young gen roots must
// consider everything above tams, since it doesn't represent a TAMS for young gen's
// SATB marking.
- const HeapWord* tams = (ctx == nullptr ? region->bottom() : ctx->top_at_mark_start(region));
+ HeapWord* const tams = (ctx == nullptr ? region->bottom() : ctx->top_at_mark_start(region));
NOT_PRODUCT(ShenandoahCardStats stats(whole_cards, card_stats(worker_id));)
@@ -162,19 +162,37 @@ void ShenandoahScanRemembered::process_clusters(size_t first_cluster, size_t cou
// [left, right) is a maximal right-open interval of dirty cards
HeapWord* left = _rs->addr_for_card_index(dirty_l); // inclusive
HeapWord* right = _rs->addr_for_card_index(dirty_r + 1); // exclusive
+ if (end_addr <= left) {
+ // The range of addresses to be scanned is empty
+ continue;
+ }
// Clip right to end_addr established above (still exclusive)
right = MIN2(right, end_addr);
assert(right <= region->top() && end_addr <= region->top(), "Busted bounds");
const MemRegion mr(left, right);
- // NOTE: We'll not call block_start() repeatedly
- // on a very large object if its head card is dirty. If not,
- // (i.e. the head card is clean) we'll call it each time we
- // process a new dirty range on the object. This is always
- // the case for large object arrays, which are typically more
+ // NOTE: We'll not call first_object_start() repeatedly
+ // on a very large object, i.e. one spanning multiple cards,
+ // if its head card is dirty. If not, (i.e. its head card is clean)
+ // we'll call it each time we process a new dirty range on the object.
+ // This is always the case for large object arrays, which are typically more
// common.
- HeapWord* p = _scc->block_start(dirty_l);
+ assert(ctx != nullptr || heap->old_generation()->is_parsable(), "Error");
+ HeapWord* p = _scc->first_object_start(dirty_l, ctx, tams, right);
+ assert((p == nullptr) || (p < right), "No first object found is denoted by nullptr, p: "
+ PTR_FORMAT ", right: " PTR_FORMAT ", end_addr: " PTR_FORMAT ", next card addr: " PTR_FORMAT,
+ p2i(p), p2i(right), p2i(end_addr), p2i(_rs->addr_for_card_index(dirty_r + 1)));
+ if (p == nullptr) {
+ // There are no live objects to be scanned in this dirty range. cur_index identifies first card in this
+ // uninteresting dirty range. At top of next loop iteration, we will either end the looop
+ // (because cur_index < start_card_index) or we will begin the search for a range of clean cards.
+ continue;
+ }
+
oop obj = cast_to_oop(p);
+ assert(oopDesc::is_oop(obj), "Not an object at " PTR_FORMAT ", left: " PTR_FORMAT ", right: " PTR_FORMAT,
+ p2i(p), p2i(left), p2i(right));
+ assert(ctx==nullptr || ctx->is_marked(obj), "Error");
// PREFIX: The object that straddles into this range of dirty cards
// from the left may be subject to special treatment unless
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp
index daf5d456af5..849ab691dcf 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.cpp
@@ -30,8 +30,8 @@
#include "gc/shenandoah/shenandoahUtils.hpp"
#include "gc/shenandoah/shenandoahYoungGeneration.hpp"
-ShenandoahYoungGeneration::ShenandoahYoungGeneration(uint max_queues, size_t max_capacity, size_t soft_max_capacity) :
- ShenandoahGeneration(YOUNG, max_queues, max_capacity, soft_max_capacity),
+ShenandoahYoungGeneration::ShenandoahYoungGeneration(uint max_queues, size_t max_capacity) :
+ ShenandoahGeneration(YOUNG, max_queues, max_capacity),
_old_gen_task_queues(nullptr) {
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp
index a8ebab507b6..14f9a9b3004 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahYoungGeneration.hpp
@@ -34,7 +34,7 @@ class ShenandoahYoungGeneration : public ShenandoahGeneration {
ShenandoahYoungHeuristics* _young_heuristics;
public:
- ShenandoahYoungGeneration(uint max_queues, size_t max_capacity, size_t max_soft_capacity);
+ ShenandoahYoungGeneration(uint max_queues, size_t max_capacity);
ShenandoahHeuristics* initialize_heuristics(ShenandoahMode* gc_mode) override;
diff --git a/src/hotspot/share/gc/z/zPageAllocator.cpp b/src/hotspot/share/gc/z/zPageAllocator.cpp
index 80a53feed71..c16354106d8 100644
--- a/src/hotspot/share/gc/z/zPageAllocator.cpp
+++ b/src/hotspot/share/gc/z/zPageAllocator.cpp
@@ -1087,7 +1087,8 @@ void ZPartition::free_memory_alloc_failed(ZMemoryAllocation* allocation) {
freed += vmem.size();
_cache.insert(vmem);
}
- assert(allocation->harvested() + allocation->committed_capacity() == freed, "must have freed all");
+ assert(allocation->harvested() + allocation->committed_capacity() == freed, "must have freed all"
+ " %zu + %zu == %zu", allocation->harvested(), allocation->committed_capacity(), freed);
// Adjust capacity to reflect the failed capacity increase
const size_t remaining = allocation->size() - freed;
@@ -1904,23 +1905,27 @@ void ZPageAllocator::cleanup_failed_commit_single_partition(ZSinglePartitionAllo
ZMemoryAllocation* const allocation = single_partition_allocation->allocation();
assert(allocation->commit_failed(), "Must have failed to commit");
+ assert(allocation->partial_vmems()->is_empty(), "Invariant for single partition commit failure");
- const size_t committed = allocation->committed_capacity();
- const ZVirtualMemory non_harvested_vmem = vmem.last_part(allocation->harvested());
- const ZVirtualMemory committed_vmem = non_harvested_vmem.first_part(committed);
- const ZVirtualMemory non_committed_vmem = non_harvested_vmem.last_part(committed);
+ // For a single partition we have unmapped the harvested memory before we
+ // started committing, and moved its physical memory association to the start
+ // of the vmem. As such, the partial_vmems is empty. All the harvested and
+ // partially successfully committed memory is mapped in the first part of vmem.
+ const size_t harvested_and_committed_capacity = allocation->harvested() + allocation->committed_capacity();
+ const ZVirtualMemory succeeded_vmem = vmem.first_part(harvested_and_committed_capacity);
+ const ZVirtualMemory failed_vmem = vmem.last_part(harvested_and_committed_capacity);
- if (committed_vmem.size() > 0) {
+ if (succeeded_vmem.size() > 0) {
// Register the committed and mapped memory. We insert the committed
// memory into partial_vmems so that it will be inserted into the cache
// in a subsequent step.
- allocation->partial_vmems()->append(committed_vmem);
+ allocation->partial_vmems()->append(succeeded_vmem);
}
// Free the virtual and physical memory we fetched to use but failed to commit
ZPartition& partition = allocation->partition();
- partition.free_physical(non_committed_vmem);
- partition.free_virtual(non_committed_vmem);
+ partition.free_physical(failed_vmem);
+ partition.free_virtual(failed_vmem);
}
void ZPageAllocator::cleanup_failed_commit_multi_partition(ZMultiPartitionAllocation* multi_partition_allocation, const ZVirtualMemory& vmem) {
@@ -1936,7 +1941,7 @@ void ZPageAllocator::cleanup_failed_commit_multi_partition(ZMultiPartitionAlloca
}
const size_t committed = allocation->committed_capacity();
- const ZVirtualMemory non_harvested_vmem = vmem.last_part(allocation->harvested());
+ const ZVirtualMemory non_harvested_vmem = partial_vmem.last_part(allocation->harvested());
const ZVirtualMemory committed_vmem = non_harvested_vmem.first_part(committed);
const ZVirtualMemory non_committed_vmem = non_harvested_vmem.last_part(committed);
diff --git a/src/hotspot/share/gc/z/zPhysicalMemoryManager.cpp b/src/hotspot/share/gc/z/zPhysicalMemoryManager.cpp
index 2c2f9988d4b..54477d19a27 100644
--- a/src/hotspot/share/gc/z/zPhysicalMemoryManager.cpp
+++ b/src/hotspot/share/gc/z/zPhysicalMemoryManager.cpp
@@ -214,9 +214,20 @@ void ZPhysicalMemoryManager::free(const ZVirtualMemory& vmem, uint32_t numa_id)
});
}
+static size_t inject_commit_limit(const ZVirtualMemory& vmem) {
+ // To facilitate easier interoperability with multi partition allocations we
+ // divide by ZNUMA::count(). Users of ZFailLargerCommits need to be aware of
+ // this when writing tests. In the future we could probe the VirtualMemoryManager
+ // and condition this division on whether the vmem is in the multi partition
+ // address space.
+ return align_up(MIN2(ZFailLargerCommits / ZNUMA::count(), vmem.size()), ZGranuleSize);
+}
+
size_t ZPhysicalMemoryManager::commit(const ZVirtualMemory& vmem, uint32_t numa_id) {
zbacking_index* const pmem = _physical_mappings.addr(vmem.start());
- const size_t size = vmem.size();
+ const size_t size = ZFailLargerCommits > 0
+ ? inject_commit_limit(vmem)
+ : vmem.size();
size_t total_committed = 0;
diff --git a/src/hotspot/share/gc/z/z_globals.hpp b/src/hotspot/share/gc/z/z_globals.hpp
index e1f525e5372..4e5d725a5fd 100644
--- a/src/hotspot/share/gc/z/z_globals.hpp
+++ b/src/hotspot/share/gc/z/z_globals.hpp
@@ -118,6 +118,11 @@
develop(bool, ZVerifyOops, false, \
"Verify accessed oops") \
\
+ develop(size_t, ZFailLargerCommits, 0, \
+ "Commits larger than ZFailLargerCommits will be truncated, " \
+ "used to stress page allocation commit failure paths " \
+ "(0: Disabled)") \
+ \
develop(uint, ZFakeNUMA, 1, \
"ZFakeNUMA is used to test the internal NUMA memory support " \
"without the need for UseNUMA") \
diff --git a/src/hotspot/share/interpreter/bytecodeStream.hpp b/src/hotspot/share/interpreter/bytecodeStream.hpp
index 89d97053b45..412951691c5 100644
--- a/src/hotspot/share/interpreter/bytecodeStream.hpp
+++ b/src/hotspot/share/interpreter/bytecodeStream.hpp
@@ -100,8 +100,23 @@ class BaseBytecodeStream: StackObj {
void set_next_bci(int bci) { assert(0 <= bci && bci <= method()->code_size(), "illegal bci"); _next_bci = bci; }
// Bytecode-specific attributes
- int dest() const { return bci() + bytecode().get_offset_s2(raw_code()); }
- int dest_w() const { return bci() + bytecode().get_offset_s4(raw_code()); }
+ int get_offset_s2() const { return bytecode().get_offset_s2(raw_code()); }
+ int get_offset_s4() const { return bytecode().get_offset_s4(raw_code()); }
+
+ // These methods are not safe to use before or during verification as they may
+ // have large offsets and cause overflows
+ int dest() const {
+ int min_offset = -1 * max_method_code_size;
+ int offset = bytecode().get_offset_s2(raw_code());
+ guarantee(offset >= min_offset && offset <= max_method_code_size, "must be");
+ return bci() + offset;
+ }
+ int dest_w() const {
+ int min_offset = -1 * max_method_code_size;
+ int offset = bytecode().get_offset_s4(raw_code());
+ guarantee(offset >= min_offset && offset <= max_method_code_size, "must be");
+ return bci() + offset;
+ }
// One-byte indices.
u1 get_index_u1() const { assert_raw_index_size(1); return *(jubyte*)(bcp()+1); }
diff --git a/src/hotspot/share/jfr/jfr.cpp b/src/hotspot/share/jfr/jfr.cpp
index 4bd57b3c324..bd5797be0a0 100644
--- a/src/hotspot/share/jfr/jfr.cpp
+++ b/src/hotspot/share/jfr/jfr.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,6 +31,7 @@
#include "jfr/recorder/repository/jfrEmergencyDump.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
#include "jfr/recorder/service/jfrOptionSet.hpp"
+#include "jfr/recorder/service/jfrRecorderService.hpp"
#include "jfr/recorder/repository/jfrRepository.hpp"
#include "jfr/support/jfrKlassExtension.hpp"
#include "jfr/support/jfrResolution.hpp"
@@ -43,6 +44,7 @@
#include "runtime/java.hpp"
#include "runtime/javaThread.hpp"
+
bool Jfr::is_enabled() {
return JfrRecorder::is_enabled();
}
@@ -151,9 +153,9 @@ void Jfr::on_resolution(const Method* caller, const Method* target, TRAPS) {
}
#endif
-void Jfr::on_vm_shutdown(bool exception_handler, bool halt) {
+void Jfr::on_vm_shutdown(bool exception_handler /* false */, bool halt /* false */, bool oom /* false */) {
if (!halt && JfrRecorder::is_recording()) {
- JfrEmergencyDump::on_vm_shutdown(exception_handler);
+ JfrEmergencyDump::on_vm_shutdown(exception_handler, oom);
}
}
@@ -170,3 +172,9 @@ bool Jfr::on_flight_recorder_option(const JavaVMOption** option, char* delimiter
bool Jfr::on_start_flight_recording_option(const JavaVMOption** option, char* delimiter) {
return JfrOptionSet::parse_start_flight_recording_option(option, delimiter);
}
+
+void Jfr::on_report_java_out_of_memory() {
+ if (CrashOnOutOfMemoryError && JfrRecorder::is_recording()) {
+ JfrRecorderService::emit_leakprofiler_events_on_oom();
+ }
+}
diff --git a/src/hotspot/share/jfr/jfr.hpp b/src/hotspot/share/jfr/jfr.hpp
index b053c6445ef..a7565620bbd 100644
--- a/src/hotspot/share/jfr/jfr.hpp
+++ b/src/hotspot/share/jfr/jfr.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -70,7 +70,7 @@ class Jfr : AllStatic {
static void on_resolution(const Method* caller, const Method* target, TRAPS);
static void on_java_thread_start(JavaThread* starter, JavaThread* startee);
static void on_set_current_thread(JavaThread* jt, oop thread);
- static void on_vm_shutdown(bool exception_handler = false, bool halt = false);
+ static void on_vm_shutdown(bool exception_handler = false, bool halt = false, bool oom = false);
static void on_vm_error_report(outputStream* st);
static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
@@ -78,6 +78,7 @@ class Jfr : AllStatic {
static void initialize_main_thread(JavaThread* jt);
static bool has_sample_request(JavaThread* jt);
static void check_and_process_sample_request(JavaThread* jt);
+ static void on_report_java_out_of_memory();
};
#endif // SHARE_JFR_JFR_HPP
diff --git a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp
index a09da529b1b..0b4ab532064 100644
--- a/src/hotspot/share/jfr/jni/jfrJniMethod.cpp
+++ b/src/hotspot/share/jfr/jni/jfrJniMethod.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -364,8 +364,7 @@ JVM_ENTRY_NO_ENV(void, jfr_set_force_instrumentation(JNIEnv* env, jclass jvm, jb
JVM_END
NO_TRANSITION(void, jfr_emit_old_object_samples(JNIEnv* env, jclass jvm, jlong cutoff_ticks, jboolean emit_all, jboolean skip_bfs))
- JfrRecorderService service;
- service.emit_leakprofiler_events(cutoff_ticks, emit_all == JNI_TRUE, skip_bfs == JNI_TRUE);
+ JfrRecorderService::emit_leakprofiler_events(cutoff_ticks, emit_all == JNI_TRUE, skip_bfs == JNI_TRUE);
NO_TRANSITION_END
JVM_ENTRY_NO_ENV(void, jfr_exclude_thread(JNIEnv* env, jclass jvm, jobject t))
diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp
index 2793a1fb984..a59503f50d8 100644
--- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp
+++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.cpp
@@ -25,7 +25,6 @@
#include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp"
#include "logging/log.hpp"
-
#if defined(LINUX)
#include "jfr/periodic/sampling/jfrThreadSampling.hpp"
#include "jfr/support/jfrThreadLocal.hpp"
@@ -68,7 +67,7 @@ static JavaThread* get_java_thread_if_valid() {
}
JfrCPUTimeTraceQueue::JfrCPUTimeTraceQueue(u4 capacity) :
- _data(nullptr), _capacity(capacity), _head(0), _lost_samples(0) {
+ _data(nullptr), _capacity(capacity), _head(0), _lost_samples(0), _lost_samples_due_to_queue_full(0) {
if (capacity != 0) {
_data = JfrCHeapObj::new_array(capacity);
}
@@ -111,10 +110,13 @@ void JfrCPUTimeTraceQueue::set_size(u4 size) {
}
u4 JfrCPUTimeTraceQueue::capacity() const {
- return _capacity;
+ return Atomic::load_acquire(&_capacity);
}
void JfrCPUTimeTraceQueue::set_capacity(u4 capacity) {
+ if (capacity == Atomic::load(&_capacity)) {
+ return;
+ }
_head = 0;
if (_data != nullptr) {
assert(_capacity != 0, "invariant");
@@ -125,7 +127,7 @@ void JfrCPUTimeTraceQueue::set_capacity(u4 capacity) {
} else {
_data = nullptr;
}
- _capacity = capacity;
+ Atomic::release_store(&_capacity, capacity);
}
bool JfrCPUTimeTraceQueue::is_empty() const {
@@ -141,28 +143,51 @@ void JfrCPUTimeTraceQueue::increment_lost_samples() {
Atomic::inc(&_lost_samples);
}
+void JfrCPUTimeTraceQueue::increment_lost_samples_due_to_queue_full() {
+ Atomic::inc(&_lost_samples_due_to_queue_full);
+}
+
u4 JfrCPUTimeTraceQueue::get_and_reset_lost_samples() {
return Atomic::xchg(&_lost_samples, (u4)0);
}
-void JfrCPUTimeTraceQueue::resize(u4 capacity) {
- if (capacity != _capacity) {
- set_capacity(capacity);
- }
+u4 JfrCPUTimeTraceQueue::get_and_reset_lost_samples_due_to_queue_full() {
+ return Atomic::xchg(&_lost_samples_due_to_queue_full, (u4)0);
}
-void JfrCPUTimeTraceQueue::resize_for_period(u4 period_millis) {
- u4 capacity = CPU_TIME_QUEUE_CAPACITY;
- if (period_millis > 0 && period_millis < 10) {
- capacity = (u4) ((double) capacity * 10 / period_millis);
- }
- resize(capacity);
+void JfrCPUTimeTraceQueue::init() {
+ set_capacity(JfrCPUTimeTraceQueue::CPU_TIME_QUEUE_INITIAL_CAPACITY);
}
void JfrCPUTimeTraceQueue::clear() {
Atomic::release_store(&_head, (u4)0);
}
+void JfrCPUTimeTraceQueue::resize_if_needed() {
+ u4 lost_samples_due_to_queue_full = get_and_reset_lost_samples_due_to_queue_full();
+ if (lost_samples_due_to_queue_full == 0) {
+ return;
+ }
+ u4 capacity = Atomic::load(&_capacity);
+ if (capacity < CPU_TIME_QUEUE_MAX_CAPACITY) {
+ float ratio = (float)lost_samples_due_to_queue_full / (float)capacity;
+ int factor = 1;
+ if (ratio > 8) { // idea is to quickly scale the queue in the worst case
+ factor = ratio;
+ } else if (ratio > 2) {
+ factor = 8;
+ } else if (ratio > 0.5) {
+ factor = 4;
+ } else if (ratio > 0.01) {
+ factor = 2;
+ }
+ if (factor > 1) {
+ u4 new_capacity = MIN2(CPU_TIME_QUEUE_MAX_CAPACITY, capacity * factor);
+ set_capacity(new_capacity);
+ }
+ }
+}
+
// A throttle is either a rate or a fixed period
class JfrCPUSamplerThrottle {
@@ -206,6 +231,8 @@ class JfrCPUSamplerThread : public NonJavaThread {
volatile bool _is_async_processing_of_cpu_time_jfr_requests_triggered;
volatile bool _warned_about_timer_creation_failure;
volatile bool _signal_handler_installed;
+ DEBUG_ONLY(volatile bool _out_of_stack_walking_enabled;)
+ DEBUG_ONLY(volatile u8 _out_of_stack_walking_iterations;)
static const u4 STOP_SIGNAL_BIT = 0x80000000;
@@ -249,8 +276,19 @@ class JfrCPUSamplerThread : public NonJavaThread {
void handle_timer_signal(siginfo_t* info, void* context);
bool init_timers();
void stop_timer();
+ virtual void print_on(outputStream* st) const;
void trigger_async_processing_of_cpu_time_jfr_requests();
+
+ #ifdef ASSERT
+ void set_out_of_stack_walking_enabled(bool runnable) {
+ Atomic::release_store(&_out_of_stack_walking_enabled, runnable);
+ }
+
+ u8 out_of_stack_walking_iterations() const {
+ return Atomic::load(&_out_of_stack_walking_iterations);
+ }
+ #endif
};
JfrCPUSamplerThread::JfrCPUSamplerThread(JfrCPUSamplerThrottle& throttle) :
@@ -276,7 +314,7 @@ void JfrCPUSamplerThread::on_javathread_create(JavaThread* thread) {
}
JfrThreadLocal* tl = thread->jfr_thread_local();
assert(tl != nullptr, "invariant");
- tl->cpu_time_jfr_queue().resize_for_period(_current_sampling_period_ns / 1000000);
+ tl->cpu_time_jfr_queue().init();
timer_t timerid;
if (create_timer_for_thread(thread, timerid)) {
tl->set_cpu_timer(&timerid);
@@ -295,12 +333,14 @@ void JfrCPUSamplerThread::on_javathread_terminate(JavaThread* thread) {
if (timer == nullptr) {
return; // no timer was created for this thread
}
+ tl->acquire_cpu_time_jfr_dequeue_lock();
tl->unset_cpu_timer();
tl->deallocate_cpu_time_jfr_queue();
s4 lost_samples = tl->cpu_time_jfr_queue().lost_samples();
if (lost_samples > 0) {
JfrCPUTimeThreadSampling::send_lost_event(JfrTicks::now(), JfrThreadLocal::thread_id(thread), lost_samples);
}
+ tl->release_cpu_time_jfr_queue_lock();
}
void JfrCPUSamplerThread::start_thread() {
@@ -353,10 +393,12 @@ void JfrCPUSamplerThread::run() {
recompute_period_if_needed();
last_recompute_check = os::javaTimeNanos();
}
-
- if (Atomic::cmpxchg(&_is_async_processing_of_cpu_time_jfr_requests_triggered, true, false)) {
- stackwalk_threads_in_native();
- }
+ DEBUG_ONLY(if (Atomic::load_acquire(&_out_of_stack_walking_enabled)) {)
+ if (Atomic::cmpxchg(&_is_async_processing_of_cpu_time_jfr_requests_triggered, true, false)) {
+ DEBUG_ONLY(Atomic::inc(&_out_of_stack_walking_iterations);)
+ stackwalk_threads_in_native();
+ }
+ DEBUG_ONLY(})
os::naked_sleep(100);
}
}
@@ -546,6 +588,21 @@ void JfrCPUTimeThreadSampling::handle_timer_signal(siginfo_t* info, void* contex
_sampler->decrement_signal_handler_count();
}
+#ifdef ASSERT
+void JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(bool runnable) {
+ if (_instance != nullptr && _instance->_sampler != nullptr) {
+ _instance->_sampler->set_out_of_stack_walking_enabled(runnable);
+ }
+}
+
+u8 JfrCPUTimeThreadSampling::out_of_stack_walking_iterations() {
+ if (_instance != nullptr && _instance->_sampler != nullptr) {
+ return _instance->_sampler->out_of_stack_walking_iterations();
+ }
+ return 0;
+}
+#endif
+
void JfrCPUSamplerThread::sample_thread(JfrSampleRequest& request, void* ucontext, JavaThread* jt, JfrThreadLocal* tl, JfrTicks& now) {
JfrSampleRequestBuilder::build_cpu_time_sample_request(request, ucontext, jt, jt->jfr_thread_local(), now);
}
@@ -591,6 +648,7 @@ void JfrCPUSamplerThread::handle_timer_signal(siginfo_t* info, void* context) {
}
} else {
queue.increment_lost_samples();
+ queue.increment_lost_samples_due_to_queue_full();
}
if (jt->thread_state() == _thread_in_native) {
@@ -732,6 +790,12 @@ void JfrCPUSamplerThread::stop_timer() {
VMThread::execute(&op);
}
+void JfrCPUSamplerThread::print_on(outputStream* st) const {
+ st->print("\"%s\" ", name());
+ Thread::print_on(st);
+ st->cr();
+}
+
void JfrCPUSamplerThread::recompute_period_if_needed() {
int64_t current_period = get_sampling_period();
int64_t period = _throttle.compute_sampling_period();
@@ -808,4 +872,10 @@ void JfrCPUTimeThreadSampling::on_javathread_create(JavaThread* thread) {
void JfrCPUTimeThreadSampling::on_javathread_terminate(JavaThread* thread) {
}
+#ifdef ASSERT
+static void set_out_of_stack_walking_enabled(bool runnable) {
+ warn();
+}
+#endif
+
#endif // defined(LINUX) && defined(INCLUDE_JFR)
diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp
index dae0be5c3a7..e17e63fc3ed 100644
--- a/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp
+++ b/src/hotspot/share/jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp
@@ -50,12 +50,15 @@ class JfrCPUTimeTraceQueue {
static const u4 CPU_TIME_QUEUE_CAPACITY = 500;
JfrCPUTimeSampleRequest* _data;
- u4 _capacity;
+ volatile u4 _capacity;
// next unfilled index
volatile u4 _head;
volatile u4 _lost_samples;
+ volatile u4 _lost_samples_due_to_queue_full;
+ static const u4 CPU_TIME_QUEUE_INITIAL_CAPACITY = 20;
+ static const u4 CPU_TIME_QUEUE_MAX_CAPACITY = 2000;
public:
JfrCPUTimeTraceQueue(u4 capacity);
@@ -81,12 +84,17 @@ class JfrCPUTimeTraceQueue {
void increment_lost_samples();
+ void increment_lost_samples_due_to_queue_full();
+
// returns the previous lost samples count
u4 get_and_reset_lost_samples();
- void resize(u4 capacity);
+ u4 get_and_reset_lost_samples_due_to_queue_full();
+
+ void resize_if_needed();
- void resize_for_period(u4 period_millis);
+ // init the queue capacity
+ void init();
void clear();
@@ -130,6 +138,10 @@ class JfrCPUTimeThreadSampling : public JfrCHeapObj {
static void send_lost_event(const JfrTicks& time, traceid tid, s4 lost_samples);
static void trigger_async_processing_of_cpu_time_jfr_requests();
+
+ DEBUG_ONLY(static void set_out_of_stack_walking_enabled(bool runnable);)
+
+ DEBUG_ONLY(static u8 out_of_stack_walking_iterations();)
};
#else
@@ -150,6 +162,8 @@ class JfrCPUTimeThreadSampling : public JfrCHeapObj {
static void on_javathread_create(JavaThread* thread);
static void on_javathread_terminate(JavaThread* thread);
+ DEBUG_ONLY(static void set_out_of_stack_walking_enabled(bool runnable));
+ DEBUG_ONLY(static u8 out_of_stack_walking_iterations();)
};
#endif // defined(LINUX)
diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp
index 4c44c43772d..596702a53dd 100644
--- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp
+++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -85,6 +85,7 @@ class JfrSamplerThread : public NonJavaThread {
bool is_JfrSampler_thread() const { return true; }
int64_t java_period() const { return Atomic::load(&_java_period_millis); };
int64_t native_period() const { return Atomic::load(&_native_period_millis); };
+ virtual void print_on(outputStream* st) const;
};
JfrSamplerThread::JfrSamplerThread(int64_t java_period_millis, int64_t native_period_millis, u4 max_frames) :
@@ -231,41 +232,50 @@ void JfrSamplerThread::task_stacktrace(JfrSampleRequestType type, JavaThread** l
JavaThread* start = nullptr;
elapsedTimer sample_time;
sample_time.start();
- ThreadsListHandle tlh;
- // Resolve a sample session relative start position index into the thread list array.
- // In cases where the last sampled thread is null or not-null but stale, find_index() returns -1.
- _cur_index = tlh.list()->find_index_of_JavaThread(*last_thread);
- JavaThread* current = _cur_index != -1 ? *last_thread : nullptr;
-
- while (num_samples < sample_limit) {
- current = next_thread(tlh.list(), start, current);
- if (current == nullptr) {
- break;
- }
- if (is_excluded(current)) {
- continue;
- }
- if (start == nullptr) {
- start = current; // remember the thread where we started to attempt sampling
- }
- bool success;
- if (JAVA_SAMPLE == type) {
- success = sample_java_thread(current);
- } else {
- assert(type == NATIVE_SAMPLE, "invariant");
- success = sample_native_thread(current);
- }
- if (success) {
- num_samples++;
- }
- if (SafepointSynchronize::is_at_safepoint()) {
- // For _thread_in_native, we cannot get the Threads_lock.
- // For _thread_in_Java, well, there are none.
- break;
+ {
+ /*
+ * Take the Threads_lock for three purposes:
+ *
+ * 1) Avoid sampling right through a safepoint,
+ * which could result in touching oops in case of virtual threads.
+ * 2) Prevent JFR from issuing an epoch rotation while the sampler thread
+ * is actively processing a thread in state native, as both threads are outside the safepoint protocol.
+ * 3) Some operating systems (BSD / Mac) require a process lock when sending a signal with pthread_kill.
+ * Holding the Threads_lock prevents a JavaThread from calling os::create_thread(), which also takes the process lock.
+ * In a sense, we provide a coarse signal mask, so we can always send the resume signal.
+ */
+ MutexLocker tlock(Threads_lock);
+ ThreadsListHandle tlh;
+ // Resolve a sample session relative start position index into the thread list array.
+ // In cases where the last sampled thread is null or not-null but stale, find_index() returns -1.
+ _cur_index = tlh.list()->find_index_of_JavaThread(*last_thread);
+ JavaThread* current = _cur_index != -1 ? *last_thread : nullptr;
+
+ while (num_samples < sample_limit) {
+ current = next_thread(tlh.list(), start, current);
+ if (current == nullptr) {
+ break;
+ }
+ if (is_excluded(current)) {
+ continue;
+ }
+ if (start == nullptr) {
+ start = current; // remember the thread where we started to attempt sampling
+ }
+ bool success;
+ if (JAVA_SAMPLE == type) {
+ success = sample_java_thread(current);
+ } else {
+ assert(type == NATIVE_SAMPLE, "invariant");
+ success = sample_native_thread(current);
+ }
+ if (success) {
+ num_samples++;
+ }
}
- }
- *last_thread = current; // remember the thread we last attempted to sample
+ *last_thread = current; // remember the thread we last attempted to sample
+ }
sample_time.stop();
log_trace(jfr)("JFR thread sampling done in %3.7f secs with %d java %d native samples",
sample_time.seconds(), type == JAVA_SAMPLE ? num_samples : 0, type == NATIVE_SAMPLE ? num_samples : 0);
@@ -296,6 +306,7 @@ class OSThreadSampler : public SuspendedThreadTask {
// Sampling a thread in state _thread_in_Java
// involves a platform-specific thread suspend and CPU context retrieval.
bool JfrSamplerThread::sample_java_thread(JavaThread* jt) {
+ assert_lock_strong(Threads_lock);
if (jt->thread_state() != _thread_in_Java) {
return false;
}
@@ -327,6 +338,7 @@ static JfrSamplerThread* _sampler_thread = nullptr;
// without thread suspension and CPU context retrieval,
// if we carefully order the loads of the thread state.
bool JfrSamplerThread::sample_native_thread(JavaThread* jt) {
+ assert_lock_strong(Threads_lock);
if (jt->thread_state() != _thread_in_native) {
return false;
}
@@ -342,24 +354,14 @@ bool JfrSamplerThread::sample_native_thread(JavaThread* jt) {
SafepointMechanism::arm_local_poll_release(jt);
- // Take the Threads_lock for two purposes:
- // 1) Avoid sampling through a safepoint which could result
- // in touching oops in case of virtual threads.
- // 2) Prevent JFR from issuing an epoch rotation while the sampler thread
- // is actively processing a thread in native, as both threads are now
- // outside the safepoint protocol.
-
- // OrderAccess::fence() as part of acquiring the lock prevents loads from floating up.
- JfrMutexTryLock threads_lock(Threads_lock);
-
- if (!threads_lock.acquired() || !jt->has_last_Java_frame()) {
- // Remove the native sample request and release the potentially waiting thread.
- JfrSampleMonitor jsm(tl);
- return false;
+ // Separate the arming of the poll (above) from the reading of JavaThread state (below).
+ if (UseSystemMemoryBarrier) {
+ SystemMemoryBarrier::emit();
+ } else {
+ OrderAccess::fence();
}
- if (jt->thread_state() != _thread_in_native) {
- assert_lock_strong(Threads_lock);
+ if (jt->thread_state() != _thread_in_native || !jt->has_last_Java_frame()) {
JfrSampleMonitor jsm(tl);
if (jsm.is_waiting()) {
// The thread has already returned from native,
@@ -384,6 +386,12 @@ void JfrSamplerThread::set_native_period(int64_t period_millis) {
Atomic::store(&_native_period_millis, period_millis);
}
+void JfrSamplerThread::print_on(outputStream* st) const {
+ st->print("\"%s\" ", name());
+ Thread::print_on(st);
+ st->cr();
+}
+
// JfrThreadSampler;
static JfrThreadSampler* _instance = nullptr;
diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp
index ddc9d59b295..534c9996cfe 100644
--- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp
+++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampling.cpp
@@ -217,7 +217,8 @@ static bool compute_top_frame(const JfrSampleRequest& request, frame& top_frame,
const PcDesc* const pc_desc = get_pc_desc(sampled_nm, sampled_pc);
if (is_valid(pc_desc)) {
intptr_t* const synthetic_sp = sender_sp - sampled_nm->frame_size();
- top_frame = frame(synthetic_sp, synthetic_sp, sender_sp, pc_desc->real_pc(sampled_nm), sampled_nm);
+ intptr_t* const synthetic_fp = sender_sp AARCH64_ONLY( - frame::sender_sp_offset);
+ top_frame = frame(synthetic_sp, synthetic_sp, synthetic_fp, pc_desc->real_pc(sampled_nm), sampled_nm);
in_continuation = is_in_continuation(top_frame, jt);
return true;
}
@@ -368,6 +369,7 @@ static void drain_enqueued_cpu_time_requests(const JfrTicks& now, JfrThreadLocal
tl->set_has_cpu_time_jfr_requests(false);
if (queue.lost_samples() > 0) {
JfrCPUTimeThreadSampling::send_lost_event( now, JfrThreadLocal::thread_id(jt), queue.get_and_reset_lost_samples());
+ queue.resize_if_needed();
}
if (lock) {
tl->release_cpu_time_jfr_queue_lock();
diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp
index e821b528707..9c57374d6c6 100644
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdKlassQueue.cpp
@@ -29,6 +29,7 @@
#include "jfr/support/jfrThreadLocal.hpp"
#include "jfr/utilities/jfrEpochQueue.inline.hpp"
#include "jfr/utilities/jfrTypes.hpp"
+#include "memory/metaspace.hpp"
#include "oops/compressedKlass.inline.hpp"
#include "utilities/macros.hpp"
@@ -73,14 +74,13 @@ static size_t element_size(bool compressed) {
return compressed ? NARROW_ELEMENT_SIZE : ELEMENT_SIZE;
}
-static bool can_compress_element(const Klass* klass) {
- return CompressedKlassPointers::is_encodable(klass) &&
- JfrTraceId::load_raw(klass) < uncompressed_threshold;
+static bool can_compress_element(traceid id) {
+ return Metaspace::using_class_space() && id < uncompressed_threshold;
}
static size_t element_size(const Klass* klass) {
assert(klass != nullptr, "invariant");
- return element_size(can_compress_element(klass));
+ return element_size(can_compress_element(JfrTraceId::load_raw(klass)));
}
static bool is_unloaded(traceid id, bool previous_epoch) {
@@ -136,8 +136,7 @@ static inline void store_traceid(JfrEpochQueueNarrowKlassElement* element, trace
}
static void store_compressed_element(traceid id, const Klass* klass, u1* pos) {
- assert(can_compress_element(klass), "invariant");
- assert(id == JfrTraceId::load_raw(klass), "invariant");
+ assert(can_compress_element(id), "invariant");
JfrEpochQueueNarrowKlassElement* const element = new (pos) JfrEpochQueueNarrowKlassElement();
store_traceid(element, id);
element->compressed_klass = encode(klass);
@@ -153,7 +152,7 @@ static void store_element(const Klass* klass, u1* pos) {
assert(pos != nullptr, "invariant");
assert(klass != nullptr, "invariant");
const traceid id = JfrTraceId::load_raw(klass);
- if (can_compress_element(klass)) {
+ if (can_compress_element(id)) {
store_compressed_element(id, klass, pos);
return;
}
diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp
index b49ce4556c7..e0c5d36a3c7 100644
--- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp
+++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,6 +37,9 @@
#include "runtime/javaThread.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/vmOperations.hpp"
+#include "runtime/vmThread.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/ostream.hpp"
@@ -450,25 +453,14 @@ const char* JfrEmergencyDump::chunk_path(const char* repository_path) {
*
* If we end up deadlocking in the attempt of dumping out jfr data,
* we rely on the WatcherThread task "is_error_reported()",
-* to exit the VM after a hard-coded timeout (disallow WatcherThread to emergency dump).
+* to exit the VM after a hard-coded timeout (the reason
+* for disallowing the WatcherThread to issue an emergency dump).
* This "safety net" somewhat explains the aggressiveness in this attempt.
*
*/
-static bool prepare_for_emergency_dump(Thread* thread) {
+static void release_locks(Thread* thread) {
assert(thread != nullptr, "invariant");
- if (thread->is_Watcher_thread()) {
- // need WatcherThread as a safeguard against potential deadlocks
- return false;
- }
-
-#ifdef ASSERT
- Mutex* owned_lock = thread->owned_locks();
- while (owned_lock != nullptr) {
- Mutex* next = owned_lock->next();
- owned_lock->unlock();
- owned_lock = next;
- }
-#endif // ASSERT
+ assert(!thread->is_Java_thread() || JavaThread::cast(thread)->thread_state() == _thread_in_vm, "invariant");
if (Threads_lock->owned_by_self()) {
Threads_lock->unlock();
@@ -517,24 +509,18 @@ static bool prepare_for_emergency_dump(Thread* thread) {
if (JfrStacktrace_lock->owned_by_self()) {
JfrStacktrace_lock->unlock();
}
- return true;
-}
-
-static volatile int jfr_shutdown_lock = 0;
-
-static bool guard_reentrancy() {
- return Atomic::cmpxchg(&jfr_shutdown_lock, 0, 1) == 0;
}
class JavaThreadInVMAndNative : public StackObj {
private:
- JavaThread* const _jt;
+ JavaThread* _jt;
JavaThreadState _original_state;
public:
- JavaThreadInVMAndNative(Thread* t) : _jt(t->is_Java_thread() ? JavaThread::cast(t) : nullptr),
+ JavaThreadInVMAndNative(Thread* t) : _jt(nullptr),
_original_state(_thread_max_state) {
- if (_jt != nullptr) {
+ if (t != nullptr && t->is_Java_thread()) {
+ _jt = JavaThread::cast(t);
_original_state = _jt->thread_state();
if (_original_state != _thread_in_vm) {
_jt->set_thread_state(_thread_in_vm);
@@ -544,6 +530,7 @@ class JavaThreadInVMAndNative : public StackObj {
~JavaThreadInVMAndNative() {
if (_original_state != _thread_max_state) {
+ assert(_jt != nullptr, "invariant");
_jt->set_thread_state(_original_state);
}
}
@@ -556,35 +543,82 @@ class JavaThreadInVMAndNative : public StackObj {
}
};
-static void post_events(bool exception_handler, Thread* thread) {
+static void post_events(bool exception_handler, bool oom, Thread * thread) {
if (exception_handler) {
EventShutdown e;
- e.set_reason("VM Error");
+ e.set_reason(oom ? "CrashOnOutOfMemoryError" : "VM Error");
e.commit();
- } else {
- // OOM
- LeakProfiler::emit_events(max_jlong, false, false);
}
EventDumpReason event;
- event.set_reason(exception_handler ? "Crash" : "Out of Memory");
+ event.set_reason(exception_handler && oom ? "CrashOnOutOfMemoryError" : exception_handler ? "Crash" : "Out of Memory");
event.set_recordingId(-1);
event.commit();
}
-void JfrEmergencyDump::on_vm_shutdown(bool exception_handler) {
- if (!guard_reentrancy()) {
- return;
+static volatile traceid _jfr_shutdown_tid = 0;
+
+static bool guard_reentrancy() {
+ const traceid shutdown_tid = Atomic::load(&_jfr_shutdown_tid);
+ if (shutdown_tid == max_julong) {
+ // Someone tried but did not have a proper thread for the purpose.
+ return false;
}
- Thread* thread = Thread::current_or_null_safe();
- if (thread == nullptr) {
+ if (shutdown_tid == 0) {
+ Thread* const thread = Thread::current_or_null_safe();
+ const traceid tid = thread != nullptr ? JFR_JVM_THREAD_ID(thread) : max_julong;
+ if (Atomic::cmpxchg(&_jfr_shutdown_tid, shutdown_tid, tid) != shutdown_tid) {
+ JavaThreadInVMAndNative jtivm(thread);
+ if (thread != nullptr) {
+ release_locks(thread);
+ }
+ log_info(jfr, system)("A jfr emergency dump is already in progress, waiting for thread id " UINT64_FORMAT_X, Atomic::load(&_jfr_shutdown_tid));
+ // Transition to a safe safepoint state for the infinite sleep. A nop for non-java threads.
+ jtivm.transition_to_native();
+ os::infinite_sleep(); // stay here until we exit normally or crash.
+ ShouldNotReachHere();
+ }
+ return tid != max_julong;
+ }
+ // Recursive case
+ assert(JFR_JVM_THREAD_ID(Thread::current_or_null_safe()) == shutdown_tid, "invariant");
+ return false;
+}
+
+void JfrEmergencyDump::on_vm_shutdown(bool exception_handler, bool oom) {
+ if (!guard_reentrancy()) {
return;
}
+
+ Thread* const thread = Thread::current_or_null_safe();
+ assert(thread != nullptr, "invariant");
+
// Ensure a JavaThread is _thread_in_vm when we make this call
JavaThreadInVMAndNative jtivm(thread);
- if (!prepare_for_emergency_dump(thread)) {
+ post_events(exception_handler, oom, thread);
+
+ if (thread->is_Watcher_thread()) {
+ // We cannot attempt an emergency dump using the Watcher thread
+ // because we rely on the WatcherThread task "is_error_reported()",
+ // to exit the VM after a hardcoded timeout, should the relatively
+ // risky operation of an emergency dump fail (deadlock, livelock).
+ log_warning(jfr, system)
+ ("The Watcher thread crashed so no jfr emergency dump will be generated.");
return;
}
- post_events(exception_handler, thread);
+
+ if (thread->is_VM_thread()) {
+ const VM_Operation* const operation = VMThread::vm_operation();
+ if (operation != nullptr && operation->type() == VM_Operation::VMOp_JFROldObject) {
+ // We will not be able to issue a rotation because the rotation lock
+ // is held by the JFR Recorder Thread that issued the VM_Operation.
+ log_warning(jfr, system)
+ ("The VM Thread crashed as part of emitting leak profiler events so no jfr emergency dump will be generated.");
+ return;
+ }
+ }
+
+ release_locks(thread);
+
// if JavaThread, transition to _thread_in_native to issue a final flushpoint
NoHandleMark nhm;
jtivm.transition_to_native();
diff --git a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp
index 7db4b511746..b337d73364a 100644
--- a/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp
+++ b/src/hotspot/share/jfr/recorder/repository/jfrEmergencyDump.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -39,7 +39,7 @@ class JfrEmergencyDump : AllStatic {
static const char* chunk_path(const char* repository_path);
static void on_vm_error(const char* repository_path);
static void on_vm_error_report(outputStream* st, const char* repository_path);
- static void on_vm_shutdown(bool exception_handler);
+ static void on_vm_shutdown(bool exception_handler, bool oom);
};
#endif // SHARE_JFR_RECORDER_REPOSITORY_JFREMERGENCYDUMP_HPP
diff --git a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp
index b1bddfff466..0c4874e5c04 100644
--- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp
+++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,7 +34,8 @@
(MSGBIT(MSG_START)) | \
(MSGBIT(MSG_CLONE_IN_MEMORY)) | \
(MSGBIT(MSG_VM_ERROR)) | \
- (MSGBIT(MSG_FLUSHPOINT)) \
+ (MSGBIT(MSG_FLUSHPOINT)) | \
+ (MSGBIT(MSG_EMIT_LEAKP_REFCHAINS)) \
)
static JfrPostBox* _instance = nullptr;
@@ -165,7 +166,7 @@ void JfrPostBox::notify_waiters() {
assert(JfrMsg_lock->owned_by_self(), "incrementing _msg_handled_serial is protected by JfrMsg_lock.");
// Update made visible on release of JfrMsg_lock via fence instruction in Monitor::IUnlock.
++_msg_handled_serial;
- JfrMsg_lock->notify();
+ JfrMsg_lock->notify_all();
}
// safeguard to ensure no threads are left waiting
diff --git a/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp b/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp
index 10457261643..92f70b1dc9b 100644
--- a/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp
+++ b/src/hotspot/share/jfr/recorder/service/jfrPostBox.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -43,6 +43,7 @@ enum JFR_Msg {
MSG_SHUTDOWN,
MSG_VM_ERROR,
MSG_FLUSHPOINT,
+ MSG_EMIT_LEAKP_REFCHAINS,
MSG_NO_OF_MSGS
};
@@ -51,23 +52,25 @@ enum JFR_Msg {
*
* Synchronous messages (posting thread waits for message completion):
*
- * MSG_CLONE_IN_MEMORY (0) ; MSGBIT(MSG_CLONE_IN_MEMORY) == (1 << 0) == 0x1
- * MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2
- * MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4
- * MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8
- * MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100
- * MSG_FLUSHPOINT (9) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0x9) == 0x200
+ * MSG_CLONE_IN_MEMORY (0) ; MSGBIT(MSG_CLONE_IN_MEMORY) == (1 << 0) == 0x1
+ * MSG_START(1) ; MSGBIT(MSG_START) == (1 << 0x1) == 0x2
+ * MSG_STOP (2) ; MSGBIT(MSG_STOP) == (1 << 0x2) == 0x4
+ * MSG_ROTATE (3) ; MSGBIT(MSG_ROTATE) == (1 << 0x3) == 0x8
+ * MSG_VM_ERROR (8) ; MSGBIT(MSG_VM_ERROR) == (1 << 0x8) == 0x100
+ * MSG_FLUSHPOINT (9) ; MSGBIT(MSG_FLUSHPOINT) == (1 << 0x9) == 0x200
+ * MSG_EMIT_LEAKP_REFCHAINS (10); MSGBIT(MSG_EMIT_LEAKP_REFCHAINS) == (1 << 0xa) == 0x400
*
* Asynchronous messages (posting thread returns immediately upon deposit):
*
- * MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10
- * MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20
- * MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40
- * MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80
+ * MSG_FULLBUFFER (4) ; MSGBIT(MSG_FULLBUFFER) == (1 << 0x4) == 0x10
+ * MSG_CHECKPOINT (5) ; MSGBIT(CHECKPOINT) == (1 << 0x5) == 0x20
+ * MSG_WAKEUP (6) ; MSGBIT(WAKEUP) == (1 << 0x6) == 0x40
+ * MSG_SHUTDOWN (7) ; MSGBIT(MSG_SHUTDOWN) == (1 << 0x7) == 0x80
*/
class JfrPostBox : public JfrCHeapObj {
friend class JfrRecorder;
+ friend class JfrRecorderService;
public:
void post(JFR_Msg msg);
diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp
index f0170bac460..a136f8f1476 100644
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -54,6 +54,7 @@
#include "runtime/safepoint.hpp"
#include "runtime/vmOperations.hpp"
#include "runtime/vmThread.hpp"
+#include "utilities/growableArray.hpp"
// incremented on each flushpoint
static u8 flushpoint_id = 0;
@@ -413,6 +414,7 @@ class JfrSafepointWriteVMOperation : public VM_Operation {
JfrRecorderService::JfrRecorderService() :
_checkpoint_manager(JfrCheckpointManager::instance()),
_chunkwriter(JfrRepository::chunkwriter()),
+ _post_box(JfrPostBox::instance()),
_repository(JfrRepository::instance()),
_stack_trace_repository(JfrStackTraceRepository::instance()),
_storage(JfrStorage::instance()),
@@ -531,6 +533,7 @@ void JfrRecorderService::rotate(int msgs) {
return;
}
if (msgs & MSGBIT(MSG_VM_ERROR)) {
+ stop();
vm_error_rotation();
return;
}
@@ -690,17 +693,173 @@ void JfrRecorderService::evaluate_chunk_size_for_rotation() {
JfrChunkRotation::evaluate(_chunkwriter);
}
-void JfrRecorderService::emit_leakprofiler_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs) {
- DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(JavaThread::current()));
- // Take the rotation lock to exclude flush() during event emits. This is because event emit
- // also creates a number checkpoint events. Those checkpoint events require a future typeset checkpoint
- // event for completeness, i.e. to be generated before being flushed to a segment.
+// LeakProfiler event serialization support.
+
+struct JfrLeakProfilerEmitRequest {
+ int64_t cutoff_ticks;
+ bool emit_all;
+ bool skip_bfs;
+ bool oom;
+};
+
+typedef GrowableArrayCHeap JfrLeakProfilerEmitRequestQueue;
+static JfrLeakProfilerEmitRequestQueue* _queue = nullptr;
+constexpr const static int64_t _no_path_to_gc_roots = 0;
+static bool _oom_emit_request_posted = false;
+static bool _oom_emit_request_delivered = false;
+
+static inline bool exclude_paths_to_gc_roots(int64_t cutoff_ticks) {
+ return cutoff_ticks <= _no_path_to_gc_roots;
+}
+
+static void enqueue(const JfrLeakProfilerEmitRequest& request) {
+ assert(JfrRotationLock::is_owner(), "invariant");
+ if (_queue == nullptr) {
+ _queue = new JfrLeakProfilerEmitRequestQueue(4);
+ }
+ assert(_queue != nullptr, "invariant");
+ assert(!_oom_emit_request_posted, "invariant");
+ if (request.oom) {
+ _oom_emit_request_posted = true;
+ }
+ _queue->append(request);
+}
+
+static JfrLeakProfilerEmitRequest dequeue() {
+ assert(JfrRotationLock::is_owner(), "invariant");
+ assert(_queue != nullptr, "invariant");
+ assert(_queue->is_nonempty(), "invariant");
+ const JfrLeakProfilerEmitRequest& request = _queue->first();
+ _queue->remove_at(0);
+ return request;
+}
+
+// This version of emit excludes path-to-gc-roots, i.e. it skips reference chains.
+static void emit_leakprofiler_events(bool emit_all, bool skip_bfs, JavaThread* jt) {
+ assert(jt != nullptr, "invariant");
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
+ // Take the rotation lock to exclude flush() during event emits. This is because the event emit operation
+ // also creates a number of checkpoint events. Those checkpoint events require a future typeset checkpoint
+ // event for completeness, i.e., to be generated before being flushed to a segment.
// The upcoming flush() or rotation() after event emit completes this typeset checkpoint
- // and serializes all event emit checkpoint events to the same segment.
+ // and serializes all checkpoint events to the same segment.
JfrRotationLock lock;
+ // Take the rotation lock before the thread transition, to avoid blocking safepoints.
+ if (_oom_emit_request_posted) {
+ // A request to emit leakprofiler events in response to CrashOnOutOfMemoryError
+ // is pending or has already been completed. We are about to crash at any time now.
+ assert(CrashOnOutOfMemoryError, "invariant");
+ return;
+ }
+ MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt));
+ ThreadInVMfromNative transition(jt);
+ // Since we are not requesting path-to-gc-roots, i.e., reference chains, we need not issue a VM_Operation.
+ // Therefore, we can let the requesting thread process the request directly, since it already holds the requisite lock.
+ LeakProfiler::emit_events(_no_path_to_gc_roots, emit_all, skip_bfs);
+}
+
+void JfrRecorderService::transition_and_post_leakprofiler_emit_msg(JavaThread* jt) {
+ assert(jt != nullptr, "invariant");
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);)
+ assert(!JfrRotationLock::is_owner(), "invariant");
+ // Transition to _thread_in_VM and post a synchronous message to the JFR Recorder Thread
+ // for it to process our enqueued request, which includes paths-to-gc-roots, i.e., reference chains.
+ MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt));
+ ThreadInVMfromNative transition(jt);
+ _post_box.post(MSG_EMIT_LEAKP_REFCHAINS);
+}
+
+// This version of emit includes path-to-gc-roots, i.e., it includes in the request traversing of reference chains.
+// Traversing reference chains is performed as part of a VM_Operation, and we initiate it from the JFR Recorder Thread.
+// Because multiple threads can concurrently report_on_java_out_of_memory(), having them all post a synchronous JFR msg,
+// they rendezvous at a safepoint in a convenient state, ThreadBlockInVM. This mechanism prevents any thread from racing past
+// this point and begin executing VMError::report_and_die(), until at least one oom request has been delivered.
+void JfrRecorderService::emit_leakprofiler_events_paths_to_gc_roots(int64_t cutoff_ticks,
+ bool emit_all,
+ bool skip_bfs,
+ bool oom,
+ JavaThread* jt) {
+ assert(jt != nullptr, "invariant");
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);)
+ assert(!exclude_paths_to_gc_roots(cutoff_ticks), "invariant");
+
+ {
+ JfrRotationLock lock;
+ // Take the rotation lock to read and post a request for the JFR Recorder Thread.
+ if (_oom_emit_request_posted) {
+ if (!oom) {
+ // A request to emit leakprofiler events in response to CrashOnOutOfMemoryError
+ // is pending or has already been completed. We are about to crash at any time now.
+ assert(CrashOnOutOfMemoryError, "invariant");
+ return;
+ }
+ } else {
+ assert(!_oom_emit_request_posted, "invariant");
+ JfrLeakProfilerEmitRequest request = { cutoff_ticks, emit_all, skip_bfs, oom };
+ enqueue(request);
+ }
+ }
+ JfrRecorderService service;
+ service.transition_and_post_leakprofiler_emit_msg(jt);
+}
+
+// Leakprofiler serialization request, the jdk.jfr.internal.JVM.emitOldObjectSamples() Java entry point.
+void JfrRecorderService::emit_leakprofiler_events(int64_t cutoff_ticks,
+ bool emit_all,
+ bool skip_bfs) {
+ JavaThread* const jt = JavaThread::current();
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt);)
+ if (exclude_paths_to_gc_roots(cutoff_ticks)) {
+ ::emit_leakprofiler_events(emit_all, skip_bfs, jt);
+ return;
+ }
+ emit_leakprofiler_events_paths_to_gc_roots(cutoff_ticks, emit_all, skip_bfs, /* oom */ false, jt);
+}
+
+// Leakprofiler serialization request, the report_on_java_out_of_memory VM entry point.
+void JfrRecorderService::emit_leakprofiler_events_on_oom() {
+ assert(CrashOnOutOfMemoryError, "invariant");
+ if (EventOldObjectSample::is_enabled()) {
+ JavaThread* const jt = JavaThread::current();
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt);)
+ ThreadToNativeFromVM transition(jt);
+ emit_leakprofiler_events_paths_to_gc_roots(max_jlong, false, false, /* oom */ true, jt);
+ }
+}
+
+// The worker routine for the JFR Recorder Thread when processing MSG_EMIT_LEAKP_REFCHAINS messages.
+void JfrRecorderService::emit_leakprofiler_events() {
+ JavaThread* const jt = JavaThread::current();
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
// Take the rotation lock before the transition.
- JavaThread* current_thread = JavaThread::current();
- MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, current_thread));
- ThreadInVMfromNative transition(current_thread);
- LeakProfiler::emit_events(cutoff_ticks, emit_all, skip_bfs);
+ JfrRotationLock lock;
+ if (_oom_emit_request_delivered) {
+ // A request to emit leakprofiler events in response to CrashOnOutOfMemoryError
+ // has already been completed. We are about to crash at any time now.
+ assert(_oom_emit_request_posted, "invariant");
+ assert(CrashOnOutOfMemoryError, "invariant");
+ return;
+ }
+
+ assert(_queue->is_nonempty(), "invariant");
+
+ {
+ MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, jt));
+ ThreadInVMfromNative transition(jt);
+ while (_queue->is_nonempty()) {
+ const JfrLeakProfilerEmitRequest& request = dequeue();
+ LeakProfiler::emit_events(request.cutoff_ticks, request.emit_all, request.skip_bfs);
+ if (_oom_emit_request_posted && request.oom) {
+ assert(CrashOnOutOfMemoryError, "invariant");
+ _oom_emit_request_delivered = true;
+ break;
+ }
+ }
+ }
+
+ // If processing involved an out-of-memory request, issue an immediate flush operation.
+ DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
+ if (_chunkwriter.is_valid() && _oom_emit_request_delivered) {
+ invoke_flush();
+ }
}
diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp
index e5b4500afc0..3759ff98828 100644
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderService.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,19 +27,23 @@
#include "jfr/utilities/jfrAllocation.hpp"
+class JavaThread;
class JfrCheckpointManager;
class JfrChunkWriter;
+class JfrPostBox;
class JfrRepository;
class JfrStackTraceRepository;
class JfrStorage;
class JfrStringPool;
class JfrRecorderService : public StackObj {
+ friend class Jfr;
friend class JfrSafepointClearVMOperation;
friend class JfrSafepointWriteVMOperation;
private:
JfrCheckpointManager& _checkpoint_manager;
JfrChunkWriter& _chunkwriter;
+ JfrPostBox& _post_box;
JfrRepository& _repository;
JfrStackTraceRepository& _stack_trace_repository;
JfrStorage& _storage;
@@ -64,6 +68,14 @@ class JfrRecorderService : public StackObj {
void invoke_safepoint_write();
void post_safepoint_write();
+ void transition_and_post_leakprofiler_emit_msg(JavaThread* jt);
+
+ static void emit_leakprofiler_events_on_oom();
+ static void emit_leakprofiler_events_paths_to_gc_roots(int64_t cutoff_ticks,
+ bool emit_all,
+ bool skip_bfs,
+ bool oom,
+ JavaThread* jt);
public:
JfrRecorderService();
void start();
@@ -72,8 +84,12 @@ class JfrRecorderService : public StackObj {
void flushpoint();
void process_full_buffers();
void evaluate_chunk_size_for_rotation();
- void emit_leakprofiler_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs);
+ void emit_leakprofiler_events();
+
static bool is_recording();
+ static void emit_leakprofiler_events(int64_t cutoff_ticks,
+ bool emit_all,
+ bool skip_bfs);
};
#endif // SHARE_JFR_RECORDER_SERVICE_JFRRECORDERSERVICE_HPP
diff --git a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp
index 8aeb745b35e..c63cb37e367 100644
--- a/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp
+++ b/src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -45,6 +45,7 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
#define ROTATE (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)))
#define FLUSHPOINT (msgs & (MSGBIT(MSG_FLUSHPOINT)))
#define PROCESS_FULL_BUFFERS (msgs & (MSGBIT(MSG_ROTATE)|MSGBIT(MSG_STOP)|MSGBIT(MSG_FULLBUFFER)))
+ #define LEAKPROFILER_REFCHAINS (msgs & MSGBIT(MSG_EMIT_LEAKP_REFCHAINS))
JfrPostBox& post_box = JfrRecorderThreadEntry::post_box();
log_debug(jfr, system)("Recorder thread STARTED");
@@ -71,6 +72,9 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
if (PROCESS_FULL_BUFFERS) {
service.process_full_buffers();
}
+ if (LEAKPROFILER_REFCHAINS) {
+ service.emit_leakprofiler_events();
+ }
// Check amount of data written to chunk already
// if it warrants asking for a new chunk.
service.evaluate_chunk_size_for_rotation();
@@ -99,5 +103,5 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
#undef ROTATE
#undef FLUSHPOINT
#undef PROCESS_FULL_BUFFERS
- #undef SCAVENGE
+ #undef LEAKPROFILER_REFCHAINS
}
diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp
index 291169b9aa7..037faee1b9f 100644
--- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp
+++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp
@@ -656,7 +656,7 @@ JfrCPUTimeTraceQueue& JfrThreadLocal::cpu_time_jfr_queue() {
}
void JfrThreadLocal::deallocate_cpu_time_jfr_queue() {
- cpu_time_jfr_queue().resize(0);
+ cpu_time_jfr_queue().set_capacity(0);
}
void JfrThreadLocal::set_do_async_processing_of_cpu_time_jfr_requests(bool wants) {
diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
index d45036ced94..22ced5a4411 100644
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
@@ -27,6 +27,7 @@
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmClasses.hpp"
+#include "code/nmethod.hpp"
#include "code/scopeDesc.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compilerEvent.hpp"
@@ -1206,7 +1207,7 @@ C2V_VMENTRY_0(jint, installCode0, (JNIEnv *env, jobject,
assert(JVMCIENV->isa_HotSpotNmethod(installed_code_handle), "wrong type");
// Clear the link to an old nmethod first
JVMCIObject nmethod_mirror = installed_code_handle;
- JVMCIENV->invalidate_nmethod_mirror(nmethod_mirror, true, JVMCI_CHECK_0);
+ JVMCIENV->invalidate_nmethod_mirror(nmethod_mirror, true, nmethod::InvalidationReason::JVMCI_REPLACED_WITH_NEW_CODE, JVMCI_CHECK_0);
} else {
assert(JVMCIENV->isa_InstalledCode(installed_code_handle), "wrong type");
}
@@ -1217,6 +1218,14 @@ C2V_VMENTRY_0(jint, installCode0, (JNIEnv *env, jobject,
return result;
C2V_END
+C2V_VMENTRY_0(jobject, getInvalidationReasonDescription, (JNIEnv *env, jobject, jint invalidation_reason))
+ HandleMark hm(THREAD);
+ JNIHandleMark jni_hm(thread);
+ nmethod::InvalidationReason reason = static_cast(invalidation_reason);
+ JVMCIObject desc = JVMCIENV->create_string(nmethod::invalidation_reason_to_string(reason), JVMCI_CHECK_NULL);
+ return JVMCIENV->get_jobject(desc);
+C2V_END
+
C2V_VMENTRY(void, resetCompilationStatistics, (JNIEnv* env, jobject))
JVMCICompiler* compiler = JVMCICompiler::instance(true, CHECK);
CompilerStatistics* stats = compiler->stats();
@@ -1382,7 +1391,7 @@ C2V_VMENTRY(void, reprofile, (JNIEnv* env, jobject, ARGUMENT_PAIR(method)))
nmethod* code = method->code();
if (code != nullptr) {
- code->make_not_entrant("JVMCI reprofile");
+ code->make_not_entrant(nmethod::InvalidationReason::JVMCI_REPROFILE);
}
MethodData* method_data = method->method_data();
@@ -1395,9 +1404,14 @@ C2V_VMENTRY(void, reprofile, (JNIEnv* env, jobject, ARGUMENT_PAIR(method)))
C2V_END
-C2V_VMENTRY(void, invalidateHotSpotNmethod, (JNIEnv* env, jobject, jobject hs_nmethod, jboolean deoptimize))
+C2V_VMENTRY(void, invalidateHotSpotNmethod, (JNIEnv* env, jobject, jobject hs_nmethod, jboolean deoptimize, jint invalidation_reason))
+ int first = static_cast(nmethod::InvalidationReason::C1_CODEPATCH);
+ int last = static_cast(nmethod::InvalidationReason::INVALIDATION_REASONS_COUNT);
+ if (invalidation_reason < first || invalidation_reason >= last) {
+ JVMCI_THROW_MSG(IllegalArgumentException, err_msg("Invalid invalidation_reason: %d", invalidation_reason));
+ }
JVMCIObject nmethod_mirror = JVMCIENV->wrap(hs_nmethod);
- JVMCIENV->invalidate_nmethod_mirror(nmethod_mirror, deoptimize, JVMCI_CHECK);
+ JVMCIENV->invalidate_nmethod_mirror(nmethod_mirror, deoptimize, static_cast(invalidation_reason), JVMCI_CHECK);
C2V_END
C2V_VMENTRY_NULL(jlongArray, collectCounters, (JNIEnv* env, jobject))
@@ -1822,7 +1836,7 @@ C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv* env, jobject, jobject _hs_
if (!fst.current()->is_compiled_frame()) {
JVMCI_THROW_MSG(IllegalStateException, "compiled stack frame expected");
}
- fst.current()->cb()->as_nmethod()->make_not_entrant("JVMCI materialize virtual objects");
+ fst.current()->cb()->as_nmethod()->make_not_entrant(nmethod::InvalidationReason::JVMCI_MATERIALIZE_VIRTUAL_OBJECT);
}
Deoptimization::deoptimize(thread, *fst.current(), Deoptimization::Reason_none);
// look for the frame again as it has been updated by deopt (pc, deopt state...)
@@ -3351,6 +3365,7 @@ JNINativeMethod CompilerToVM::methods[] = {
{CC "getResolvedJavaType0", CC "(Ljava/lang/Object;JZ)" HS_KLASS, FN_PTR(getResolvedJavaType0)},
{CC "readConfiguration", CC "()[" OBJECT, FN_PTR(readConfiguration)},
{CC "installCode0", CC "(JJZ" HS_COMPILED_CODE "[" OBJECT INSTALLED_CODE "J[B)I", FN_PTR(installCode0)},
+ {CC "getInvalidationReasonDescription", CC "(I)" STRING, FN_PTR(getInvalidationReasonDescription)},
{CC "getInstallCodeFlags", CC "()I", FN_PTR(getInstallCodeFlags)},
{CC "resetCompilationStatistics", CC "()V", FN_PTR(resetCompilationStatistics)},
{CC "disassembleCodeBlob", CC "(" INSTALLED_CODE ")" STRING, FN_PTR(disassembleCodeBlob)},
@@ -3359,7 +3374,7 @@ JNINativeMethod CompilerToVM::methods[] = {
{CC "getLocalVariableTableStart", CC "(" HS_METHOD2 ")J", FN_PTR(getLocalVariableTableStart)},
{CC "getLocalVariableTableLength", CC "(" HS_METHOD2 ")I", FN_PTR(getLocalVariableTableLength)},
{CC "reprofile", CC "(" HS_METHOD2 ")V", FN_PTR(reprofile)},
- {CC "invalidateHotSpotNmethod", CC "(" HS_NMETHOD "Z)V", FN_PTR(invalidateHotSpotNmethod)},
+ {CC "invalidateHotSpotNmethod", CC "(" HS_NMETHOD "ZI)V", FN_PTR(invalidateHotSpotNmethod)},
{CC "collectCounters", CC "()[J", FN_PTR(collectCounters)},
{CC "getCountersSize", CC "()I", FN_PTR(getCountersSize)},
{CC "setCountersSize", CC "(I)Z", FN_PTR(setCountersSize)},
diff --git a/src/hotspot/share/jvmci/jvmciEnv.cpp b/src/hotspot/share/jvmci/jvmciEnv.cpp
index b8474552256..fd2b1dbd6b6 100644
--- a/src/hotspot/share/jvmci/jvmciEnv.cpp
+++ b/src/hotspot/share/jvmci/jvmciEnv.cpp
@@ -1750,7 +1750,7 @@ void JVMCIEnv::initialize_installed_code(JVMCIObject installed_code, CodeBlob* c
}
-void JVMCIEnv::invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimize, JVMCI_TRAPS) {
+void JVMCIEnv::invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimize, nmethod::InvalidationReason invalidation_reason, JVMCI_TRAPS) {
if (mirror.is_null()) {
JVMCI_THROW(NullPointerException);
}
@@ -1773,7 +1773,7 @@ void JVMCIEnv::invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimize, JV
if (!deoptimize) {
// Prevent future executions of the nmethod but let current executions complete.
- nm->make_not_entrant("JVMCI invalidate nmethod mirror");
+ nm->make_not_entrant(invalidation_reason);
// Do not clear the address field here as the Java code may still
// want to later call this method with deoptimize == true. That requires
@@ -1782,7 +1782,7 @@ void JVMCIEnv::invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimize, JV
// Deoptimize the nmethod immediately.
DeoptimizationScope deopt_scope;
deopt_scope.mark(nm);
- nm->make_not_entrant("JVMCI invalidate nmethod mirror");
+ nm->make_not_entrant(invalidation_reason);
nm->make_deoptimized();
deopt_scope.deoptimize_marked();
diff --git a/src/hotspot/share/jvmci/jvmciEnv.hpp b/src/hotspot/share/jvmci/jvmciEnv.hpp
index 69f6647b0d6..b49bba88b6b 100644
--- a/src/hotspot/share/jvmci/jvmciEnv.hpp
+++ b/src/hotspot/share/jvmci/jvmciEnv.hpp
@@ -462,7 +462,7 @@ class JVMCIEnv : public ResourceObj {
// field of `mirror` to prevent it from being called.
// If `deoptimize` is true, the nmethod is immediately deoptimized.
// The HotSpotNmethod.address field is zero upon returning.
- void invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimze, JVMCI_TRAPS);
+ void invalidate_nmethod_mirror(JVMCIObject mirror, bool deoptimze, nmethod::InvalidationReason invalidation_reason, JVMCI_TRAPS);
void initialize_installed_code(JVMCIObject installed_code, CodeBlob* cb, JVMCI_TRAPS);
diff --git a/src/hotspot/share/jvmci/jvmciJavaClasses.hpp b/src/hotspot/share/jvmci/jvmciJavaClasses.hpp
index d5fcd2aaaba..432fefe56d1 100644
--- a/src/hotspot/share/jvmci/jvmciJavaClasses.hpp
+++ b/src/hotspot/share/jvmci/jvmciJavaClasses.hpp
@@ -102,6 +102,7 @@
boolean_field(HotSpotNmethod, isDefault) \
long_field(HotSpotNmethod, compileIdSnapshot) \
object_field(HotSpotNmethod, method, "Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl;") \
+ int_field(HotSpotNmethod, invalidationReason) \
jvmci_constructor(HotSpotNmethod, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethodImpl;Ljava/lang/String;ZJ)V") \
end_class \
start_class(HotSpotCompiledCode, jdk_vm_ci_hotspot_HotSpotCompiledCode) \
diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp
index bee5f4ea445..24ea4936822 100644
--- a/src/hotspot/share/jvmci/jvmciRuntime.cpp
+++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp
@@ -797,7 +797,7 @@ void JVMCINMethodData::set_nmethod_mirror(nmethod* nm, oop new_mirror) {
Universe::heap()->register_nmethod(nm);
}
-void JVMCINMethodData::invalidate_nmethod_mirror(nmethod* nm) {
+void JVMCINMethodData::invalidate_nmethod_mirror(nmethod* nm, nmethod::InvalidationReason invalidation_reason) {
oop nmethod_mirror = get_nmethod_mirror(nm);
if (nmethod_mirror == nullptr) {
return;
@@ -816,12 +816,20 @@ void JVMCINMethodData::invalidate_nmethod_mirror(nmethod* nm) {
HotSpotJVMCI::InstalledCode::set_address(jvmciEnv, nmethod_mirror, 0);
HotSpotJVMCI::InstalledCode::set_entryPoint(jvmciEnv, nmethod_mirror, 0);
HotSpotJVMCI::HotSpotInstalledCode::set_codeStart(jvmciEnv, nmethod_mirror, 0);
+ if (HotSpotJVMCI::HotSpotNmethod::invalidationReason(jvmciEnv, nmethod_mirror) ==
+ static_cast(nmethod::InvalidationReason::NOT_INVALIDATED)) {
+ HotSpotJVMCI::HotSpotNmethod::set_invalidationReason(jvmciEnv, nmethod_mirror, static_cast(invalidation_reason));
+ }
} else if (nm->is_not_entrant()) {
// Zero the entry point so any new invocation will fail but keep
// the address link around that so that existing activations can
// be deoptimized via the mirror (i.e. JVMCIEnv::invalidate_installed_code).
HotSpotJVMCI::InstalledCode::set_entryPoint(jvmciEnv, nmethod_mirror, 0);
HotSpotJVMCI::HotSpotInstalledCode::set_codeStart(jvmciEnv, nmethod_mirror, 0);
+ if (HotSpotJVMCI::HotSpotNmethod::invalidationReason(jvmciEnv, nmethod_mirror) ==
+ static_cast(nmethod::InvalidationReason::NOT_INVALIDATED)) {
+ HotSpotJVMCI::HotSpotNmethod::set_invalidationReason(jvmciEnv, nmethod_mirror, static_cast(invalidation_reason));
+ }
}
}
@@ -2184,7 +2192,7 @@ JVMCI::CodeInstallResult JVMCIRuntime::register_method(JVMCIEnv* JVMCIENV,
tty->print_cr("Replacing method %s", method_name);
}
if (old != nullptr) {
- old->make_not_entrant("JVMCI register method");
+ old->make_not_entrant(nmethod::InvalidationReason::JVMCI_REPLACED_WITH_NEW_CODE);
}
LogTarget(Info, nmethod, install) lt;
diff --git a/src/hotspot/share/jvmci/jvmciRuntime.hpp b/src/hotspot/share/jvmci/jvmciRuntime.hpp
index b49e09a1884..95c7d32f928 100644
--- a/src/hotspot/share/jvmci/jvmciRuntime.hpp
+++ b/src/hotspot/share/jvmci/jvmciRuntime.hpp
@@ -121,7 +121,7 @@ class JVMCINMethodData : public ResourceObj {
// Clears the HotSpotNmethod.address field in the mirror. If nm
// is dead, the HotSpotNmethod.entryPoint field is also cleared.
- void invalidate_nmethod_mirror(nmethod* nm);
+ void invalidate_nmethod_mirror(nmethod* nm, nmethod::InvalidationReason invalidation_reason);
// Gets the mirror from nm's oops table.
oop get_nmethod_mirror(nmethod* nm);
diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
index e26c815946d..eb2843008fd 100644
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
@@ -564,7 +564,30 @@
declare_constant_with_value("LockStack::_end_offset", LockStack::end_offset()) \
declare_constant_with_value("OMCache::oop_to_oop_difference", OMCache::oop_to_oop_difference()) \
declare_constant_with_value("OMCache::oop_to_monitor_difference", OMCache::oop_to_monitor_difference()) \
- \
+ \
+ declare_constant(nmethod::InvalidationReason::NOT_INVALIDATED) \
+ declare_constant(nmethod::InvalidationReason::C1_CODEPATCH) \
+ declare_constant(nmethod::InvalidationReason::C1_DEOPTIMIZE) \
+ declare_constant(nmethod::InvalidationReason::C1_DEOPTIMIZE_FOR_PATCHING) \
+ declare_constant(nmethod::InvalidationReason::C1_PREDICATE_FAILED_TRAP) \
+ declare_constant(nmethod::InvalidationReason::CI_REPLAY) \
+ declare_constant(nmethod::InvalidationReason::UNLOADING) \
+ declare_constant(nmethod::InvalidationReason::UNLOADING_COLD) \
+ declare_constant(nmethod::InvalidationReason::JVMCI_INVALIDATE) \
+ declare_constant(nmethod::InvalidationReason::JVMCI_MATERIALIZE_VIRTUAL_OBJECT) \
+ declare_constant(nmethod::InvalidationReason::JVMCI_REPLACED_WITH_NEW_CODE) \
+ declare_constant(nmethod::InvalidationReason::JVMCI_REPROFILE) \
+ declare_constant(nmethod::InvalidationReason::MARKED_FOR_DEOPTIMIZATION) \
+ declare_constant(nmethod::InvalidationReason::MISSING_EXCEPTION_HANDLER) \
+ declare_constant(nmethod::InvalidationReason::NOT_USED) \
+ declare_constant(nmethod::InvalidationReason::OSR_INVALIDATION_BACK_BRANCH) \
+ declare_constant(nmethod::InvalidationReason::OSR_INVALIDATION_FOR_COMPILING_WITH_C1) \
+ declare_constant(nmethod::InvalidationReason::OSR_INVALIDATION_OF_LOWER_LEVEL) \
+ declare_constant(nmethod::InvalidationReason::SET_NATIVE_FUNCTION) \
+ declare_constant(nmethod::InvalidationReason::UNCOMMON_TRAP) \
+ declare_constant(nmethod::InvalidationReason::WHITEBOX_DEOPTIMIZATION) \
+ declare_constant(nmethod::InvalidationReason::ZOMBIE) \
+ \
declare_constant(CodeInstaller::VERIFIED_ENTRY) \
declare_constant(CodeInstaller::UNVERIFIED_ENTRY) \
declare_constant(CodeInstaller::OSR_ENTRY) \
@@ -809,7 +832,6 @@
\
AARCH64_ONLY(declare_constant(NMethodPatchingType::stw_instruction_and_data_patch)) \
AARCH64_ONLY(declare_constant(NMethodPatchingType::conc_instruction_and_data_patch)) \
- AARCH64_ONLY(declare_constant(NMethodPatchingType::conc_data_patch)) \
\
declare_constant(ObjectMonitor::NO_OWNER) \
declare_constant(ObjectMonitor::ANONYMOUS_OWNER) \
diff --git a/src/hotspot/share/logging/logAsyncWriter.cpp b/src/hotspot/share/logging/logAsyncWriter.cpp
index cfb6a991c4c..d184827f582 100644
--- a/src/hotspot/share/logging/logAsyncWriter.cpp
+++ b/src/hotspot/share/logging/logAsyncWriter.cpp
@@ -318,13 +318,13 @@ void AsyncLogWriter::initialize() {
AsyncLogWriter* self = new AsyncLogWriter();
if (self->_initialized) {
- Atomic::release_store_fence(&AsyncLogWriter::_instance, self);
- // All readers of _instance after the fence see non-null.
// We use LogOutputList's RCU counters to ensure all synchronous logsites have completed.
- // After that, we start AsyncLog Thread and it exclusively takes over all logging I/O.
+ // After that, we publish the initalized _instance to readers.
+ // Then we start the AsyncLog Thread and it exclusively takes over all logging I/O.
for (LogTagSet* ts = LogTagSet::first(); ts != nullptr; ts = ts->next()) {
ts->wait_until_no_readers();
}
+ Atomic::release_store_fence(&AsyncLogWriter::_instance, self);
os::start_thread(self);
log_debug(logging, thread)("Async logging thread started.");
} else {
diff --git a/src/hotspot/share/memory/allocation.cpp b/src/hotspot/share/memory/allocation.cpp
index 0d99c1bea68..9941fdb0cfc 100644
--- a/src/hotspot/share/memory/allocation.cpp
+++ b/src/hotspot/share/memory/allocation.cpp
@@ -74,7 +74,7 @@ void* MetaspaceObj::operator new(size_t size, ClassLoaderData* loader_data,
MetaspaceObj::Type type, TRAPS) throw() {
// Klass has its own operator new
assert(type != ClassType, "class has its own operator new");
- return Metaspace::allocate(loader_data, word_size, type, /*use_class_space*/ false, THREAD);
+ return Metaspace::allocate(loader_data, word_size, type, THREAD);
}
void* MetaspaceObj::operator new(size_t size, ClassLoaderData* loader_data,
@@ -82,7 +82,7 @@ void* MetaspaceObj::operator new(size_t size, ClassLoaderData* loader_data,
MetaspaceObj::Type type) throw() {
assert(!Thread::current()->is_Java_thread(), "only allowed by non-Java thread");
assert(type != ClassType, "class has its own operator new");
- return Metaspace::allocate(loader_data, word_size, type, /*use_class_space*/ false);
+ return Metaspace::allocate(loader_data, word_size, type);
}
// This is used for allocating training data. We are allocating training data in many cases where a GC cannot be triggered.
diff --git a/src/hotspot/share/memory/guardedMemory.hpp b/src/hotspot/share/memory/guardedMemory.hpp
index a3bbf48b0cd..2b6d34e8e0a 100644
--- a/src/hotspot/share/memory/guardedMemory.hpp
+++ b/src/hotspot/share/memory/guardedMemory.hpp
@@ -42,9 +42,10 @@
* |Offset | Content | Description |
* |------------------------------------------------------------
* |base_addr | 0xABABABABABABABAB | Head guard |
- * |+16 | | User data size |
- * |+sizeof(uintptr_t) | | Tag word |
- * |+sizeof(uintptr_t) | | Tag word |
+ * |+GUARD_SIZE | | User data size |
+ * |+sizeof(size_t) | | Tag word |
+ * |+sizeof(void*) | | Tag word |
+ * |+sizeof(void*) | | Padding |
* |+sizeof(void*) | 0xF1 ( | User data |
* |+user_size | 0xABABABABABABABAB | Tail guard |
* -------------------------------------------------------------
@@ -52,6 +53,8 @@
* Where:
* - guard padding uses "badResourceValue" (0xAB)
* - tag word and tag2 word are general purpose
+ * - padding is inserted as-needed by the compiler to ensure
+ * the user data is aligned on a 16-byte boundary
* - user data
* -- initially padded with "uninitBlockPad" (0xF1),
* -- to "freeBlockPad" (0xBA), when freed
@@ -132,12 +135,15 @@ class GuardedMemory : StackObj { // Wrapper on stack
/**
* Header guard and size
+ *
+ * NB: the size and placement of the GuardHeader must be such that the
+ * user-ptr is maximally aligned i.e. 16-byte alignment for x86 ABI for
+ * stack alignment and use of vector (xmm) instructions. We use alignas
+ * to achieve this.
*/
- class GuardHeader : Guard {
+ class alignas(16) GuardHeader : Guard {
friend class GuardedMemory;
protected:
- // Take care in modifying fields here, will effect alignment
- // e.g. x86 ABI 16 byte stack alignment
union {
uintptr_t __unused_full_word1;
size_t _user_size;
diff --git a/src/hotspot/share/memory/memoryReserver.cpp b/src/hotspot/share/memory/memoryReserver.cpp
index 457818139cd..9d091795a4d 100644
--- a/src/hotspot/share/memory/memoryReserver.cpp
+++ b/src/hotspot/share/memory/memoryReserver.cpp
@@ -113,12 +113,13 @@ static char* reserve_memory_inner(char* requested_address,
ReservedSpace MemoryReserver::reserve_memory(char* requested_address,
size_t size,
size_t alignment,
+ size_t page_size,
bool exec,
MemTag mem_tag) {
char* base = reserve_memory_inner(requested_address, size, alignment, exec, mem_tag);
if (base != nullptr) {
- return ReservedSpace(base, size, alignment, os::vm_page_size(), exec, false /* special */);
+ return ReservedSpace(base, size, alignment, page_size, exec, false /* special */);
}
// Failed
@@ -191,7 +192,7 @@ ReservedSpace MemoryReserver::reserve(char* requested_address,
}
// == Case 3 ==
- return reserve_memory(requested_address, size, alignment, executable, mem_tag);
+ return reserve_memory(requested_address, size, alignment, page_size, executable, mem_tag);
}
ReservedSpace MemoryReserver::reserve(char* requested_address,
diff --git a/src/hotspot/share/memory/memoryReserver.hpp b/src/hotspot/share/memory/memoryReserver.hpp
index f8f642cca95..fd052e5e283 100644
--- a/src/hotspot/share/memory/memoryReserver.hpp
+++ b/src/hotspot/share/memory/memoryReserver.hpp
@@ -34,6 +34,7 @@ class MemoryReserver : AllStatic {
static ReservedSpace reserve_memory(char* requested_address,
size_t size,
size_t alignment,
+ size_t page_size,
bool exec,
MemTag mem_tag);
diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp
index 7eca79142a6..3b2fb23677d 100644
--- a/src/hotspot/share/memory/metaspace.cpp
+++ b/src/hotspot/share/memory/metaspace.cpp
@@ -868,7 +868,7 @@ size_t Metaspace::max_allocation_word_size() {
// is suitable for calling from non-Java threads.
// Callers are responsible for checking null.
MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
- MetaspaceObj::Type type, bool use_class_space) {
+ MetaspaceObj::Type type) {
assert(word_size <= Metaspace::max_allocation_word_size(),
"allocation size too large (%zu)", word_size);
@@ -878,7 +878,7 @@ MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
// Deal with concurrent unloading failed allocation starvation
MetaspaceCriticalAllocation::block_if_concurrent_purge();
- MetadataType mdtype = use_class_space ? ClassType : NonClassType;
+ MetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;
// Try to allocate metadata.
MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype);
@@ -902,7 +902,7 @@ MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
}
MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
- MetaspaceObj::Type type, bool use_class_space, TRAPS) {
+ MetaspaceObj::Type type, TRAPS) {
if (HAS_PENDING_EXCEPTION) {
assert(false, "Should not allocate with exception pending");
@@ -910,10 +910,10 @@ MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,
}
assert(!THREAD->owns_locks(), "allocating metaspace while holding mutex");
- MetaWord* result = allocate(loader_data, word_size, type, use_class_space);
+ MetaWord* result = allocate(loader_data, word_size, type);
if (result == nullptr) {
- MetadataType mdtype = use_class_space ? ClassType : NonClassType;
+ MetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;
tracer()->report_metaspace_allocation_failure(loader_data, word_size, type, mdtype);
// Allocation failed.
diff --git a/src/hotspot/share/memory/metaspace.hpp b/src/hotspot/share/memory/metaspace.hpp
index 293782c0d75..86a89777cd4 100644
--- a/src/hotspot/share/memory/metaspace.hpp
+++ b/src/hotspot/share/memory/metaspace.hpp
@@ -120,12 +120,12 @@ class Metaspace : public AllStatic {
static constexpr size_t min_allocation_word_size = min_allocation_alignment_words;
static MetaWord* allocate(ClassLoaderData* loader_data, size_t word_size,
- MetaspaceObj::Type type, bool use_class_space, TRAPS);
+ MetaspaceObj::Type type, TRAPS);
// Non-TRAPS version of allocate which can be called by a non-Java thread, that returns
// null on failure.
static MetaWord* allocate(ClassLoaderData* loader_data, size_t word_size,
- MetaspaceObj::Type type, bool use_class_space);
+ MetaspaceObj::Type type);
// Returns true if the pointer points into class space, non-class metaspace, or the
// metadata portion of the CDS archive.
diff --git a/src/hotspot/share/nmt/mallocHeader.inline.hpp b/src/hotspot/share/nmt/mallocHeader.inline.hpp
index 34ec891d33f..8b1862332fc 100644
--- a/src/hotspot/share/nmt/mallocHeader.inline.hpp
+++ b/src/hotspot/share/nmt/mallocHeader.inline.hpp
@@ -104,7 +104,7 @@ inline OutTypeParam MallocHeader::resolve_checked_impl(InTypeParam memblock) {
OutTypeParam header_pointer = (OutTypeParam)memblock - 1;
if (!header_pointer->check_block_integrity(msg, sizeof(msg), &corruption)) {
header_pointer->print_block_on_error(tty, corruption != nullptr ? corruption : (address)header_pointer);
- fatal("NMT corruption: Block at " PTR_FORMAT ": %s", p2i(memblock), msg);
+ fatal("NMT has detected a memory corruption bug. Block at " PTR_FORMAT ": %s", p2i(memblock), msg);
}
return header_pointer;
}
diff --git a/src/hotspot/share/nmt/mallocTracker.cpp b/src/hotspot/share/nmt/mallocTracker.cpp
index 6a2da5f79cd..d919f3ce873 100644
--- a/src/hotspot/share/nmt/mallocTracker.cpp
+++ b/src/hotspot/share/nmt/mallocTracker.cpp
@@ -207,6 +207,12 @@ void* MallocTracker::record_free_block(void* memblock) {
deaccount(header->free_info());
+ if (ZapCHeap) {
+ // To do this zapping, we need to know the block size.
+ // This is why we have to do it here, and not in os::free.
+ memset(memblock, freeBlockPad, header->size());
+ }
+
header->mark_block_as_dead();
return (void*)header;
diff --git a/src/hotspot/share/oops/array.inline.hpp b/src/hotspot/share/oops/array.inline.hpp
index 3fa7fd15fb3..30cf2e38f77 100644
--- a/src/hotspot/share/oops/array.inline.hpp
+++ b/src/hotspot/share/oops/array.inline.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 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
@@ -34,14 +34,14 @@ template
inline void* Array::operator new(size_t size, ClassLoaderData* loader_data, int length, TRAPS) throw() {
size_t word_size = Array::size(length);
return (void*) Metaspace::allocate(loader_data, word_size,
- MetaspaceObj::array_type(sizeof(T)), false, THREAD);
+ MetaspaceObj::array_type(sizeof(T)), THREAD);
}
template
inline void* Array::operator new(size_t size, ClassLoaderData* loader_data, int length) throw() {
size_t word_size = Array::size(length);
return (void*) Metaspace::allocate(loader_data, word_size,
- MetaspaceObj::array_type(sizeof(T)), false);
+ MetaspaceObj::array_type(sizeof(T)));
}
template
diff --git a/src/hotspot/share/oops/arrayKlass.cpp b/src/hotspot/share/oops/arrayKlass.cpp
index 5f6f6ac674d..fcc683cac8b 100644
--- a/src/hotspot/share/oops/arrayKlass.cpp
+++ b/src/hotspot/share/oops/arrayKlass.cpp
@@ -41,10 +41,6 @@
#include "oops/oop.inline.hpp"
#include "runtime/handles.inline.hpp"
-void* ArrayKlass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() {
- return Metaspace::allocate(loader_data, word_size, MetaspaceObj::ClassType, true, THREAD);
-}
-
ArrayKlass::ArrayKlass() {
assert(CDSConfig::is_dumping_static_archive() || CDSConfig::is_using_archive(), "only for CDS");
}
diff --git a/src/hotspot/share/oops/arrayKlass.hpp b/src/hotspot/share/oops/arrayKlass.hpp
index 5bfe46573d3..02d72c3cde8 100644
--- a/src/hotspot/share/oops/arrayKlass.hpp
+++ b/src/hotspot/share/oops/arrayKlass.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -49,8 +49,6 @@ class ArrayKlass: public Klass {
ArrayKlass(Symbol* name, KlassKind kind);
ArrayKlass();
- void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw();
-
public:
// Testing operation
DEBUG_ONLY(bool is_array_klass_slow() const { return true; })
diff --git a/src/hotspot/share/oops/cpCache.inline.hpp b/src/hotspot/share/oops/cpCache.inline.hpp
index 791f0a1f5ce..8dec47a3c73 100644
--- a/src/hotspot/share/oops/cpCache.inline.hpp
+++ b/src/hotspot/share/oops/cpCache.inline.hpp
@@ -73,6 +73,9 @@ inline ResolvedIndyEntry* ConstantPoolCache::resolved_indy_entry_at(int index) c
}
inline int ConstantPoolCache::resolved_indy_entries_length() const {
+ if (_resolved_indy_entries == nullptr) {
+ return 0;
+ }
return _resolved_indy_entries->length();
}
#endif // SHARE_OOPS_CPCACHE_INLINE_HPP
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index 7f617b98c1a..dd15294c3cf 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -312,12 +312,11 @@ InstanceKlass* InstanceKlass::nest_host(TRAPS) {
ss.print("Nest host resolution of %s with host %s failed: ",
this->external_name(), target_host_class);
java_lang_Throwable::print(PENDING_EXCEPTION, &ss);
- const char* msg = ss.as_string(true /* on C-heap */);
constantPoolHandle cph(THREAD, constants());
- SystemDictionary::add_nest_host_error(cph, _nest_host_index, msg);
+ SystemDictionary::add_nest_host_error(cph, _nest_host_index, ss);
CLEAR_PENDING_EXCEPTION;
- log_trace(class, nestmates)("%s", msg);
+ log_trace(class, nestmates)("%s", ss.base());
} else {
// A valid nest-host is an instance class in the current package that lists this
// class as a nest member. If any of these conditions are not met the class is
@@ -356,10 +355,9 @@ InstanceKlass* InstanceKlass::nest_host(TRAPS) {
k->external_name(),
k->class_loader_data()->loader_name_and_id(),
error);
- const char* msg = ss.as_string(true /* on C-heap */);
constantPoolHandle cph(THREAD, constants());
- SystemDictionary::add_nest_host_error(cph, _nest_host_index, msg);
- log_trace(class, nestmates)("%s", msg);
+ SystemDictionary::add_nest_host_error(cph, _nest_host_index, ss);
+ log_trace(class, nestmates)("%s", ss.base());
}
}
} else {
@@ -455,11 +453,6 @@ const char* InstanceKlass::nest_host_error() {
}
}
-void* InstanceKlass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size,
- bool use_class_space, TRAPS) throw() {
- return Metaspace::allocate(loader_data, word_size, ClassType, use_class_space, THREAD);
-}
-
InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& parser, TRAPS) {
const int size = InstanceKlass::size(parser.vtable_size(),
parser.itable_size(),
@@ -472,27 +465,26 @@ InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& par
assert(loader_data != nullptr, "invariant");
InstanceKlass* ik;
- const bool use_class_space = parser.klass_needs_narrow_id();
// Allocation
if (parser.is_instance_ref_klass()) {
// java.lang.ref.Reference
- ik = new (loader_data, size, use_class_space, THREAD) InstanceRefKlass(parser);
+ ik = new (loader_data, size, THREAD) InstanceRefKlass(parser);
} else if (class_name == vmSymbols::java_lang_Class()) {
// mirror - java.lang.Class
- ik = new (loader_data, size, use_class_space, THREAD) InstanceMirrorKlass(parser);
+ ik = new (loader_data, size, THREAD) InstanceMirrorKlass(parser);
} else if (is_stack_chunk_class(class_name, loader_data)) {
// stack chunk
- ik = new (loader_data, size, use_class_space, THREAD) InstanceStackChunkKlass(parser);
+ ik = new (loader_data, size, THREAD) InstanceStackChunkKlass(parser);
} else if (is_class_loader(class_name, parser)) {
// class loader - java.lang.ClassLoader
- ik = new (loader_data, size, use_class_space, THREAD) InstanceClassLoaderKlass(parser);
+ ik = new (loader_data, size, THREAD) InstanceClassLoaderKlass(parser);
} else {
// normal
- ik = new (loader_data, size, use_class_space, THREAD) InstanceKlass(parser);
+ ik = new (loader_data, size, THREAD) InstanceKlass(parser);
}
- if (ik != nullptr && UseCompressedClassPointers && use_class_space) {
+ if (ik != nullptr && UseCompressedClassPointers) {
assert(CompressedKlassPointers::is_encodable(ik),
"Klass " PTR_FORMAT "needs a narrow Klass ID, but is not encodable", p2i(ik));
}
@@ -3501,7 +3493,7 @@ void InstanceKlass::add_osr_nmethod(nmethod* n) {
for (int l = CompLevel_limited_profile; l < n->comp_level(); l++) {
nmethod *inv = lookup_osr_nmethod(n->method(), n->osr_entry_bci(), l, true);
if (inv != nullptr && inv->is_in_use()) {
- inv->make_not_entrant("OSR invalidation of lower levels");
+ inv->make_not_entrant(nmethod::InvalidationReason::OSR_INVALIDATION_OF_LOWER_LEVEL);
}
}
}
diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp
index 55ab0996a4a..e253691b3d4 100644
--- a/src/hotspot/share/oops/instanceKlass.hpp
+++ b/src/hotspot/share/oops/instanceKlass.hpp
@@ -143,8 +143,6 @@ class InstanceKlass: public Klass {
protected:
InstanceKlass(const ClassFileParser& parser, KlassKind kind = Kind, ReferenceType reference_type = REF_NONE);
- void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, bool use_class_space, TRAPS) throw();
-
public:
InstanceKlass();
diff --git a/src/hotspot/share/oops/instanceMirrorKlass.hpp b/src/hotspot/share/oops/instanceMirrorKlass.hpp
index 9783d416a1d..e9928647db9 100644
--- a/src/hotspot/share/oops/instanceMirrorKlass.hpp
+++ b/src/hotspot/share/oops/instanceMirrorKlass.hpp
@@ -52,6 +52,9 @@ class InstanceMirrorKlass: public InstanceKlass {
InstanceMirrorKlass(const ClassFileParser& parser) : InstanceKlass(parser, Kind) {}
+ template
+ inline void do_metadata(oop obj, OopClosureType* closure);
+
public:
InstanceMirrorKlass();
diff --git a/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp b/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp
index 867a0580a12..eed87d2644b 100644
--- a/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp
+++ b/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp
@@ -46,38 +46,41 @@ void InstanceMirrorKlass::oop_oop_iterate_statics(oop obj, OopClosureType* closu
}
}
+template
+void InstanceMirrorKlass::do_metadata(oop obj, OopClosureType* closure) {
+ Klass* klass = java_lang_Class::as_Klass(obj);
+ if (klass != nullptr) {
+ if (klass->class_loader_data() == nullptr) {
+ // This is a mirror that belongs to a shared class that has not been loaded yet.
+ assert(klass->is_shared(), "Must be");
+ } else if (klass->is_instance_klass() && klass->class_loader_data()->has_class_mirror_holder()) {
+ // A non-strong hidden class doesn't have its own class loader,
+ // so when handling the java mirror for the class we need to make sure its class
+ // loader data is claimed, this is done by calling do_cld explicitly.
+ // For non-strong hidden classes the call to do_cld is made when the class
+ // loader itself is handled.
+ Devirtualizer::do_cld(closure, klass->class_loader_data());
+ } else {
+ Devirtualizer::do_klass(closure, klass);
+ }
+ } else {
+ // Java mirror -> Klass* "nullptr" backlink means either:
+ // 1. This is a Java mirror for a primitive class. We do not need to follow it,
+ // these mirrors are always strong roots.
+ // 2. This is a Java mirror for a newly allocated non-primitive class, and we
+ // somehow managed to reach the newly allocated Java mirror with not yet
+ // installed backlink. We cannot do anything here, this case would be handled
+ // separately by GC, e.g. by keeping the relevant metadata alive during the GC.
+ // Unfortunately, the existence of corner case (2) prevents us from asserting (1).
+ }
+}
+
template
void InstanceMirrorKlass::oop_oop_iterate(oop obj, OopClosureType* closure) {
InstanceKlass::oop_oop_iterate(obj, closure);
if (Devirtualizer::do_metadata(closure)) {
- Klass* klass = java_lang_Class::as_Klass(obj);
- // We'll get null for primitive mirrors.
- if (klass != nullptr) {
- if (klass->class_loader_data() == nullptr) {
- // This is a mirror that belongs to a shared class that has not be loaded yet.
- assert(klass->is_shared(), "must be");
- } else if (klass->is_instance_klass() && klass->class_loader_data()->has_class_mirror_holder()) {
- // A non-strong hidden class doesn't have its own class loader,
- // so when handling the java mirror for the class we need to make sure its class
- // loader data is claimed, this is done by calling do_cld explicitly.
- // For non-strong hidden classes the call to do_cld is made when the class
- // loader itself is handled.
- Devirtualizer::do_cld(closure, klass->class_loader_data());
- } else {
- Devirtualizer::do_klass(closure, klass);
- }
- } else {
- // We would like to assert here (as below) that if klass has been null, then
- // this has been a mirror for a primitive type that we do not need to follow
- // as they are always strong roots.
- // However, we might get across a klass that just changed during CMS concurrent
- // marking if allocation occurred in the old generation.
- // This is benign here, as we keep alive all CLDs that were loaded during the
- // CMS concurrent phase in the class loading, i.e. they will be iterated over
- // and kept alive during remark.
- // assert(java_lang_Class::is_primitive(obj), "Sanity check");
- }
+ do_metadata(obj, closure);
}
oop_oop_iterate_statics(obj, closure);
@@ -121,11 +124,7 @@ void InstanceMirrorKlass::oop_oop_iterate_bounded(oop obj, OopClosureType* closu
if (Devirtualizer::do_metadata(closure)) {
if (mr.contains(obj)) {
- Klass* klass = java_lang_Class::as_Klass(obj);
- // We'll get null for primitive mirrors.
- if (klass != nullptr) {
- Devirtualizer::do_klass(closure, klass);
- }
+ do_metadata(obj, closure);
}
}
diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp
index aa31f586aab..66e29384d86 100644
--- a/src/hotspot/share/oops/klass.cpp
+++ b/src/hotspot/share/oops/klass.cpp
@@ -279,18 +279,19 @@ static markWord make_prototype(const Klass* kls) {
#ifdef _LP64
if (UseCompactObjectHeaders) {
// With compact object headers, the narrow Klass ID is part of the mark word.
- // We therfore seed the mark word with the narrow Klass ID.
- // Note that only those Klass that can be instantiated have a narrow Klass ID.
- // For those who don't, we leave the klass bits empty and assert if someone
- // tries to use those.
- const narrowKlass nk = CompressedKlassPointers::is_encodable(kls) ?
- CompressedKlassPointers::encode(const_cast(kls)) : 0;
+ // We therefore seed the mark word with the narrow Klass ID.
+ precond(CompressedKlassPointers::is_encodable(kls));
+ const narrowKlass nk = CompressedKlassPointers::encode(const_cast(kls));
prototype = prototype.set_narrow_klass(nk);
}
#endif
return prototype;
}
+void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() {
+ return Metaspace::allocate(loader_data, word_size, MetaspaceObj::ClassType, THREAD);
+}
+
Klass::Klass() : _kind(UnknownKlassKind) {
assert(CDSConfig::is_dumping_static_archive() || CDSConfig::is_using_archive(), "only for cds");
}
@@ -1063,7 +1064,7 @@ void Klass::verify_on(outputStream* st) {
// This can be expensive, but it is worth checking that this klass is actually
// in the CLD graph but not in production.
#ifdef ASSERT
- if (UseCompressedClassPointers && needs_narrow_id()) {
+ if (UseCompressedClassPointers) {
// Stricter checks for both correct alignment and placement
CompressedKlassPointers::check_encodable(this);
} else {
diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp
index b92f7bb8d99..f32f9fe1185 100644
--- a/src/hotspot/share/oops/klass.hpp
+++ b/src/hotspot/share/oops/klass.hpp
@@ -208,6 +208,8 @@ class Klass : public Metadata {
Klass(KlassKind kind);
Klass();
+ void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw();
+
public:
int kind() { return _kind; }
@@ -785,10 +787,6 @@ class Klass : public Metadata {
static bool is_valid(Klass* k);
static void on_secondary_supers_verification_failure(Klass* super, Klass* sub, bool linear_result, bool table_result, const char* msg);
-
- // Returns true if this Klass needs to be addressable via narrow Klass ID.
- inline bool needs_narrow_id() const;
-
};
#endif // SHARE_OOPS_KLASS_HPP
diff --git a/src/hotspot/share/oops/klass.inline.hpp b/src/hotspot/share/oops/klass.inline.hpp
index b37c5105f64..4ac50cbc180 100644
--- a/src/hotspot/share/oops/klass.inline.hpp
+++ b/src/hotspot/share/oops/klass.inline.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -175,13 +175,4 @@ inline bool Klass::search_secondary_supers(Klass *k) const {
return result;
}
-// Returns true if this Klass needs to be addressable via narrow Klass ID.
-inline bool Klass::needs_narrow_id() const {
- // Classes that are never instantiated need no narrow Klass Id, since the
- // only point of having a narrow id is to put it into an object header. Keeping
- // never instantiated classes out of class space lessens the class space pressure.
- // For more details, see JDK-8338526.
- // Note: don't call this function before access flags are initialized.
- return !is_abstract() && !is_interface();
-}
#endif // SHARE_OOPS_KLASS_INLINE_HPP
diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp
index d13458dfa93..bb7a0576eec 100644
--- a/src/hotspot/share/oops/method.cpp
+++ b/src/hotspot/share/oops/method.cpp
@@ -1028,7 +1028,7 @@ void Method::set_native_function(address function, bool post_event_flag) {
// If so, we have to make it not_entrant.
nmethod* nm = code(); // Put it into local variable to guard against concurrent updates
if (nm != nullptr) {
- nm->make_not_entrant("set native function");
+ nm->make_not_entrant(nmethod::InvalidationReason::SET_NATIVE_FUNCTION);
}
}
diff --git a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp
index 2ae1ac18699..44ebf3d1ef3 100644
--- a/src/hotspot/share/oops/trainingData.cpp
+++ b/src/hotspot/share/oops/trainingData.cpp
@@ -316,7 +316,9 @@ void CompileTrainingData::notice_jit_observation(ciEnv* env, ciBaseObject* what)
// This JIT task is (probably) requesting that ik be initialized,
// so add him to my _init_deps list.
TrainingDataLocker l;
- add_init_dep(ktd);
+ if (l.can_add()) {
+ add_init_dep(ktd);
+ }
}
}
}
diff --git a/src/hotspot/share/oops/trainingData.hpp b/src/hotspot/share/oops/trainingData.hpp
index 6acf19b4d5e..487ceae1f1b 100644
--- a/src/hotspot/share/oops/trainingData.hpp
+++ b/src/hotspot/share/oops/trainingData.hpp
@@ -96,16 +96,18 @@ class TrainingData : public Metadata {
// TrainingDataLocker is used to guard read/write operations on non-MT-safe data structures.
// It supports recursive locking and a read-only mode (in which case no locks are taken).
- // It is also a part of the TD collection termination protocol (see the "spanshot" field).
+ // It is also a part of the TD collection termination protocol (see the "snapshot" field).
class TrainingDataLocker {
+#if INCLUDE_CDS
static volatile bool _snapshot; // If true we're not allocating new training data
+#endif
static int _lock_mode;
const bool _recursive;
static void lock() {
#if INCLUDE_CDS
assert(_lock_mode != 0, "Forgot to call TrainingDataLocker::initialize()");
if (_lock_mode > 0) {
- TrainingData_lock->lock();
+ TrainingData_lock->lock_without_safepoint_check();
}
#endif
}
@@ -150,6 +152,11 @@ class TrainingData : public Metadata {
static void initialize() {
#if INCLUDE_CDS
_lock_mode = need_data() ? +1 : -1; // if -1, we go lock-free
+#endif
+ }
+ static void assert_locked_or_snapshotted() {
+#if INCLUDE_CDS
+ assert(safely_locked() || _snapshot, "use under TrainingDataLocker or after snapshot");
#endif
}
static void assert_locked() {
@@ -338,20 +345,24 @@ class TrainingData : public Metadata {
}
int length() const {
+ TrainingDataLocker::assert_locked_or_snapshotted();
return (_deps_dyn != nullptr ? _deps_dyn->length()
: _deps != nullptr ? _deps->length()
: 0);
}
E* adr_at(int i) const {
+ TrainingDataLocker::assert_locked_or_snapshotted();
return (_deps_dyn != nullptr ? _deps_dyn->adr_at(i)
: _deps != nullptr ? _deps->adr_at(i)
: nullptr);
}
E at(int i) const {
+ TrainingDataLocker::assert_locked_or_snapshotted();
assert(i >= 0 && i < length(), "oob");
return *adr_at(i);
}
bool append_if_missing(E dep) {
+ TrainingDataLocker::assert_can_add();
if (_deps_dyn == nullptr) {
_deps_dyn = new GrowableArrayCHeap(10);
_deps_dyn->append(dep);
@@ -361,23 +372,27 @@ class TrainingData : public Metadata {
}
}
bool remove_if_existing(E dep) {
+ TrainingDataLocker::assert_can_add();
if (_deps_dyn != nullptr) {
return _deps_dyn->remove_if_existing(dep);
}
return false;
}
void clear() {
+ TrainingDataLocker::assert_can_add();
if (_deps_dyn != nullptr) {
_deps_dyn->clear();
}
}
void append(E dep) {
+ TrainingDataLocker::assert_can_add();
if (_deps_dyn == nullptr) {
_deps_dyn = new GrowableArrayCHeap(10);
}
_deps_dyn->append(dep);
}
bool contains(E dep) {
+ TrainingDataLocker::assert_locked();
for (int i = 0; i < length(); i++) {
if (dep == at(i)) {
return true; // found
@@ -593,6 +608,7 @@ class CompileTrainingData : public TrainingData {
DepList _data;
public:
OptionalReturnType find(const Args&... args) {
+ TrainingDataLocker l;
ArgumentsType a(args...);
for (int i = 0; i < _data.length(); i++) {
if (_data.at(i).arguments() == a) {
@@ -601,8 +617,11 @@ class CompileTrainingData : public TrainingData {
}
return OptionalReturnType(false, ReturnType());
}
- bool append_if_missing(const ReturnType& result, const Args&... args) {
- return _data.append_if_missing(Record(result, ArgumentsType(args...)));
+ void append_if_missing(const ReturnType& result, const Args&... args) {
+ TrainingDataLocker l;
+ if (l.can_add()) {
+ _data.append_if_missing(Record(result, ArgumentsType(args...)));
+ }
}
#if INCLUDE_CDS
void remove_unshareable_info() { _data.remove_unshareable_info(); }
diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp
index 143398c57d0..4f8590082cd 100644
--- a/src/hotspot/share/opto/addnode.cpp
+++ b/src/hotspot/share/opto/addnode.cpp
@@ -1425,8 +1425,10 @@ static Node* fold_subI_no_underflow_pattern(Node* n, PhaseGVN* phase) {
Node* x = add2->in(1);
Node* con2 = add2->in(2);
if (is_sub_con(con2)) {
+ // The graph could be dying (i.e. x is top) in which case type(x) is not a long.
+ const TypeLong* x_long = phase->type(x)->isa_long();
// Collapsed graph not equivalent if potential over/underflow -> bailing out (*)
- if (can_overflow(phase->type(x)->is_long(), con1->get_long() + con2->get_long())) {
+ if (x_long == nullptr || can_overflow(x_long, con1->get_long() + con2->get_long())) {
return nullptr;
}
Node* new_con = phase->transform(new AddLNode(con1, con2));
diff --git a/src/hotspot/share/opto/bytecodeInfo.cpp b/src/hotspot/share/opto/bytecodeInfo.cpp
index e618a708f61..908fff22a77 100644
--- a/src/hotspot/share/opto/bytecodeInfo.cpp
+++ b/src/hotspot/share/opto/bytecodeInfo.cpp
@@ -60,6 +60,7 @@ InlineTree::InlineTree(Compile* c,
// Keep a private copy of the caller_jvms:
_caller_jvms = new (C) JVMState(caller_jvms->method(), caller_tree->caller_jvms());
_caller_jvms->set_bci(caller_jvms->bci());
+ _caller_jvms->set_receiver_info(caller_jvms->receiver_info());
assert(!caller_jvms->should_reexecute(), "there should be no reexecute bytecode with inlining");
assert(_caller_jvms->same_calls_as(caller_jvms), "consistent JVMS");
}
@@ -437,24 +438,26 @@ bool InlineTree::try_to_inline(ciMethod* callee_method, ciMethod* caller_method,
// detect direct and indirect recursive inlining
{
- // count the current method and the callee
const bool is_compiled_lambda_form = callee_method->is_compiled_lambda_form();
- int inline_level = 0;
- if (!is_compiled_lambda_form) {
- if (method() == callee_method) {
- inline_level++;
- }
+ const bool is_method_handle_invoker = is_compiled_lambda_form && !jvms->method()->is_compiled_lambda_form();
+
+ ciInstance* lform_callee_recv = nullptr;
+ if (is_compiled_lambda_form && !is_method_handle_invoker) { // MH invokers don't have a receiver
+ lform_callee_recv = jvms->compute_receiver_info(callee_method);
}
- // count callers of current method and callee
- Node* callee_argument0 = is_compiled_lambda_form ? jvms->map()->argument(jvms, 0)->uncast() : nullptr;
- for (JVMState* j = jvms->caller(); j != nullptr && j->has_method(); j = j->caller()) {
+
+ int inline_level = 0;
+ for (JVMState* j = jvms; j != nullptr && j->has_method(); j = j->caller()) {
if (j->method() == callee_method) {
- if (is_compiled_lambda_form) {
- // Since compiled lambda forms are heavily reused we allow recursive inlining. If it is truly
- // a recursion (using the same "receiver") we limit inlining otherwise we can easily blow the
- // compiler stack.
- Node* caller_argument0 = j->map()->argument(j, 0)->uncast();
- if (caller_argument0 == callee_argument0) {
+ // Since compiled lambda forms are heavily reused we allow recursive inlining. If it is truly
+ // a recursion (using the same "receiver") we limit inlining otherwise we can easily blow the
+ // compiler stack.
+ if (lform_callee_recv != nullptr) {
+ ciInstance* lform_caller_recv = j->receiver_info();
+ assert(lform_caller_recv != nullptr || j->depth() == 1 ||
+ !j->caller()->method()->is_compiled_lambda_form(), // MH invoker
+ "missing receiver info");
+ if (lform_caller_recv == lform_callee_recv || lform_caller_recv == nullptr) {
inline_level++;
}
} else {
diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp
index 3527e46c24b..f0fa73f11ce 100644
--- a/src/hotspot/share/opto/callnode.cpp
+++ b/src/hotspot/share/opto/callnode.cpp
@@ -262,7 +262,8 @@ uint TailJumpNode::match_edge(uint idx) const {
//=============================================================================
JVMState::JVMState(ciMethod* method, JVMState* caller) :
- _method(method) {
+ _method(method),
+ _receiver_info(nullptr) {
assert(method != nullptr, "must be valid call site");
_bci = InvocationEntryBci;
_reexecute = Reexecute_Undefined;
@@ -278,7 +279,8 @@ JVMState::JVMState(ciMethod* method, JVMState* caller) :
_sp = 0;
}
JVMState::JVMState(int stack_size) :
- _method(nullptr) {
+ _method(nullptr),
+ _receiver_info(nullptr) {
_bci = InvocationEntryBci;
_reexecute = Reexecute_Undefined;
DEBUG_ONLY(_map = (SafePointNode*)-1);
@@ -613,6 +615,7 @@ JVMState* JVMState::clone_shallow(Compile* C) const {
n->set_endoff(_endoff);
n->set_sp(_sp);
n->set_map(_map);
+ n->set_receiver_info(_receiver_info);
return n;
}
@@ -687,6 +690,20 @@ int JVMState::interpreter_frame_size() const {
return size + Deoptimization::last_frame_adjust(0, callee_locals) * BytesPerWord;
}
+// Compute receiver info for a compiled lambda form at call site.
+ciInstance* JVMState::compute_receiver_info(ciMethod* callee) const {
+ assert(callee != nullptr && callee->is_compiled_lambda_form(), "");
+ if (has_method() && method()->is_compiled_lambda_form()) { // callee is not a MH invoker
+ Node* recv = map()->argument(this, 0);
+ assert(recv != nullptr, "");
+ const TypeOopPtr* recv_toop = recv->bottom_type()->isa_oopptr();
+ if (recv_toop != nullptr && recv_toop->const_oop() != nullptr) {
+ return recv_toop->const_oop()->as_instance();
+ }
+ }
+ return nullptr;
+}
+
//=============================================================================
bool CallNode::cmp( const Node &n ) const
{ return _tf == ((CallNode&)n)._tf && _jvms == ((CallNode&)n)._jvms; }
@@ -1313,7 +1330,7 @@ void CallLeafNode::dump_spec(outputStream *st) const {
//=============================================================================
-void SafePointNode::set_local(JVMState* jvms, uint idx, Node *c) {
+void SafePointNode::set_local(const JVMState* jvms, uint idx, Node *c) {
assert(verify_jvms(jvms), "jvms must match");
int loc = jvms->locoff() + idx;
if (in(loc)->is_top() && idx > 0 && !c->is_top() ) {
diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp
index db857d4c6d1..2a95cb3b1f2 100644
--- a/src/hotspot/share/opto/callnode.hpp
+++ b/src/hotspot/share/opto/callnode.hpp
@@ -217,6 +217,7 @@ class JVMState : public ResourceObj {
int _bci; // Byte Code Index of this JVM point
ReexecuteState _reexecute; // Whether this bytecode need to be re-executed
ciMethod* _method; // Method Pointer
+ ciInstance* _receiver_info; // Constant receiver instance for compiled lambda forms
SafePointNode* _map; // Map node associated with this scope
public:
friend class Compile;
@@ -259,6 +260,7 @@ class JVMState : public ResourceObj {
bool is_reexecute_undefined() const { return _reexecute==Reexecute_Undefined; }
bool has_method() const { return _method != nullptr; }
ciMethod* method() const { assert(has_method(), ""); return _method; }
+ ciInstance* receiver_info() const { assert(has_method(), ""); return _receiver_info; }
JVMState* caller() const { return _caller; }
SafePointNode* map() const { return _map; }
uint depth() const { return _depth; }
@@ -304,6 +306,7 @@ class JVMState : public ResourceObj {
// _reexecute is initialized to "undefined" for a new bci
void set_bci(int bci) {if(_bci != bci)_reexecute=Reexecute_Undefined; _bci = bci; }
void set_should_reexecute(bool reexec) {_reexecute = reexec ? Reexecute_True : Reexecute_False;}
+ void set_receiver_info(ciInstance* recv) { assert(has_method() || recv == nullptr, ""); _receiver_info = recv; }
// Miscellaneous utility functions
JVMState* clone_deep(Compile* C) const; // recursively clones caller chain
@@ -311,6 +314,7 @@ class JVMState : public ResourceObj {
void set_map_deep(SafePointNode *map);// reset map for all callers
void adapt_position(int delta); // Adapt offsets in in-array after adding an edge.
int interpreter_frame_size() const;
+ ciInstance* compute_receiver_info(ciMethod* callee) const;
#ifndef PRODUCT
void print_method_with_lineno(outputStream* st, bool show_name) const;
@@ -373,7 +377,7 @@ class SafePointNode : public MultiNode {
}
private:
- void verify_input(JVMState* jvms, uint idx) const {
+ void verify_input(const JVMState* jvms, uint idx) const {
assert(verify_jvms(jvms), "jvms must match");
Node* n = in(idx);
assert((!n->bottom_type()->isa_long() && !n->bottom_type()->isa_double()) ||
@@ -382,34 +386,44 @@ class SafePointNode : public MultiNode {
public:
// Functionality from old debug nodes which has changed
- Node *local(JVMState* jvms, uint idx) const {
- verify_input(jvms, jvms->locoff() + idx);
- return in(jvms->locoff() + idx);
+ Node* local(const JVMState* jvms, uint idx) const {
+ uint loc_idx = jvms->locoff() + idx;
+ assert(jvms->is_loc(loc_idx), "not a local slot");
+ verify_input(jvms, loc_idx);
+ return in(loc_idx);
}
- Node *stack(JVMState* jvms, uint idx) const {
- verify_input(jvms, jvms->stkoff() + idx);
- return in(jvms->stkoff() + idx);
+ Node* stack(const JVMState* jvms, uint idx) const {
+ uint stk_idx = jvms->stkoff() + idx;
+ assert(jvms->is_stk(stk_idx), "not a stack slot");
+ verify_input(jvms, stk_idx);
+ return in(stk_idx);
}
- Node *argument(JVMState* jvms, uint idx) const {
- verify_input(jvms, jvms->argoff() + idx);
+ Node* argument(const JVMState* jvms, uint idx) const {
+ uint arg_idx = jvms->argoff() + idx;
+ assert(jvms->is_stk(arg_idx), "not an argument slot");
+ verify_input(jvms, arg_idx);
return in(jvms->argoff() + idx);
}
- Node *monitor_box(JVMState* jvms, uint idx) const {
+ Node* monitor_box(const JVMState* jvms, uint idx) const {
assert(verify_jvms(jvms), "jvms must match");
- return in(jvms->monitor_box_offset(idx));
+ uint mon_box_idx = jvms->monitor_box_offset(idx);
+ assert(jvms->is_monitor_box(mon_box_idx), "not a monitor box offset");
+ return in(mon_box_idx);
}
- Node *monitor_obj(JVMState* jvms, uint idx) const {
+ Node* monitor_obj(const JVMState* jvms, uint idx) const {
assert(verify_jvms(jvms), "jvms must match");
- return in(jvms->monitor_obj_offset(idx));
+ uint mon_obj_idx = jvms->monitor_obj_offset(idx);
+ assert(jvms->is_mon(mon_obj_idx) && !jvms->is_monitor_box(mon_obj_idx), "not a monitor obj offset");
+ return in(mon_obj_idx);
}
- void set_local(JVMState* jvms, uint idx, Node *c);
+ void set_local(const JVMState* jvms, uint idx, Node *c);
- void set_stack(JVMState* jvms, uint idx, Node *c) {
+ void set_stack(const JVMState* jvms, uint idx, Node *c) {
assert(verify_jvms(jvms), "jvms must match");
set_req(jvms->stkoff() + idx, c);
}
- void set_argument(JVMState* jvms, uint idx, Node *c) {
+ void set_argument(const JVMState* jvms, uint idx, Node *c) {
assert(verify_jvms(jvms), "jvms must match");
set_req(jvms->argoff() + idx, c);
}
diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp
index e14d63c7651..30368d80ca9 100644
--- a/src/hotspot/share/opto/chaitin.cpp
+++ b/src/hotspot/share/opto/chaitin.cpp
@@ -2375,7 +2375,7 @@ void PhaseChaitin::dump_frame() const {
tty->print_cr("saved fp register");
else if (return_addr == OptoReg::add(reg, 2*VMRegImpl::slots_per_word) &&
VerifyStackAtCalls)
- tty->print_cr("0xBADB100D +VerifyStackAtCalls");
+ tty->print_cr(" +VerifyStackAtCalls");
else
tty->print_cr("in_preserve");
} else if ((int)OptoReg::reg2stack(reg) < fixed_slots) {
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index a5023408cdf..8dc493956ae 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -3596,7 +3596,10 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
} else if (t->isa_oopptr()) {
new_in2 = ConNode::make(t->make_narrowoop());
} else if (t->isa_klassptr()) {
- new_in2 = ConNode::make(t->make_narrowklass());
+ ciKlass* klass = t->is_klassptr()->exact_klass();
+ if (klass->is_in_encoding_range()) {
+ new_in2 = ConNode::make(t->make_narrowklass());
+ }
}
}
if (new_in2 != nullptr) {
@@ -3633,7 +3636,13 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
} else if (t->isa_oopptr()) {
n->subsume_by(ConNode::make(t->make_narrowoop()), this);
} else if (t->isa_klassptr()) {
- n->subsume_by(ConNode::make(t->make_narrowklass()), this);
+ ciKlass* klass = t->is_klassptr()->exact_klass();
+ if (klass->is_in_encoding_range()) {
+ n->subsume_by(ConNode::make(t->make_narrowklass()), this);
+ } else {
+ assert(false, "unencodable klass in ConP -> EncodeP");
+ C->record_failure("unencodable klass in ConP -> EncodeP");
+ }
}
}
if (in1->outcnt() == 0) {
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index 29f737bce08..f74af38387c 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -1575,9 +1575,14 @@ bool LibraryCallKit::inline_string_toBytesU() {
Node* src_start = array_element_address(value, offset, T_CHAR);
Node* dst_start = basic_plus_adr(newcopy, arrayOopDesc::base_offset_in_bytes(T_BYTE));
- // Check if src array address is aligned to HeapWordSize (dst is always aligned)
- const TypeInt* toffset = gvn().type(offset)->is_int();
- bool aligned = toffset->is_con() && ((toffset->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
+ // Check if dst array address is aligned to HeapWordSize
+ bool aligned = (arrayOopDesc::base_offset_in_bytes(T_BYTE) % HeapWordSize == 0);
+ // If true, then check if src array address is aligned to HeapWordSize
+ if (aligned) {
+ const TypeInt* toffset = gvn().type(offset)->is_int();
+ aligned = toffset->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) +
+ toffset->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
+ }
// Figure out which arraycopy runtime method to call (disjoint, uninitialized).
const char* copyfunc_name = "arraycopy";
@@ -1658,8 +1663,8 @@ bool LibraryCallKit::inline_string_getCharsU() {
// Check if array addresses are aligned to HeapWordSize
const TypeInt* tsrc = gvn().type(src_begin)->is_int();
const TypeInt* tdst = gvn().type(dst_begin)->is_int();
- bool aligned = tsrc->is_con() && ((tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) &&
- tdst->is_con() && ((tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
+ bool aligned = tsrc->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) + tsrc->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0) &&
+ tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_CHAR) + tdst->get_con() * type2aelembytes(T_CHAR)) % HeapWordSize == 0);
// Figure out which arraycopy runtime method to call (disjoint, uninitialized).
const char* copyfunc_name = "arraycopy";
@@ -3240,7 +3245,7 @@ bool LibraryCallKit::inline_native_jvm_commit() {
lease_compare_io->init_req(_true_path, i_o());
lease_compare_io->init_req(_false_path, input_io_state);
- lease_result_value->init_req(_true_path, null()); // if the lease was returned, return 0.
+ lease_result_value->init_req(_true_path, _gvn.longcon(0)); // if the lease was returned, return 0L.
lease_result_value->init_req(_false_path, arg); // if not lease, return new updated position.
RegionNode* result_rgn = new RegionNode(PATH_LIMIT);
diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp
index c6ed2411fe3..99fedcdf880 100644
--- a/src/hotspot/share/opto/macro.cpp
+++ b/src/hotspot/share/opto/macro.cpp
@@ -606,6 +606,11 @@ bool PhaseMacroExpand::can_eliminate_allocation(PhaseIterGVN* igvn, AllocateNode
for (DUIterator_Fast kmax, k = use->fast_outs(kmax);
k < kmax && can_eliminate; k++) {
Node* n = use->fast_out(k);
+ if (n->is_Mem() && n->as_Mem()->is_mismatched_access()) {
+ DEBUG_ONLY(disq_node = n);
+ NOT_PRODUCT(fail_eliminate = "Mismatched access");
+ can_eliminate = false;
+ }
if (!n->is_Store() && n->Opcode() != Op_CastP2X && !bs->is_gc_pre_barrier_node(n) && !reduce_merge_precheck) {
DEBUG_ONLY(disq_node = n;)
if (n->is_Load() || n->is_LoadStore()) {
@@ -743,6 +748,41 @@ void PhaseMacroExpand::undo_previous_scalarizations(GrowableArray basic_type();
+ BasicType field_bt = field_type->basic_type();
+
+ // Primitive types must match.
+ if (is_java_primitive(value_bt) && value_bt == field_bt) { return; }
+
+ // I have been struggling to make a similar assert for non-primitive
+ // types. I we can add one in the future. For now, I just let them
+ // pass without checks.
+ // In particular, I was struggling with a value that came from a call,
+ // and had only a non-null check CastPP. There was also a checkcast
+ // in the graph to verify the interface, but the corresponding
+ // CheckCastPP result was not updated in the stack slot, and so
+ // we ended up using the CastPP. That means that the field knows
+ // that it should get an oop from an interface, but the value lost
+ // that information, and so it is not a subtype.
+ // There may be other issues, feel free to investigate further!
+ if (!is_java_primitive(value_bt)) { return; }
+
+ tty->print_cr("value not compatible for field: %s vs %s",
+ type2name(value_bt),
+ type2name(field_bt));
+ tty->print("value_type: ");
+ value_type->dump();
+ tty->cr();
+ tty->print("field_type: ");
+ field_type->dump();
+ tty->cr();
+ assert(false, "value_type does not fit field_type");
+ }
+#endif
+
SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_description(AllocateNode *alloc, SafePointNode* sfpt) {
// Fields of scalar objs are referenced only at the end
// of regular debuginfo at the last (youngest) JVMS.
@@ -859,6 +899,7 @@ SafePointScalarObjectNode* PhaseMacroExpand::create_scalarized_object_descriptio
field_val = transform_later(new DecodeNNode(field_val, field_val->get_ptr_type()));
}
}
+ DEBUG_ONLY(verify_type_compatability(field_val->bottom_type(), field_type);)
sfpt->add_req(field_val);
}
diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp
index 6fa0b0f497d..94b8044f814 100644
--- a/src/hotspot/share/opto/parse1.cpp
+++ b/src/hotspot/share/opto/parse1.cpp
@@ -1149,6 +1149,13 @@ SafePointNode* Parse::create_entry_map() {
// Create an initial safepoint to hold JVM state during parsing
JVMState* jvms = new (C) JVMState(method(), _caller->has_method() ? _caller : nullptr);
set_map(new SafePointNode(len, jvms));
+
+ // Capture receiver info for compiled lambda forms.
+ if (method()->is_compiled_lambda_form()) {
+ ciInstance* recv_info = _caller->compute_receiver_info(method());
+ jvms->set_receiver_info(recv_info);
+ }
+
jvms->set_map(map());
record_for_igvn(map());
assert(jvms->endoff() == len, "correct jvms sizing");
diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp
index 641634b906e..28936a04219 100644
--- a/src/hotspot/share/opto/stringopts.cpp
+++ b/src/hotspot/share/opto/stringopts.cpp
@@ -1473,9 +1473,14 @@ void PhaseStringOpts::arraycopy(GraphKit& kit, IdealKit& ideal, Node* src_array,
Node* src_ptr = __ array_element_address(src_array, __ intcon(0), T_BYTE);
Node* dst_ptr = __ array_element_address(dst_array, start, T_BYTE);
- // Check if destination address is aligned to HeapWordSize
- const TypeInt* tdst = __ gvn().type(start)->is_int();
- bool aligned = tdst->is_con() && ((tdst->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0);
+ // Check if src array address is aligned to HeapWordSize
+ bool aligned = (arrayOopDesc::base_offset_in_bytes(T_BYTE) % HeapWordSize == 0);
+ // If true, then check if dst array address is aligned to HeapWordSize
+ if (aligned) {
+ const TypeInt* tdst = __ gvn().type(start)->is_int();
+ aligned = tdst->is_con() && ((arrayOopDesc::base_offset_in_bytes(T_BYTE) +
+ tdst->get_con() * type2aelembytes(T_BYTE)) % HeapWordSize == 0);
+ }
// Figure out which arraycopy runtime method to call (disjoint, uninitialized).
const char* copyfunc_name = "arraycopy";
address copyfunc_addr = StubRoutines::select_arraycopy_function(elembt, aligned, true, copyfunc_name, true);
diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp
index 168724ad9b8..9c5c4dac749 100644
--- a/src/hotspot/share/opto/superword.cpp
+++ b/src/hotspot/share/opto/superword.cpp
@@ -2593,6 +2593,8 @@ static bool can_subword_truncate(Node* in, const Type* type) {
case Op_ReverseI:
case Op_CountLeadingZerosI:
case Op_CountTrailingZerosI:
+ case Op_IsFiniteF:
+ case Op_IsFiniteD:
case Op_IsInfiniteF:
case Op_IsInfiniteD:
case Op_ExtractS:
diff --git a/src/hotspot/share/prims/jvmtiAgentList.cpp b/src/hotspot/share/prims/jvmtiAgentList.cpp
index a4b7606056d..6a9c62babe5 100644
--- a/src/hotspot/share/prims/jvmtiAgentList.cpp
+++ b/src/hotspot/share/prims/jvmtiAgentList.cpp
@@ -201,6 +201,11 @@ void JvmtiAgentList::load_xrun_agents() {
// Invokes Agent_OnAttach for agents loaded dynamically during runtime.
void JvmtiAgentList::load_agent(const char* agent_name, bool is_absolute_path,
const char* options, outputStream* st) {
+ if (JvmtiEnvBase::get_phase() != JVMTI_PHASE_LIVE) {
+ st->print_cr("Dynamic agent loading is only permitted in the live phase");
+ return;
+ }
+
JvmtiAgent* const agent = new JvmtiAgent(agent_name, options, is_absolute_path, /* dynamic agent */ true);
if (agent->load(st)) {
add(agent);
diff --git a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
index 731db2e82f1..381ad7d12fb 100644
--- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
+++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp
@@ -996,6 +996,11 @@ void JvmtiClassFileReconstituter::write_u8(u8 x) {
void JvmtiClassFileReconstituter::copy_bytecodes(const methodHandle& mh,
unsigned char* bytecodes) {
+ // We must copy bytecodes only from linked classes.
+ // Being linked guarantees we are not getting bytecodes at
+ // the same time the linking process is rewriting them.
+ guarantee(mh->method_holder()->is_linked(), "Bytecodes must be copied from a linked class");
+
// use a BytecodeStream to iterate over the bytecodes. JVM/fast bytecodes
// and the breakpoint bytecode are converted to their original bytecodes.
diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp
index 6a1f451bb74..ed575cfb575 100644
--- a/src/hotspot/share/prims/jvmtiEnv.cpp
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp
@@ -451,6 +451,18 @@ JvmtiEnv::RetransformClasses(jint class_count, const jclass* classes) {
InstanceKlass* ik = InstanceKlass::cast(klass);
if (ik->get_cached_class_file_bytes() == nullptr) {
+ // Link the class to avoid races with the rewriter. This will call the verifier also
+ // on the class. Linking is also done in VM_RedefineClasses below, but we need
+ // to keep that for other VM_RedefineClasses callers.
+ JavaThread* THREAD = current_thread;
+ ik->link_class(THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ // Retransform/JVMTI swallows error messages. Using this class will rerun the verifier in a context
+ // that propagates the VerifyError, if thrown.
+ CLEAR_PENDING_EXCEPTION;
+ return JVMTI_ERROR_INVALID_CLASS;
+ }
+
// Not cached, we need to reconstitute the class file from the
// VM representation. We don't attach the reconstituted class
// bytes to the InstanceKlass here because they have not been
@@ -3428,7 +3440,8 @@ jvmtiError
JvmtiEnv::GetBytecodes(Method* method, jint* bytecode_count_ptr, unsigned char** bytecodes_ptr) {
NULL_CHECK(method, JVMTI_ERROR_INVALID_METHODID);
- methodHandle mh(Thread::current(), method);
+ JavaThread* current_thread = JavaThread::current();
+ methodHandle mh(current_thread, method);
jint size = (jint)mh->code_size();
jvmtiError err = allocate(size, bytecodes_ptr);
if (err != JVMTI_ERROR_NONE) {
@@ -3437,6 +3450,13 @@ JvmtiEnv::GetBytecodes(Method* method, jint* bytecode_count_ptr, unsigned char**
(*bytecode_count_ptr) = size;
// get byte codes
+ // Make sure the class is verified and rewritten first.
+ JavaThread* THREAD = current_thread;
+ mh->method_holder()->link_class(THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ return JVMTI_ERROR_INVALID_CLASS;
+ }
JvmtiClassFileReconstituter::copy_bytecodes(mh, *bytecodes_ptr);
return JVMTI_ERROR_NONE;
diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp
index facbaaf9ad0..bf8df8acf3b 100644
--- a/src/hotspot/share/prims/jvmtiEventController.cpp
+++ b/src/hotspot/share/prims/jvmtiEventController.cpp
@@ -785,9 +785,6 @@ void JvmtiEventControllerPrivate::set_event_callbacks(JvmtiEnvBase *env,
assert(Threads::number_of_threads() == 0 || JvmtiThreadState_lock->is_locked(), "sanity check");
EC_TRACE(("[*] # set event callbacks"));
- // May be changing the event handler for ObjectFree.
- flush_object_free_events(env);
-
env->set_event_callbacks(callbacks, size_of_callbacks);
jlong enabled_bits = env->env_event_enable()->_event_callback_enabled.get_bits();
@@ -1097,6 +1094,8 @@ JvmtiEventController::set_event_callbacks(JvmtiEnvBase *env,
// call the functionality without holding the JvmtiThreadState_lock.
JvmtiEventControllerPrivate::set_event_callbacks(env, callbacks, size_of_callbacks);
} else {
+ JvmtiEventControllerPrivate::flush_object_free_events(env);
+
MutexLocker mu(JvmtiThreadState_lock);
JvmtiEventControllerPrivate::set_event_callbacks(env, callbacks, size_of_callbacks);
}
@@ -1184,6 +1183,8 @@ JvmtiEventController::env_dispose(JvmtiEnvBase *env) {
// call the functionality without holding the JvmtiThreadState_lock.
JvmtiEventControllerPrivate::env_dispose(env);
} else {
+ JvmtiEventControllerPrivate::flush_object_free_events(env);
+
MutexLocker mu(JvmtiThreadState_lock);
JvmtiEventControllerPrivate::env_dispose(env);
}
diff --git a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp
index d3bf8862d37..63d8e31c0c0 100644
--- a/src/hotspot/share/prims/jvmtiTagMap.cpp
+++ b/src/hotspot/share/prims/jvmtiTagMap.cpp
@@ -944,6 +944,7 @@ class IterateOverHeapObjectClosure: public ObjectClosure {
// invoked for each object in the heap
void IterateOverHeapObjectClosure::do_object(oop o) {
+ assert(o != nullptr, "Heap iteration should never produce null!");
// check if iteration has been halted
if (is_iteration_aborted()) return;
@@ -953,7 +954,7 @@ void IterateOverHeapObjectClosure::do_object(oop o) {
}
// skip if object is a dormant shared object whose mirror hasn't been loaded
- if (o != nullptr && o->klass()->java_mirror() == nullptr) {
+ if (o->klass()->java_mirror() == nullptr) {
log_debug(aot, heap)("skipped dormant archived object " INTPTR_FORMAT " (%s)", p2i(o),
o->klass()->external_name());
return;
@@ -1032,6 +1033,7 @@ class IterateThroughHeapObjectClosure: public ObjectClosure {
// invoked for each object in the heap
void IterateThroughHeapObjectClosure::do_object(oop obj) {
+ assert(obj != nullptr, "Heap iteration should never produce null!");
// check if iteration has been halted
if (is_iteration_aborted()) return;
@@ -1039,7 +1041,7 @@ void IterateThroughHeapObjectClosure::do_object(oop obj) {
if (is_filtered_by_klass_filter(obj, klass())) return;
// skip if object is a dormant shared object whose mirror hasn't been loaded
- if (obj != nullptr && obj->klass()->java_mirror() == nullptr) {
+ if (obj->klass()->java_mirror() == nullptr) {
log_debug(aot, heap)("skipped dormant archived object " INTPTR_FORMAT " (%s)", p2i(obj),
obj->klass()->external_name());
return;
diff --git a/src/hotspot/share/prims/vectorSupport.cpp b/src/hotspot/share/prims/vectorSupport.cpp
index c907ddb4885..92c1b225a6a 100644
--- a/src/hotspot/share/prims/vectorSupport.cpp
+++ b/src/hotspot/share/prims/vectorSupport.cpp
@@ -593,6 +593,25 @@ int VectorSupport::vop2ideal(jint id, BasicType bt) {
break;
}
+ case VECTOR_OP_TAN: // fall-through
+ case VECTOR_OP_TANH: // fall-through
+ case VECTOR_OP_SIN: // fall-through
+ case VECTOR_OP_SINH: // fall-through
+ case VECTOR_OP_COS: // fall-through
+ case VECTOR_OP_COSH: // fall-through
+ case VECTOR_OP_ASIN: // fall-through
+ case VECTOR_OP_ACOS: // fall-through
+ case VECTOR_OP_ATAN: // fall-through
+ case VECTOR_OP_ATAN2: // fall-through
+ case VECTOR_OP_CBRT: // fall-through
+ case VECTOR_OP_LOG: // fall-through
+ case VECTOR_OP_LOG10: // fall-through
+ case VECTOR_OP_LOG1P: // fall-through
+ case VECTOR_OP_POW: // fall-through
+ case VECTOR_OP_EXP: // fall-through
+ case VECTOR_OP_EXPM1: // fall-through
+ case VECTOR_OP_HYPOT: return 0; // not supported; should be handled in Java code
+
default: fatal("unknown op: %d", vop);
}
return 0; // Unimplemented
diff --git a/src/hotspot/share/prims/vectorSupport.hpp b/src/hotspot/share/prims/vectorSupport.hpp
index 5ba18cdfaa8..9ec6500543c 100644
--- a/src/hotspot/share/prims/vectorSupport.hpp
+++ b/src/hotspot/share/prims/vectorSupport.hpp
@@ -101,6 +101,25 @@ class VectorSupport : AllStatic {
VECTOR_OP_COMPRESS_BITS = 33,
VECTOR_OP_EXPAND_BITS = 34,
+ VECTOR_OP_TAN = 101,
+ VECTOR_OP_TANH = 102,
+ VECTOR_OP_SIN = 103,
+ VECTOR_OP_SINH = 104,
+ VECTOR_OP_COS = 105,
+ VECTOR_OP_COSH = 106,
+ VECTOR_OP_ASIN = 107,
+ VECTOR_OP_ACOS = 108,
+ VECTOR_OP_ATAN = 109,
+ VECTOR_OP_ATAN2 = 110,
+ VECTOR_OP_CBRT = 111,
+ VECTOR_OP_LOG = 112,
+ VECTOR_OP_LOG10 = 113,
+ VECTOR_OP_LOG1P = 114,
+ VECTOR_OP_POW = 115,
+ VECTOR_OP_EXP = 116,
+ VECTOR_OP_EXPM1 = 117,
+ VECTOR_OP_HYPOT = 118,
+
VECTOR_OP_SADD = 119,
VECTOR_OP_SSUB = 120,
VECTOR_OP_SUADD = 121,
diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp
index f14c5efc65d..c84bef57e9b 100644
--- a/src/hotspot/share/prims/whitebox.cpp
+++ b/src/hotspot/share/prims/whitebox.cpp
@@ -46,6 +46,7 @@
#include "gc/shared/concurrentGCBreakpoints.hpp"
#include "gc/shared/gcConfig.hpp"
#include "gc/shared/genArguments.hpp"
+#include "jfr/periodic/sampling/jfrCPUTimeThreadSampler.hpp"
#include "jvm.h"
#include "jvmtifiles/jvmtiEnv.hpp"
#include "logging/log.hpp"
@@ -195,6 +196,10 @@ WB_ENTRY(jint, WB_TakeLockAndHangInSafepoint(JNIEnv* env, jobject wb))
return 0;
WB_END
+WB_ENTRY(jlong, WB_GetMinimumJavaStackSize(JNIEnv* env, jobject o))
+ return os::get_minimum_java_stack_size();
+WB_END
+
class WBIsKlassAliveClosure : public LockedClassesDo {
Symbol* _name;
int _count;
@@ -504,6 +509,14 @@ WB_ENTRY(jboolean, WB_ConcurrentGCRunTo(JNIEnv* env, jobject o, jobject at))
return ConcurrentGCBreakpoints::run_to(c_name);
WB_END
+WB_ENTRY(jboolean, WB_HasExternalSymbolsStripped(JNIEnv* env, jobject o))
+#if defined(HAS_STRIPPED_DEBUGINFO)
+ return true;
+#else
+ return false;
+#endif
+WB_END
+
#if INCLUDE_G1GC
WB_ENTRY(jboolean, WB_G1IsHumongous(JNIEnv* env, jobject o, jobject obj))
@@ -794,7 +807,7 @@ class VM_WhiteBoxDeoptimizeFrames : public VM_WhiteBoxOperation {
if (_make_not_entrant) {
nmethod* nm = CodeCache::find_nmethod(f->pc());
assert(nm != nullptr, "did not find nmethod");
- nm->make_not_entrant("Whitebox deoptimization");
+ nm->make_not_entrant(nmethod::InvalidationReason::WHITEBOX_DEOPTIMIZATION);
}
++_result;
}
@@ -1097,6 +1110,22 @@ bool WhiteBox::validate_cgroup(bool cgroups_v2_enabled,
}
#endif
+bool WhiteBox::is_asan_enabled() {
+#ifdef ADDRESS_SANITIZER
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool WhiteBox::is_ubsan_enabled() {
+#ifdef UNDEFINED_BEHAVIOR_SANITIZER
+ return true;
+#else
+ return false;
+#endif
+}
+
bool WhiteBox::compile_method(Method* method, int comp_level, int bci, JavaThread* THREAD) {
// Screen for unavailable/bad comp level or null method
AbstractCompiler* comp = CompileBroker::compiler(comp_level);
@@ -1908,6 +1937,14 @@ WB_ENTRY(jboolean, WB_IsMonitorInflated(JNIEnv* env, jobject wb, jobject obj))
return (jboolean) obj_oop->mark().has_monitor();
WB_END
+WB_ENTRY(jboolean, WB_IsAsanEnabled(JNIEnv* env))
+ return (jboolean) WhiteBox::is_asan_enabled();
+WB_END
+
+WB_ENTRY(jboolean, WB_IsUbsanEnabled(JNIEnv* env))
+ return (jboolean) WhiteBox::is_ubsan_enabled();
+WB_END
+
WB_ENTRY(jlong, WB_getInUseMonitorCount(JNIEnv* env, jobject wb))
return (jlong) WhiteBox::get_in_use_monitor_count();
WB_END
@@ -2659,6 +2696,32 @@ WB_ENTRY(void, WB_WaitUnsafe(JNIEnv* env, jobject wb, jint time))
os::naked_short_sleep(time);
WB_END
+WB_ENTRY(void, WB_BusyWait(JNIEnv* env, jobject wb, jint time))
+ ThreadToNativeFromVM ttn(thread);
+ u8 start = os::current_thread_cpu_time();
+ u8 target_duration = time * (u8)1000000;
+ while (os::current_thread_cpu_time() - start < target_duration) {
+ for (volatile int i = 0; i < 1000000; i++);
+ }
+WB_END
+
+WB_ENTRY(jboolean, WB_CPUSamplerSetOutOfStackWalking(JNIEnv* env, jobject wb, jboolean enable))
+ #if defined(ASSERT) && INCLUDE_JFR && defined(LINUX)
+ JfrCPUTimeThreadSampling::set_out_of_stack_walking_enabled(enable == JNI_TRUE);
+ return JNI_TRUE;
+ #else
+ return JNI_FALSE;
+ #endif
+WB_END
+
+WB_ENTRY(jlong, WB_CPUSamplerOutOfStackWalkingIterations(JNIEnv* env, jobject wb))
+ #if defined(ASSERT) && INCLUDE_JFR && defined(LINUX)
+ return (jlong)JfrCPUTimeThreadSampling::out_of_stack_walking_iterations();
+ #else
+ return 0;
+ #endif
+WB_END
+
WB_ENTRY(jstring, WB_GetLibcName(JNIEnv* env, jobject o))
ThreadToNativeFromVM ttn(thread);
jstring info_string = env->NewStringUTF(XSTR(LIBC));
@@ -2737,6 +2800,7 @@ static JNINativeMethod methods[] = {
{CC"getVMLargePageSize", CC"()J", (void*)&WB_GetVMLargePageSize},
{CC"getHeapSpaceAlignment", CC"()J", (void*)&WB_GetHeapSpaceAlignment},
{CC"getHeapAlignment", CC"()J", (void*)&WB_GetHeapAlignment},
+ {CC"hasExternalSymbolsStripped", CC"()Z", (void*)&WB_HasExternalSymbolsStripped},
{CC"countAliveClasses0", CC"(Ljava/lang/String;)I", (void*)&WB_CountAliveClasses },
{CC"getSymbolRefcount", CC"(Ljava/lang/String;)I", (void*)&WB_GetSymbolRefcount },
{CC"parseCommandLine0",
@@ -2908,6 +2972,8 @@ static JNINativeMethod methods[] = {
(void*)&WB_AddModuleExportsToAll },
{CC"deflateIdleMonitors", CC"()Z", (void*)&WB_DeflateIdleMonitors },
{CC"isMonitorInflated0", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsMonitorInflated },
+ {CC"isAsanEnabled", CC"()Z", (void*)&WB_IsAsanEnabled },
+ {CC"isUbsanEnabled", CC"()Z", (void*)&WB_IsUbsanEnabled },
{CC"getInUseMonitorCount", CC"()J", (void*)&WB_getInUseMonitorCount },
{CC"getLockStackCapacity", CC"()I", (void*)&WB_getLockStackCapacity },
{CC"supportsRecursiveLightweightLocking", CC"()Z", (void*)&WB_supportsRecursiveLightweightLocking },
@@ -3006,6 +3072,9 @@ static JNINativeMethod methods[] = {
{CC"isJVMTIIncluded", CC"()Z", (void*)&WB_IsJVMTIIncluded},
{CC"waitUnsafe", CC"(I)V", (void*)&WB_WaitUnsafe},
+ {CC"busyWait", CC"(I)V", (void*)&WB_BusyWait},
+ {CC"cpuSamplerSetOutOfStackWalking", CC"(Z)Z", (void*)&WB_CPUSamplerSetOutOfStackWalking},
+ {CC"cpuSamplerOutOfStackWalkingIterations", CC"()J",(void*)&WB_CPUSamplerOutOfStackWalkingIterations},
{CC"getLibcName", CC"()Ljava/lang/String;", (void*)&WB_GetLibcName},
{CC"pinObject", CC"(Ljava/lang/Object;)V", (void*)&WB_PinObject},
@@ -3015,7 +3084,8 @@ static JNINativeMethod methods[] = {
{CC"cleanMetaspaces", CC"()V", (void*)&WB_CleanMetaspaces},
{CC"rss", CC"()J", (void*)&WB_Rss},
{CC"printString", CC"(Ljava/lang/String;I)Ljava/lang/String;", (void*)&WB_PrintString},
- {CC"lockAndStuckInSafepoint", CC"()V", (void*)&WB_TakeLockAndHangInSafepoint},
+ {CC"lockAndStuckInSafepoint", CC"()V", (void*)&WB_TakeLockAndHangInSafepoint},
+ {CC"getMinimumJavaStackSize", CC"()J", (void*)&WB_GetMinimumJavaStackSize},
{CC"wordSize", CC"()J", (void*)&WB_WordSize},
{CC"rootChunkWordSize", CC"()J", (void*)&WB_RootChunkWordSize},
{CC"isStatic", CC"()Z", (void*)&WB_IsStaticallyLinked}
diff --git a/src/hotspot/share/prims/whitebox.hpp b/src/hotspot/share/prims/whitebox.hpp
index 4ba684fc09a..c20d35abbbb 100644
--- a/src/hotspot/share/prims/whitebox.hpp
+++ b/src/hotspot/share/prims/whitebox.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -72,6 +72,9 @@ class WhiteBox : public AllStatic {
#ifdef LINUX
static bool validate_cgroup(bool cgroups_v2_enabled, const char* controllers_file, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags);
#endif
+ // provide info about enabling of Address Sanitizer / Undefined Behavior Sanitizer
+ static bool is_asan_enabled();
+ static bool is_ubsan_enabled();
};
#endif // SHARE_PRIMS_WHITEBOX_HPP
diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp
index 5b9d36115f8..6012b87e50b 100644
--- a/src/hotspot/share/runtime/abstract_vm_version.cpp
+++ b/src/hotspot/share/runtime/abstract_vm_version.cpp
@@ -271,6 +271,20 @@ const char* Abstract_VM_Version::internal_vm_info_string() {
#define HOTSPOT_BUILD_COMPILER "MS VC++ 17.7 (VS2022)"
#elif _MSC_VER == 1938
#define HOTSPOT_BUILD_COMPILER "MS VC++ 17.8 (VS2022)"
+ #elif _MSC_VER == 1939
+ #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.9 (VS2022)"
+ #elif _MSC_VER == 1940
+ #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.10 (VS2022)"
+ #elif _MSC_VER == 1941
+ #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.11 (VS2022)"
+ #elif _MSC_VER == 1942
+ #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.12 (VS2022)"
+ #elif _MSC_VER == 1943
+ #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.13 (VS2022)"
+ #elif _MSC_VER == 1944
+ #define HOTSPOT_BUILD_COMPILER "MS VC++ 17.14 (VS2022)"
+ #elif _MSC_VER == 1950
+ #define HOTSPOT_BUILD_COMPILER "MS VC++ 18.0 (VS2026)"
#else
#define HOTSPOT_BUILD_COMPILER "unknown MS VC++:" XSTR(_MSC_VER)
#endif
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index 7592d233f0c..47398ef4812 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -536,6 +536,7 @@ static SpecialFlag const special_jvm_flags[] = {
#ifdef _LP64
{ "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(26), JDK_Version::undefined() },
#endif
+ { "ShenandoahPacing", JDK_Version::jdk(25), JDK_Version::jdk(26), JDK_Version::jdk(27) },
// --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in:
{ "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() },
diff --git a/src/hotspot/share/runtime/continuationEntry.hpp b/src/hotspot/share/runtime/continuationEntry.hpp
index 3c8532b9e87..a9f37bddb43 100644
--- a/src/hotspot/share/runtime/continuationEntry.hpp
+++ b/src/hotspot/share/runtime/continuationEntry.hpp
@@ -39,6 +39,7 @@ class RegisterMap;
// Metadata stored in the continuation entry frame
class ContinuationEntry {
+ friend class VMStructs;
friend class JVMCIVMStructs;
ContinuationEntryPD _pd;
#ifdef ASSERT
diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp
index 4042bb01c58..63de5e6d37a 100644
--- a/src/hotspot/share/runtime/deoptimization.cpp
+++ b/src/hotspot/share/runtime/deoptimization.cpp
@@ -1382,6 +1382,9 @@ void Deoptimization::reassign_type_array_elements(frame* fr, RegisterMap* reg_ma
case T_INT: case T_FLOAT: { // 4 bytes.
assert(value->type() == T_INT, "Agreement.");
+#if INCLUDE_JVMCI
+ // big_value allows encoding double/long value as e.g. [int = 0, long], and storing
+ // the value in two array elements.
bool big_value = false;
if (i + 1 < sv->field_size() && type == T_INT) {
if (sv->field_at(i)->is_location()) {
@@ -1409,6 +1412,9 @@ void Deoptimization::reassign_type_array_elements(frame* fr, RegisterMap* reg_ma
} else {
obj->int_at_put(index, value->get_jint());
}
+#else // not INCLUDE_JVMCI
+ obj->int_at_put(index, value->get_jint());
+#endif // INCLUDE_JVMCI
break;
}
@@ -1826,7 +1832,7 @@ void Deoptimization::deoptimize(JavaThread* thread, frame fr, DeoptReason reason
#if INCLUDE_JVMCI
address Deoptimization::deoptimize_for_missing_exception_handler(nmethod* nm) {
// there is no exception handler for this pc => deoptimize
- nm->make_not_entrant("missing exception handler");
+ nm->make_not_entrant(nmethod::InvalidationReason::MISSING_EXCEPTION_HANDLER);
// Use Deoptimization::deoptimize for all of its side-effects:
// gathering traps statistics, logging...
@@ -2455,7 +2461,7 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr
// Recompile
if (make_not_entrant) {
- if (!nm->make_not_entrant("uncommon trap")) {
+ if (!nm->make_not_entrant(nmethod::InvalidationReason::UNCOMMON_TRAP)) {
return; // the call did not change nmethod's state
}
diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp
index efffa8ac753..6c97ae55df5 100644
--- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp
+++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.cpp
@@ -39,6 +39,14 @@ JVMFlag::Error AOTCacheConstraintFunc(ccstr value, bool verbose) {
return JVMFlag::SUCCESS;
}
+JVMFlag::Error AOTCacheOutputConstraintFunc(ccstr value, bool verbose) {
+ if (value == nullptr) {
+ JVMFlag::printError(verbose, "AOTCacheOutput cannot be empty\n");
+ return JVMFlag::VIOLATES_CONSTRAINT;
+ }
+ return JVMFlag::SUCCESS;
+}
+
JVMFlag::Error AOTConfigurationConstraintFunc(ccstr value, bool verbose) {
if (value == nullptr) {
JVMFlag::printError(verbose, "AOTConfiguration cannot be empty\n");
@@ -139,3 +147,13 @@ JVMFlag::Error NUMAInterleaveGranularityConstraintFunc(size_t value, bool verbos
return JVMFlag::SUCCESS;
}
+
+JVMFlag::Error LargePageSizeInBytesConstraintFunc(size_t value, bool verbose) {
+ if (!is_power_of_2(value)) {
+ JVMFlag::printError(verbose, "LargePageSizeInBytes ( %zu ) must be "
+ "a power of 2\n",
+ value);
+ return JVMFlag::VIOLATES_CONSTRAINT;
+ }
+ return JVMFlag::SUCCESS;
+}
diff --git a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp
index 3040dafabc5..5259f87c19d 100644
--- a/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp
+++ b/src/hotspot/share/runtime/flags/jvmFlagConstraintsRuntime.hpp
@@ -35,13 +35,15 @@
#define RUNTIME_CONSTRAINTS(f) \
f(ccstr, AOTCacheConstraintFunc) \
+ f(ccstr, AOTCacheOutputConstraintFunc) \
f(ccstr, AOTConfigurationConstraintFunc) \
f(ccstr, AOTModeConstraintFunc) \
f(int, ObjectAlignmentInBytesConstraintFunc) \
f(int, ContendedPaddingWidthConstraintFunc) \
f(int, PerfDataSamplingIntervalFunc) \
f(uintx, VMPageSizeConstraintFunc) \
- f(size_t, NUMAInterleaveGranularityConstraintFunc)
+ f(size_t, NUMAInterleaveGranularityConstraintFunc) \
+ f(size_t, LargePageSizeInBytesConstraintFunc)
RUNTIME_CONSTRAINTS(DECLARE_CONSTRAINT)
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 75736d0dc7d..4b32e370dcd 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -242,8 +242,10 @@ const int ObjectAlignmentInBytes = 8;
\
product(size_t, LargePageSizeInBytes, 0, \
"Maximum large page size used (0 will use the default large " \
- "page size for the environment as the maximum)") \
+ "page size for the environment as the maximum) " \
+ "(must be a power of 2)") \
range(0, max_uintx) \
+ constraint(LargePageSizeInBytesConstraintFunc, AtParse) \
\
product(size_t, LargePageHeapSizeThreshold, 128*M, \
"Use large pages if maximum heap is at least this big") \
@@ -293,6 +295,9 @@ const int ObjectAlignmentInBytes = 8;
product(bool, UseInlineCaches, true, \
"Use Inline Caches for virtual calls ") \
\
+ develop(bool, VerifyInlineCaches, true, \
+ "Verify Inline Caches") \
+ \
product(bool, InlineArrayCopy, true, DIAGNOSTIC, \
"Inline arraycopy native that is known to be part of " \
"base library DLL") \
@@ -485,6 +490,9 @@ const int ObjectAlignmentInBytes = 8;
develop(bool, ZapFillerObjects, trueInDebug, \
"Zap filler objects") \
\
+ develop(bool, ZapCHeap, trueInDebug, \
+ "Zap allocated/freed C heap space") \
+ \
develop(bool, ZapTLAB, trueInDebug, \
"Zap allocated TLABs") \
develop(bool, TestingAsyncLoggingDeathTest, false, \
@@ -2009,6 +2017,7 @@ const int ObjectAlignmentInBytes = 8;
develop(uint, BinarySearchThreshold, 16, \
"Minimal number of elements in a sorted collection to prefer" \
"binary search over simple linear search." ) \
+ \
// end of RUNTIME_FLAGS
diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp
index aa0b5dca3e4..83280c1f9f1 100644
--- a/src/hotspot/share/runtime/java.cpp
+++ b/src/hotspot/share/runtime/java.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp
index ddb682cbc75..20de4beebdd 100644
--- a/src/hotspot/share/runtime/javaThread.cpp
+++ b/src/hotspot/share/runtime/javaThread.cpp
@@ -1339,7 +1339,7 @@ void JavaThread::make_zombies() {
// it is a Java nmethod
nmethod* nm = CodeCache::find_nmethod(fst.current()->pc());
assert(nm != nullptr, "did not find nmethod");
- nm->make_not_entrant("zombie");
+ nm->make_not_entrant(nmethod::InvalidationReason::ZOMBIE);
}
}
}
diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
index b917bd78d35..bff6e3d913b 100644
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -259,7 +259,7 @@ void mutex_init() {
MUTEX_DEFN(CompiledIC_lock , PaddedMutex , nosafepoint); // locks VtableStubs_lock
MUTEX_DEFN(MethodCompileQueue_lock , PaddedMonitor, safepoint);
- MUTEX_DEFL(TrainingData_lock , PaddedMutex , MethodCompileQueue_lock);
+ MUTEX_DEFN(TrainingData_lock , PaddedMutex , nosafepoint);
MUTEX_DEFN(TrainingReplayQueue_lock , PaddedMonitor, safepoint);
MUTEX_DEFN(CompileStatistics_lock , PaddedMutex , safepoint);
MUTEX_DEFN(DirectivesStack_lock , PaddedMutex , nosafepoint);
diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
index ee1f0a3b081..1e1f8e90be6 100644
--- a/src/hotspot/share/runtime/os.cpp
+++ b/src/hotspot/share/runtime/os.cpp
@@ -666,8 +666,8 @@ void* os::malloc(size_t size, MemTag mem_tag, const NativeCallStack& stack) {
if (CDSConfig::is_dumping_static_archive()) {
// Need to deterministically fill all the alignment gaps in C++ structures.
::memset(inner_ptr, 0, size);
- } else {
- DEBUG_ONLY(::memset(inner_ptr, uninitBlockPad, size);)
+ } else if (ZapCHeap) {
+ ::memset(inner_ptr, uninitBlockPad, size);
}
DEBUG_ONLY(break_if_ptr_caught(inner_ptr);)
return inner_ptr;
@@ -740,7 +740,7 @@ void* os::realloc(void *memblock, size_t size, MemTag mem_tag, const NativeCallS
#ifdef ASSERT
assert(old_size == free_info.size, "Sanity");
- if (old_size < size) {
+ if (ZapCHeap && old_size < size) {
// We also zap the newly extended region.
::memset((char*)new_inner_ptr + old_size, uninitBlockPad, size - old_size);
}
@@ -2573,6 +2573,10 @@ jint os::set_minimum_stack_sizes() {
return JNI_OK;
}
+jlong os::get_minimum_java_stack_size() {
+ return static_cast(_java_thread_min_stack_allowed);
+}
+
// Builds a platform dependent Agent_OnLoad_ function name
// which is used to find statically linked in agents.
// Parameters:
diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp
index b26ec280e72..bb07abad6b1 100644
--- a/src/hotspot/share/runtime/os.hpp
+++ b/src/hotspot/share/runtime/os.hpp
@@ -397,6 +397,8 @@ class os: AllStatic {
static jint set_minimum_stack_sizes();
public:
+ // get allowed minimum java stack size
+ static jlong get_minimum_java_stack_size();
// Find committed memory region within specified range (start, start + size),
// return true if found any
static bool committed_in_range(address start, size_t size, address& committed_start, size_t& committed_size);
diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
index 9c710fc98e4..b9610064c13 100644
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -3364,7 +3364,7 @@ JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *current) )
RegisterMap::WalkContinuation::skip);
frame sender = fr.sender(&map);
if (sender.is_interpreted_frame()) {
- current->push_cont_fastpath(sender.sp());
+ current->push_cont_fastpath(sender.unextended_sp());
}
return buf;
diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp
index 5075efb5c75..879a2324e54 100644
--- a/src/hotspot/share/runtime/vmStructs.cpp
+++ b/src/hotspot/share/runtime/vmStructs.cpp
@@ -624,6 +624,7 @@
nonstatic_field(JavaThread, _active_handles, JNIHandleBlock*) \
nonstatic_field(JavaThread, _monitor_owner_id, int64_t) \
volatile_nonstatic_field(JavaThread, _terminated, JavaThread::TerminatedTypes) \
+ nonstatic_field(JavaThread, _cont_entry, ContinuationEntry*) \
nonstatic_field(Thread, _osthread, OSThread*) \
\
/************/ \
@@ -667,6 +668,14 @@
static_field(VMRegImpl, regName[0], const char*) \
static_field(VMRegImpl, stack0, VMReg) \
\
+ /******************************************************************************************/ \
+ /* CI (NOTE: these CI fields are retained in VMStructs for the benefit of external tools, */ \
+ /* to ease their migration to a future alternative.) */ \
+ /******************************************************************************************/ \
+ \
+ nonstatic_field(CompilerThread, _env, ciEnv*) \
+ nonstatic_field(ciEnv, _task, CompileTask*) \
+ \
/************/ \
/* Monitors */ \
/************/ \
@@ -798,7 +807,8 @@
nonstatic_field(Mutex, _name, const char*) \
static_field(Mutex, _mutex_array, Mutex**) \
static_field(Mutex, _num_mutex, int) \
- volatile_nonstatic_field(Mutex, _owner, Thread*)
+ volatile_nonstatic_field(Mutex, _owner, Thread*) \
+ static_field(ContinuationEntry, _return_pc, address)
//--------------------------------------------------------------------------------
// VM_TYPES
@@ -1150,6 +1160,12 @@
declare_toplevel_type(BasicLock) \
declare_toplevel_type(BasicObjectLock) \
\
+ /*********************/ \
+ /* CI */ \
+ /*********************/ \
+ \
+ declare_toplevel_type(ciEnv) \
+ \
/********************/ \
/* -XX flags */ \
/********************/ \
@@ -1266,6 +1282,7 @@
declare_toplevel_type(FileMapHeader) \
declare_toplevel_type(CDSFileMapRegion) \
declare_toplevel_type(UpcallStub::FrameData) \
+ declare_toplevel_type(ContinuationEntry) \
\
/************/ \
/* GC types */ \
diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp
index abe3d6757b5..bd5adc7acf9 100644
--- a/src/hotspot/share/utilities/debug.cpp
+++ b/src/hotspot/share/utilities/debug.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -63,12 +63,17 @@
#include "utilities/nativeStackPrinter.hpp"
#include "utilities/unsigned5.hpp"
#include "utilities/vmError.hpp"
+#if INCLUDE_JFR
+#include "jfr/jfr.hpp"
+#endif
#include
#include
-// These functions needs to be exported on Windows only
-#define DEBUGEXPORT WINDOWS_ONLY(JNIEXPORT)
+// These functions needs to be exported on Windows
+// On Linux it is also beneficial to export them to avoid
+// losing them e.g. with linktime gc
+#define DEBUGEXPORT JNIEXPORT
// Support for showing register content on asserts/guarantees.
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
@@ -262,6 +267,8 @@ void report_untested(const char* file, int line, const char* message) {
void report_java_out_of_memory(const char* message) {
static int out_of_memory_reported = 0;
+ JFR_ONLY(Jfr::on_report_java_out_of_memory();)
+
// A number of threads may attempt to report OutOfMemoryError at around the
// same time. To avoid dumping the heap or executing the data collection
// commands multiple times we just do it once when the first threads reports
@@ -610,36 +617,60 @@ extern "C" DEBUGEXPORT intptr_t u5p(intptr_t addr,
void pp(intptr_t p) { pp((void*)p); }
void pp(oop p) { pp((void*)p); }
-void help() {
+extern "C" DEBUGEXPORT void help() {
Command c("help");
tty->print_cr("basic");
- tty->print_cr(" pp(void* p) - try to make sense of p");
- tty->print_cr(" ps() - print current thread stack");
- tty->print_cr(" pss() - print all thread stacks");
- tty->print_cr(" pm(int pc) - print Method* given compiled PC");
- tty->print_cr(" findm(intptr_t pc) - finds Method*");
- tty->print_cr(" find(intptr_t x) - finds & prints nmethod/stub/bytecode/oop based on pointer into it");
- tty->print_cr(" pns(void* sp, void* fp, void* pc) - print native (i.e. mixed) stack trace. E.g.");
- tty->print_cr(" pns($sp, $rbp, $pc) on Linux/amd64 or");
- tty->print_cr(" pns($sp, $ebp, $pc) on Linux/x86 or");
- tty->print_cr(" pns($sp, $fp, $pc) on Linux/AArch64 or");
- tty->print_cr(" pns($sp, 0, $pc) on Linux/ppc64 or");
- tty->print_cr(" pns($sp, $s8, $pc) on Linux/mips or");
- tty->print_cr(" pns($sp, $fp, $pc) on Linux/RISC-V");
+ tty->print_cr(" pp(void* p) - try to make sense of p");
+ tty->print_cr(" ps() - print current thread stack");
+ tty->print_cr(" pss() - print all thread stacks");
+ tty->print_cr(" findnm(intptr_t pc) - find nmethod*");
+ tty->print_cr(" findm(intptr_t pc) - find Method*");
+ tty->print_cr(" find(intptr_t x) - find & print nmethod/stub/bytecode/oop based on pointer into it");
+ tty->print_cr(" findpc(intptr_t x) - find & print nmethod/stub/bytecode/oop based on pointer into it (verbose)");
+
+#ifndef PRODUCT
+ tty->print_cr(" pns(void* sp, void* fp, void* pc) - print native (i.e. mixed) stack trace, e.g.");
+#ifdef LINUX
+ AMD64_ONLY( tty->print_cr(" pns($sp, $rbp, $pc) on Linux/amd64"));
+ IA32_ONLY( tty->print_cr(" pns($sp, $ebp, $pc) on Linux/x86"));
+ AARCH64_ONLY(tty->print_cr(" pns($sp, $fp, $pc) on Linux/AArch64"));
+ RISCV_ONLY( tty->print_cr(" pns($sp, $fp, $pc) on Linux/RISC-V"));
+ PPC64_ONLY( tty->print_cr(" pns($sp, 0, $pc) on Linux/ppc64"));
+#endif // LINUX
tty->print_cr(" - in gdb do 'set overload-resolution off' before calling pns()");
tty->print_cr(" - in dbx do 'frame 1' before calling pns()");
+#endif // !PRODUCT
+
+ tty->print_cr("universe.");
+ tty->print_cr(" verify(intptr_t p) - run verify on Universe");
+ tty->print_cr(" threads() - print all threads");
+ tty->print_cr(" psd() - print system dictionary");
+
tty->print_cr("class metadata.");
tty->print_cr(" findclass(name_pattern, flags)");
tty->print_cr(" findmethod(class_name_pattern, method_pattern, flags)");
- tty->print_cr("misc.");
- tty->print_cr(" flush() - flushes the log file");
- tty->print_cr(" events() - dump events from ring buffers");
+ tty->print_cr("method metadata.");
+ tty->print_cr(" blob(CodeBlob* p) - print CodeBlob");
+ tty->print_cr(" dump_vtable(address p) - dump vtable of the Klass");
+ tty->print_cr(" nm(intptr_t p) - find & print CodeBlob details");
+ tty->print_cr(" disnm(intptr_t p) - find & print disassembly of CodeBlob");
+ tty->print_cr(" printnm(intptr_t p) - print nmethod details");
+ tty->print_cr(" findbcp(method, bcp) - find & prints bcp");
+ tty->print_cr("stack frame details.");
+ tty->print_cr(" pfl() - print frame layout");
+ tty->print_cr(" psf() - print stack frames");
+
+ tty->print_cr("misc.");
+ tty->print_cr(" flush() - flush the log file");
+ tty->print_cr(" events() - dump events from ring buffers");
+ tty->print_cr(" u5decode(intptr_t addr) - decode a single u5 value");
+ tty->print_cr(" u5p(intptr_t addr, intptr_t limit, int count) - decode u5 values");
tty->print_cr("compiler debugging");
- tty->print_cr(" debug() - to set things up for compiler debugging");
- tty->print_cr(" ndebug() - undo debug");
+ tty->print_cr(" debug() - set things up for compiler debugging");
+ tty->print_cr(" ndebug() - undo debug");
}
#ifndef PRODUCT
diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp
index 7d1b17d5deb..93ae435ae72 100644
--- a/src/hotspot/share/utilities/vmError.cpp
+++ b/src/hotspot/share/utilities/vmError.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2024 SAP SE. All rights reserved.
* Copyright (c) 2023, 2025, Red Hat, Inc. and/or its affiliates.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -109,12 +109,13 @@ const intptr_t VMError::segfault_address = pd_segfault_address;
static const char* env_list[] = {
// All platforms
"JAVA_HOME", "JAVA_TOOL_OPTIONS", "_JAVA_OPTIONS", "CLASSPATH",
- "PATH", "USERNAME",
+ "JDK_AOT_VM_OPTIONS",
+ "JAVA_OPTS", "PATH", "USERNAME",
"XDG_CACHE_HOME", "XDG_CONFIG_HOME", "FC_LANG", "FONTCONFIG_USE_MMAP",
// Env variables that are defined on Linux/BSD
- "LD_LIBRARY_PATH", "LD_PRELOAD", "SHELL", "DISPLAY",
+ "LD_LIBRARY_PATH", "LD_PRELOAD", "SHELL", "DISPLAY", "WAYLAND_DISPLAY",
"HOSTTYPE", "OSTYPE", "ARCH", "MACHTYPE",
"LANG", "LC_ALL", "LC_CTYPE", "LC_NUMERIC", "LC_TIME",
"TERM", "TMPDIR", "TZ",
@@ -1856,7 +1857,7 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt
log.set_fd(-1);
}
- JFR_ONLY(Jfr::on_vm_shutdown(true);)
+ JFR_ONLY(Jfr::on_vm_shutdown(true, false, static_cast(_id) == OOM_JAVA_HEAP_FATAL);)
if (PrintNMTStatistics) {
fdStream fds(fd_out);
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/RSACipherAdaptor.java b/src/java.base/share/classes/com/sun/crypto/provider/RSACipherAdaptor.java
new file mode 100644
index 00000000000..c0fd9ffa599
--- /dev/null
+++ b/src/java.base/share/classes/com/sun/crypto/provider/RSACipherAdaptor.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package com.sun.crypto.provider;
+
+import java.io.ByteArrayOutputStream;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.SignatureSpi;
+import java.security.InvalidKeyException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.SignatureException;
+import java.security.spec.AlgorithmParameterSpec;
+import javax.crypto.Cipher;
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+
+/**
+ * NONEwithRSA Signature implementation using the RSA/ECB/PKCS1Padding Cipher
+ * implementation from SunJCE.
+ */
+public final class RSACipherAdaptor extends SignatureSpi {
+
+ private final RSACipher c;
+ private ByteArrayOutputStream verifyBuf;
+
+ public RSACipherAdaptor() {
+ c = new RSACipher();
+ }
+
+ @Override
+ protected void engineInitVerify(PublicKey publicKey)
+ throws InvalidKeyException {
+ c.engineInit(Cipher.DECRYPT_MODE, publicKey, null);
+ if (verifyBuf == null) {
+ verifyBuf = new ByteArrayOutputStream(128);
+ } else {
+ verifyBuf.reset();
+ }
+ }
+
+ @Override
+ protected void engineInitSign(PrivateKey privateKey)
+ throws InvalidKeyException {
+ c.engineInit(Cipher.ENCRYPT_MODE, privateKey, null);
+ verifyBuf = null;
+ }
+
+ @Override
+ protected void engineInitSign(PrivateKey privateKey, SecureRandom random)
+ throws InvalidKeyException {
+ c.engineInit(Cipher.ENCRYPT_MODE, privateKey, random);
+ verifyBuf = null;
+ }
+
+ @Override
+ protected void engineUpdate(byte b) throws SignatureException {
+ engineUpdate(new byte[] {b}, 0, 1);
+ }
+
+ @Override
+ protected void engineUpdate(byte[] b, int off, int len)
+ throws SignatureException {
+ if (verifyBuf != null) {
+ verifyBuf.write(b, off, len);
+ } else {
+ byte[] out = c.engineUpdate(b, off, len);
+ if ((out != null) && (out.length != 0)) {
+ throw new SignatureException
+ ("Cipher unexpectedly returned data");
+ }
+ }
+ }
+
+ @Override
+ protected byte[] engineSign() throws SignatureException {
+ try {
+ return c.engineDoFinal(null, 0, 0);
+ } catch (IllegalBlockSizeException | BadPaddingException e) {
+ throw new SignatureException("doFinal() failed", e);
+ }
+ }
+
+ @Override
+ protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
+ try {
+ byte[] out = c.engineDoFinal(sigBytes, 0, sigBytes.length);
+ byte[] data = verifyBuf.toByteArray();
+ verifyBuf.reset();
+ return MessageDigest.isEqual(out, data);
+ } catch (BadPaddingException e) {
+ // e.g. wrong public key used
+ // return false rather than throwing exception
+ return false;
+ } catch (IllegalBlockSizeException e) {
+ throw new SignatureException("doFinal() failed", e);
+ }
+ }
+
+ @Override
+ protected void engineSetParameter(AlgorithmParameterSpec params)
+ throws InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidParameterException("Parameters not supported");
+ }
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ protected void engineSetParameter(String param, Object value)
+ throws InvalidParameterException {
+ throw new InvalidParameterException("Parameters not supported");
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ protected Object engineGetParameter(String param)
+ throws InvalidParameterException {
+ throw new InvalidParameterException("Parameters not supported");
+ }
+}
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
index cc99464dff3..22d5f17c6e0 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
@@ -136,6 +136,12 @@ public SunJCE() {
void putEntries() {
// reuse attribute map and reset before each reuse
HashMap attrs = new HashMap<>(3);
+ attrs.put("SupportedKeyClasses",
+ "java.security.interfaces.RSAPublicKey" +
+ "|java.security.interfaces.RSAPrivateKey");
+ ps("Signature", "NONEwithRSA",
+ "com.sun.crypto.provider.RSACipherAdaptor", null, attrs);
+ // continue adding cipher specific attributes
attrs.put("SupportedModes", "ECB");
attrs.put("SupportedPaddings", "NOPADDING|PKCS1PADDING|OAEPPADDING"
+ "|OAEPWITHMD5ANDMGF1PADDING"
@@ -147,9 +153,6 @@ void putEntries() {
+ "|OAEPWITHSHA-512ANDMGF1PADDING"
+ "|OAEPWITHSHA-512/224ANDMGF1PADDING"
+ "|OAEPWITHSHA-512/256ANDMGF1PADDING");
- attrs.put("SupportedKeyClasses",
- "java.security.interfaces.RSAPublicKey" +
- "|java.security.interfaces.RSAPrivateKey");
ps("Cipher", "RSA",
"com.sun.crypto.provider.RSACipher", null, attrs);
diff --git a/src/java.base/share/classes/java/io/Console.java b/src/java.base/share/classes/java/io/Console.java
index cedb6124a31..c9c47666bf0 100644
--- a/src/java.base/share/classes/java/io/Console.java
+++ b/src/java.base/share/classes/java/io/Console.java
@@ -25,6 +25,7 @@
package java.io;
+import java.lang.annotation.Native;
import java.util.*;
import java.nio.charset.Charset;
import jdk.internal.access.JavaIOAccess;
@@ -545,7 +546,7 @@ public Charset charset() {
* @since 22
*/
public boolean isTerminal() {
- return istty;
+ return isStdinTty() && isStdoutTty();
}
private static UnsupportedOperationException newUnsupportedOperationException() {
@@ -553,7 +554,12 @@ private static UnsupportedOperationException newUnsupportedOperationException()
"Console class itself does not provide implementation");
}
- private static final boolean istty = istty();
+ @Native static final int TTY_STDIN_MASK = 0x00000001;
+ @Native static final int TTY_STDOUT_MASK = 0x00000002;
+ @Native static final int TTY_STDERR_MASK = 0x00000004;
+ // ttyStatus() returns bit patterns above, a bit is set if the corresponding file
+ // descriptor is a character device
+ private static final int ttyStatus = ttyStatus();
private static final Charset STDIN_CHARSET =
Charset.forName(System.getProperty("stdin.encoding"), UTF_8.INSTANCE);
private static final Charset STDOUT_CHARSET =
@@ -565,6 +571,9 @@ private static UnsupportedOperationException newUnsupportedOperationException()
public Console console() {
return cons;
}
+ public boolean isStdinTty() {
+ return Console.isStdinTty();
+ }
});
}
@@ -586,7 +595,7 @@ private static Console instantiateConsole() {
for (var jcp : ServiceLoader.load(ModuleLayer.boot(), JdkConsoleProvider.class)) {
if (consModName.equals(jcp.getClass().getModule().getName())) {
- var jc = jcp.console(istty, STDIN_CHARSET, STDOUT_CHARSET);
+ var jc = jcp.console(isStdinTty() && isStdoutTty(), STDIN_CHARSET, STDOUT_CHARSET);
if (jc != null) {
c = new ProxyingConsole(jc);
}
@@ -597,12 +606,21 @@ private static Console instantiateConsole() {
}
// If not found, default to built-in Console
- if (istty && c == null) {
+ if (isStdinTty() && isStdoutTty() && c == null) {
c = new ProxyingConsole(new JdkConsoleImpl(STDIN_CHARSET, STDOUT_CHARSET));
}
return c;
}
- private static native boolean istty();
+ private static boolean isStdinTty() {
+ return (ttyStatus & TTY_STDIN_MASK) != 0;
+ }
+ private static boolean isStdoutTty() {
+ return (ttyStatus & TTY_STDOUT_MASK) != 0;
+ }
+ private static boolean isStderrTty() {
+ return (ttyStatus & TTY_STDERR_MASK) != 0;
+ }
+ private static native int ttyStatus();
}
diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
index d317557cbb1..f1da102236a 100644
--- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
+++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
@@ -1448,8 +1448,8 @@ public AbstractStringBuilder insert(int dstOffset, CharSequence s,
shift(currValue, coder, count, dstOffset, len);
count += len;
// Coder of CharSequence may be a mismatch, requiring the value array to be inflated
- byte[] newValue = (s instanceof String str)
- ? putStringAt(currValue, coder, count, dstOffset, str, start, end)
+ byte[] newValue = (s instanceof String str && str.length() == len)
+ ? putStringAt(currValue, coder, count, dstOffset, str)
: putCharsAt(currValue, coder, count, dstOffset, s, start, end);
if (currValue != newValue) {
this.coder = UTF16;
@@ -1928,10 +1928,10 @@ private static byte[] inflateIfNeededFor(byte[] value, int count, byte coder, by
* @param index the index to insert the string
* @param str the string
*/
- private static byte[] putStringAt(byte[] value, byte coder, int count, int index, String str, int off, int end) {
+ private static byte[] putStringAt(byte[] value, byte coder, int count, int index, String str) {
byte[] newValue = inflateIfNeededFor(value, count, coder, str.coder());
coder = (newValue == value) ? coder : UTF16;
- str.getBytes(newValue, off, index, coder, end - off);
+ str.getBytes(newValue, 0, index, coder, str.length());
return newValue;
}
diff --git a/src/java.base/share/classes/java/lang/ClassValue.java b/src/java.base/share/classes/java/lang/ClassValue.java
index dfbdf3c5bf8..2a133324fb5 100644
--- a/src/java.base/share/classes/java/lang/ClassValue.java
+++ b/src/java.base/share/classes/java/lang/ClassValue.java
@@ -476,6 +476,9 @@ synchronized Object readAccess(ClassValue classValue) {
if (updated != entry) {
put(classValue.identity, updated);
}
+ // Add to the cache, to enable the fast path, next time.
+ checkCacheLoad();
+ addToCache(classValue, updated);
}
return item;
}
diff --git a/src/java.base/share/classes/java/lang/ScopedValue.java b/src/java.base/share/classes/java/lang/ScopedValue.java
index 206c81d5238..c3d4cab162d 100644
--- a/src/java.base/share/classes/java/lang/ScopedValue.java
+++ b/src/java.base/share/classes/java/lang/ScopedValue.java
@@ -566,7 +566,7 @@ public T get() {
@SuppressWarnings("unchecked")
private T slowGet() {
- var value = findBinding();
+ Object value = scopedValueBindings().find(this);
if (value == Snapshot.NIL) {
throw new NoSuchElementException("ScopedValue not bound");
}
@@ -575,32 +575,35 @@ private T slowGet() {
}
/**
- * {@return {@code true} if this scoped value is bound in the current thread}
+ * Return the value of the scoped value or NIL if not bound.
+ * Consult the cache, and only if the value is not found there
+ * search the list of bindings. Update the cache if the binding
+ * was found.
*/
- public boolean isBound() {
+ private Object findBinding() {
Object[] objects = scopedValueCache();
if (objects != null) {
int n = (hash & Cache.SLOT_MASK) * 2;
if (objects[n] == this) {
- return true;
+ return objects[n + 1];
}
n = ((hash >>> Cache.INDEX_BITS) & Cache.SLOT_MASK) * 2;
if (objects[n] == this) {
- return true;
+ return objects[n + 1];
}
}
- var value = findBinding();
- boolean result = (value != Snapshot.NIL);
- if (result) Cache.put(this, value);
- return result;
+ Object value = scopedValueBindings().find(this);
+ boolean found = (value != Snapshot.NIL);
+ if (found) Cache.put(this, value);
+ return value;
}
/**
- * Return the value of the scoped value or NIL if not bound.
+ * {@return {@code true} if this scoped value is bound in the current thread}
*/
- private Object findBinding() {
- Object value = scopedValueBindings().find(this);
- return value;
+ public boolean isBound() {
+ Object obj = findBinding();
+ return obj != Snapshot.NIL;
}
/**
diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java
index bcdbc39e665..bc42aa8a9fe 100644
--- a/src/java.base/share/classes/java/lang/System.java
+++ b/src/java.base/share/classes/java/lang/System.java
@@ -238,10 +238,11 @@ public static void setErr(PrintStream err) {
private static volatile Console cons;
/**
- * Returns the unique {@link java.io.Console Console} object associated
+ * Returns the unique {@link Console Console} object associated
* with the current Java virtual machine, if any.
*
* @return The system console, if any, otherwise {@code null}.
+ * @see Console
*
* @since 1.6
*/
diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java
index 19465eb32db..b0b134e69a3 100644
--- a/src/java.base/share/classes/java/lang/VirtualThread.java
+++ b/src/java.base/share/classes/java/lang/VirtualThread.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -89,15 +89,19 @@ final class VirtualThread extends BaseVirtualThread {
*
* RUNNING -> PARKING // Thread parking with LockSupport.park
* PARKING -> PARKED // cont.yield successful, parked indefinitely
- * PARKING -> PINNED // cont.yield failed, parked indefinitely on carrier
* PARKED -> UNPARKED // unparked, may be scheduled to continue
- * PINNED -> RUNNING // unparked, continue execution on same carrier
* UNPARKED -> RUNNING // continue execution after park
*
+ * PARKING -> RUNNING // cont.yield failed, need to park on carrier
+ * RUNNING -> PINNED // park on carrier
+ * PINNED -> RUNNING // unparked, continue execution on same carrier
+ *
* RUNNING -> TIMED_PARKING // Thread parking with LockSupport.parkNanos
* TIMED_PARKING -> TIMED_PARKED // cont.yield successful, timed-parked
- * TIMED_PARKING -> TIMED_PINNED // cont.yield failed, timed-parked on carrier
* TIMED_PARKED -> UNPARKED // unparked, may be scheduled to continue
+ *
+ * TIMED_PARKING -> RUNNING // cont.yield failed, need to park on carrier
+ * RUNNING -> TIMED_PINNED // park on carrier
* TIMED_PINNED -> RUNNING // unparked, continue execution on same carrier
*
* RUNNING -> BLOCKING // blocking on monitor enter
@@ -108,7 +112,7 @@ final class VirtualThread extends BaseVirtualThread {
* RUNNING -> WAITING // transitional state during wait on monitor
* WAITING -> WAIT // waiting on monitor
* WAIT -> BLOCKED // notified, waiting to be unblocked by monitor owner
- * WAIT -> UNBLOCKED // timed-out/interrupted
+ * WAIT -> UNBLOCKED // interrupted
*
* RUNNING -> TIMED_WAITING // transition state during timed-waiting on monitor
* TIMED_WAITING -> TIMED_WAIT // timed-waiting on monitor
@@ -838,16 +842,20 @@ private void parkOnCarrierThread(boolean timed, long nanos) {
* Re-enables this virtual thread for scheduling. If this virtual thread is parked
* then its task is scheduled to continue, otherwise its next call to {@code park} or
* {@linkplain #parkNanos(long) parkNanos} is guaranteed not to block.
+ * @param lazySubmit to use lazySubmit if possible
* @throws RejectedExecutionException if the scheduler cannot accept a task
*/
- @Override
- void unpark() {
+ private void unpark(boolean lazySubmit) {
if (!getAndSetParkPermit(true) && currentThread() != this) {
int s = state();
// unparked while parked
if ((s == PARKED || s == TIMED_PARKED) && compareAndSetState(s, UNPARKED)) {
- submitRunContinuation();
+ if (lazySubmit) {
+ lazySubmitRunContinuation();
+ } else {
+ submitRunContinuation();
+ }
return;
}
@@ -870,6 +878,11 @@ void unpark() {
}
}
+ @Override
+ void unpark() {
+ unpark(false);
+ }
+
/**
* Invoked by unblocker thread to unblock this virtual thread.
*/
@@ -886,11 +899,7 @@ private void unblock() {
*/
private void parkTimeoutExpired() {
assert !VirtualThread.currentThread().isVirtual();
- if (!getAndSetParkPermit(true)
- && (state() == TIMED_PARKED)
- && compareAndSetState(TIMED_PARKED, UNPARKED)) {
- lazySubmitRunContinuation();
- }
+ unpark(true);
}
/**
diff --git a/src/java.base/share/classes/java/net/InMemoryCookieStore.java b/src/java.base/share/classes/java/net/InMemoryCookieStore.java
index 0ce3dc5b68c..1c72549e37d 100644
--- a/src/java.base/share/classes/java/net/InMemoryCookieStore.java
+++ b/src/java.base/share/classes/java/net/InMemoryCookieStore.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -25,10 +25,6 @@
package java.net;
-import java.net.URI;
-import java.net.CookieStore;
-import java.net.HttpCookie;
-import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
@@ -72,6 +68,7 @@ public InMemoryCookieStore() {
/**
* Add one cookie into cookie store.
*/
+ @Override
public void add(URI uri, HttpCookie cookie) {
// pre-condition : argument can't be null
if (cookie == null) {
@@ -109,6 +106,7 @@ public void add(URI uri, HttpCookie cookie) {
* 3) not expired.
* See RFC 2965 sec. 3.3.4 for more detail.
*/
+ @Override
public List get(URI uri) {
// argument can't be null
if (uri == null) {
@@ -127,12 +125,13 @@ public List get(URI uri) {
lock.unlock();
}
- return cookies;
+ return Collections.unmodifiableList(cookies);
}
/**
* Get all cookies in cookie store, except those have expired
*/
+ @Override
public List getCookies() {
List rt;
@@ -156,6 +155,7 @@ public List getCookies() {
* Get all URIs, which are associated with at least one cookie
* of this cookie store.
*/
+ @Override
public List getURIs() {
List uris = new ArrayList<>();
@@ -165,7 +165,7 @@ public List getURIs() {
while (it.hasNext()) {
URI uri = it.next();
List cookies = uriIndex.get(uri);
- if (cookies == null || cookies.size() == 0) {
+ if (cookies == null || cookies.isEmpty()) {
// no cookies list or an empty list associated with
// this uri entry, delete it
it.remove();
@@ -176,13 +176,14 @@ public List getURIs() {
lock.unlock();
}
- return uris;
+ return Collections.unmodifiableList(uris);
}
/**
* Remove a cookie from store
*/
+ @Override
public boolean remove(URI uri, HttpCookie ck) {
// argument can't be null
if (ck == null) {
@@ -204,6 +205,7 @@ public boolean remove(URI uri, HttpCookie ck) {
/**
* Remove all cookies in this cookie store.
*/
+ @Override
public boolean removeAll() {
lock.lock();
try {
diff --git a/src/java.base/share/classes/java/nio/Bits.java b/src/java.base/share/classes/java/nio/Bits.java
index b11cb4947db..a9a22d6079e 100644
--- a/src/java.base/share/classes/java/nio/Bits.java
+++ b/src/java.base/share/classes/java/nio/Bits.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -234,4 +234,28 @@ public long getMemoryUsed() {
// of an element by element copy. These numbers may change over time.
static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6;
static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6;
+
+ // Maximum number of bytes to set in one call to {@code Unsafe.setMemory}.
+ // This threshold allows safepoint polling during large memory operations.
+ static final long UNSAFE_SET_THRESHOLD = 1024 * 1024;
+
+ /**
+ * Sets a block of memory starting from a given address to a specified byte value.
+ *
+ * @param srcAddr
+ * the starting memory address
+ * @param count
+ * the number of bytes to set
+ * @param value
+ * the byte value to set
+ */
+ static void setMemory(long srcAddr, long count, byte value) {
+ long offset = 0;
+ while (offset < count) {
+ long len = Math.min(UNSAFE_SET_THRESHOLD, count - offset);
+ UNSAFE.setMemory(srcAddr + offset, len, value);
+ offset += len;
+ }
+ }
+
}
diff --git a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
index 0da1dc66a9a..580109a2eb0 100644
--- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
+++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -114,7 +114,7 @@ class Direct$Type$Buffer$RW$$BO$
Bits.unreserveMemory(size, cap);
throw x;
}
- UNSAFE.setMemory(base, size, (byte) 0);
+ Bits.setMemory(base, size, (byte) 0);
if (pa && (base % ps != 0)) {
// Round up to page boundary
address = base + ps - (base & (ps - 1));
diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java
index 9e50a1588e7..8f3d4ba29fd 100644
--- a/src/java.base/share/classes/java/security/KeyStore.java
+++ b/src/java.base/share/classes/java/security/KeyStore.java
@@ -37,6 +37,7 @@
import javax.security.auth.callback.*;
import sun.security.util.Debug;
+import sun.security.util.CryptoAlgorithmConstraints;
/**
* This class represents a storage facility for cryptographic
@@ -841,12 +842,21 @@ private String getProviderName() {
* the {@link Security#getProviders() Security.getProviders()} method.
*
* @implNote
- * The JDK Reference Implementation additionally uses the
- * {@code jdk.security.provider.preferred}
+ * The JDK Reference Implementation additionally uses
+ *
+ * - the {@code jdk.security.provider.preferred}
* {@link Security#getProperty(String) Security} property to determine
- * the preferred provider order for the specified algorithm. This
+ * the preferred provider order for the specified keystore type. This
* may be different from the order of providers returned by
* {@link Security#getProviders() Security.getProviders()}.
+ *
+ * - the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified keystore type is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
+ *
*
* @param type the type of keystore.
* See the KeyStore section in the Note that the list of registered providers may be retrieved via
* the {@link Security#getProviders() Security.getProviders()} method.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses
+ * the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified keystore type is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
* @param type the type of keystore.
* See the KeyStore section in the
@@ -917,8 +940,15 @@ public static KeyStore getInstance(String type, String provider)
throws KeyStoreException, NoSuchProviderException
{
Objects.requireNonNull(type, "null type name");
- if (provider == null || provider.isEmpty())
+
+ if (provider == null || provider.isEmpty()) {
throw new IllegalArgumentException("missing provider");
+ }
+
+ if (!CryptoAlgorithmConstraints.permits("KEYSTORE", type)) {
+ throw new KeyStoreException(type + " is disabled");
+ }
+
try {
Object[] objs = Security.getImpl(type, "KeyStore", provider);
return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type);
@@ -935,6 +965,14 @@ public static KeyStore getInstance(String type, String provider)
* object is returned. Note that the specified provider object
* does not have to be registered in the provider list.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses
+ * the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified keystore type is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
* @param type the type of keystore.
* See the KeyStore section in the
@@ -963,8 +1001,15 @@ public static KeyStore getInstance(String type, Provider provider)
throws KeyStoreException
{
Objects.requireNonNull(type, "null type name");
- if (provider == null)
+
+ if (provider == null) {
throw new IllegalArgumentException("missing provider");
+ }
+
+ if (!CryptoAlgorithmConstraints.permits("KEYSTORE", type)) {
+ throw new KeyStoreException(type + " is disabled");
+ }
+
try {
Object[] objs = Security.getImpl(type, "KeyStore", provider);
return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type);
@@ -1677,6 +1722,14 @@ public final void setEntry(String alias, Entry entry,
* Note that the list of registered providers may be retrieved via
* the {@link Security#getProviders() Security.getProviders()} method.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses
+ * the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified keystore type is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value. Disallowed type will be skipped.
+ *
* @param file the keystore file
* @param password the keystore password, which may be {@code null}
*
@@ -1730,6 +1783,14 @@ public static final KeyStore getInstance(File file, char[] password)
*
Note that the list of registered providers may be retrieved via
* the {@link Security#getProviders() Security.getProviders()} method.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses
+ * the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified keystore type is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value. Disallowed type will be skipped.
+ *
* @param file the keystore file
* @param param the {@code LoadStoreParameter} that specifies how to load
* the keystore, which may be {@code null}
@@ -1798,8 +1859,12 @@ private static final KeyStore getInstance(File file, char[] password,
kdebug.println(s.getAlgorithm()
+ " keystore detected: " + file);
}
- keystore = new KeyStore(impl, p, s.getAlgorithm());
- break;
+ String ksAlgo = s.getAlgorithm();
+ if (CryptoAlgorithmConstraints.permits(
+ "KEYSTORE", ksAlgo)) {
+ keystore = new KeyStore(impl, p, ksAlgo);
+ break;
+ }
}
} catch (NoSuchAlgorithmException e) {
// ignore
diff --git a/src/java.base/share/classes/java/security/MessageDigest.java b/src/java.base/share/classes/java/security/MessageDigest.java
index fa8d3dea8fd..6e8f64f7ebe 100644
--- a/src/java.base/share/classes/java/security/MessageDigest.java
+++ b/src/java.base/share/classes/java/security/MessageDigest.java
@@ -33,6 +33,7 @@
import sun.security.jca.GetInstance;
import sun.security.util.Debug;
import sun.security.util.MessageDigestSpi2;
+import sun.security.util.CryptoAlgorithmConstraints;
import javax.crypto.SecretKey;
@@ -155,12 +156,22 @@ private MessageDigest(String algorithm, Provider p) {
* the {@link Security#getProviders() Security.getProviders()} method.
*
* @implNote
- * The JDK Reference Implementation additionally uses the
- * {@code jdk.security.provider.preferred}
+ * The JDK Reference Implementation additionally uses the following
+ * security properties:
+ *
+ * - the {@code jdk.security.provider.preferred}
* {@link Security#getProperty(String) Security} property to determine
* the preferred provider order for the specified algorithm. This
* may be different from the order of providers returned by
* {@link Security#getProviders() Security.getProviders()}.
+ *
+ * - the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified algorithm is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
+ *
*
* @param algorithm the name of the algorithm requested.
* See the MessageDigest section in the Note that the list of registered providers may be retrieved via
* the {@link Security#getProviders() Security.getProviders()} method.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses
+ * the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified algorithm is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
* @param algorithm the name of the algorithm requested.
* See the MessageDigest section in the
@@ -246,12 +269,18 @@ public static MessageDigest getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException
{
Objects.requireNonNull(algorithm, "null algorithm name");
- if (provider == null || provider.isEmpty())
+
+ if (provider == null || provider.isEmpty()) {
throw new IllegalArgumentException("missing provider");
+ }
+
+ if (!CryptoAlgorithmConstraints.permits("MessageDigest", algorithm)) {
+ throw new NoSuchAlgorithmException(algorithm + " is disabled");
+ }
- MessageDigest md;
GetInstance.Instance instance = GetInstance.getInstance("MessageDigest",
MessageDigestSpi.class, algorithm, provider);
+ MessageDigest md;
if (instance.impl instanceof MessageDigest messageDigest) {
md = messageDigest;
md.provider = instance.provider;
@@ -271,6 +300,14 @@ public static MessageDigest getInstance(String algorithm, String provider)
* is returned. Note that the specified provider does not
* have to be registered in the provider list.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses
+ * the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified algorithm is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
* @param algorithm the name of the algorithm requested.
* See the MessageDigest section in the
@@ -301,8 +338,15 @@ public static MessageDigest getInstance(String algorithm,
throws NoSuchAlgorithmException
{
Objects.requireNonNull(algorithm, "null algorithm name");
- if (provider == null)
+
+ if (provider == null) {
throw new IllegalArgumentException("missing provider");
+ }
+
+ if (!CryptoAlgorithmConstraints.permits("MessageDigest", algorithm)) {
+ throw new NoSuchAlgorithmException(algorithm + " is disabled");
+ }
+
Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider);
if (objs[0] instanceof MessageDigest md) {
md.provider = (Provider)objs[1];
diff --git a/src/java.base/share/classes/java/security/Signature.java b/src/java.base/share/classes/java/security/Signature.java
index 52aa4328b2c..228d6fff82b 100644
--- a/src/java.base/share/classes/java/security/Signature.java
+++ b/src/java.base/share/classes/java/security/Signature.java
@@ -36,14 +36,12 @@
import java.security.Provider.Service;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.BadPaddingException;
-import javax.crypto.NoSuchPaddingException;
import jdk.internal.access.JavaSecuritySignatureAccess;
import jdk.internal.access.SharedSecrets;
import sun.security.util.Debug;
+import sun.security.util.CryptoAlgorithmConstraints;
+
import sun.security.jca.*;
import sun.security.jca.GetInstance.Instance;
import sun.security.util.KnownOIDs;
@@ -213,20 +211,6 @@ protected Signature(String algorithm) {
this.algorithm = algorithm;
}
- // name of the special signature alg
- private static final String RSA_SIGNATURE = "NONEwithRSA";
-
- // name of the equivalent cipher alg
- private static final String RSA_CIPHER = "RSA/ECB/PKCS1Padding";
-
- // all the services we need to lookup for compatibility with Cipher
- private static final List rsaIds = List.of(
- new ServiceId("Signature", "NONEwithRSA"),
- new ServiceId("Cipher", "RSA/ECB/PKCS1Padding"),
- new ServiceId("Cipher", "RSA/ECB"),
- new ServiceId("Cipher", "RSA//PKCS1Padding"),
- new ServiceId("Cipher", "RSA"));
-
/**
* Returns a {@code Signature} object that implements the specified
* signature algorithm.
@@ -241,12 +225,22 @@ protected Signature(String algorithm) {
* the {@link Security#getProviders() Security.getProviders()} method.
*
* @implNote
- * The JDK Reference Implementation additionally uses the
- * {@code jdk.security.provider.preferred}
+ * The JDK Reference Implementation additionally uses the following
+ * security properties:
+ *
+ * - the {@code jdk.security.provider.preferred}
* {@link Security#getProperty(String) Security} property to determine
* the preferred provider order for the specified algorithm. This
* may be different from the order of providers returned by
* {@link Security#getProviders() Security.getProviders()}.
+ *
+ * - the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified algorithm is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
+ *
*
* @param algorithm the standard name of the algorithm requested.
* See the Signature section in the t;
- if (algorithm.equalsIgnoreCase(RSA_SIGNATURE)) {
- t = GetInstance.getServices(rsaIds);
- } else {
- t = GetInstance.getServices("Signature", algorithm);
+
+ if (!CryptoAlgorithmConstraints.permits("Signature", algorithm)) {
+ throw new NoSuchAlgorithmException(algorithm + " is disabled");
}
+
+ Iterator t = GetInstance.getServices("Signature", algorithm);
if (!t.hasNext()) {
throw new NoSuchAlgorithmException
(algorithm + " Signature not available");
@@ -329,10 +323,6 @@ private static Signature getInstance(Instance instance, String algorithm) {
}
private static boolean isSpi(Service s) {
- if (s.getType().equals("Cipher")) {
- // must be a CipherSpi, which we can wrap with the CipherAdapter
- return true;
- }
String className = s.getClassName();
Boolean result = signatureInfo.get(className);
if (result == null) {
@@ -370,6 +360,14 @@ private static boolean isSpi(Service s) {
* Note that the list of registered providers may be retrieved via
* the {@link Security#getProviders() Security.getProviders()} method.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses
+ * the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified algorithm is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
* @param algorithm the name of the algorithm requested.
* See the Signature section in the
@@ -398,18 +396,11 @@ private static boolean isSpi(Service s) {
public static Signature getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
Objects.requireNonNull(algorithm, "null algorithm name");
- if (algorithm.equalsIgnoreCase(RSA_SIGNATURE)) {
- // exception compatibility with existing code
- if (provider == null || provider.isEmpty()) {
- throw new IllegalArgumentException("missing provider");
- }
- Provider p = Security.getProvider(provider);
- if (p == null) {
- throw new NoSuchProviderException
- ("no such provider: " + provider);
- }
- return getInstanceRSA(p);
+
+ if (!CryptoAlgorithmConstraints.permits("Signature", algorithm)) {
+ throw new NoSuchAlgorithmException(algorithm + " is disabled");
}
+
Instance instance = GetInstance.getInstance
("Signature", SignatureSpi.class, algorithm, provider);
return getInstance(instance, algorithm);
@@ -424,6 +415,14 @@ public static Signature getInstance(String algorithm, String provider)
* is returned. Note that the specified provider does not
* have to be registered in the provider list.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses
+ * the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified algorithm is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
* @param algorithm the name of the algorithm requested.
* See the Signature section in the
@@ -450,40 +449,16 @@ public static Signature getInstance(String algorithm, String provider)
public static Signature getInstance(String algorithm, Provider provider)
throws NoSuchAlgorithmException {
Objects.requireNonNull(algorithm, "null algorithm name");
- if (algorithm.equalsIgnoreCase(RSA_SIGNATURE)) {
- // exception compatibility with existing code
- if (provider == null) {
- throw new IllegalArgumentException("missing provider");
- }
- return getInstanceRSA(provider);
+
+ if (!CryptoAlgorithmConstraints.permits("Signature", algorithm)) {
+ throw new NoSuchAlgorithmException(algorithm + " is disabled");
}
+
Instance instance = GetInstance.getInstance
("Signature", SignatureSpi.class, algorithm, provider);
return getInstance(instance, algorithm);
}
- // return an implementation for NONEwithRSA, which is a special case
- // because of the Cipher.RSA/ECB/PKCS1Padding compatibility wrapper
- private static Signature getInstanceRSA(Provider p)
- throws NoSuchAlgorithmException {
- // try Signature first
- Service s = p.getService("Signature", RSA_SIGNATURE);
- if (s != null) {
- Instance instance = GetInstance.getInstance(s, SignatureSpi.class);
- return getInstance(instance, RSA_SIGNATURE);
- }
- // check Cipher
- try {
- Cipher c = Cipher.getInstance(RSA_CIPHER, p);
- return Delegate.of(new CipherAdapter(c), RSA_SIGNATURE);
- } catch (GeneralSecurityException e) {
- // throw Signature style exception message to avoid confusion,
- // but append Cipher exception as cause
- throw new NoSuchAlgorithmException("no such algorithm: "
- + RSA_SIGNATURE + " for provider " + p.getName(), e);
- }
- }
-
/**
* Returns the provider of this {@code Signature} object.
*
@@ -1179,22 +1154,12 @@ public Object clone() throws CloneNotSupportedException {
private static SignatureSpi newInstance(Service s)
throws NoSuchAlgorithmException {
- if (s.getType().equals("Cipher")) {
- // must be NONEwithRSA
- try {
- Cipher c = Cipher.getInstance(RSA_CIPHER, s.getProvider());
- return new CipherAdapter(c);
- } catch (NoSuchPaddingException e) {
- throw new NoSuchAlgorithmException(e);
- }
- } else {
- Object o = s.newInstance(null);
- if (!(o instanceof SignatureSpi)) {
- throw new NoSuchAlgorithmException
- ("Not a SignatureSpi: " + o.getClass().getName());
- }
- return (SignatureSpi)o;
+ Object o = s.newInstance(null);
+ if (!(o instanceof SignatureSpi)) {
+ throw new NoSuchAlgorithmException
+ ("Not a SignatureSpi: " + o.getClass().getName());
}
+ return (SignatureSpi)o;
}
// max number of debug warnings to print from chooseFirstProvider()
@@ -1471,92 +1436,4 @@ protected AlgorithmParameters engineGetParameters() {
return sigSpi.engineGetParameters();
}
}
-
- // adapter for RSA/ECB/PKCS1Padding ciphers
- @SuppressWarnings("deprecation")
- private static class CipherAdapter extends SignatureSpi {
-
- private final Cipher cipher;
-
- private ByteArrayOutputStream data;
-
- CipherAdapter(Cipher cipher) {
- this.cipher = cipher;
- }
-
- protected void engineInitVerify(PublicKey publicKey)
- throws InvalidKeyException {
- cipher.init(Cipher.DECRYPT_MODE, publicKey);
- if (data == null) {
- data = new ByteArrayOutputStream(128);
- } else {
- data.reset();
- }
- }
-
- protected void engineInitSign(PrivateKey privateKey)
- throws InvalidKeyException {
- cipher.init(Cipher.ENCRYPT_MODE, privateKey);
- data = null;
- }
-
- protected void engineInitSign(PrivateKey privateKey,
- SecureRandom random) throws InvalidKeyException {
- cipher.init(Cipher.ENCRYPT_MODE, privateKey, random);
- data = null;
- }
-
- protected void engineUpdate(byte b) throws SignatureException {
- engineUpdate(new byte[] {b}, 0, 1);
- }
-
- protected void engineUpdate(byte[] b, int off, int len)
- throws SignatureException {
- if (data != null) {
- data.write(b, off, len);
- return;
- }
- byte[] out = cipher.update(b, off, len);
- if ((out != null) && (out.length != 0)) {
- throw new SignatureException
- ("Cipher unexpectedly returned data");
- }
- }
-
- protected byte[] engineSign() throws SignatureException {
- try {
- return cipher.doFinal();
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- throw new SignatureException("doFinal() failed", e);
- }
- }
-
- protected boolean engineVerify(byte[] sigBytes)
- throws SignatureException {
- try {
- byte[] out = cipher.doFinal(sigBytes);
- byte[] dataBytes = data.toByteArray();
- data.reset();
- return MessageDigest.isEqual(out, dataBytes);
- } catch (BadPaddingException e) {
- // e.g. wrong public key used
- // return false rather than throwing exception
- return false;
- } catch (IllegalBlockSizeException e) {
- throw new SignatureException("doFinal() failed", e);
- }
- }
-
- protected void engineSetParameter(String param, Object value)
- throws InvalidParameterException {
- throw new InvalidParameterException("Parameters not supported");
- }
-
- protected Object engineGetParameter(String param)
- throws InvalidParameterException {
- throw new InvalidParameterException("Parameters not supported");
- }
-
- }
-
}
diff --git a/src/java.base/share/classes/java/text/Collator.java b/src/java.base/share/classes/java/text/Collator.java
index 7f6db310366..9b8ab0e5d9a 100644
--- a/src/java.base/share/classes/java/text/Collator.java
+++ b/src/java.base/share/classes/java/text/Collator.java
@@ -111,8 +111,13 @@
*
* @apiNote {@code CollationKey}s from different
* {@code Collator}s can not be compared. See the class description
- * for {@link CollationKey}
- * for an example using {@code CollationKey}s.
+ * for {@link CollationKey} for an example using {@code CollationKey}s.
+ *
+ * @implNote Significant thread contention may occur during concurrent usage
+ * of the JDK Reference Implementation's {@link RuleBasedCollator}, which is the
+ * subtype returned by the default provider of the {@link #getInstance()} factory
+ * methods. As such, users should consider retrieving a separate instance for
+ * each thread when used in multithreaded environments.
*
* @see RuleBasedCollator
* @see CollationKey
diff --git a/src/java.base/share/classes/java/text/CompactNumberFormat.java b/src/java.base/share/classes/java/text/CompactNumberFormat.java
index fae11cbdba1..b14d9645f8a 100644
--- a/src/java.base/share/classes/java/text/CompactNumberFormat.java
+++ b/src/java.base/share/classes/java/text/CompactNumberFormat.java
@@ -247,38 +247,43 @@ public final class CompactNumberFormat extends NumberFormat {
/**
* List of positive prefix patterns of this formatter's
- * compact number patterns.
+ * compact number patterns. This field is a read-only
+ * constant once initialized.
*/
private transient List positivePrefixPatterns;
/**
* List of negative prefix patterns of this formatter's
- * compact number patterns.
+ * compact number patterns. This field is a read-only
+ * constant once initialized.
*/
private transient List negativePrefixPatterns;
/**
* List of positive suffix patterns of this formatter's
- * compact number patterns.
+ * compact number patterns. This field is a read-only
+ * constant once initialized.
*/
private transient List positiveSuffixPatterns;
/**
* List of negative suffix patterns of this formatter's
- * compact number patterns.
+ * compact number patterns. This field is a read-only
+ * constant once initialized.
*/
private transient List negativeSuffixPatterns;
/**
* List of divisors of this formatter's compact number patterns.
* Divisor can be either Long or BigInteger (if the divisor value goes
- * beyond long boundary)
+ * beyond long boundary). This field is a read-only constant
+ * once initialized.
*/
private transient List divisors;
/**
* List of place holders that represent minimum integer digits at each index
- * for each count.
+ * for each count. This field is a read-only constant once initialized.
*/
private transient List placeHolderPatterns;
@@ -371,7 +376,7 @@ public final class CompactNumberFormat extends NumberFormat {
/**
* The map for plural rules that maps LDML defined tags (e.g. "one") to
- * its rule.
+ * its rule. This field is a read-only constant once initialized.
*/
private transient Map rulesMap;
@@ -1512,7 +1517,7 @@ private void applyPattern(String count, String pattern, int index) {
}
}
- private final transient DigitList digitList = new DigitList();
+ private transient DigitList digitList = new DigitList();
private static final int STATUS_INFINITE = 0;
private static final int STATUS_POSITIVE = 1;
private static final int STATUS_LENGTH = 2;
@@ -2504,8 +2509,14 @@ public String toString() {
@Override
public CompactNumberFormat clone() {
CompactNumberFormat other = (CompactNumberFormat) super.clone();
+
+ // Cloning reference fields. Other fields (e.g., "positivePrefixPatterns")
+ // are not cloned since they are read-only constants after initialization.
other.compactPatterns = compactPatterns.clone();
other.symbols = (DecimalFormatSymbols) symbols.clone();
+ other.decimalFormat = (DecimalFormat) decimalFormat.clone();
+ other.defaultDecimalFormat = (DecimalFormat) defaultDecimalFormat.clone();
+ other.digitList = (DigitList) digitList.clone();
return other;
}
diff --git a/src/java.base/share/classes/java/text/DigitList.java b/src/java.base/share/classes/java/text/DigitList.java
index d757f03bb84..fb012e86f6e 100644
--- a/src/java.base/share/classes/java/text/DigitList.java
+++ b/src/java.base/share/classes/java/text/DigitList.java
@@ -361,6 +361,11 @@ private void set(boolean isNegative, String s,
decimalAt += exponent - leadingZerosAfterDecimal;
}
+ // Eliminate trailing zeros.
+ while (count > 1 && digits[count - 1] == '0') {
+ --count;
+ }
+
if (fixedPoint) {
// The negative of the exponent represents the number of leading
// zeros between the decimal and the first non-zero digit, for
@@ -387,11 +392,6 @@ private void set(boolean isNegative, String s,
// else fall through
}
- // Eliminate trailing zeros.
- while (count > 1 && digits[count - 1] == '0') {
- --count;
- }
-
// Eliminate digits beyond maximum digits to be displayed.
// Round up if appropriate.
round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits,
diff --git a/src/java.base/share/classes/java/text/RuleBasedCollator.java b/src/java.base/share/classes/java/text/RuleBasedCollator.java
index dc45dafb846..af1b6b62bdf 100644
--- a/src/java.base/share/classes/java/text/RuleBasedCollator.java
+++ b/src/java.base/share/classes/java/text/RuleBasedCollator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -38,10 +38,6 @@
package java.text;
-import java.text.Normalizer;
-import java.util.Vector;
-import java.util.Locale;
-
/**
* The {@code RuleBasedCollator} class is a concrete subclass of
* {@code Collator} that provides a simple, data-driven, table
@@ -239,6 +235,11 @@
*
*
*
+ * @implNote For this implementation, concurrent usage of this class may
+ * lead to significant thread contention since {@code synchronized} is employed
+ * to ensure thread-safety. As such, users of this class should consider creating
+ * a separate instance for each thread when used in multithreaded environments.
+ *
* @see Collator
* @see CollationElementIterator
* @author Helena Shih, Laura Werner, Richard Gillam
diff --git a/src/java.base/share/classes/java/time/LocalDate.java b/src/java.base/share/classes/java/time/LocalDate.java
index 6724410da2b..016bdab5394 100644
--- a/src/java.base/share/classes/java/time/LocalDate.java
+++ b/src/java.base/share/classes/java/time/LocalDate.java
@@ -182,11 +182,11 @@ public final class LocalDate
/**
* @serial The month-of-year.
*/
- private final byte month;
+ private final short month;
/**
* @serial The day-of-month.
*/
- private final byte day;
+ private final short day;
//-----------------------------------------------------------------------
/**
@@ -490,8 +490,8 @@ private static LocalDate resolvePreviousValid(int year, int month, int day) {
*/
private LocalDate(int year, int month, int dayOfMonth) {
this.year = year;
- this.month = (byte) month;
- this.day = (byte) dayOfMonth;
+ this.month = (short) month;
+ this.day = (short) dayOfMonth;
}
//-----------------------------------------------------------------------
diff --git a/src/java.base/share/classes/java/time/MonthDay.java b/src/java.base/share/classes/java/time/MonthDay.java
index 6244c14e6e1..1de4fa84d3e 100644
--- a/src/java.base/share/classes/java/time/MonthDay.java
+++ b/src/java.base/share/classes/java/time/MonthDay.java
@@ -146,11 +146,11 @@ public final class MonthDay
/**
* @serial The month-of-year, not null.
*/
- private final byte month;
+ private final int month;
/**
* @serial The day-of-month.
*/
- private final byte day;
+ private final int day;
//-----------------------------------------------------------------------
/**
@@ -319,8 +319,8 @@ public static MonthDay parse(CharSequence text, DateTimeFormatter formatter) {
* @param dayOfMonth the day-of-month to represent, validated from 1 to 29-31
*/
private MonthDay(int month, int dayOfMonth) {
- this.month = (byte) month;
- this.day = (byte) dayOfMonth;
+ this.month = month;
+ this.day = dayOfMonth;
}
//-----------------------------------------------------------------------
diff --git a/src/java.base/share/classes/java/time/YearMonth.java b/src/java.base/share/classes/java/time/YearMonth.java
index b24151de3f0..8ad1172811f 100644
--- a/src/java.base/share/classes/java/time/YearMonth.java
+++ b/src/java.base/share/classes/java/time/YearMonth.java
@@ -153,7 +153,7 @@ public final class YearMonth
/**
* @serial The month-of-year, not null.
*/
- private final byte month;
+ private final int month;
//-----------------------------------------------------------------------
/**
@@ -306,7 +306,7 @@ public static YearMonth parse(CharSequence text, DateTimeFormatter formatter) {
*/
private YearMonth(int year, int month) {
this.year = year;
- this.month = (byte) month;
+ this.month = month;
}
/**
diff --git a/src/java.base/share/classes/java/time/chrono/HijrahDate.java b/src/java.base/share/classes/java/time/chrono/HijrahDate.java
index 2d3e4f93e69..114a47e4797 100644
--- a/src/java.base/share/classes/java/time/chrono/HijrahDate.java
+++ b/src/java.base/share/classes/java/time/chrono/HijrahDate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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
@@ -137,11 +137,11 @@ public final class HijrahDate
/**
* The month-of-year.
*/
- private final transient byte monthOfYear;
+ private final transient int monthOfYear;
/**
* The day-of-month.
*/
- private final transient byte dayOfMonth;
+ private final transient int dayOfMonth;
//-------------------------------------------------------------------------
/**
@@ -273,8 +273,8 @@ private HijrahDate(HijrahChronology chrono, int prolepticYear, int monthOfYear,
this.chrono = chrono;
this.prolepticYear = prolepticYear;
- this.monthOfYear = (byte) monthOfYear;
- this.dayOfMonth = (byte) dayOfMonth;
+ this.monthOfYear = monthOfYear;
+ this.dayOfMonth = dayOfMonth;
}
/**
@@ -287,8 +287,8 @@ private HijrahDate(HijrahChronology chrono, long epochDay) {
this.chrono = chrono;
this.prolepticYear = dateInfo[0];
- this.monthOfYear = (byte) dateInfo[1];
- this.dayOfMonth = (byte) dateInfo[2];
+ this.monthOfYear = dateInfo[1];
+ this.dayOfMonth = dateInfo[2];
}
//-----------------------------------------------------------------------
diff --git a/src/java.base/share/classes/java/util/GregorianCalendar.java b/src/java.base/share/classes/java/util/GregorianCalendar.java
index 9c75cbc5732..26b0c7c1f2f 100644
--- a/src/java.base/share/classes/java/util/GregorianCalendar.java
+++ b/src/java.base/share/classes/java/util/GregorianCalendar.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -1214,8 +1214,10 @@ public void roll(int field, int amount) {
d.setHours(hourOfDay);
time = calsys.getTime(d);
- // If we stay on the same wall-clock time, try the next or previous hour.
- if (internalGet(HOUR_OF_DAY) == d.getHours()) {
+ // If the rolled amount is not a full HOUR/HOUR_OF_DAY (12/24-hour) cycle and
+ // if we stay on the same wall-clock time, try the next or previous hour.
+ if (((field == HOUR_OF_DAY && amount % 24 != 0) || (field == HOUR && amount % 12 != 0))
+ && internalGet(HOUR_OF_DAY) == d.getHours()) {
hourOfDay = getRolledValue(rolledValue, amount > 0 ? +1 : -1, min, max);
if (field == HOUR && internalGet(AM_PM) == PM) {
hourOfDay += 12;
diff --git a/src/java.base/share/classes/java/util/SimpleTimeZone.java b/src/java.base/share/classes/java/util/SimpleTimeZone.java
index 58d43692ee0..f047ce26e58 100644
--- a/src/java.base/share/classes/java/util/SimpleTimeZone.java
+++ b/src/java.base/share/classes/java/util/SimpleTimeZone.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -112,15 +112,15 @@
*
* // Base GMT offset: -8:00
* // DST starts: at 2:00am in standard time
- * // on the first Sunday in April
+ * // on the second Sunday in March
* // DST ends: at 2:00am in daylight time
- * // on the last Sunday in October
+ * // on the first Sunday in November
* // Save: 1 hour
* SimpleTimeZone(-28800000,
* "America/Los_Angeles",
- * Calendar.APRIL, 1, -Calendar.SUNDAY,
+ * Calendar.MARCH, 8, -Calendar.SUNDAY,
* 7200000,
- * Calendar.OCTOBER, -1, Calendar.SUNDAY,
+ * Calendar.NOVEMBER, 1, -Calendar.SUNDAY,
* 7200000,
* 3600000)
*
@@ -863,13 +863,24 @@ public Object clone()
}
/**
- * Generates the hash code for the SimpleDateFormat object.
+ * Generates the hash code for the SimpleTimeZone object.
* @return the hash code for this object
*/
public int hashCode()
{
- return startMonth ^ startDay ^ startDayOfWeek ^ startTime ^
- endMonth ^ endDay ^ endDayOfWeek ^ endTime ^ rawOffset;
+ int hash = 31 * getID().hashCode() + rawOffset;
+ hash = 31 * hash + Boolean.hashCode(useDaylight);
+ if (useDaylight) {
+ hash = 31 * hash + startMonth;
+ hash = 31 * hash + startDay;
+ hash = 31 * hash + startDayOfWeek;
+ hash = 31 * hash + startTime;
+ hash = 31 * hash + endMonth;
+ hash = 31 * hash + endDay;
+ hash = 31 * hash + endDayOfWeek;
+ hash = 31 * hash + endTime;
+ }
+ return hash;
}
/**
diff --git a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
index 7503c154ddb..1338f2fd804 100644
--- a/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
+++ b/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
@@ -1904,8 +1904,8 @@ private Object waitingGet(boolean interruptible) {
while ((r = result) == null) {
if (q == null) {
q = new Signaller(interruptible, 0L, 0L);
- if (Thread.currentThread() instanceof ForkJoinWorkerThread)
- ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q);
+ if (Thread.currentThread() instanceof ForkJoinWorkerThread wt)
+ ForkJoinPool.helpAsyncBlocker(wt.pool, q);
}
else if (!queued)
queued = tryPushStack(q);
@@ -1950,8 +1950,8 @@ else if (nanos <= 0L)
break;
else if (q == null) {
q = new Signaller(true, nanos, deadline);
- if (Thread.currentThread() instanceof ForkJoinWorkerThread)
- ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q);
+ if (Thread.currentThread() instanceof ForkJoinWorkerThread wt)
+ ForkJoinPool.helpAsyncBlocker(wt.pool, q);
}
else if (!queued)
queued = tryPushStack(q);
diff --git a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java
index cef1682b0b1..abc9fdb348c 100644
--- a/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java
+++ b/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArraySet.java
@@ -35,6 +35,13 @@
package java.util.concurrent;
+import jdk.internal.misc.Unsafe;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamException;
+import java.io.Serial;
+import java.io.StreamCorruptedException;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
@@ -445,4 +452,38 @@ public Spliterator spliterator() {
return Spliterators.spliterator
(al.getArray(), Spliterator.IMMUTABLE | Spliterator.DISTINCT);
}
+
+ /**
+ * De-serialization without data not supported for this class.
+ */
+ @Serial
+ private void readObjectNoData() throws ObjectStreamException {
+ throw new StreamCorruptedException("Deserialized CopyOnWriteArraySet requires data");
+ }
+
+ /**
+ * Reconstitutes the {@code CopyOnWriteArraySet} instance from a stream
+ * (that is, deserializes it).
+ * @throws StreamCorruptedException if the object read from the stream is invalid.
+ */
+ @Serial
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ CopyOnWriteArrayList newAl; // Set during the duplicate check
+
+ @SuppressWarnings("unchecked")
+ CopyOnWriteArrayList inAl = (CopyOnWriteArrayList) in.readFields().get("al", null);
+
+ if (inAl == null
+ || inAl.getClass() != CopyOnWriteArrayList.class
+ || (newAl = new CopyOnWriteArrayList<>()).addAllAbsent(inAl) != inAl.size()) {
+ throw new StreamCorruptedException("Content is invalid");
+ }
+
+ final Unsafe U = Unsafe.getUnsafe();
+ U.putReference(
+ this,
+ U.objectFieldOffset(CopyOnWriteArraySet.class, "al"),
+ newAl
+ );
+ }
}
diff --git a/src/java.base/share/classes/java/util/concurrent/DelayScheduler.java b/src/java.base/share/classes/java/util/concurrent/DelayScheduler.java
index 358d3b69f1e..339249d8eb7 100644
--- a/src/java.base/share/classes/java/util/concurrent/DelayScheduler.java
+++ b/src/java.base/share/classes/java/util/concurrent/DelayScheduler.java
@@ -360,31 +360,36 @@ private static int replace(ScheduledForkJoinTask>[] h, int k, int n) {
u.heapIndex = -1;
}
}
- if (t != null) { // sift down
- for (int cs; (cs = (k << 2) + 1) < n; ) {
- ScheduledForkJoinTask> leastChild = null, c;
+ if (t != null) {
+ while (k > 0) { // sift up if replaced with smaller value
+ ScheduledForkJoinTask> parent; int pk;
+ if ((parent = h[pk = (k - 1) >>> 2]) == null ||
+ parent.when <= d)
+ break;
+ parent.heapIndex = k;
+ h[k] = parent;
+ k = pk;
+ }
+ for (int cs; (cs = (k << 2) + 1) < n; ) { // sift down
+ ScheduledForkJoinTask> leastChild = null;
int leastIndex = 0;
- long leastValue = Long.MAX_VALUE;
- for (int ck = cs, j = 4;;) { // at most 4 children
- if ((c = h[ck]) == null)
- break;
- long cd = c.when;
- if (c.status < 0 && alsoReplace < 0) {
- alsoReplace = ck; // at most once per pass
- c.heapIndex = -1;
- }
- else if (leastChild == null || cd < leastValue) {
+ long leastValue = d; // at most 4 children
+ for (int ck, j = 0; j < 4 && (ck = j + cs) < n; ++j) {
+ ScheduledForkJoinTask> c; long cd;
+ if ((c = h[ck]) != null && (cd = c.when) < leastValue) {
leastValue = cd;
leastIndex = ck;
leastChild = c;
}
- if (--j == 0 || ++ck >= n)
- break;
}
- if (leastChild == null || d <= leastValue)
+ if (leastChild == null) // already ordered
break;
- leastChild.heapIndex = k;
- h[k] = leastChild;
+ if ((h[k] = leastChild).status >= 0 || alsoReplace >= 0)
+ leastChild.heapIndex = k;
+ else {
+ leastChild.heapIndex = -1;
+ alsoReplace = k;
+ }
k = leastIndex;
}
t.heapIndex = k;
@@ -393,6 +398,7 @@ else if (leastChild == null || cd < leastValue) {
k = alsoReplace;
}
}
+ assert checkHeap(h, n);
return n;
}
@@ -451,6 +457,33 @@ private static void cancelAll(ScheduledForkJoinTask>[] h, int n) {
}
}
+ /**
+ * Invariant checks
+ */
+ private static boolean checkHeap(ScheduledForkJoinTask>[] h, int n) {
+ for (int i = 0; i < h.length; ++i) {
+ ScheduledForkJoinTask> t = h[i];
+ if (t == null) { // unused slots all null
+ if (i < n)
+ return false;
+ }
+ else {
+ long v = t.when;
+ int x = t.heapIndex;
+ if (x != i && x >= 0) // valid index unless removing
+ return false;
+ if (i > 0 && h[(i - 1) >>> 2].when > v) // ordered wrt parent
+ return false;
+ int cs = (i << 2) + 1; // ordered wrt children
+ for (int ck, j = 0; j < 4 && (ck = cs + j) < n; ++j) {
+ if (h[ck].when < v)
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
/**
* Task class for DelayScheduler operations
*/
diff --git a/src/java.base/share/classes/java/util/concurrent/Executors.java b/src/java.base/share/classes/java/util/concurrent/Executors.java
index ba7c2e1efee..b3ffcdfb4ef 100644
--- a/src/java.base/share/classes/java/util/concurrent/Executors.java
+++ b/src/java.base/share/classes/java/util/concurrent/Executors.java
@@ -755,6 +755,13 @@ public void shutdown() {
super.shutdown();
cleanable.clean(); // unregisters the cleanable
}
+
+ @Override
+ public List shutdownNow() {
+ List unexecuted = super.shutdownNow();
+ cleanable.clean(); // unregisters the cleanable
+ return unexecuted;
+ }
}
/**
diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
index a7821921bc9..cdc5e0b9ea6 100644
--- a/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
+++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
@@ -1297,7 +1297,7 @@ final void push(ForkJoinTask> task, ForkJoinPool pool, boolean internal) {
unlockPhase();
if (room < 0)
throw new RejectedExecutionException("Queue capacity exceeded");
- if ((room == 0 || a[m & (s - pk)] == null) &&
+ if ((room == 0 || U.getReferenceAcquire(a, slotOffset(m & (s - pk))) == null) &&
pool != null)
pool.signalWork(); // may have appeared empty
}
diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java
index 51b2488264e..137cac45ed0 100644
--- a/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java
+++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java
@@ -531,19 +531,22 @@ final void doExec() {
* still correct, although it may contain a misleading stack
* trace.
*
- * @param asExecutionException true if wrap as ExecutionException
+ * @param asExecutionException true if wrap the result as an
+ * ExecutionException. This applies only to actual exceptions, not
+ * implicit CancellationExceptions issued when not THROWN or
+ * available, which are not wrapped because by default they are
+ * issued separately from ExecutionExceptions by callers. Which
+ * may require further handling when this is not true (currently
+ * only in InvokeAnyTask).
* @return the exception, or null if none
*/
private Throwable getException(boolean asExecutionException) {
int s; Throwable ex; Aux a;
if ((s = status) >= 0 || (s & ABNORMAL) == 0)
return null;
- else if ((s & THROWN) == 0 || (a = aux) == null || (ex = a.ex) == null) {
- ex = new CancellationException();
- if (!asExecutionException || !(this instanceof InterruptibleTask))
- return ex; // else wrap below
- }
- else if (a.thread != Thread.currentThread()) {
+ if ((s & THROWN) == 0 || (a = aux) == null || (ex = a.ex) == null)
+ return new CancellationException();
+ if (a.thread != Thread.currentThread()) {
try {
Constructor> noArgCtor = null, oneArgCtor = null;
for (Constructor> c : ex.getClass().getConstructors()) {
@@ -1814,6 +1817,8 @@ final T invokeAny(Collection extends Callable> tasks,
(t = new InvokeAnyTask(c, this, t)));
}
return timed ? get(nanos, TimeUnit.NANOSECONDS) : get();
+ } catch (CancellationException ce) {
+ throw new ExecutionException(ce);
} finally {
for (; t != null; t = t.pred)
t.onRootCompletion();
diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java
index b942d3ecd09..566fc417952 100644
--- a/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java
+++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java
@@ -267,10 +267,8 @@ public void setUncaughtExceptionHandler(UncaughtExceptionHandler x) { }
@Override // to record changes
public void setContextClassLoader(ClassLoader cl) {
- if (ClassLoader.getSystemClassLoader() != cl) {
- resetCCL = true;
- super.setContextClassLoader(cl);
- }
+ resetCCL = ClassLoader.getSystemClassLoader() != cl;
+ super.setContextClassLoader(cl);
}
@Override // to re-establish CCL if necessary
diff --git a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java
index ab7ea53793f..ebcb9e3204c 100644
--- a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java
+++ b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -79,7 +79,11 @@ public GZIPInputStream(InputStream in, int size) throws IOException {
super(in, createInflater(in, size), size);
usesDefaultInflater = true;
try {
- readHeader(in);
+ // we don't expect the stream to be at EOF
+ // and if it is, then we want readHeader to
+ // raise an exception, so we pass "true" for
+ // the "failOnEOF" param.
+ readHeader(in, true);
} catch (IOException ioe) {
this.inf.end();
throw ioe;
@@ -190,12 +194,40 @@ public void close() throws IOException {
/*
* Reads GZIP member header and returns the total byte number
* of this member header.
+ * If failOnEOF is false and if the given InputStream has already
+ * reached EOF when this method was invoked, then this method returns
+ * -1 (indicating that there's no GZIP member header).
+ * In all other cases of malformed header or EOF being detected
+ * when reading the header, this method will throw an IOException.
*/
- private int readHeader(InputStream this_in) throws IOException {
+ private int readHeader(InputStream this_in, boolean failOnEOF) throws IOException {
CheckedInputStream in = new CheckedInputStream(this_in, crc);
crc.reset();
+
+ int magic;
+ if (!failOnEOF) {
+ // read an unsigned short value representing the GZIP magic header.
+ // this is the same as calling readUShort(in), except that here,
+ // when reading the first byte, we don't raise an EOFException
+ // if the stream has already reached EOF.
+
+ // read unsigned byte
+ int b = in.read();
+ if (b == -1) { // EOF
+ crc.reset();
+ return -1; // represents no header bytes available
+ }
+ checkUnexpectedByte(b);
+ // read the next unsigned byte to form the unsigned
+ // short. we throw the usual EOFException/ZipException
+ // from this point on if there is no more data or
+ // the data doesn't represent a header.
+ magic = (readUByte(in) << 8) | b;
+ } else {
+ magic = readUShort(in);
+ }
// Check header magic
- if (readUShort(in) != GZIP_MAGIC) {
+ if (magic != GZIP_MAGIC) {
throw new ZipException("Not in GZIP format");
}
// Check compression method
@@ -261,7 +293,11 @@ public void close() throws IOException {}
// try concatenated case
int m = 8; // this.trailer
try {
- m += readHeader(in); // next.header
+ int numNextHeaderBytes = readHeader(in, false); // next.header (if available)
+ if (numNextHeaderBytes == -1) {
+ return true; // end of stream reached
+ }
+ m += numNextHeaderBytes;
} catch (IOException ze) {
return true; // ignore any malformed, do nothing
}
@@ -295,12 +331,16 @@ private int readUByte(InputStream in) throws IOException {
if (b == -1) {
throw new EOFException();
}
+ checkUnexpectedByte(b);
+ return b;
+ }
+
+ private void checkUnexpectedByte(final int b) throws IOException {
if (b < -1 || b > 255) {
- // Report on this.in, not argument in; see read{Header, Trailer}.
+ // report the InputStream type which returned this unexpected byte
throw new IOException(this.in.getClass().getName()
- + ".read() returned value out of range -1..255: " + b);
+ + ".read() returned value out of range -1..255: " + b);
}
- return b;
}
private byte[] tmpbuf = new byte[128];
diff --git a/src/java.base/share/classes/javax/crypto/Cipher.java b/src/java.base/share/classes/javax/crypto/Cipher.java
index 3de732c6687..e00d17e2549 100644
--- a/src/java.base/share/classes/javax/crypto/Cipher.java
+++ b/src/java.base/share/classes/javax/crypto/Cipher.java
@@ -30,7 +30,6 @@
import java.util.concurrent.ConcurrentMap;
import java.util.regex.*;
-
import java.security.*;
import java.security.Provider.Service;
import java.security.spec.AlgorithmParameterSpec;
@@ -46,6 +45,7 @@
import sun.security.util.Debug;
import sun.security.jca.*;
import sun.security.util.KnownOIDs;
+import sun.security.util.CryptoAlgorithmConstraints;
/**
* This class provides the functionality of a cryptographic cipher for
@@ -316,19 +316,23 @@ private Cipher(CipherSpi firstSpi, Service firstService,
private static final String SHA512TRUNCATED = "SHA512/2";
+ // Parse the specified cipher transformation for algorithm and the
+ // optional mode and padding. If the transformation contains only
+ // algorithm, then only algorithm is returned. Otherwise, the
+ // transformation must contain all 3 and they must be non-empty.
private static String[] tokenizeTransformation(String transformation)
throws NoSuchAlgorithmException {
if (transformation == null) {
throw new NoSuchAlgorithmException("No transformation given");
}
+
/*
- * array containing the components of a cipher transformation:
+ * Components of a cipher transformation:
*
- * index 0: algorithm component (e.g., AES)
- * index 1: feedback component (e.g., CFB)
- * index 2: padding component (e.g., PKCS5Padding)
+ * 1) algorithm component (e.g., AES)
+ * 2) feedback component (e.g., CFB) - optional
+ * 3) padding component (e.g., PKCS5Padding) - optional
*/
- String[] parts = { "", "", "" };
// check if the transformation contains algorithms with "/" in their
// name which can cause the parsing logic to go wrong
@@ -337,27 +341,35 @@ private static String[] tokenizeTransformation(String transformation)
int startIdx = (sha512Idx == -1 ? 0 :
sha512Idx + SHA512TRUNCATED.length());
int endIdx = transformation.indexOf('/', startIdx);
- if (endIdx == -1) {
- // algorithm
- parts[0] = transformation.trim();
+
+ boolean algorithmOnly = (endIdx == -1);
+ String algo = (algorithmOnly ? transformation.trim() :
+ transformation.substring(0, endIdx).trim());
+ if (algo.isEmpty()) {
+ throw new NoSuchAlgorithmException("Invalid transformation: " +
+ "algorithm not specified-"
+ + transformation);
+ }
+ if (algorithmOnly) { // done
+ return new String[] { algo };
} else {
- // algorithm/mode/padding
- parts[0] = transformation.substring(0, endIdx).trim();
+ // continue parsing mode and padding
startIdx = endIdx+1;
endIdx = transformation.indexOf('/', startIdx);
if (endIdx == -1) {
throw new NoSuchAlgorithmException("Invalid transformation"
+ " format:" + transformation);
}
- parts[1] = transformation.substring(startIdx, endIdx).trim();
- parts[2] = transformation.substring(endIdx+1).trim();
- }
- if (parts[0].isEmpty()) {
- throw new NoSuchAlgorithmException("Invalid transformation: " +
- "algorithm not specified-"
+ String mode = transformation.substring(startIdx, endIdx).trim();
+ String padding = transformation.substring(endIdx+1).trim();
+ // ensure mode and padding are specified
+ if (mode.isEmpty() || padding.isEmpty()) {
+ throw new NoSuchAlgorithmException("Invalid transformation: " +
+ "missing mode and/or padding-"
+ transformation);
+ }
+ return new String[] { algo, mode, padding };
}
- return parts;
}
// Provider attribute name for supported chaining mode
@@ -453,22 +465,17 @@ private static List getTransforms(String transformation)
throws NoSuchAlgorithmException {
String[] parts = tokenizeTransformation(transformation);
- String alg = parts[0];
- String mode = parts[1];
- String pad = parts[2];
-
- if ((mode.length() == 0) && (pad.length() == 0)) {
+ if (parts.length == 1) {
// Algorithm only
- Transform tr = new Transform(alg, "", null, null);
- return Collections.singletonList(tr);
+ return List.of(new Transform(parts[0], "", null, null));
} else {
- // Algorithm w/ at least mode or padding or both
- List list = new ArrayList<>(4);
- list.add(new Transform(alg, "/" + mode + "/" + pad, null, null));
- list.add(new Transform(alg, "/" + mode, null, pad));
- list.add(new Transform(alg, "//" + pad, mode, null));
- list.add(new Transform(alg, "", mode, pad));
- return list;
+ // Algorithm w/ both mode and padding
+ return List.of(
+ new Transform(parts[0], "/" + parts[1] + "/" + parts[2],
+ null, null),
+ new Transform(parts[0], "/" + parts[1], null, parts[2]),
+ new Transform(parts[0], "//" + parts[2], parts[1], null),
+ new Transform(parts[0], "", parts[1], parts[2]));
}
}
@@ -504,8 +511,10 @@ private static Transform getTransform(Service s,
* requirements of your application.
*
* @implNote
- * The JDK Reference Implementation additionally uses the
- * {@code jdk.security.provider.preferred}
+ * The JDK Reference Implementation additionally uses the following
+ * security properties:
+ *
+ * - the {@code jdk.security.provider.preferred}
* {@link Security#getProperty(String) Security} property to determine
* the preferred provider order for the specified algorithm. This
* may be different than the order of providers returned by
@@ -513,6 +522,14 @@ private static Transform getTransform(Service s,
* See also the Cipher Transformations section of the {@extLink
* security_guide_jdk_providers JDK Providers} document for information
* on the transformation defaults used by JDK providers.
+ *
+ * - the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified algorithm is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
+ *
*
* @param transformation the name of the transformation, e.g.,
* AES/CBC/PKCS5Padding.
@@ -541,6 +558,13 @@ public static final Cipher getInstance(String transformation)
if ((transformation == null) || transformation.isEmpty()) {
throw new NoSuchAlgorithmException("Null or empty transformation");
}
+
+ // throws NoSuchAlgorithmException if java.security disables it
+ if (!CryptoAlgorithmConstraints.permits("Cipher", transformation)) {
+ throw new NoSuchAlgorithmException(transformation +
+ " is disabled");
+ }
+
List transforms = getTransforms(transformation);
List cipherServices = new ArrayList<>(transforms.size());
for (Transform transform : transforms) {
@@ -604,6 +628,14 @@ public static final Cipher getInstance(String transformation)
* security_guide_jdk_providers JDK Providers} document for information
* on the transformation defaults used by JDK providers.
*
+ * @implNote
+ * The JDK Reference Implementation additionally uses
+ * the {@code jdk.crypto.disabledAlgorithms}
+ * {@link Security#getProperty(String) Security} property to determine
+ * if the specified algorithm is allowed. If the
+ * {@systemProperty jdk.crypto.disabledAlgorithms} is set, it supersedes
+ * the security property value.
+ *
* @param transformation the name of the transformation,
* e.g., AES/CBC/PKCS5Padding.
* See the Cipher section in the AES/CBC/PKCS5Padding.
* See the Cipher section in the transforms = getTransforms(transformation);
boolean providerChecked = false;
diff --git a/src/java.base/share/classes/jdk/internal/access/JavaIOAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaIOAccess.java
index 532c1f259d4..bdeb2282a02 100644
--- a/src/java.base/share/classes/jdk/internal/access/JavaIOAccess.java
+++ b/src/java.base/share/classes/jdk/internal/access/JavaIOAccess.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -29,4 +29,5 @@
public interface JavaIOAccess {
Console console();
+ boolean isStdinTty();
}
diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java
index 4c2104c5a78..b94e1ba3921 100644
--- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java
+++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java
@@ -1515,13 +1515,6 @@ void verify_field_instructions(RawBytecodeHelper bcs, VerificationFrame current_
}
}
- // Return TRUE if all code paths starting with start_bc_offset end in
- // bytecode athrow or loop.
- boolean ends_in_athrow(int start_bc_offset) {
- log_info("unimplemented VerifierImpl.ends_in_athrow");
- return true;
- }
-
boolean verify_invoke_init(RawBytecodeHelper bcs, int ref_class_index, VerificationType ref_class_type,
VerificationFrame current_frame, int code_length, boolean in_try_block,
boolean this_uninit, ConstantPoolWrapper cp, VerificationTable stackmap_table) {
@@ -1534,16 +1527,6 @@ boolean verify_invoke_init(RawBytecodeHelper bcs, int ref_class_index, Verificat
verifyError("Bad method call");
}
if (in_try_block) {
- for(var exhandler : _method.exceptionTable()) {
- int start_pc = exhandler[0];
- int end_pc = exhandler[1];
-
- if (bci >= start_pc && bci < end_pc) {
- if (!ends_in_athrow(exhandler[2])) {
- verifyError("Bad method call from after the start of a try block");
- }
- }
- }
verify_exception_handler_targets(bci, true, current_frame, stackmap_table);
}
current_frame.initialize_object(type, current_type());
diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java
index a1323e16945..20ccec61fd2 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/abi/BindingSpecializer.java
@@ -297,7 +297,7 @@ private void specialize() {
if (callingSequence.allocationSize() != 0) {
cb.loadConstant(callingSequence.allocationSize())
.invokestatic(CD_SharedUtils, "newBoundedArena", MTD_NEW_BOUNDED_ARENA);
- } else if (callingSequence.forUpcall() && needsSession()) {
+ } else if (callingSequence.forUpcall() && anyArgNeedsScope()) {
cb.invokestatic(CD_SharedUtils, "newEmptyArena", MTD_NEW_EMPTY_ARENA);
} else {
cb.getstatic(CD_SharedUtils, "DUMMY_ARENA", CD_Arena);
@@ -437,7 +437,7 @@ private void specialize() {
cb.exceptionCatchAll(tryStart, tryEnd, catchStart);
}
- private boolean needsSession() {
+ private boolean anyArgNeedsScope() {
return callingSequence.argumentBindings()
.filter(BoxAddress.class::isInstance)
.map(BoxAddress.class::cast)
@@ -590,7 +590,7 @@ private void emitBoxAddress(BoxAddress boxAddress) {
popType(long.class);
cb.loadConstant(boxAddress.size())
.loadConstant(boxAddress.align());
- if (needsSession()) {
+ if (boxAddress.needsScope()) {
emitLoadInternalSession();
cb.invokestatic(CD_Utils, "longToAddress", MTD_LONG_TO_ADDRESS_SCOPE);
} else {
diff --git a/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java b/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java
index ec94d4ec4d6..f6ea183e8a7 100644
--- a/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java
+++ b/src/java.base/share/classes/jdk/internal/io/JdkConsoleImpl.java
@@ -38,10 +38,13 @@
import java.util.Formatter;
import java.util.Locale;
import java.util.Objects;
+import java.util.Optional;
import jdk.internal.access.SharedSecrets;
+import jdk.internal.util.StaticProperty;
import sun.nio.cs.StreamDecoder;
import sun.nio.cs.StreamEncoder;
+import sun.nio.cs.UTF_8;
/**
* JdkConsole implementation based on the platform's TTY.
@@ -103,6 +106,42 @@ public String readLine() {
@Override
public char[] readPassword(Locale locale, String format, Object ... args) {
+ return readPassword0(false, locale, format, args);
+ }
+
+ // These two methods are intended for sun.security.util.Password, so tools like keytool can
+ // use JdkConsoleImpl even when standard output is redirected. The Password class should first
+ // check if `System.console()` returns a Console instance and use it if available. Otherwise,
+ // it should call this method to obtain a JdkConsoleImpl. This ensures only one Console
+ // instance exists in the Java runtime.
+ private static final StableValue> INSTANCE = StableValue.of();
+ public static Optional passwordConsole() {
+ return INSTANCE.orElseSet(() -> {
+ // If there's already a proper console, throw an exception
+ if (System.console() != null) {
+ throw new IllegalStateException("Can’t create a dedicated password " +
+ "console since a real console already exists");
+ }
+
+ // If stdin is NOT redirected, return an Optional containing a JdkConsoleImpl
+ // instance, otherwise an empty Optional.
+ return SharedSecrets.getJavaIOAccess().isStdinTty() ?
+ Optional.of(
+ new JdkConsoleImpl(
+ UTF_8.INSTANCE,
+ UTF_8.INSTANCE)) :
+ Optional.empty();
+ });
+ }
+
+ // Dedicated entry for sun.security.util.Password when stdout is redirected.
+ // This method strictly avoids producing any output by using noNewLine = true
+ // and an empty format string.
+ public char[] readPasswordNoNewLine() {
+ return readPassword0(true, Locale.getDefault(Locale.Category.FORMAT), "");
+ }
+
+ private char[] readPassword0(boolean noNewLine, Locale locale, String format, Object ... args) {
char[] passwd = null;
synchronized (writeLock) {
synchronized(readLock) {
@@ -135,7 +174,9 @@ public char[] readPassword(Locale locale, String format, Object ... args) {
ioe.addSuppressed(x);
}
if (ioe != null) {
- Arrays.fill(passwd, ' ');
+ if (passwd != null) {
+ Arrays.fill(passwd, ' ');
+ }
try {
if (reader instanceof LineReader lr) {
lr.zeroOut();
@@ -146,7 +187,9 @@ public char[] readPassword(Locale locale, String format, Object ... args) {
throw ioe;
}
}
- pw.println();
+ if (!noNewLine) {
+ pw.println();
+ }
}
}
return passwd;
diff --git a/src/java.base/share/classes/jdk/internal/util/Exceptions.java b/src/java.base/share/classes/jdk/internal/util/Exceptions.java
index eb4286cd1af..70ee7026a7b 100644
--- a/src/java.base/share/classes/jdk/internal/util/Exceptions.java
+++ b/src/java.base/share/classes/jdk/internal/util/Exceptions.java
@@ -274,12 +274,9 @@ public static boolean enhancedSocketExceptions() {
*/
public static IOException ioException(IOException e, SocketAddress addr) {
setup();
- if (addr == null) {
+ if (!enhancedSocketExceptionText || addr == null) {
return e;
}
- if (!enhancedSocketExceptionText) {
- return create(e, e.getMessage());
- }
if (addr instanceof UnixDomainSocketAddress) {
return ofUnixDomain(e, (UnixDomainSocketAddress)addr);
} else if (addr instanceof InetSocketAddress) {
diff --git a/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java b/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java
index 53fd6322758..76bbff2a610 100644
--- a/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java
+++ b/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 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
@@ -96,8 +96,15 @@ private static Class> parseSig(String str, int[] i, int end, ClassLoader loade
}
} else if (c == '[') {
Class> t = parseSig(str, i, end, loader);
- if (t != null)
- t = t.arrayType();
+ if (t != null) {
+ try {
+ t = t.arrayType();
+ } catch (UnsupportedOperationException ex) {
+ // Bad arrays, such as [V or more than 255 dims
+ // We have a more informative IAE
+ return null;
+ }
+ }
return t;
} else {
return Wrapper.forBasicType(c).primitiveType();
diff --git a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java
index 28729a56dbd..3e1fc8db164 100644
--- a/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java
+++ b/src/java.base/share/classes/sun/security/provider/certpath/URICertStore.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 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
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URLConnection;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
@@ -48,8 +49,11 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Optional;
+import java.util.Set;
import sun.security.x509.AccessDescription;
import sun.security.x509.GeneralNameInterface;
@@ -58,6 +62,8 @@
import sun.security.util.Debug;
import sun.security.util.SecurityProperties;
+import javax.security.auth.x500.X500Principal;
+
/**
* A CertStore that retrieves Certificates or
* CRLs from a URI, for example, as specified in an X.509
@@ -182,6 +188,166 @@ private static int initializeTimeout(String prop, int def) {
return timeoutVal;
}
+ /**
+ * Enumeration for the allowed schemes we support when following a
+ * URI from an authorityInfoAccess extension on a certificate.
+ */
+ private enum AllowedScheme {
+ HTTP(HttpFtpRuleMatcher.HTTP),
+ HTTPS(HttpFtpRuleMatcher.HTTPS),
+ LDAP(LdapRuleMatcher.LDAP),
+ LDAPS(LdapRuleMatcher.LDAPS),
+ FTP(HttpFtpRuleMatcher.FTP);
+
+ final URIRuleMatcher ruleMatcher;
+
+ AllowedScheme(URIRuleMatcher matcher) {
+ ruleMatcher = matcher;
+ }
+
+ /**
+ * Return an {@code AllowedScheme} based on a case-insensitive match
+ * @param name the scheme name to be matched
+ * @return the {@code AllowedScheme} that corresponds to the
+ * {@code name} provided, or null if there is no match.
+ */
+ static AllowedScheme nameOf(String name) {
+ if (name == null) {
+ return null;
+ }
+
+ try {
+ return AllowedScheme.valueOf(name.toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException _) {
+ return null;
+ }
+ }
+ }
+
+ private static Set CA_ISS_URI_FILTERS = null;
+ private static final boolean CA_ISS_ALLOW_ANY;
+
+ static {
+ boolean allowAny = false;
+ try {
+ if (Builder.USE_AIA) {
+ CA_ISS_URI_FILTERS = new LinkedHashSet<>();
+ String aiaPropVal = Optional.ofNullable(
+ SecurityProperties.getOverridableProperty(
+ "com.sun.security.allowedAIALocations")).
+ map(String::trim).orElse("");
+ if (aiaPropVal.equalsIgnoreCase("any")) {
+ allowAny = true;
+ if (debug != null) {
+ debug.println("allowedAIALocations: Warning: " +
+ "Allow-All URI filtering enabled!");
+ }
+ } else {
+ // Load all the valid rules from the Security property
+ if (!aiaPropVal.isEmpty()) {
+ String[] aiaUriStrs = aiaPropVal.trim().split("\\s+");
+ addCaIssUriFilters(aiaUriStrs);
+ }
+
+ if (CA_ISS_URI_FILTERS.isEmpty()) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: Warning: " +
+ "No valid filters found. Deny-all URI " +
+ "filtering is active.");
+ }
+ }
+ }
+ }
+ } finally {
+ CA_ISS_ALLOW_ANY = allowAny;
+ }
+ }
+
+ /**
+ * Populate the filter collection from the list of AIA CA issuer URIs
+ * found in the {@code com.sun.security.allowedAIALocations} security
+ * or system property.
+ *
+ * @param aiaUriStrs array containing String URI filters
+ */
+ private static void addCaIssUriFilters(String[] aiaUriStrs) {
+ for (String aiaStr : aiaUriStrs) {
+ if (aiaStr != null && !aiaStr.isEmpty()) {
+ try {
+ AllowedScheme scheme;
+ URI aiaUri = new URI(aiaStr).normalize();
+ // It must be absolute and non-opaque
+ if (!aiaUri.isAbsolute() || aiaUri.isOpaque()) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: Skipping " +
+ "non-absolute or opaque URI " + aiaUri);
+ }
+ } else if (aiaUri.getHost() == null) {
+ // We do not allow rules with URIs that omit a hostname
+ // or address.
+ if (debug != null) {
+ debug.println("allowedAIALocations: Skipping " +
+ "URI rule with no hostname or address: " +
+ aiaUri);
+ }
+ } else if ((scheme = AllowedScheme.nameOf(
+ aiaUri.getScheme())) != null) {
+ // When it is an LDAP type, we can check the path
+ // portion (the DN) for proper structure and reject
+ // the rule early if it isn't correct.
+ if (scheme == AllowedScheme.LDAP ||
+ scheme == AllowedScheme.LDAPS) {
+ try {
+ new X500Principal(aiaUri.getPath().
+ replaceFirst("^/+", ""));
+ } catch (IllegalArgumentException iae) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: " +
+ "Skipping LDAP rule: " + iae);
+ }
+ continue;
+ }
+ }
+
+ // When a URI has a non-null query or fragment
+ // warn the user upon adding the rule that those
+ // components will be ignored
+ if (aiaUri.getQuery() != null) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: " +
+ "Rule will ignore non-null query");
+ }
+ }
+ if (aiaUri.getFragment() != null) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: " +
+ "Rule will ignore non-null fragment");
+ }
+ }
+
+ CA_ISS_URI_FILTERS.add(aiaUri);
+ if (debug != null) {
+ debug.println("allowedAIALocations: Added " +
+ aiaUri + " to URI filters");
+ }
+ } else {
+ if (debug != null) {
+ debug.println("allowedAIALocations: Disallowed " +
+ "filter URI scheme: " +
+ aiaUri.getScheme());
+ }
+ }
+ } catch (URISyntaxException urise) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: Skipping " +
+ "filter URI entry " + aiaStr +
+ ": parse failure at index " + urise.getIndex());
+ }
+ }
+ }
+ }
+ }
+
/**
* Creates a URICertStore.
*
@@ -244,6 +410,39 @@ static CertStore getInstance(AccessDescription ad) {
return null;
}
URI uri = ((URIName) gn).getURI();
+
+ // Before performing any instantiation make sure that
+ // the URI passes any filtering rules. This processing should
+ // only occur if the com.sun.security.enableAIAcaIssuers is true
+ // and the "any" rule has not been specified.
+ if (Builder.USE_AIA && !CA_ISS_ALLOW_ANY) {
+ URI normAIAUri = uri.normalize();
+ AllowedScheme scheme = AllowedScheme.nameOf(normAIAUri.getScheme());
+
+ if (scheme == null) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: No matching ruleset " +
+ "for scheme " + normAIAUri.getScheme());
+ }
+ return null;
+ }
+
+ // Go through each of the filter rules and see if any will
+ // make a positive match against the caIssuer URI. If nothing
+ // matches then we won't instantiate a URICertStore.
+ if (CA_ISS_URI_FILTERS.stream().noneMatch(rule ->
+ scheme.ruleMatcher.matchRule(rule, normAIAUri))) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: Warning - " +
+ "The caIssuer URI " + normAIAUri +
+ " in the AuthorityInfoAccess extension is denied " +
+ "access. Use the com.sun.security.allowedAIALocations" +
+ " security/system property to allow access.");
+ }
+ return null;
+ }
+ }
+
try {
return URICertStore.getInstance(new URICertStoreParameters(uri));
} catch (Exception ex) {
@@ -270,7 +469,7 @@ static CertStore getInstance(AccessDescription ad) {
@Override
@SuppressWarnings("unchecked")
public synchronized Collection engineGetCertificates
- (CertSelector selector) throws CertStoreException {
+ (CertSelector selector) throws CertStoreException {
if (ldap) {
// caching mechanism, see the class description for more info.
@@ -462,4 +661,159 @@ protected UCS(CertStoreSpi spi, Provider p, String type,
super(spi, p, type, params);
}
}
+
+ /**
+ * URIRuleMatcher - abstract base class for the rule sets used for
+ * various URI schemes.
+ */
+ static abstract class URIRuleMatcher {
+ protected final int wellKnownPort;
+
+ protected URIRuleMatcher(int port) {
+ wellKnownPort = port;
+ }
+
+ /**
+ * Attempt to match the scheme, host and port between a filter
+ * rule URI and a URI coming from an AIA extension.
+ *
+ * @param filterRule the filter rule to match against
+ * @param caIssuer the AIA URI being compared
+ * @return true if the scheme, host and port numbers match, false if
+ * any of the components do not match. If a port number is omitted in
+ * either the filter rule or AIA URI, the well-known port for that
+ * scheme is used in the comparison.
+ */
+ boolean schemeHostPortCheck(URI filterRule, URI caIssuer) {
+ if (!filterRule.getScheme().equalsIgnoreCase(
+ caIssuer.getScheme())) {
+ return false;
+ } else if (!filterRule.getHost().equalsIgnoreCase(
+ caIssuer.getHost())) {
+ return false;
+ } else {
+ try {
+ // Check for port matching, taking into consideration
+ // default ports
+ int fPort = (filterRule.getPort() == -1) ? wellKnownPort :
+ filterRule.getPort();
+ int caiPort = (caIssuer.getPort() == -1) ? wellKnownPort :
+ caIssuer.getPort();
+ if (fPort != caiPort) {
+ return false;
+ }
+ } catch (IllegalArgumentException iae) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Attempt to match an AIA URI against a specific filter rule. The
+ * specific rules to apply are implementation dependent.
+ *
+ * @param filterRule the filter rule to match against
+ * @param caIssuer the AIA URI being compared
+ * @return true if all matching rules pass, false if any fail.
+ */
+ abstract boolean matchRule(URI filterRule, URI caIssuer);
+ }
+
+ static class HttpFtpRuleMatcher extends URIRuleMatcher {
+ static final HttpFtpRuleMatcher HTTP = new HttpFtpRuleMatcher(80);
+ static final HttpFtpRuleMatcher HTTPS = new HttpFtpRuleMatcher(443);
+ static final HttpFtpRuleMatcher FTP = new HttpFtpRuleMatcher(21);
+
+ private HttpFtpRuleMatcher(int port) {
+ super(port);
+ }
+
+ @Override
+ boolean matchRule(URI filterRule, URI caIssuer) {
+ // Check for scheme/host/port matching
+ if (!schemeHostPortCheck(filterRule, caIssuer)) {
+ return false;
+ }
+
+ // Check the path component to make sure the filter is at
+ // least a root of the AIA caIssuer URI's path. It must be
+ // a case-sensitive match for all platforms.
+ if (!isRootOf(filterRule, caIssuer)) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: Match failed: " +
+ "AIA URI is not within the rule's path hierarchy.");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Performs a hierarchical containment check, ensuring that the
+ * base URI's path is a root component of the candidate path. The
+ * path comparison is case-sensitive. If the base path ends in a
+ * slash (/) then all candidate paths that begin with the base
+ * path are allowed. If it does not end in a slash, then it is
+ * assumed that the leaf node in the base path is a file component
+ * and both paths must match exactly.
+ *
+ * @param base the URI that contains the root path
+ * @param candidate the URI that contains the path being evaluated
+ * @return true if {@code candidate} is a child path of {@code base},
+ * false otherwise.
+ */
+ private static boolean isRootOf(URI base, URI candidate) {
+ // Note: The URIs have already been normalized at this point and
+ // HTTP URIs cannot have null paths. If it's an empty path
+ // then consider the path to be "/".
+ String basePath = Optional.of(base.getPath()).
+ filter(p -> !p.isEmpty()).orElse("/");
+ String candPath = Optional.of(candidate.getPath()).
+ filter(p -> !p.isEmpty()).orElse("/");
+ return (basePath.endsWith("/")) ? candPath.startsWith(basePath) :
+ candPath.equals(basePath);
+ }
+ }
+
+ static class LdapRuleMatcher extends URIRuleMatcher {
+ static final LdapRuleMatcher LDAP = new LdapRuleMatcher(389);
+ static final LdapRuleMatcher LDAPS = new LdapRuleMatcher(636);
+
+ private LdapRuleMatcher(int port) {
+ super(port);
+ }
+
+ @Override
+ boolean matchRule(URI filterRule, URI caIssuer) {
+ // Check for scheme/host/port matching
+ if (!schemeHostPortCheck(filterRule, caIssuer)) {
+ return false;
+ }
+
+ // Obtain the base DN component and compare
+ try {
+ X500Principal filterBaseDn = new X500Principal(
+ filterRule.getPath().replaceFirst("^/+", ""));
+ X500Principal caIssBaseDn = new X500Principal(
+ caIssuer.getPath().replaceFirst("^/+", ""));
+ if (!filterBaseDn.equals(caIssBaseDn)) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: Match failed: " +
+ "Base DN mismatch (" + filterBaseDn + " vs " +
+ caIssBaseDn + ")");
+ }
+ return false;
+ }
+ } catch (IllegalArgumentException iae) {
+ if (debug != null) {
+ debug.println("allowedAIALocations: Match failed on DN: " +
+ iae);
+ }
+ return false;
+ }
+
+ return true;
+ }
+ }
}
diff --git a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java
index e0196f3009c..101a42a5407 100644
--- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java
+++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -801,8 +801,11 @@ void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException {
// buffer this fragment
if (hsf.handshakeType == SSLHandshake.FINISHED.id) {
- // Need no status update.
- bufferedFragments.add(hsf);
+ // Make sure it's not a retransmitted message
+ if (hsf.recordEpoch > handshakeEpoch) {
+ bufferedFragments.add(hsf);
+ flightIsReady = holes.isEmpty();
+ }
} else {
bufferFragment(hsf);
}
diff --git a/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java b/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java
index 5c2a09b7c03..b3155f5170a 100644
--- a/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java
+++ b/src/java.base/share/classes/sun/security/ssl/HelloCookieManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -187,7 +187,7 @@ boolean isCookieValid(ServerHandshakeContext context,
byte[] secret;
d10ManagerLock.lock();
try {
- if (((cookieVersion >> 24) & 0xFF) == cookie[0]) {
+ if ((byte) ((cookieVersion >> 24) & 0xFF) == cookie[0]) {
secret = cookieSecret;
} else {
secret = legacySecret; // including out of window cookies
diff --git a/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java
index dc5b1aafb20..d05fb262fa8 100644
--- a/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java
+++ b/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -46,7 +46,16 @@ protected AbstractAlgorithmConstraints(AlgorithmDecomposer decomposer) {
// Get algorithm constraints from the specified security property.
static Set getAlgorithms(String propertyName) {
- String property = Security.getProperty(propertyName);
+ return getAlgorithms(propertyName, false);
+ }
+
+ // Get algorithm constraints from the specified security property or
+ // system property if allowSystemOverride == true.
+ static Set getAlgorithms(String propertyName,
+ boolean allowSystemOverride) {
+ String property = allowSystemOverride ?
+ SecurityProperties.getOverridableProperty(propertyName) :
+ Security.getProperty(propertyName);
String[] algorithmsInProperty = null;
if (property != null && !property.isEmpty()) {
@@ -65,7 +74,8 @@ static Set getAlgorithms(String propertyName) {
if (algorithmsInProperty == null) {
return Collections.emptySet();
}
- Set algorithmsInPropertySet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+ Set algorithmsInPropertySet =
+ new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
algorithmsInPropertySet.addAll(Arrays.asList(algorithmsInProperty));
return algorithmsInPropertySet;
}
@@ -80,17 +90,17 @@ static boolean checkAlgorithm(Set algorithms, String algorithm,
return false;
}
- // decompose the algorithm into sub-elements
- Set elements = decomposer.decompose(algorithm);
+ if (decomposer != null) {
+ // decompose the algorithm into sub-elements
+ Set elements = decomposer.decompose(algorithm);
- // check the element of the elements
- for (String element : elements) {
- if (algorithms.contains(element)) {
- return false;
+ // check the element of the elements
+ for (String element : elements) {
+ if (algorithms.contains(element)) {
+ return false;
+ }
}
}
-
return true;
}
-
}
diff --git a/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java
new file mode 100644
index 00000000000..ad3beab350f
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package sun.security.util;
+
+import java.lang.ref.SoftReference;
+import java.security.AlgorithmParameters;
+import java.security.CryptoPrimitive;
+import java.security.Key;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class implements the algorithm constraints for the
+ * "jdk.crypto.disabledAlgorithms" security property. This security property
+ * can be overridden by the system property of the same name. See the
+ * java.security file for the syntax of the property value.
+ */
+public class CryptoAlgorithmConstraints extends AbstractAlgorithmConstraints {
+ private static final Debug debug = Debug.getInstance("jca");
+
+ // for validating the service
+ private static final Set SUPPORTED_SERVICES =
+ Set.of("Cipher", "KeyStore", "MessageDigest", "Signature");
+
+ // Disabled algorithm security property for JCE crypto services
+ private static final String PROPERTY_CRYPTO_DISABLED_ALGS =
+ "jdk.crypto.disabledAlgorithms";
+
+ private static class CryptoHolder {
+ static final CryptoAlgorithmConstraints CONSTRAINTS =
+ new CryptoAlgorithmConstraints(PROPERTY_CRYPTO_DISABLED_ALGS);
+ }
+
+ private static void debug(String msg) {
+ if (debug != null) {
+ debug.println("CryptoAlgoConstraints: ", msg);
+ }
+ }
+
+ public static boolean permits(String service, String algo) {
+ return CryptoHolder.CONSTRAINTS.cachedCheckAlgorithm(
+ service + "." + algo);
+ }
+
+ private final Set disabledServices; // syntax is .
+ private volatile SoftReference