mirror of
https://github.com/openjdk/jdk.git
synced 2026-08-03 14:47:03 +00:00
Merge branch 'master' into JDK-8344159 to fix conflicts.
This commit is contained in:
commit
42e65de95e
184
.git-blame-ignore-revs
Normal file
184
.git-blame-ignore-revs
Normal file
@ -0,0 +1,184 @@
|
||||
# git blame ignore revs file
|
||||
#
|
||||
# The list of revisions below will be ignored by git-blame (1) if this file gets
|
||||
# passed via the --ignore-revs-file command line option or is configured using
|
||||
# the blame.ignoreRevsFile key.
|
||||
#
|
||||
# Only add commits that obviously do not change semantics, e.g. mechanical refactorings
|
||||
# or formatting. Always add the commit message as a comment above the revision
|
||||
# to keep the file readable.
|
||||
|
||||
# 8299973: Replace NULL with nullptr in share/utilities/
|
||||
1084fd24eb118d4131538c2a3ead714db7d0357b
|
||||
|
||||
# 8299974: Replace NULL with nullptr in share/adlc/
|
||||
62537d200f01d58ff1c236f31f71c5839316db9e
|
||||
|
||||
# 8300081: Replace NULL with nullptr in share/asm/
|
||||
9d5bab11f08a992803399f422d75b17f8607df72
|
||||
|
||||
# 8300086: Replace NULL with nullptr in share/c1/
|
||||
90d5041b6a055d6266140ffea2aa9a3b08b32209
|
||||
|
||||
# 8300087: Replace NULL with nullptr in share/cds/
|
||||
eca64795be63c599a637ce2a7f740b2d0a1ec9bc
|
||||
|
||||
# 8300222: Replace NULL with nullptr in share/logging
|
||||
bd5ca953058704087da4bc5796b3ce28ce2a8f78
|
||||
|
||||
# 8300240: Replace NULL with nullptr in share/ci/
|
||||
f52d35c84b7333809156d201c866793854143888
|
||||
|
||||
# 8300241: Replace NULL with nullptr in share/classfile/
|
||||
49ff52087be8b95cbf369518281312ecc9d83618
|
||||
|
||||
# 8300242: Replace NULL with nullptr in share/code/
|
||||
cfe57466ddecb93b528478d0b053b089dd1ed285
|
||||
|
||||
# 8300243: Replace NULL with nullptr in share/compiler/
|
||||
fcbf9d052efd16821750fb20813f8030ee828472
|
||||
|
||||
# 8300244: Replace NULL with nullptr in share/interpreter/
|
||||
a5d8e12872d9de399fa97b33896635d101b71372
|
||||
|
||||
# 8300245: Replace NULL with nullptr in share/jfr/
|
||||
cc396895e5a1dac49f4e341ce91c04b8c092d0af
|
||||
|
||||
# 8300651: Replace NULL with nullptr in share/runtime/
|
||||
71107f4648d8f31a7bcc0aa5202ef46230df583f
|
||||
|
||||
# 8301068: Replace NULL with nullptr in share/jvmci/
|
||||
90ec19efeda90f13a918b4481fe6ee552ab2af66
|
||||
|
||||
# 8301069: Replace NULL with nullptr in share/libadt/
|
||||
b0376a5f4421fb58c0feeddfce2c2083314e400c
|
||||
|
||||
# 8301070: Replace NULL with nullptr in share/memory/
|
||||
d98a323a8b972c17a066c597a81b164681ad5589
|
||||
|
||||
# 8301072: Replace NULL with nullptr in share/oops/
|
||||
c8ace482edead720c865cf996729a316025d937e
|
||||
|
||||
# 8301074: Replace NULL with nullptr in share/opto/
|
||||
5726d31e56530bbe7dee61ae04b126e20cb3611d
|
||||
|
||||
# 8301076: Replace NULL with nullptr in share/prims/
|
||||
b76a52f2104b63e84e5d09f47ce01dd0cb3935d7
|
||||
|
||||
# 8301077: Replace NULL with nullptr in share/services/
|
||||
5c1ec82656323872c4628026662fe5b62e7a61e3
|
||||
|
||||
# 8301178: Replace NULL with nullptr in share/gc/epsilon/
|
||||
b77abc6a0daed0e01a9003d42493320376dc98bc
|
||||
|
||||
# 8301179: Replace NULL with nullptr in share/gc/serial/
|
||||
107e184d59c0bbed6441a3c1a9bfd4527da3bce5
|
||||
|
||||
# 8301180: Replace NULL with nullptr in share/gc/parallel/
|
||||
3758487fda61b27e7e684413793ed28c0b9e64d3
|
||||
|
||||
# 8301223: Replace NULL with nullptr in share/gc/g1/
|
||||
75a4edca6b9fa6b3e66b564aeb4d7ca8acf02491
|
||||
|
||||
# 8301225: Replace NULL with nullptr in share/gc/shenandoah/
|
||||
0c9658446d111ec944f06b7a8a4e3ae7bf53ee8d
|
||||
|
||||
# 8301477: Replace NULL with nullptr in os/aix
|
||||
43288bbd684abfcefdf385ed1e0307070399ccbf
|
||||
|
||||
# 8301478: Replace NULL with nullptr in os/bsd
|
||||
716f1df609e7f0aa7b3b9383d23dde5c71017d02
|
||||
|
||||
# 8301479: Replace NULL with nullptr in os/linux
|
||||
ac9e046748a9bb6ee065dc473d82135ce36043b7
|
||||
|
||||
# 8301480: Replace NULL with nullptr in os/posix
|
||||
4539899c55c77771b951d005c17550ef9ac94819
|
||||
|
||||
# 8301481: Replace NULL with nullptr in os/windows
|
||||
c91cd2814baa8dee2af8af0fecf9185d4a0a44cf
|
||||
|
||||
# 8301493: Replace NULL with nullptr in cpu/aarch64
|
||||
948f3b3c24709eca3aa6c3f0db6adb9226d6f9ac
|
||||
|
||||
# 8301494: Replace NULL with nullptr in cpu/arm
|
||||
c4ffe4bf6369d5b271aa8689b8648f3fe8dcabed
|
||||
|
||||
# 8301495: Replace NULL with nullptr in cpu/ppc
|
||||
0826ceee65ab83f643a77716f8f12d0060369923
|
||||
|
||||
# 8301496: Replace NULL with nullptr in cpu/riscv
|
||||
d2ce04bb101002abfdb7c8adb3fa8ea267903c36
|
||||
|
||||
# 8301497: Replace NULL with nullptr in cpu/s390
|
||||
54f7b6ca34986cc26c5b91c6724b9a1754c94391
|
||||
|
||||
# 8301498: Replace NULL with nullptr in cpu/x86
|
||||
4154a980ca28c1ae56db26e3dce64c07c225de12
|
||||
|
||||
# 8301499: Replace NULL with nullptr in cpu/zero
|
||||
4e327db1d127c652ef39e31c164e36ae429a0065
|
||||
|
||||
# 8301500: Replace NULL with nullptr in os_cpu/aix_ppc
|
||||
c8307e37fdf4453cade84efc113d93dd14333fd0
|
||||
|
||||
# 8301501: Replace NULL with nullptr in os_cpu/bsd_aarch64
|
||||
218223e4a31d485935655cb3f186a752defd8fa8
|
||||
|
||||
# 8301502: Replace NULL with nullptr in os_cpu/bsd_x86
|
||||
6daff6b26946748360d59a12e9069a08ab5ca06d
|
||||
|
||||
# 8301503: Replace NULL with nullptr in os_cpu/bsd_zero
|
||||
8cc399b672c6ce08037685b3a3a2db3c53a87b50
|
||||
|
||||
# 8301504: Replace NULL with nullptr in os_cpu/linux_aarch64
|
||||
13fcd602d37eb0095f169255128588b872639571
|
||||
|
||||
# 8301505: Replace NULL with nullptr in os_cpu/linux_arm
|
||||
b81f0ff43ac8d1431f2f5dccb7499a3a1503823d
|
||||
|
||||
# 8301506: Replace NULL with nullptr in os_cpu/linux_ppc
|
||||
b1e96989b693aadea082a01576e25f85ed28ff0d
|
||||
|
||||
# 8301507: Replace NULL with nullptr in os_cpu/linux_riscv
|
||||
182d1b2fb7034b6e9177dc360cbea43d548c3ff0
|
||||
|
||||
# 8301508: Replace NULL with nullptr in os_cpu/linux_s390
|
||||
d097b5e6285e1a59632211e006592fedf2047c09
|
||||
|
||||
# 8301509: Replace NULL with nullptr in os_cpu/linux_x86
|
||||
5d1f71daf06870810c9ca24e911d6191cc4f3006
|
||||
|
||||
# 8301511: Replace NULL with nullptr in os_cpu/linux_zero
|
||||
42a286a15862d9a05ea3477a9eeab46e7b33e599
|
||||
|
||||
# 8301512: Replace NULL with nullptr in os_cpu/windows_aarch64
|
||||
ad79e49141f063a61090eda69d96dc580db88949
|
||||
|
||||
# 8301513: Replace NULL with nullptr in os_cpu/windows_x86
|
||||
c109dae48c61c6fbeacadf59d509d37d2c4d2bb8
|
||||
|
||||
# 8308092: Replace NULL with nullptr in gc/x
|
||||
599fa774b875da971d66f79e5e43ede2b5ce18aa
|
||||
|
||||
# 8309044: Replace NULL with nullptr, final sweep of hotspot code
|
||||
4f16161607edbf69f423ced1d3c24f7af058d46b
|
||||
|
||||
# 8324286: Fix backsliding on use of nullptr instead of NULL
|
||||
bcb340da091e3287da8d2ecfcd017ebcc6613cae
|
||||
|
||||
# 8324678: Replace NULL with nullptr in HotSpot gtests
|
||||
c1281e6b45ed167df69d29a6039d81854c145ae6
|
||||
|
||||
# 8324679: Replace NULL with nullptr in HotSpot .ad files
|
||||
b3ecd55601d483359819d02e70789bbd412b13da
|
||||
|
||||
# 8324680: Replace NULL with nullptr in JVMTI generated code
|
||||
267780bf0adf4bfd831fbc04347e297fa8f3bb01
|
||||
|
||||
# 8324681: Replace NULL with nullptr in HotSpot jtreg test native code files
|
||||
a6bdee48f39993128d8095d40ab417f0102af0f4
|
||||
|
||||
# 8324799: Use correct extension for C++ test headers
|
||||
998d0baab0fd051c38d9fd6021628eb863b80554
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[general]
|
||||
project=jdk
|
||||
jbs=JDK
|
||||
version=27
|
||||
version=28
|
||||
|
||||
[checks]
|
||||
error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists,copyright
|
||||
|
||||
@ -394,11 +394,11 @@ to date at the time of writing.</p>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td>Linux/x64</td>
|
||||
<td>Oracle Enterprise Linux 6.4 / 8.x</td>
|
||||
<td>Oracle Linux 6.4 / 8.x</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td>Linux/aarch64</td>
|
||||
<td>Oracle Enterprise Linux 7.6 / 8.x</td>
|
||||
<td>Oracle Linux 7.6 / 8.x</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td>macOS</td>
|
||||
@ -1495,26 +1495,24 @@ following targets are known to work:</p>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><code>BASE_OS</code> must be one of <code>OL</code> for Oracle
|
||||
Enterprise Linux or <code>Fedora</code>. If the base OS is
|
||||
<code>Fedora</code> the corresponding Fedora release can be specified
|
||||
with the help of the <code>BASE_OS_VERSION</code> option. If the build
|
||||
is successful, the new devkits can be found in the
|
||||
<p><code>BASE_OS</code> must be one of <code>OL</code> for Oracle Linux
|
||||
or <code>Fedora</code>. The release/version of the base OS can be
|
||||
specified using the <code>BASE_OS_VERSION</code> option. If the build is
|
||||
successful, the new devkits can be found in the
|
||||
<code>build/devkit/result</code> subdirectory:</p>
|
||||
<pre><code>cd make/devkit
|
||||
make TARGETS="ppc64le-linux-gnu aarch64-linux-gnu" BASE_OS=Fedora BASE_OS_VERSION=21
|
||||
make TARGETS="ppc64le-linux-gnu aarch64-linux-gnu" BASE_OS=Fedora
|
||||
ls -1 ../../build/devkit/result/
|
||||
x86_64-linux-gnu-to-aarch64-linux-gnu
|
||||
x86_64-linux-gnu-to-ppc64le-linux-gnu</code></pre>
|
||||
<p>Notice that devkits are not only useful for targeting different build
|
||||
platforms. Because they contain the full build dependencies for a system
|
||||
(i.e. compiler and root file system), they can easily be used to build
|
||||
well-known, reliable and reproducible build environments. You can for
|
||||
example create and use a devkit with GCC 7.3 and a Fedora 12 sysroot
|
||||
environment (with glibc 2.11) on Ubuntu 14.04 (which doesn't have GCC
|
||||
7.3 by default) to produce JDK binaries which will run on all Linux
|
||||
systems with runtime libraries newer than the ones from Fedora 12 (e.g.
|
||||
Ubuntu 16.04, SLES 11 or RHEL 6).</p>
|
||||
(i.e., compiler and root file system/sysroot), they can easily be used
|
||||
to build well-known, reliable, and reproducible build environments. You
|
||||
can, for example, create and use a devkit with a version of the GCC
|
||||
compiler not provided by the host OS, using a sysroot from an older
|
||||
Linux distribution to produce JDK binaries which will run on all Linux
|
||||
systems with newer runtime libraries.</p>
|
||||
<h4 id="using-debian-debootstrap">Using Debian debootstrap</h4>
|
||||
<p>On Debian (or a derivative like Ubuntu), you can create sysroots for
|
||||
foreign architectures with tools provided by the OS. You can use
|
||||
|
||||
@ -195,8 +195,8 @@ time of writing.
|
||||
|
||||
| Operating system | Vendor/version used |
|
||||
| ----------------- | ---------------------------------- |
|
||||
| Linux/x64 | Oracle Enterprise Linux 6.4 / 8.x |
|
||||
| Linux/aarch64 | Oracle Enterprise Linux 7.6 / 8.x |
|
||||
| Linux/x64 | Oracle Linux 6.4 / 8.x |
|
||||
| Linux/aarch64 | Oracle Linux 7.6 / 8.x |
|
||||
| macOS | macOS 14.x |
|
||||
| Windows | Windows Server 2016 |
|
||||
|
||||
@ -1288,27 +1288,26 @@ at least the following targets are known to work:
|
||||
| riscv64-linux-gnu |
|
||||
| s390x-linux-gnu |
|
||||
|
||||
`BASE_OS` must be one of `OL` for Oracle Enterprise Linux or `Fedora`. If the
|
||||
base OS is `Fedora` the corresponding Fedora release can be specified with the
|
||||
help of the `BASE_OS_VERSION` option. If the build is successful, the new
|
||||
devkits can be found in the `build/devkit/result` subdirectory:
|
||||
`BASE_OS` must be one of `OL` for Oracle Linux or `Fedora`. The release/version
|
||||
of the base OS can be specified using the `BASE_OS_VERSION` option. If the build
|
||||
is successful, the new devkits can be found in the `build/devkit/result`
|
||||
subdirectory:
|
||||
|
||||
```
|
||||
cd make/devkit
|
||||
make TARGETS="ppc64le-linux-gnu aarch64-linux-gnu" BASE_OS=Fedora BASE_OS_VERSION=21
|
||||
make TARGETS="ppc64le-linux-gnu aarch64-linux-gnu" BASE_OS=Fedora
|
||||
ls -1 ../../build/devkit/result/
|
||||
x86_64-linux-gnu-to-aarch64-linux-gnu
|
||||
x86_64-linux-gnu-to-ppc64le-linux-gnu
|
||||
```
|
||||
|
||||
Notice that devkits are not only useful for targeting different build
|
||||
platforms. Because they contain the full build dependencies for a system (i.e.
|
||||
compiler and root file system), they can easily be used to build well-known,
|
||||
reliable and reproducible build environments. You can for example create and
|
||||
use a devkit with GCC 7.3 and a Fedora 12 sysroot environment (with glibc 2.11)
|
||||
on Ubuntu 14.04 (which doesn't have GCC 7.3 by default) to produce JDK binaries
|
||||
which will run on all Linux systems with runtime libraries newer than the ones
|
||||
from Fedora 12 (e.g. Ubuntu 16.04, SLES 11 or RHEL 6).
|
||||
platforms. Because they contain the full build dependencies for a system (i.e.,
|
||||
compiler and root file system/sysroot), they can easily be used to build
|
||||
well-known, reliable, and reproducible build environments. You can, for example,
|
||||
create and use a devkit with a version of the GCC compiler not provided by the
|
||||
host OS, using a sysroot from an older Linux distribution to produce JDK
|
||||
binaries which will run on all Linux systems with newer runtime libraries.
|
||||
|
||||
#### Using Debian debootstrap
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2011, 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
|
||||
@ -173,41 +173,41 @@ $(BUILD_DEMO_CodePointIM_JAR): $(CODEPOINT_METAINF_SERVICE_FILE)
|
||||
|
||||
$(eval $(call SetupBuildDemo, FileChooserDemo, \
|
||||
DEMO_SUBDIR := jfc, \
|
||||
DISABLED_WARNINGS := rawtypes deprecation unchecked this-escape, \
|
||||
DISABLED_WARNINGS := this-escape, \
|
||||
))
|
||||
|
||||
$(eval $(call SetupBuildDemo, SwingSet2, \
|
||||
DEMO_SUBDIR := jfc, \
|
||||
EXTRA_COPY_TO_JAR := .java, \
|
||||
EXTRA_MANIFEST_ATTR := SplashScreen-Image: resources/images/splash.png, \
|
||||
DISABLED_WARNINGS := rawtypes deprecation unchecked static serial cast this-escape, \
|
||||
DISABLED_WARNINGS := rawtypes static serial cast this-escape, \
|
||||
))
|
||||
|
||||
$(eval $(call SetupBuildDemo, Font2DTest, \
|
||||
DISABLED_WARNINGS := rawtypes deprecation unchecked serial cast this-escape dangling-doc-comments, \
|
||||
DISABLED_WARNINGS := serial dangling-doc-comments, \
|
||||
DEMO_SUBDIR := jfc, \
|
||||
))
|
||||
|
||||
$(eval $(call SetupBuildDemo, J2Ddemo, \
|
||||
DEMO_SUBDIR := jfc, \
|
||||
MAIN_CLASS := java2d.J2Ddemo, \
|
||||
DISABLED_WARNINGS := rawtypes deprecation unchecked cast lossy-conversions this-escape, \
|
||||
DISABLED_WARNINGS := cast lossy-conversions this-escape, \
|
||||
JAR_NAME := J2Ddemo, \
|
||||
))
|
||||
|
||||
$(eval $(call SetupBuildDemo, Metalworks, \
|
||||
DISABLED_WARNINGS := rawtypes unchecked this-escape, \
|
||||
DISABLED_WARNINGS := this-escape, \
|
||||
DEMO_SUBDIR := jfc, \
|
||||
))
|
||||
|
||||
$(eval $(call SetupBuildDemo, Notepad, \
|
||||
DISABLED_WARNINGS := rawtypes this-escape, \
|
||||
DISABLED_WARNINGS := this-escape, \
|
||||
DEMO_SUBDIR := jfc, \
|
||||
))
|
||||
|
||||
$(eval $(call SetupBuildDemo, Stylepad, \
|
||||
DEMO_SUBDIR := jfc, \
|
||||
DISABLED_WARNINGS := rawtypes unchecked this-escape, \
|
||||
DISABLED_WARNINGS := this-escape, \
|
||||
EXTRA_SRC_DIR := $(DEMO_SHARE_SRC)/jfc/Notepad, \
|
||||
EXCLUDE_FILES := $(DEMO_SHARE_SRC)/jfc/Notepad/README.txt, \
|
||||
))
|
||||
@ -217,7 +217,7 @@ $(eval $(call SetupBuildDemo, SampleTree, \
|
||||
))
|
||||
|
||||
$(eval $(call SetupBuildDemo, TableExample, \
|
||||
DISABLED_WARNINGS := rawtypes unchecked deprecation this-escape dangling-doc-comments, \
|
||||
DISABLED_WARNINGS := dangling-doc-comments, \
|
||||
DEMO_SUBDIR := jfc, \
|
||||
))
|
||||
|
||||
|
||||
@ -1192,8 +1192,8 @@ var getJibProfilesDependencies = function (input, common) {
|
||||
server: "jpg",
|
||||
product: "jcov",
|
||||
version: "3.0",
|
||||
build_number: "5",
|
||||
file: "bundles/jcov-3.0+5.zip",
|
||||
build_number: "6",
|
||||
file: "bundles/jcov-3.0+6.zip",
|
||||
environment_name: "JCOV_HOME",
|
||||
},
|
||||
|
||||
|
||||
@ -26,17 +26,17 @@
|
||||
# Default version, product, and vendor information to use,
|
||||
# unless overridden by configure
|
||||
|
||||
DEFAULT_VERSION_FEATURE=27
|
||||
DEFAULT_VERSION_FEATURE=28
|
||||
DEFAULT_VERSION_INTERIM=0
|
||||
DEFAULT_VERSION_UPDATE=0
|
||||
DEFAULT_VERSION_PATCH=0
|
||||
DEFAULT_VERSION_EXTRA1=0
|
||||
DEFAULT_VERSION_EXTRA2=0
|
||||
DEFAULT_VERSION_EXTRA3=0
|
||||
DEFAULT_VERSION_DATE=2026-09-15
|
||||
DEFAULT_VERSION_CLASSFILE_MAJOR=71 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
|
||||
DEFAULT_VERSION_DATE=2027-03-23
|
||||
DEFAULT_VERSION_CLASSFILE_MAJOR=72 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
|
||||
DEFAULT_VERSION_CLASSFILE_MINOR=0
|
||||
DEFAULT_VERSION_DOCS_API_SINCE=11
|
||||
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="26 27"
|
||||
DEFAULT_JDK_SOURCE_TARGET_VERSION=27
|
||||
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="26 27 28"
|
||||
DEFAULT_JDK_SOURCE_TARGET_VERSION=28
|
||||
DEFAULT_PROMOTED_VERSION_PRE=ea
|
||||
|
||||
107
make/devkit/Common.gmk
Normal file
107
make/devkit/Common.gmk
Normal file
@ -0,0 +1,107 @@
|
||||
#
|
||||
# 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
|
||||
# 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.
|
||||
#
|
||||
|
||||
ARCH := $(word 1,$(subst -, ,$(TARGET)))
|
||||
|
||||
ifeq ($(TARGET), arm-linux-gnueabihf)
|
||||
ARCH=armhfp
|
||||
endif
|
||||
|
||||
$(info ARCH=$(ARCH))
|
||||
|
||||
ifeq ($(PREFIX),)
|
||||
$(error PREFIX not set)
|
||||
endif
|
||||
|
||||
BUILDDIR := $(OUTPUT_ROOT)/$(HOST)/$(TARGET)
|
||||
TARGETDIR := $(PREFIX)/$(TARGET)
|
||||
SYSROOT := $(TARGETDIR)/sysroot
|
||||
|
||||
# Base OS information and repositories
|
||||
#
|
||||
# BASE_OS_REPOS is a space-separated set of repositories. Each entry
|
||||
# is of the format <repo_name>,<url> where <repo_name> is a unique
|
||||
# identifier across the set of repositories.
|
||||
|
||||
ifeq ($(BASE_OS), OL)
|
||||
ifeq ($(filter aarch64 x86_64, $(ARCH)), )
|
||||
$(error Only "aarch64 x86_64" architectures are supported for OL, but "$(ARCH)" was requested)
|
||||
endif
|
||||
BASE_OS_VERSION ?= 7
|
||||
ifeq ($(BASE_OS_VERSION), 6)
|
||||
ifeq ($(filter x86_64, $(ARCH)), )
|
||||
$(error Only "x86_64" architectures are supported for OL6, but "$(ARCH)" was requested)
|
||||
endif
|
||||
BASE_OS_DESCRIPTION_VERSION := 6.4
|
||||
REPO_BASE_URL := https://yum.oracle.com/repo/OracleLinux/OL6/4
|
||||
BASE_OS_REPOS := base,$(REPO_BASE_URL)/base/$(ARCH)
|
||||
else ifeq ($(BASE_OS_VERSION), 7)
|
||||
BASE_OS_DESCRIPTION_VERSION := 7.6
|
||||
REPO_BASE_URL := https://yum.oracle.com/repo/OracleLinux/OL7/6
|
||||
BASE_OS_REPOS := base,$(REPO_BASE_URL)/base/$(ARCH)
|
||||
else
|
||||
REPO_BASE_URL := https://yum.oracle.com/repo/OracleLinux/OL$(BASE_OS_VERSION)
|
||||
BASE_OS_REPOS := baseos,$(REPO_BASE_URL)/baseos/latest/$(ARCH) appstream,$(REPO_BASE_URL)/appstream/$(ARCH)
|
||||
BASE_OS_DESCRIPTION_VERSION := $(BASE_OS_VERSION)
|
||||
endif
|
||||
BASE_OS_DESCRIPTION := OL$(BASE_OS_DESCRIPTION_VERSION)
|
||||
else ifeq ($(BASE_OS), Fedora)
|
||||
ifeq ($(filter aarch64 armhfp ppc64le riscv64 s390x x86_64, $(ARCH)), )
|
||||
$(error Only "aarch64 armhfp ppc64le riscv64 s390x x86_64" architectures are supported for Fedora, but "$(ARCH)" was requested)
|
||||
endif
|
||||
ifeq ($(ARCH), riscv64)
|
||||
BASE_OS_VERSION ?= 43
|
||||
BASE_OS_BUILD := 6640
|
||||
REPO_BASE_URL := https://riscv-koji.fedoraproject.org/repos-dist/f$(BASE_OS_VERSION)/$(BASE_OS_BUILD)/riscv64
|
||||
BASE_OS_REPOS := riscv64,$(REPO_BASE_URL)
|
||||
else
|
||||
ifeq ($(ARCH), armhfp)
|
||||
BASE_OS_VERSION ?= 36
|
||||
else
|
||||
BASE_OS_VERSION ?= 41
|
||||
endif
|
||||
ifeq ($(ARCH), armhfp)
|
||||
ifneq ($(BASE_OS_VERSION), 36)
|
||||
$(error Fedora 36 is the last release supporting "armhfp", but $(BASE_OS_VERSION) was requested)
|
||||
endif
|
||||
endif
|
||||
LATEST_ARCHIVED_OS_VERSION := 41
|
||||
ifeq ($(filter aarch64 x86_64 armhfp, $(ARCH)), )
|
||||
FEDORA_TYPE := fedora-secondary
|
||||
else
|
||||
FEDORA_TYPE := fedora/linux
|
||||
endif
|
||||
NOT_ARCHIVED := $(shell [ $(BASE_OS_VERSION) -gt $(LATEST_ARCHIVED_OS_VERSION) ] && echo true)
|
||||
ifeq ($(NOT_ARCHIVED),true)
|
||||
RPM_REPO_URL := https://dl.fedoraproject.org/pub/$(FEDORA_TYPE)/releases/$(BASE_OS_VERSION)/Everything/$(ARCH)/os
|
||||
else
|
||||
RPM_REPO_URL := https://archives.fedoraproject.org/pub/archive/$(FEDORA_TYPE)/releases/$(BASE_OS_VERSION)/Everything/$(ARCH)/os
|
||||
endif
|
||||
BASE_OS_REPOS := os,$(RPM_REPO_URL)
|
||||
endif
|
||||
BASE_OS_DESCRIPTION := Fedora_$(BASE_OS_VERSION)
|
||||
else
|
||||
$(error Unknown base OS $(BASE_OS))
|
||||
endif
|
||||
@ -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
|
||||
@ -39,7 +39,7 @@
|
||||
#
|
||||
# make TARGETS="aarch64-linux-gnu" BASE_OS=Fedora
|
||||
# or
|
||||
# make TARGETS="arm-linux-gnueabihf ppc64le-linux-gnu" BASE_OS=Fedora BASE_OS_VERSION=17
|
||||
# make TARGETS="aarch64-linux-gnu ppc64le-linux-gnu" BASE_OS=Fedora BASE_OS_VERSION=41
|
||||
#
|
||||
# to build several devkits for a specific OS version at once.
|
||||
# You can find the final results under ../../build/devkit/result/<host>-to-<target>
|
||||
@ -50,7 +50,7 @@
|
||||
# makefile again for cross compilation. Ex:
|
||||
#
|
||||
# PATH=$PWD/../../build/devkit/result/x86_64-linux-gnu-to-x86_64-linux-gnu/bin:$PATH \
|
||||
# make TARGETS="arm-linux-gnueabihf ppc64le-linux-gnu" BASE_OS=Fedora
|
||||
# make TARGETS="aarch64-linux-gnu ppc64le-linux-gnu" BASE_OS=Fedora
|
||||
#
|
||||
# This is the makefile which iterates over all host and target platforms.
|
||||
#
|
||||
@ -79,10 +79,10 @@ TARGET_PLATFORMS := $(PLATFORMS)
|
||||
$(info HOST_PLATFORMS $(HOST_PLATFORMS))
|
||||
$(info TARGET_PLATFORMS $(TARGET_PLATFORMS))
|
||||
|
||||
all compile : $(PLATFORMS)
|
||||
all compile: $(PLATFORMS)
|
||||
|
||||
ifeq ($(SKIP_ME), )
|
||||
$(foreach p,$(filter-out $(ME),$(PLATFORMS)),$(eval $(p) : $$(ME)))
|
||||
$(foreach p,$(filter-out $(ME),$(PLATFORMS)),$(eval $(p): $$(ME)))
|
||||
endif
|
||||
|
||||
OUTPUT_ROOT = $(abspath ../../build/devkit)
|
||||
@ -90,38 +90,39 @@ RESULT = $(OUTPUT_ROOT)/result
|
||||
|
||||
SUBMAKEVARS = HOST=$@ BUILD=$(ME) RESULT=$(RESULT) OUTPUT_ROOT=$(OUTPUT_ROOT)
|
||||
|
||||
$(HOST_PLATFORMS) :
|
||||
$(HOST_PLATFORMS):
|
||||
@echo 'Building compilers for $@'
|
||||
@echo 'Targets: $(TARGET_PLATFORMS)'
|
||||
for p in $(filter $@, $(TARGET_PLATFORMS)) $(filter-out $@, $(TARGET_PLATFORMS)); do \
|
||||
$(MAKE) -f Tools.gmk download-rpms $(SUBMAKEVARS) \
|
||||
$(MAKE) -f Sysroot.gmk sysroot $(SUBMAKEVARS) \
|
||||
TARGET=$$p PREFIX=$(RESULT)/$@-to-$$p && \
|
||||
$(MAKE) -f Tools.gmk all $(SUBMAKEVARS) \
|
||||
TARGET=$$p PREFIX=$(RESULT)/$@-to-$$p && \
|
||||
$(MAKE) -f Tools.gmk ccache $(SUBMAKEVARS) \
|
||||
TARGET=$@ PREFIX=$(RESULT)/$@-to-$$p || exit 1 ; \
|
||||
TARGET=$@ PREFIX=$(RESULT)/$@-to-$$p || exit 1; \
|
||||
done
|
||||
@echo 'All done"'
|
||||
@echo 'All done'
|
||||
|
||||
TODAY := $(shell date +%Y%m%d)
|
||||
|
||||
define Mktar
|
||||
$(1)-to-$(2)_tar = $$(RESULT)/sdk-$(1)-to-$(2)-$$(TODAY).tar.gz
|
||||
$$($(1)-to-$(2)_tar) : PLATFORM = $(1)-to-$(2)
|
||||
$$($(1)-to-$(2)_tar): PLATFORM = $(1)-to-$(2)
|
||||
TARFILES += $$($(1)-to-$(2)_tar)
|
||||
endef
|
||||
|
||||
$(foreach p,$(HOST_PLATFORMS),$(foreach t,$(TARGET_PLATFORMS),$(eval $(call Mktar,$(p),$(t)))))
|
||||
|
||||
tars : all $(TARFILES)
|
||||
onlytars : $(TARFILES)
|
||||
%.tar.gz :
|
||||
tars: all $(TARFILES)
|
||||
onlytars: $(TARFILES)
|
||||
|
||||
%.tar.gz:
|
||||
$(MAKE) -r -f Tars.gmk SRC_DIR=$(RESULT)/$(PLATFORM) TAR_FILE=$@
|
||||
|
||||
clean :
|
||||
clean:
|
||||
rm -rf $(addprefix ../../build/devkit/, result $(HOST_PLATFORMS))
|
||||
|
||||
dist-clean: clean
|
||||
rm -rf $(addprefix ../../build/devkit/, src download)
|
||||
|
||||
FORCE :
|
||||
.PHONY : all compile tars $(HOST_PLATFORMS) clean dist-clean
|
||||
.PHONY: all compile tars $(HOST_PLATFORMS) clean dist-clean
|
||||
|
||||
208
make/devkit/Sysroot.gmk
Normal file
208
make/devkit/Sysroot.gmk
Normal file
@ -0,0 +1,208 @@
|
||||
#
|
||||
# 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
|
||||
# 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.
|
||||
#
|
||||
|
||||
include Common.gmk
|
||||
|
||||
$(info TARGET=$(TARGET))
|
||||
$(info HOST=$(HOST))
|
||||
$(info BUILD=$(BUILD))
|
||||
|
||||
ifeq ($(BASE_OS)-$(BASE_OS_VERSION)-$(ARCH), OL-7-aarch64)
|
||||
KERNEL_HEADERS_RPM := kernel-uek-headers
|
||||
else
|
||||
KERNEL_HEADERS_RPM := kernel-headers
|
||||
endif
|
||||
|
||||
ifneq ($(BASE_OS)-$(BASE_OS_VERSION), OL-6)
|
||||
WAYLAND_RPMS := wayland-devel wayland-protocols-devel
|
||||
endif
|
||||
|
||||
ZLIB_RPMS := zlib zlib-devel
|
||||
ifeq ($(BASE_OS), Fedora)
|
||||
ifneq ($(ARCH), armhfp)
|
||||
ZLIB_RPMS := zlib-ng zlib-ng-devel
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
# RPMs to include
|
||||
RPM_LIST := \
|
||||
filesystem \
|
||||
$(KERNEL_HEADERS_RPM) \
|
||||
glibc glibc-devel \
|
||||
cups-libs cups-devel \
|
||||
libX11 libX11-devel \
|
||||
libxcb xorg-x11-proto-devel \
|
||||
alsa-lib alsa-lib-devel \
|
||||
libXext libXext-devel \
|
||||
libXtst libXtst-devel \
|
||||
libXrender libXrender-devel \
|
||||
libXrandr libXrandr-devel \
|
||||
freetype freetype-devel \
|
||||
libXt libXt-devel \
|
||||
libSM libSM-devel \
|
||||
libICE libICE-devel \
|
||||
libXi libXi-devel \
|
||||
libXau libXau-devel \
|
||||
libgcc \
|
||||
$(ZLIB_RPMS) \
|
||||
libffi libffi-devel \
|
||||
fontconfig fontconfig-devel \
|
||||
systemtap-sdt-devel \
|
||||
$(WAYLAND_RPMS) \
|
||||
#
|
||||
|
||||
################################################################################
|
||||
# Define common directories and files
|
||||
|
||||
DOWNLOAD := $(OUTPUT_ROOT)/download
|
||||
DOWNLOAD_RPMS := $(DOWNLOAD)/rpms/$(TARGET)-$(BASE_OS)-$(BASE_OS_VERSION)
|
||||
SRCDIR := $(OUTPUT_ROOT)/src
|
||||
|
||||
################################################################################
|
||||
# Marker files
|
||||
|
||||
DOWNLOAD_RPMS_MARKER := $(BUILDDIR)/download-rpms.marker
|
||||
RPMS_UNPACKED_MARKER := $(BUILDDIR)/rpms_unpacked.marker
|
||||
UNPATCHED_SYSROOT_MARKER := $(BUILDDIR)/sysroot_unpatched.marker
|
||||
PATCHED_SYSROOT_MARKER := $(BUILDDIR)/sysroot_patched.marker
|
||||
|
||||
################################################################################
|
||||
# Download RPMs
|
||||
|
||||
ifeq ($(ARCH), armhfp)
|
||||
RPM_ARCH := armv7hl
|
||||
else
|
||||
RPM_ARCH := $(ARCH)
|
||||
endif
|
||||
|
||||
RPM_ARCHS := $(RPM_ARCH) noarch
|
||||
ifeq ($(ARCH), x86_64)
|
||||
# Enable mixed mode.
|
||||
RPM_ARCHS += i386 i686
|
||||
endif
|
||||
|
||||
EMPTY :=
|
||||
SPACE := $(EMPTY) $(EMPTY)
|
||||
COMMA := ,
|
||||
DNF_ARCHS := $(foreach arch,$(RPM_ARCHS),--arch $(arch))
|
||||
|
||||
# Specify a dummy installation root, otherwise dnf will run into
|
||||
# problems trying to reconcile with the local/system state
|
||||
DNF_DUMMY_INSTALL_ROOT := $(BUILDDIR)/dnf-dummy-install-root
|
||||
|
||||
DNF_REPOS := $(foreach repo, $(BASE_OS_REPOS), \
|
||||
--repofrompath $(repo) \
|
||||
--enablerepo $(word 1,$(subst $(COMMA),$(SPACE),$(repo))))
|
||||
|
||||
DNF_DOWNLOAD_FLAGS := \
|
||||
--disablerepo='*' \
|
||||
$(DNF_REPOS) \
|
||||
--resolve \
|
||||
$(DNF_ARCHS) \
|
||||
--forcearch $(RPM_ARCH) \
|
||||
--installroot $(DNF_DUMMY_INSTALL_ROOT) \
|
||||
--releasever $(BASE_OS_VERSION) \
|
||||
#
|
||||
|
||||
$(DOWNLOAD_RPMS_MARKER):
|
||||
@mkdir -p $(@D)
|
||||
mkdir -p $(DOWNLOAD_RPMS)
|
||||
echo $(RPM_LIST) | \
|
||||
xargs dnf download $(DNF_DOWNLOAD_FLAGS) --destdir $(DOWNLOAD_RPMS)
|
||||
touch $@
|
||||
|
||||
################################################################################
|
||||
# Unpack RPMS
|
||||
|
||||
RPM_PATTERNS := $(foreach arch,$(RPM_ARCHS),$(DOWNLOAD_RPMS)/*.$(arch).rpm)
|
||||
|
||||
CPIO_EXCLUDES := \
|
||||
"./usr/share/doc/*" \
|
||||
"./usr/share/man/*" \
|
||||
"./usr/X11R6/man/*" \
|
||||
"*/X11/locale/*" \
|
||||
#
|
||||
|
||||
$(RPMS_UNPACKED_MARKER): $(DOWNLOAD_RPMS_MARKER)
|
||||
if [ -d $(SYSROOT) ]; then echo "WARNING: Sysroot directory ($(SYSROOT)) already exists, proceeding anyway..."; fi
|
||||
@mkdir -p $(SYSROOT)
|
||||
# The -e test below is needed to skip unmatched glob patterns
|
||||
( \
|
||||
cd $(SYSROOT); \
|
||||
for rpm in $(RPM_PATTERNS); do \
|
||||
if [ ! -e "$$rpm" ]; then continue; fi; \
|
||||
echo Extracting $$rpm...; \
|
||||
rpm2cpio $$rpm | \
|
||||
cpio --extract --make-directories -f $(CPIO_EXCLUDES) \
|
||||
|| exit 1; \
|
||||
done \
|
||||
)
|
||||
touch $@
|
||||
|
||||
################################################################################
|
||||
|
||||
$(UNPATCHED_SYSROOT_MARKER): $(RPMS_UNPACKED_MARKER)
|
||||
touch $@
|
||||
|
||||
################################################################################
|
||||
# Patch sysroot
|
||||
|
||||
# Note: MUST create a <sysroot>/usr/lib even if not really needed.
|
||||
# gcc will use a path relative to it to resolve lib64. (x86_64).
|
||||
# we're creating multi-lib compiler with 32bit libc as well, so we should
|
||||
# have it anyway, but just to make sure...
|
||||
# Patch GNU ld scripts to force linking against libraries in the sysroot
|
||||
# and not the ones installed on the build machine.
|
||||
|
||||
LD_SCRIPT_PATCHES := \
|
||||
-e 's|/usr/lib64/||g' \
|
||||
-e 's|/usr/lib/||g' \
|
||||
-e 's|/lib64/||g' \
|
||||
-e 's|/lib/||g' \
|
||||
#
|
||||
|
||||
$(PATCHED_SYSROOT_MARKER): $(UNPATCHED_SYSROOT_MARKER)
|
||||
@echo Patching GNU ld scripts
|
||||
@( \
|
||||
for f in $$(find $(SYSROOT) -name "*.so" -type f 2>/dev/null); do \
|
||||
if grep -Iq 'GNU ld script' "$$f"; then \
|
||||
sed $(LD_SCRIPT_PATCHES) "$$f" > "$$f.tmp" && \
|
||||
mv "$$f.tmp" "$$f"; \
|
||||
fi; \
|
||||
done \
|
||||
)
|
||||
@mkdir -p $(SYSROOT)/usr/lib
|
||||
@touch $@
|
||||
|
||||
################################################################################
|
||||
|
||||
download-rpms: $(DOWNLOAD_RPMS_MARKER)
|
||||
unpatched-sysroot: $(UNPATCHED_SYSROOT_MARKER)
|
||||
sysroot: $(PATCHED_SYSROOT_MARKER)
|
||||
|
||||
.PHONY: download-rpms unpatched-sysroot sysroot
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2013, 2019, 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
|
||||
@ -39,9 +39,9 @@ endif
|
||||
|
||||
default: tars
|
||||
|
||||
tars : $(TAR_FILE)
|
||||
tars: $(TAR_FILE)
|
||||
|
||||
$(TAR_FILE): $(shell find $(SRC_DIR) -type f)
|
||||
$(TAR_FILE): $(shell find $(SRC_DIR) -type f | sed 's/ /\\ /g')
|
||||
@echo 'Creating compiler package $@'
|
||||
cd $(dir $(SRC_DIR)) && tar -czf $@ $(notdir $(SRC_DIR))/*
|
||||
touch $@
|
||||
|
||||
@ -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
|
||||
@ -39,68 +39,14 @@
|
||||
# Fix this...
|
||||
#
|
||||
|
||||
include Common.gmk
|
||||
|
||||
lowercase = $(shell echo $1 | tr A-Z a-z)
|
||||
|
||||
$(info TARGET=$(TARGET))
|
||||
$(info HOST=$(HOST))
|
||||
$(info BUILD=$(BUILD))
|
||||
|
||||
ARCH := $(word 1,$(subst -, ,$(TARGET)))
|
||||
|
||||
ifeq ($(TARGET), arm-linux-gnueabihf)
|
||||
ARCH=armhfp
|
||||
endif
|
||||
|
||||
$(info ARCH=$(ARCH))
|
||||
|
||||
KERNEL_HEADERS_RPM := kernel-headers
|
||||
|
||||
ifeq ($(BASE_OS), OL)
|
||||
ifeq ($(ARCH), aarch64)
|
||||
BASE_URL := https://yum.oracle.com/repo/OracleLinux/OL7/6/base/$(ARCH)/
|
||||
LINUX_VERSION := OL7.6
|
||||
KERNEL_HEADERS_RPM := kernel-uek-headers
|
||||
else
|
||||
BASE_URL := https://yum.oracle.com/repo/OracleLinux/OL6/4/base/$(ARCH)/
|
||||
LINUX_VERSION := OL6.4
|
||||
endif
|
||||
else ifeq ($(BASE_OS), Fedora)
|
||||
DEFAULT_OS_VERSION := 41
|
||||
ifeq ($(BASE_OS_VERSION), )
|
||||
BASE_OS_VERSION := $(DEFAULT_OS_VERSION)
|
||||
endif
|
||||
ifeq ($(filter aarch64 armhfp ppc64le riscv64 s390x x86_64, $(ARCH)), )
|
||||
$(error Only "aarch64 armhfp ppc64le riscv64 s390x x86_64" architectures are supported for Fedora, but "$(ARCH)" was requested)
|
||||
endif
|
||||
ifeq ($(ARCH), riscv64)
|
||||
ifeq ($(filter 38 39 40 41, $(BASE_OS_VERSION)), )
|
||||
$(error Only Fedora 38-41 are supported for "$(ARCH)", but Fedora $(BASE_OS_VERSION) was requested)
|
||||
endif
|
||||
BASE_URL := http://fedora.riscv.rocks/repos-dist/f$(BASE_OS_VERSION)/latest/$(ARCH)/Packages/
|
||||
else
|
||||
LATEST_ARCHIVED_OS_VERSION := 41
|
||||
ifeq ($(filter aarch64 armhfp x86_64, $(ARCH)), )
|
||||
FEDORA_TYPE := fedora-secondary
|
||||
else
|
||||
FEDORA_TYPE := fedora/linux
|
||||
endif
|
||||
ifeq ($(ARCH), armhfp)
|
||||
ifneq ($(BASE_OS_VERSION), 36)
|
||||
$(error Fedora 36 is the last release supporting "armhfp", but $(BASE_OS) was requested)
|
||||
endif
|
||||
endif
|
||||
NOT_ARCHIVED := $(shell [ $(BASE_OS_VERSION) -gt $(LATEST_ARCHIVED_OS_VERSION) ] && echo true)
|
||||
ifeq ($(NOT_ARCHIVED),true)
|
||||
BASE_URL := https://dl.fedoraproject.org/pub/$(FEDORA_TYPE)/releases/$(BASE_OS_VERSION)/Everything/$(ARCH)/os/Packages/
|
||||
else
|
||||
BASE_URL := https://archives.fedoraproject.org/pub/archive/$(FEDORA_TYPE)/releases/$(BASE_OS_VERSION)/Everything/$(ARCH)/os/Packages/
|
||||
endif
|
||||
endif
|
||||
LINUX_VERSION := Fedora_$(BASE_OS_VERSION)
|
||||
else
|
||||
$(error Unknown base OS $(BASE_OS))
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
# Define external dependencies
|
||||
|
||||
@ -156,32 +102,6 @@ ifneq ($(REQUIRED_MIN_MAKE_MAJOR_VERSION),)
|
||||
endif
|
||||
endif
|
||||
|
||||
# RPMs used by all BASE_OS
|
||||
RPM_LIST := \
|
||||
$(KERNEL_HEADERS_RPM) \
|
||||
glibc glibc-headers glibc-devel \
|
||||
cups-libs cups-devel \
|
||||
libX11 libX11-devel \
|
||||
libxcb xorg-x11-proto-devel \
|
||||
alsa-lib alsa-lib-devel \
|
||||
libXext libXext-devel \
|
||||
libXtst libXtst-devel \
|
||||
libXrender libXrender-devel \
|
||||
libXrandr libXrandr-devel \
|
||||
freetype freetype-devel \
|
||||
libXt libXt-devel \
|
||||
libSM libSM-devel \
|
||||
libICE libICE-devel \
|
||||
libXi libXi-devel \
|
||||
libXdmcp libXdmcp-devel \
|
||||
libXau libXau-devel \
|
||||
libgcc libxcrypt \
|
||||
zlib zlib-devel \
|
||||
libffi libffi-devel \
|
||||
fontconfig fontconfig-devel \
|
||||
systemtap-sdt-devel \
|
||||
#
|
||||
|
||||
################################################################################
|
||||
# Define common directories and files
|
||||
|
||||
@ -194,28 +114,11 @@ else
|
||||
endif
|
||||
|
||||
# Define directories
|
||||
BUILDDIR := $(OUTPUT_ROOT)/$(HOST)/$(TARGET)
|
||||
TARGETDIR := $(PREFIX)/$(TARGET)
|
||||
SYSROOT := $(TARGETDIR)/sysroot
|
||||
DOWNLOAD := $(OUTPUT_ROOT)/download
|
||||
DOWNLOAD_RPMS := $(DOWNLOAD)/rpms/$(TARGET)-$(LINUX_VERSION)
|
||||
SRCDIR := $(OUTPUT_ROOT)/src
|
||||
|
||||
# Marker file for unpacking rpms
|
||||
RPMS := $(SYSROOT)/rpms_unpacked
|
||||
|
||||
# Need to patch libs that are linker scripts to use non-absolute paths
|
||||
LIBS := $(SYSROOT)/libs_patched
|
||||
|
||||
################################################################################
|
||||
# Download RPMs
|
||||
download-rpms:
|
||||
mkdir -p $(DOWNLOAD_RPMS)
|
||||
# Only run this if rpm dir is empty.
|
||||
ifeq ($(wildcard $(DOWNLOAD_RPMS)/*.rpm), )
|
||||
cd $(DOWNLOAD_RPMS) && \
|
||||
wget -r -np -nd $(patsubst %, -A "*%*.rpm", $(RPM_LIST)) $(BASE_URL)
|
||||
endif
|
||||
# Marker files
|
||||
LINK_LIBS_MARKER := $(BUILDDIR)/link_libs.marker
|
||||
|
||||
################################################################################
|
||||
# Unpack source packages
|
||||
@ -236,24 +139,24 @@ define DownloadVerify
|
||||
endif
|
||||
$(1)_FILE = $(DOWNLOAD)/$(notdir $($(1)_URL))
|
||||
|
||||
$$($(1)_SRC_MARKER) : $$($(1)_FILE)
|
||||
$$($(1)_SRC_MARKER): $$($(1)_FILE)
|
||||
mkdir -p $$(SRCDIR)
|
||||
tar -C $$(SRCDIR) -xf $$<
|
||||
$$(foreach p,$$(abspath $$(wildcard patches/$$(ARCH)-$$(notdir $$($(1)_DIR)).patch)), \
|
||||
echo PATCHING $$(p) ; \
|
||||
patch -d $$($(1)_DIR) -p1 -i $$(p) ; \
|
||||
echo PATCHING $$(p); \
|
||||
patch -d $$($(1)_DIR) -p1 -i $$(p); \
|
||||
)
|
||||
touch $$@
|
||||
|
||||
$$($(1)_FILE) :
|
||||
$$($(1)_FILE):
|
||||
mkdir -p $$(@D)
|
||||
wget -O - $$($(1)_URL) > $$@.tmp
|
||||
sha512_actual="$$$$(sha512sum $$@.tmp | awk '{ print $$$$1; }')"; \
|
||||
if [ x"$$$${sha512_actual}" != x"$$($(1)_SHA512)" ]; then \
|
||||
echo "Checksum mismatch for $$@.tmp"; \
|
||||
echo " Expected: $$($(1)_SHA512)"; \
|
||||
echo " Actual: $$$${sha512_actual}"; \
|
||||
exit 1; \
|
||||
echo "Checksum mismatch for $$@.tmp"; \
|
||||
echo " Expected: $$($(1)_SHA512)"; \
|
||||
echo " Actual: $$$${sha512_actual}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
mv $$@.tmp $$@
|
||||
endef
|
||||
@ -261,89 +164,17 @@ endef
|
||||
# Download and unpack all source packages
|
||||
$(foreach dep,$(DEPENDENCIES),$(eval $(call DownloadVerify,$(dep))))
|
||||
|
||||
################################################################################
|
||||
# Unpack RPMS
|
||||
|
||||
RPM_ARCHS := $(ARCH) noarch
|
||||
ifeq ($(ARCH),x86_64)
|
||||
# Enable mixed mode.
|
||||
RPM_ARCHS += i386 i686
|
||||
else ifeq ($(ARCH),i686)
|
||||
RPM_ARCHS += i386
|
||||
else ifeq ($(ARCH), armhfp)
|
||||
RPM_ARCHS += armv7hl
|
||||
endif
|
||||
|
||||
RPM_FILE_LIST := $(sort $(foreach a, $(RPM_ARCHS), \
|
||||
$(wildcard $(patsubst %,$(DOWNLOAD_RPMS)/%*$a.rpm,$(RPM_LIST))) \
|
||||
))
|
||||
|
||||
# Note. For building linux you should install rpm2cpio.
|
||||
define unrpm
|
||||
$(SYSROOT)/$(notdir $(1)).unpacked : $(1)
|
||||
$$(RPMS) : $(SYSROOT)/$(notdir $(1)).unpacked
|
||||
endef
|
||||
|
||||
%.unpacked :
|
||||
$(info Unpacking target rpms and libraries from $<)
|
||||
@(mkdir -p $(@D); \
|
||||
cd $(@D); \
|
||||
rpm2cpio $< | \
|
||||
cpio --extract --make-directories \
|
||||
-f \
|
||||
"./usr/share/doc/*" \
|
||||
"./usr/share/man/*" \
|
||||
"./usr/X11R6/man/*" \
|
||||
"*/X11/locale/*" \
|
||||
|| die ; )
|
||||
touch $@
|
||||
|
||||
$(foreach p,$(RPM_FILE_LIST),$(eval $(call unrpm,$(p))))
|
||||
|
||||
################################################################################
|
||||
|
||||
# Note: MUST create a <sysroot>/usr/lib even if not really needed.
|
||||
# gcc will use a path relative to it to resolve lib64. (x86_64).
|
||||
# we're creating multi-lib compiler with 32bit libc as well, so we should
|
||||
# have it anyway, but just to make sure...
|
||||
# Patch libc.so and libpthread.so to force linking against libraries in sysroot
|
||||
# and not the ones installed on the build machine.
|
||||
$(LIBS) : $(RPMS)
|
||||
@echo Patching libc and pthreads
|
||||
@(for f in `find $(SYSROOT) -name libc.so -o -name libpthread.so`; do \
|
||||
(cat $$f | sed -e 's|/usr/lib64/||g' \
|
||||
-e 's|/usr/lib/||g' \
|
||||
-e 's|/lib64/||g' \
|
||||
-e 's|/lib/||g' ) > $$f.tmp ; \
|
||||
mv $$f.tmp $$f ; \
|
||||
done)
|
||||
@mkdir -p $(SYSROOT)/usr/lib
|
||||
@touch $@
|
||||
|
||||
################################################################################
|
||||
# Create links for ffi header files so that they become visible by default when using the
|
||||
# devkit.
|
||||
ifeq ($(ARCH), x86_64)
|
||||
$(SYSROOT)/usr/include/ffi.h: $(RPMS)
|
||||
cd $(@D) && rm -f $(@F) && ln -s ../lib/libffi-*/include/$(@F) .
|
||||
|
||||
$(SYSROOT)/usr/include/ffitarget.h: $(RPMS)
|
||||
cd $(@D) && rm -f $(@F) && ln -s ../lib/libffi-*/include/$(@F) .
|
||||
|
||||
SYSROOT_LINKS += $(SYSROOT)/usr/include/ffi.h $(SYSROOT)/usr/include/ffitarget.h
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
|
||||
# Define marker files for each source package to be compiled
|
||||
$(foreach dep,$(DEPENDENCIES),$(eval $(dep) = $(TARGETDIR)/$($(dep)_VER).done))
|
||||
|
||||
################################################################################
|
||||
|
||||
# Default base config
|
||||
CONFIG = --target=$(TARGET) \
|
||||
CONFIG = \
|
||||
--target=$(TARGET) \
|
||||
--host=$(HOST) --build=$(BUILD) \
|
||||
--prefix=$(PREFIX)
|
||||
--prefix=$(PREFIX) \
|
||||
#
|
||||
|
||||
CMAKE_CONFIG = -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$(PREFIX)
|
||||
|
||||
@ -357,7 +188,6 @@ BUILDPAR = -j$(NUM_CORES)
|
||||
MAKECMD =
|
||||
INSTALLCMD = install
|
||||
|
||||
|
||||
declare_tools = CC$(1)=$(2)gcc LD$(1)=$(2)ld AR$(1)=$(2)ar AS$(1)=$(2)as RANLIB$(1)=$(2)ranlib CXX$(1)=$(2)g++ OBJDUMP$(1)=$(2)objdump
|
||||
|
||||
ifeq ($(HOST),$(BUILD))
|
||||
@ -376,10 +206,10 @@ TOOLS ?= $(call declare_tools,_FOR_TARGET,$(TARGET)-)
|
||||
# CFLAG_<name> to most likely -m32.
|
||||
define mk_bfd
|
||||
$$(info Libs for $(1))
|
||||
$$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile \
|
||||
: CFLAGS += $$(CFLAGS_$(1))
|
||||
$$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile \
|
||||
: LIBDIRS = --libdir=$(TARGETDIR)/$(1)
|
||||
$$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile: \
|
||||
CFLAGS += $$(CFLAGS_$(1))
|
||||
$$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile: \
|
||||
LIBDIRS = --libdir=$(TARGETDIR)/$(1)
|
||||
|
||||
BFDLIB += $$(TARGETDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1)).done
|
||||
BFDMAKES += $$(BUILDDIR)/$$(BINUTILS_VER)-$(subst /,-,$(1))/Makefile
|
||||
@ -389,18 +219,18 @@ endef
|
||||
$(foreach l,$(LIBDIRS),$(eval $(call mk_bfd,$(l))))
|
||||
|
||||
# Only build these two libs.
|
||||
$(BFDLIB) : MAKECMD = all-libiberty all-bfd
|
||||
$(BFDLIB) : INSTALLCMD = install-libiberty install-bfd
|
||||
$(BFDLIB): MAKECMD = all-libiberty all-bfd
|
||||
$(BFDLIB): INSTALLCMD = install-libiberty install-bfd
|
||||
|
||||
# Building targets libbfd + libiberty. HOST==TARGET, i.e not
|
||||
# for a cross env.
|
||||
$(BFDMAKES) : CONFIG = --target=$(TARGET) \
|
||||
$(BFDMAKES): CONFIG = --target=$(TARGET) \
|
||||
--host=$(TARGET) --build=$(BUILD) \
|
||||
--prefix=$(TARGETDIR) \
|
||||
--with-sysroot=$(SYSROOT) \
|
||||
$(LIBDIRS)
|
||||
|
||||
$(BFDMAKES) : TOOLS = $(call declare_tools,_FOR_TARGET,$(TARGET)-) $(call declare_tools,,$(TARGET)-)
|
||||
$(BFDMAKES): TOOLS = $(call declare_tools,_FOR_TARGET,$(TARGET)-) $(call declare_tools,,$(TARGET)-)
|
||||
|
||||
################################################################################
|
||||
|
||||
@ -410,50 +240,52 @@ $(GCC) \
|
||||
$(MPFR) \
|
||||
$(MPC) \
|
||||
$(BFDMAKES) \
|
||||
$(CCACHE) : ENVS += $(TOOLS)
|
||||
$(CCACHE): ENVS += $(TOOLS)
|
||||
|
||||
# libdir to work around hateful bfd stuff installing into wrong dirs...
|
||||
# ensure we have 64 bit bfd support in the HOST library. I.e our
|
||||
# compiler on i686 will know 64 bit symbols, BUT later
|
||||
# we build just the libs again for TARGET, then with whatever the arch
|
||||
# wants.
|
||||
$(BUILDDIR)/$(BINUTILS_VER)/Makefile : CONFIG += --enable-64-bit-bfd --libdir=$(PREFIX)/$(word 1,$(LIBDIRS))
|
||||
$(BUILDDIR)/$(BINUTILS_VER)/Makefile: CONFIG += --enable-64-bit-bfd --libdir=$(PREFIX)/$(word 1,$(LIBDIRS))
|
||||
|
||||
ifeq ($(filter $(ARCH), s390x riscv64 ppc64le), )
|
||||
# gold compiles but cannot link properly on s390x @ gcc 13.2 and Fedore 41
|
||||
# gold is not available for riscv64 and ppc64le,
|
||||
# and subsequent linking will fail if we try to enable it.
|
||||
LINKER_CONFIG := --enable-gold=default
|
||||
LINKER_CONFIG_ENABLE_GOLD := --enable-gold=default
|
||||
endif
|
||||
|
||||
ifeq ($(filter riscv64 ppc64le s390x armhfp, $(ARCH)), )
|
||||
ENABLE_MULTILIB := --enable-multilib
|
||||
endif
|
||||
|
||||
# Makefile creation. Simply run configure in build dir.
|
||||
# Setting CFLAGS to -O2 generates a much faster ld.
|
||||
$(BFDMAKES) \
|
||||
$(BUILDDIR)/$(BINUTILS_VER)/Makefile \
|
||||
: $(BINUTILS_CFG)
|
||||
$(BUILDDIR)/$(BINUTILS_VER)/Makefile: $(BINUTILS_CFG)
|
||||
$(info Configuring $@. Log in $(@D)/log.config)
|
||||
@mkdir -p $(@D)
|
||||
( \
|
||||
cd $(@D) ; \
|
||||
cd $(@D); \
|
||||
$(PATHPRE) $(ENVS) CFLAGS="-O2 $(CFLAGS)" \
|
||||
$(BINUTILS_CFG) \
|
||||
$(CONFIG) \
|
||||
$(LINKER_CONFIG) \
|
||||
$(LINKER_CONFIG_ENABLE_GOLD) \
|
||||
--with-sysroot=$(SYSROOT) \
|
||||
--disable-nls \
|
||||
--program-prefix=$(TARGET)- \
|
||||
--enable-multilib \
|
||||
$(ENABLE_MULTILIB) \
|
||||
--enable-threads \
|
||||
--enable-plugins \
|
||||
) > $(@D)/log.config 2>&1
|
||||
@echo 'done'
|
||||
|
||||
$(BUILDDIR)/$(MPFR_VER)/Makefile \
|
||||
: $(MPFR_CFG)
|
||||
$(BUILDDIR)/$(MPFR_VER)/Makefile: $(MPFR_CFG)
|
||||
$(info Configuring $@. Log in $(@D)/log.config)
|
||||
@mkdir -p $(@D)
|
||||
( \
|
||||
cd $(@D) ; \
|
||||
cd $(@D); \
|
||||
$(PATHPRE) $(ENVS) CFLAGS="$(CFLAGS)" \
|
||||
$(MPFR_CFG) \
|
||||
$(CONFIG) \
|
||||
@ -463,12 +295,11 @@ $(BUILDDIR)/$(MPFR_VER)/Makefile \
|
||||
) > $(@D)/log.config 2>&1
|
||||
@echo 'done'
|
||||
|
||||
$(BUILDDIR)/$(GMP_VER)/Makefile \
|
||||
: $(GMP_CFG)
|
||||
$(BUILDDIR)/$(GMP_VER)/Makefile: $(GMP_CFG)
|
||||
$(info Configuring $@. Log in $(@D)/log.config)
|
||||
@mkdir -p $(@D)
|
||||
( \
|
||||
cd $(@D) ; \
|
||||
cd $(@D); \
|
||||
$(PATHPRE) $(ENVS) CFLAGS="$(CFLAGS)" \
|
||||
$(GMP_CFG) \
|
||||
--host=$(HOST) --build=$(BUILD) \
|
||||
@ -480,12 +311,11 @@ $(BUILDDIR)/$(GMP_VER)/Makefile \
|
||||
) > $(@D)/log.config 2>&1
|
||||
@echo 'done'
|
||||
|
||||
$(BUILDDIR)/$(MPC_VER)/Makefile \
|
||||
: $(MPC_CFG)
|
||||
$(BUILDDIR)/$(MPC_VER)/Makefile: $(MPC_CFG)
|
||||
$(info Configuring $@. Log in $(@D)/log.config)
|
||||
@mkdir -p $(@D)
|
||||
( \
|
||||
cd $(@D) ; \
|
||||
cd $(@D); \
|
||||
$(PATHPRE) $(ENVS) CFLAGS="$(CFLAGS)" \
|
||||
$(MPC_CFG) \
|
||||
$(CONFIG) \
|
||||
@ -499,14 +329,18 @@ $(BUILDDIR)/$(MPC_VER)/Makefile \
|
||||
# Only valid if glibc target -> linux
|
||||
# proper destructor handling for c++
|
||||
ifneq (,$(findstring linux,$(TARGET)))
|
||||
$(BUILDDIR)/$(GCC_VER)/Makefile : CONFIG += --enable-__cxa_atexit
|
||||
$(BUILDDIR)/$(GCC_VER)/Makefile: CONFIG += --enable-__cxa_atexit
|
||||
endif
|
||||
|
||||
$(BUILDDIR)/$(GCC_VER)/Makefile: CONFIG += --disable-libgomp
|
||||
ifeq ($(ARCH), armhfp)
|
||||
$(BUILDDIR)/$(GCC_VER)/Makefile : CONFIG += --with-float=hard
|
||||
$(BUILDDIR)/$(GCC_VER)/Makefile: CONFIG += --with-float=hard
|
||||
endif
|
||||
ifeq ($(ARCH), riscv64)
|
||||
$(BUILDDIR)/$(GCC_VER)/Makefile: CONFIG += --disable-libsanitizer
|
||||
endif
|
||||
|
||||
ifneq ($(filter riscv64 ppc64le s390x, $(ARCH)), )
|
||||
ifneq ($(filter riscv64 ppc64le s390x armhfp, $(ARCH)), )
|
||||
# We only support 64-bit on these platforms anyway
|
||||
CONFIG += --disable-multilib
|
||||
endif
|
||||
@ -518,12 +352,11 @@ endif
|
||||
# skip native language.
|
||||
# and link and assemble with the binutils we created
|
||||
# earlier, so --with-gnu*
|
||||
$(BUILDDIR)/$(GCC_VER)/Makefile \
|
||||
: $(GCC_CFG)
|
||||
$(BUILDDIR)/$(GCC_VER)/Makefile: $(GCC_CFG)
|
||||
$(info Configuring $@. Log in $(@D)/log.config)
|
||||
mkdir -p $(@D)
|
||||
( \
|
||||
cd $(@D) ; \
|
||||
cd $(@D); \
|
||||
$(PATHPRE) $(ENVS) $(GCC_CFG) $(EXTRA_CFLAGS) \
|
||||
$(CONFIG) \
|
||||
--with-sysroot=$(SYSROOT) \
|
||||
@ -540,12 +373,12 @@ $(BUILDDIR)/$(GCC_VER)/Makefile \
|
||||
@echo 'done'
|
||||
|
||||
# need binutils for gcc
|
||||
$(GCC) : $(BINUTILS)
|
||||
$(GCC): $(BINUTILS)
|
||||
|
||||
# as of 4.3 or so need these for doing config
|
||||
$(BUILDDIR)/$(GCC_VER)/Makefile : $(GMP) $(MPFR) $(MPC)
|
||||
$(MPFR) : $(GMP)
|
||||
$(MPC) : $(GMP) $(MPFR)
|
||||
$(BUILDDIR)/$(GCC_VER)/Makefile: $(GMP) $(MPFR) $(MPC)
|
||||
$(MPFR): $(GMP)
|
||||
$(MPC): $(GMP) $(MPFR)
|
||||
|
||||
################################################################################
|
||||
# Build gdb but only where host and target match
|
||||
@ -554,7 +387,7 @@ ifeq ($(HOST), $(TARGET))
|
||||
$(info Configuring $@. Log in $(@D)/log.config)
|
||||
mkdir -p $(@D)
|
||||
( \
|
||||
cd $(@D) ; \
|
||||
cd $(@D); \
|
||||
$(PATHPRE) $(ENVS) CFLAGS="$(CFLAGS)" $(GDB_CFG) \
|
||||
$(CONFIG) \
|
||||
--with-sysroot=$(SYSROOT) \
|
||||
@ -574,14 +407,12 @@ endif
|
||||
|
||||
################################################################################
|
||||
# very straightforward. just build a ccache. it is only for host.
|
||||
$(BUILDDIR)/$(CCACHE_VER)/Makefile \
|
||||
: $(CCACHE_SRC_MARKER)
|
||||
$(BUILDDIR)/$(CCACHE_VER)/Makefile: $(CCACHE_SRC_MARKER)
|
||||
$(info Configuring $@. Log in $(@D)/log.config)
|
||||
@mkdir -p $(@D)
|
||||
@( \
|
||||
cd $(@D) ; \
|
||||
$(PATHPRE) $(ENVS) $(CCACHE_CFG) \
|
||||
$(CCACHE_CONFIG) \
|
||||
cd $(@D); \
|
||||
$(PATHPRE) $(ENVS) $(CCACHE_CFG) $(CCACHE_CONFIG) \
|
||||
) > $(@D)/log.config 2>&1
|
||||
@echo 'done'
|
||||
|
||||
@ -590,13 +421,12 @@ GCC_PATCHED = $(TARGETDIR)/gcc-patched
|
||||
################################################################################
|
||||
# For some reason cpp is not created as a target-compiler
|
||||
ifeq ($(HOST),$(TARGET))
|
||||
$(GCC_PATCHED) : $(GCC) link_libs
|
||||
$(GCC_PATCHED): $(LINK_LIBS_MARKER)
|
||||
@echo -n 'Creating compiler symlinks...'
|
||||
@for f in cpp; do \
|
||||
if [ ! -e $(PREFIX)/bin/$(TARGET)-$$f ]; \
|
||||
then \
|
||||
if [ ! -e $(PREFIX)/bin/$(TARGET)-$$f ]; then \
|
||||
cd $(PREFIX)/bin && \
|
||||
ln -fs $$f $(TARGET)-$$f ; \
|
||||
ln -fs $$f $(TARGET)-$$f; \
|
||||
fi \
|
||||
done
|
||||
@touch $@
|
||||
@ -606,19 +436,21 @@ ifeq ($(HOST),$(TARGET))
|
||||
# Ugly at best. Seems that when we compile host->host compiler, that are NOT
|
||||
# the BUILD compiler, the result will not try searching for libs in package root.
|
||||
# "Solve" this by create links from the target libdirs to where they are.
|
||||
link_libs:
|
||||
$(LINK_LIBS_MARKER): $(GCC)
|
||||
@echo -n 'Creating library symlinks...'
|
||||
@$(foreach l,$(LIBDIRS), \
|
||||
for f in `cd $(PREFIX)/$(l) && ls`; do \
|
||||
if [ ! -e $(TARGETDIR)/$(l)/$$f ]; then \
|
||||
mkdir -p $(TARGETDIR)/$(l) && \
|
||||
cd $(TARGETDIR)/$(l)/ && \
|
||||
ln -fs $(if $(findstring /,$(l)),../,)../../$(l)/$$f $$f; \
|
||||
fi \
|
||||
done;)
|
||||
@for l in $(LIBDIRS); do \
|
||||
for f in `cd $(PREFIX)/$$l && ls`; do \
|
||||
if [ ! -e $(TARGETDIR)/$$l/$$f ]; then \
|
||||
mkdir -p $(TARGETDIR)/$$l && \
|
||||
cd $(TARGETDIR)/$$l/ && \
|
||||
ln -fs ../../$$l/$$f $$f; \
|
||||
fi \
|
||||
done \
|
||||
done
|
||||
@touch $@
|
||||
@echo 'done'
|
||||
else
|
||||
$(GCC_PATCHED) :
|
||||
$(GCC_PATCHED):
|
||||
@echo 'done'
|
||||
endif
|
||||
|
||||
@ -628,7 +460,7 @@ endif
|
||||
# make install.
|
||||
# Use path to our build hosts cross tools
|
||||
# Always need to build cross tools for build host self.
|
||||
$(TARGETDIR)/%.done : $(BUILDDIR)/%/Makefile
|
||||
$(TARGETDIR)/%.done: $(BUILDDIR)/%/Makefile
|
||||
$(info Building $(basename $@). Log in $(<D)/log.build)
|
||||
$(PATHPRE) $(ENVS) $(MAKE) $(BUILDPAR) -f $< -C $(<D) $(MAKECMD) $(MAKECMD.$(notdir $@)) > $(<D)/log.build 2>&1
|
||||
@echo -n 'installing...'
|
||||
@ -646,26 +478,20 @@ $(PREFIX)/devkit.info:
|
||||
echo '# This file describes to configure how to interpret the contents of this' >> $@
|
||||
echo '# devkit' >> $@
|
||||
echo '' >> $@
|
||||
echo 'DEVKIT_NAME="$(GCC_VER) - $(LINUX_VERSION)"' >> $@
|
||||
echo 'DEVKIT_NAME="$(GCC_VER) - $(BASE_OS_DESCRIPTION)"' >> $@
|
||||
echo 'DEVKIT_TOOLCHAIN_PATH="$$DEVKIT_ROOT/bin"' >> $@
|
||||
echo 'DEVKIT_SYSROOT="$$DEVKIT_ROOT/$(TARGET)/sysroot"' >> $@
|
||||
echo 'DEVKIT_EXTRA_PATH="$$DEVKIT_ROOT/bin"' >> $@
|
||||
|
||||
################################################################################
|
||||
# Copy these makefiles into the root of the kit
|
||||
$(PREFIX)/Makefile: ./Makefile
|
||||
rm -rf $@
|
||||
cp $< $@
|
||||
|
||||
$(PREFIX)/Tools.gmk: ./Tools.gmk
|
||||
rm -rf $@
|
||||
cp $< $@
|
||||
THESE_MAKEFILES := Makefile $(wildcard *.gmk) patches
|
||||
COPIED_MAKEFILES := $(addprefix $(PREFIX)/, $(THESE_MAKEFILES))
|
||||
|
||||
$(PREFIX)/Tars.gmk: ./Tars.gmk
|
||||
$(PREFIX)/%: ./%
|
||||
rm -rf $@
|
||||
cp $< $@
|
||||
|
||||
THESE_MAKEFILES := $(PREFIX)/Makefile $(PREFIX)/Tools.gmk $(PREFIX)/Tars.gmk
|
||||
cp -r $< $@
|
||||
|
||||
################################################################################
|
||||
|
||||
@ -682,9 +508,14 @@ ifeq ($(TARGET), $(HOST))
|
||||
@echo 'Creating missing $* soft link'
|
||||
ln -s $(TARGET)-$* $@
|
||||
|
||||
MISSING_LINKS := $(addprefix $(PREFIX)/bin/, \
|
||||
MISSING_LINK_NAMES = \
|
||||
addr2line ar as c++ c++filt dwp elfedit g++ gcc gcc-$(GCC_VER_ONLY) gprof ld ld.bfd \
|
||||
ld.gold nm objcopy objdump ranlib readelf size strings strip)
|
||||
nm objcopy objdump ranlib readelf size strings strip
|
||||
ifneq ($(LINKER_CONFIG_ENABLE_GOLD), )
|
||||
MISSING_LINK_NAMES += ld.gold
|
||||
endif
|
||||
|
||||
MISSING_LINKS := $(addprefix $(PREFIX)/bin/, $(MISSING_LINK_NAMES))
|
||||
endif
|
||||
|
||||
# Add link to work around "plugin needed to handle lto object" (JDK-8344272)
|
||||
@ -697,17 +528,14 @@ MISSING_LINKS += $(PREFIX)/lib/bfd-plugins/liblto_plugin.so
|
||||
|
||||
################################################################################
|
||||
|
||||
bfdlib : $(BFDLIB)
|
||||
binutils : $(BINUTILS)
|
||||
rpms : $(RPMS)
|
||||
libs : $(LIBS)
|
||||
sysroot : rpms libs
|
||||
gcc : sysroot $(GCC) $(GCC_PATCHED)
|
||||
gdb : $(GDB)
|
||||
all : binutils gcc bfdlib $(PREFIX)/devkit.info $(MISSING_LINKS) $(SYSROOT_LINKS) \
|
||||
$(THESE_MAKEFILES) gdb
|
||||
bfdlib: $(BFDLIB)
|
||||
binutils: $(BINUTILS)
|
||||
gcc: $(GCC) $(GCC_PATCHED)
|
||||
gdb: $(GDB)
|
||||
all: binutils gcc bfdlib gdb \
|
||||
$(MISSING_LINKS) $(COPIED_MAKEFILES) $(PREFIX)/devkit.info
|
||||
|
||||
# this is only built for host. so separate.
|
||||
ccache : $(CCACHE)
|
||||
ccache: $(CCACHE)
|
||||
|
||||
.PHONY : gcc all binutils bfdlib link_libs rpms libs sysroot
|
||||
.PHONY: bfdlib binutils gcc gdb all ccache
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
|
||||
################################################################################
|
||||
|
||||
DISABLED_WARNINGS_java += dangling-doc-comments this-escape suppression
|
||||
DISABLED_WARNINGS_java += suppression
|
||||
|
||||
COPY += .properties .caps .txt
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
|
||||
################################################################################
|
||||
|
||||
DISABLED_WARNINGS_java += dangling-doc-comments suppression
|
||||
DISABLED_WARNINGS_java += suppression
|
||||
|
||||
COPY += .gif .png .txt .spec .script .prerm .preinst \
|
||||
.postrm .postinst .list .sh .desktop .copyright .control .plist .template \
|
||||
|
||||
@ -83,8 +83,8 @@ $(eval $(call SetupJavaCompilation, BUILD_JDK_MICROBENCHMARK, \
|
||||
SMALL_JAVA := false, \
|
||||
CLASSPATH := $(JMH_COMPILE_JARS), \
|
||||
CREATE_API_DIGEST := true, \
|
||||
DISABLED_WARNINGS := restricted this-escape processing rawtypes removal cast \
|
||||
serial preview dangling-doc-comments suppression, \
|
||||
DISABLED_WARNINGS := restricted this-escape rawtypes removal cast \
|
||||
serial preview suppression, \
|
||||
SRC := $(MICROBENCHMARK_SRC), \
|
||||
BIN := $(MICROBENCHMARK_CLASSES), \
|
||||
JAVAC_FLAGS := \
|
||||
|
||||
@ -46,7 +46,6 @@ $(eval $(call SetupJavaCompilation, BUILD_WB_JAR, \
|
||||
SRC := $(TEST_LIB_SOURCE_DIR)/jdk/test/whitebox/, \
|
||||
BIN := $(TEST_LIB_SUPPORT)/wb_classes, \
|
||||
JAR := $(TEST_LIB_SUPPORT)/wb.jar, \
|
||||
DISABLED_WARNINGS := deprecation removal preview, \
|
||||
JAVAC_FLAGS := --enable-preview, \
|
||||
))
|
||||
|
||||
|
||||
@ -1671,24 +1671,42 @@ instruct vnotL(vReg dst, vReg src, immL_M1 m1) %{
|
||||
|
||||
// vector not - predicated
|
||||
|
||||
instruct vnotI_masked(vReg dst_src, immI_M1 m1, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vnotI_masked(vReg dst, vReg src, immI_M1 m1, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (XorV (Binary dst_src (Replicate m1)) pg));
|
||||
format %{ "vnotI_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (XorV (Binary src (Replicate m1)) pg));
|
||||
format %{ "vnotI_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_not($dst_src$$FloatRegister, get_reg_variant(this),
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_not($dst$$FloatRegister, get_reg_variant(this),
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vnotL_masked(vReg dst_src, immL_M1 m1, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vnotL_masked(vReg dst, vReg src, immL_M1 m1, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (XorV (Binary dst_src (Replicate m1)) pg));
|
||||
format %{ "vnotL_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (XorV (Binary src (Replicate m1)) pg));
|
||||
format %{ "vnotL_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_not($dst_src$$FloatRegister, get_reg_variant(this),
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_not($dst$$FloatRegister, get_reg_variant(this),
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -1985,62 +2003,116 @@ instruct vabsD(vReg dst, vReg src) %{
|
||||
|
||||
// vector abs - predicated
|
||||
|
||||
instruct vabsB_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vabsB_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (AbsVB dst_src pg));
|
||||
format %{ "vabsB_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (AbsVB src pg));
|
||||
format %{ "vabsB_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_abs($dst_src$$FloatRegister, __ B, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_abs($dst$$FloatRegister, __ B, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vabsS_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vabsS_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (AbsVS dst_src pg));
|
||||
format %{ "vabsS_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (AbsVS src pg));
|
||||
format %{ "vabsS_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_abs($dst_src$$FloatRegister, __ H, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_abs($dst$$FloatRegister, __ H, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vabsI_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vabsI_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (AbsVI dst_src pg));
|
||||
format %{ "vabsI_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (AbsVI src pg));
|
||||
format %{ "vabsI_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_abs($dst_src$$FloatRegister, __ S, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_abs($dst$$FloatRegister, __ S, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vabsL_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vabsL_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (AbsVL dst_src pg));
|
||||
format %{ "vabsL_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (AbsVL src pg));
|
||||
format %{ "vabsL_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_abs($dst_src$$FloatRegister, __ D, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_abs($dst$$FloatRegister, __ D, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vabsF_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vabsF_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (AbsVF dst_src pg));
|
||||
format %{ "vabsF_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (AbsVF src pg));
|
||||
format %{ "vabsF_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_fabs($dst_src$$FloatRegister, __ S, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_fabs($dst$$FloatRegister, __ S, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vabsD_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vabsD_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (AbsVD dst_src pg));
|
||||
format %{ "vabsD_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (AbsVD src pg));
|
||||
format %{ "vabsD_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_fabs($dst_src$$FloatRegister, __ D, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_fabs($dst$$FloatRegister, __ D, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -2158,44 +2230,80 @@ instruct vnegD(vReg dst, vReg src) %{
|
||||
|
||||
// vector neg - predicated
|
||||
|
||||
instruct vnegI_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vnegI_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (NegVI dst_src pg));
|
||||
format %{ "vnegI_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (NegVI src pg));
|
||||
format %{ "vnegI_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
__ sve_neg($dst_src$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_neg($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vnegL_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vnegL_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (NegVL dst_src pg));
|
||||
format %{ "vnegL_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (NegVL src pg));
|
||||
format %{ "vnegL_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_neg($dst_src$$FloatRegister, __ D, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_neg($dst$$FloatRegister, __ D, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vnegF_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vnegF_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (NegVF dst_src pg));
|
||||
format %{ "vnegF_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (NegVF src pg));
|
||||
format %{ "vnegF_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_fneg($dst_src$$FloatRegister, __ S, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_fneg($dst$$FloatRegister, __ S, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vnegD_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vnegD_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (NegVD dst_src pg));
|
||||
format %{ "vnegD_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (NegVD src pg));
|
||||
format %{ "vnegD_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_fneg($dst_src$$FloatRegister, __ D, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_fneg($dst$$FloatRegister, __ D, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -2251,22 +2359,40 @@ instruct vsqrtD(vReg dst, vReg src) %{
|
||||
|
||||
// vector sqrt - predicated
|
||||
|
||||
instruct vsqrtF_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vsqrtF_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (SqrtVF dst_src pg));
|
||||
format %{ "vsqrtF_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (SqrtVF src pg));
|
||||
format %{ "vsqrtF_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_fsqrt($dst_src$$FloatRegister, __ S, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_fsqrt($dst$$FloatRegister, __ S, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vsqrtD_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vsqrtD_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (SqrtVD dst_src pg));
|
||||
format %{ "vsqrtD_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (SqrtVD src pg));
|
||||
format %{ "vsqrtD_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_fsqrt($dst_src$$FloatRegister, __ D, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_fsqrt($dst$$FloatRegister, __ D, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -5331,9 +5457,7 @@ instruct insertI_index_lt32(vReg dst, vReg src, iRegIorL2I val, immI idx,
|
||||
__ sve_index($tmp$$FloatRegister, size, -16, 1);
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, size, ptrue,
|
||||
$tmp$$FloatRegister, (int)($idx$$constant) - 16);
|
||||
if ($dst$$FloatRegister != $src$$FloatRegister) {
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
}
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, size, $pgtmp$$PRegister, $val$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -5356,9 +5480,7 @@ instruct insertI_index_ge32(vReg dst, vReg src, iRegIorL2I val, immI idx, vReg t
|
||||
__ sve_dup($tmp2$$FloatRegister, size, (int)($idx$$constant));
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, size, ptrue,
|
||||
$tmp1$$FloatRegister, $tmp2$$FloatRegister);
|
||||
if ($dst$$FloatRegister != $src$$FloatRegister) {
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
}
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, size, $pgtmp$$PRegister, $val$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -5392,9 +5514,7 @@ instruct insertL_gt128b(vReg dst, vReg src, iRegL val, immI idx,
|
||||
__ sve_index($tmp$$FloatRegister, __ D, -16, 1);
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, __ D, ptrue,
|
||||
$tmp$$FloatRegister, (int)($idx$$constant) - 16);
|
||||
if ($dst$$FloatRegister != $src$$FloatRegister) {
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
}
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, __ D, $pgtmp$$PRegister, $val$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -5432,7 +5552,7 @@ instruct insertF_index_lt32(vReg dst, vReg src, vRegF val, immI idx,
|
||||
__ sve_index($dst$$FloatRegister, __ S, -16, 1);
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, __ S, ptrue,
|
||||
$dst$$FloatRegister, (int)($idx$$constant) - 16);
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, __ S, $pgtmp$$PRegister, $val$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -5451,7 +5571,7 @@ instruct insertF_index_ge32(vReg dst, vReg src, vRegF val, immI idx, vReg tmp,
|
||||
__ sve_dup($dst$$FloatRegister, __ S, (int)($idx$$constant));
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, __ S, ptrue,
|
||||
$tmp$$FloatRegister, $dst$$FloatRegister);
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, __ S, $pgtmp$$PRegister, $val$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -5486,7 +5606,7 @@ instruct insertD_gt128b(vReg dst, vReg src, vRegD val, immI idx,
|
||||
__ sve_index($dst$$FloatRegister, __ D, -16, 1);
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, __ D, ptrue,
|
||||
$dst$$FloatRegister, (int)($idx$$constant) - 16);
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, __ D, $pgtmp$$PRegister, $val$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -5656,8 +5776,12 @@ instruct extractF(vRegF dst, vReg src, immI idx) %{
|
||||
__ ins($dst$$FloatRegister, __ S, $src$$FloatRegister, 0, index);
|
||||
} else {
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_ext($dst$$FloatRegister, $dst$$FloatRegister, index << 2);
|
||||
__ sve_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the second source of ext. The movprfx destination register
|
||||
// must not appear in any source operand of the following instruction
|
||||
// except as the destructive operand.
|
||||
__ sve_ext($dst$$FloatRegister, $src$$FloatRegister, index << 2);
|
||||
}
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -5677,8 +5801,12 @@ instruct extractD(vRegD dst, vReg src, immI idx) %{
|
||||
__ ins($dst$$FloatRegister, __ D, $src$$FloatRegister, 0, index);
|
||||
} else {
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_ext($dst$$FloatRegister, $dst$$FloatRegister, index << 3);
|
||||
__ sve_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the second source of ext. The movprfx destination register
|
||||
// must not appear in any source operand of the following instruction
|
||||
// except as the destructive operand.
|
||||
__ sve_ext($dst$$FloatRegister, $src$$FloatRegister, index << 3);
|
||||
}
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -6855,25 +6983,43 @@ instruct vpopcountL(vReg dst, vReg src) %{
|
||||
|
||||
// vector popcount - predicated
|
||||
|
||||
instruct vpopcountI_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vpopcountI_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (PopCountVI dst_src pg));
|
||||
format %{ "vpopcountI_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (PopCountVI src pg));
|
||||
format %{ "vpopcountI_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
__ sve_cnt($dst_src$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_cnt($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct vpopcountL_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vpopcountL_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (PopCountVL dst_src pg));
|
||||
format %{ "vpopcountL_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (PopCountVL src pg));
|
||||
format %{ "vpopcountL_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_cnt($dst_src$$FloatRegister, __ D,
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_cnt($dst$$FloatRegister, __ D,
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -7240,14 +7386,23 @@ instruct vcountLeadingZeros(vReg dst, vReg src) %{
|
||||
// The dst and src should use the same register to make sure the
|
||||
// inactive lanes in dst save the same elements as src.
|
||||
|
||||
instruct vcountLeadingZeros_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vcountLeadingZeros_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (CountLeadingZerosV dst_src pg));
|
||||
format %{ "vcountLeadingZeros_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (CountLeadingZerosV src pg));
|
||||
format %{ "vcountLeadingZeros_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
__ sve_clz($dst_src$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_clz($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -7296,19 +7451,26 @@ instruct vcountTrailingZeros(vReg dst, vReg src) %{
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
// The dst and src should use the same register to make sure the
|
||||
// inactive lanes in dst save the same elements as src.
|
||||
instruct vcountTrailingZeros_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vcountTrailingZeros_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (CountTrailingZerosV dst_src pg));
|
||||
format %{ "vcountTrailingZeros_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (CountTrailingZerosV src pg));
|
||||
format %{ "vcountTrailingZeros_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
Assembler::SIMD_RegVariant size = __ elemType_to_regVariant(bt);
|
||||
__ sve_rbit($dst_src$$FloatRegister, size,
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ sve_clz($dst_src$$FloatRegister, size,
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_rbit($dst$$FloatRegister, size,
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
__ sve_clz($dst$$FloatRegister, size,
|
||||
$pg$$PRegister, $dst$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -7347,14 +7509,23 @@ instruct vreverse(vReg dst, vReg src) %{
|
||||
// The dst and src should use the same register to make sure the
|
||||
// inactive lanes in dst save the same elements as src.
|
||||
|
||||
instruct vreverse_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vreverse_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (ReverseV dst_src pg));
|
||||
format %{ "vreverse_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (ReverseV src pg));
|
||||
format %{ "vreverse_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
__ sve_rbit($dst_src$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_rbit($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -7393,19 +7564,28 @@ instruct vreverseBytes(vReg dst, vReg src) %{
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
// The dst and src should use the same register to make sure the
|
||||
// inactive lanes in dst save the same elements as src.
|
||||
instruct vreverseBytes_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vreverseBytes_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (ReverseBytesV dst_src pg));
|
||||
format %{ "vreverseBytes_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (ReverseBytesV src pg));
|
||||
format %{ "vreverseBytes_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
if (bt == T_BYTE) {
|
||||
// do nothing
|
||||
if ($dst$$FloatRegister != $src$$FloatRegister) {
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
}
|
||||
} else {
|
||||
__ sve_revb($dst_src$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_revb($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
}
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
|
||||
@ -899,13 +899,22 @@ dnl
|
||||
dnl VECTOR_NOT_PREDICATE($1 )
|
||||
dnl VECTOR_NOT_PREDICATE(type)
|
||||
define(`VECTOR_NOT_PREDICATE', `
|
||||
instruct vnot$1_masked`'(vReg dst_src, imm$1_M1 m1, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vnot$1_masked`'(vReg dst, vReg src, imm$1_M1 m1, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (XorV (Binary dst_src (Replicate m1)) pg));
|
||||
format %{ "vnot$1_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (XorV (Binary src (Replicate m1)) pg));
|
||||
format %{ "vnot$1_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_not($dst_src$$FloatRegister, get_reg_variant(this),
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_not($dst$$FloatRegister, get_reg_variant(this),
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}')dnl
|
||||
@ -1042,14 +1051,23 @@ dnl
|
||||
dnl UNARY_OP_PREDICATE($1, $2, $3 )
|
||||
dnl UNARY_OP_PREDICATE(rule_name, op_name, insn)
|
||||
define(`UNARY_OP_PREDICATE', `
|
||||
instruct $1_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct $1_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src ($2 dst_src pg));
|
||||
format %{ "$1_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst ($2 src pg));
|
||||
format %{ "$1_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
__ $3($dst_src$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ $3($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}')dnl
|
||||
@ -1057,12 +1075,21 @@ dnl
|
||||
dnl UNARY_OP_PREDICATE_WITH_SIZE($1, $2, $3, $4 )
|
||||
dnl UNARY_OP_PREDICATE_WITH_SIZE(rule_name, op_name, insn, size)
|
||||
define(`UNARY_OP_PREDICATE_WITH_SIZE', `
|
||||
instruct $1_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct $1_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src ($2 dst_src pg));
|
||||
format %{ "$1_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst ($2 src pg));
|
||||
format %{ "$1_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ $3($dst_src$$FloatRegister, __ $4, $pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ $3($dst$$FloatRegister, __ $4, $pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}')dnl
|
||||
@ -3368,9 +3395,7 @@ instruct insertI_index_lt32(vReg dst, vReg src, iRegIorL2I val, immI idx,
|
||||
__ sve_index($tmp$$FloatRegister, size, -16, 1);
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, size, ptrue,
|
||||
$tmp$$FloatRegister, (int)($idx$$constant) - 16);
|
||||
if ($dst$$FloatRegister != $src$$FloatRegister) {
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
}
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, size, $pgtmp$$PRegister, $val$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -3393,9 +3418,7 @@ instruct insertI_index_ge32(vReg dst, vReg src, iRegIorL2I val, immI idx, vReg t
|
||||
__ sve_dup($tmp2$$FloatRegister, size, (int)($idx$$constant));
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, size, ptrue,
|
||||
$tmp1$$FloatRegister, $tmp2$$FloatRegister);
|
||||
if ($dst$$FloatRegister != $src$$FloatRegister) {
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
}
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, size, $pgtmp$$PRegister, $val$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -3429,9 +3452,7 @@ instruct insertL_gt128b(vReg dst, vReg src, iRegL val, immI idx,
|
||||
__ sve_index($tmp$$FloatRegister, __ D, -16, 1);
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, __ D, ptrue,
|
||||
$tmp$$FloatRegister, (int)($idx$$constant) - 16);
|
||||
if ($dst$$FloatRegister != $src$$FloatRegister) {
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
}
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, __ D, $pgtmp$$PRegister, $val$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -3469,7 +3490,7 @@ instruct insertF_index_lt32(vReg dst, vReg src, vRegF val, immI idx,
|
||||
__ sve_index($dst$$FloatRegister, __ S, -16, 1);
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, __ S, ptrue,
|
||||
$dst$$FloatRegister, (int)($idx$$constant) - 16);
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, __ S, $pgtmp$$PRegister, $val$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -3488,7 +3509,7 @@ instruct insertF_index_ge32(vReg dst, vReg src, vRegF val, immI idx, vReg tmp,
|
||||
__ sve_dup($dst$$FloatRegister, __ S, (int)($idx$$constant));
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, __ S, ptrue,
|
||||
$tmp$$FloatRegister, $dst$$FloatRegister);
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, __ S, $pgtmp$$PRegister, $val$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -3523,7 +3544,7 @@ instruct insertD_gt128b(vReg dst, vReg src, vRegD val, immI idx,
|
||||
__ sve_index($dst$$FloatRegister, __ D, -16, 1);
|
||||
__ sve_cmp(Assembler::EQ, $pgtmp$$PRegister, __ D, ptrue,
|
||||
$dst$$FloatRegister, (int)($idx$$constant) - 16);
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_cpy($dst$$FloatRegister, __ D, $pgtmp$$PRegister, $val$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -3621,8 +3642,12 @@ instruct extract$1(vReg$1 dst, vReg src, immI idx) %{
|
||||
__ ins($dst$$FloatRegister, __ $4, $src$$FloatRegister, 0, index);
|
||||
} else {
|
||||
assert(UseSVE > 0, "must be sve");
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
__ sve_ext($dst$$FloatRegister, $dst$$FloatRegister, index << $5);
|
||||
__ sve_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the second source of ext. The movprfx destination register
|
||||
// must not appear in any source operand of the following instruction
|
||||
// except as the destructive operand.
|
||||
__ sve_ext($dst$$FloatRegister, $src$$FloatRegister, index << $5);
|
||||
}
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
@ -4682,13 +4707,22 @@ instruct vpopcountL(vReg dst, vReg src) %{
|
||||
// vector popcount - predicated
|
||||
UNARY_OP_PREDICATE(vpopcountI, PopCountVI, sve_cnt)
|
||||
|
||||
instruct vpopcountL_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vpopcountL_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (PopCountVL dst_src pg));
|
||||
format %{ "vpopcountL_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (PopCountVL src pg));
|
||||
format %{ "vpopcountL_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
__ sve_cnt($dst_src$$FloatRegister, __ D,
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_cnt($dst$$FloatRegister, __ D,
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -5100,19 +5134,26 @@ instruct vcountTrailingZeros(vReg dst, vReg src) %{
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
// The dst and src should use the same register to make sure the
|
||||
// inactive lanes in dst save the same elements as src.
|
||||
instruct vcountTrailingZeros_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vcountTrailingZeros_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (CountTrailingZerosV dst_src pg));
|
||||
format %{ "vcountTrailingZeros_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (CountTrailingZerosV src pg));
|
||||
format %{ "vcountTrailingZeros_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
Assembler::SIMD_RegVariant size = __ elemType_to_regVariant(bt);
|
||||
__ sve_rbit($dst_src$$FloatRegister, size,
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ sve_clz($dst_src$$FloatRegister, size,
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_rbit($dst$$FloatRegister, size,
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
__ sve_clz($dst$$FloatRegister, size,
|
||||
$pg$$PRegister, $dst$$FloatRegister);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
@ -5186,19 +5227,28 @@ instruct vreverseBytes(vReg dst, vReg src) %{
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
// The dst and src should use the same register to make sure the
|
||||
// inactive lanes in dst save the same elements as src.
|
||||
instruct vreverseBytes_masked(vReg dst_src, pRegGov pg) %{
|
||||
// The Java Vector API specification requires that for masked unary operations,
|
||||
// suppressed lanes are filled from the first vector operand (see "Masked
|
||||
// Operations" in Vector.java around line 568). So we use movprfx to copy src
|
||||
// into dst before emitting the predicated instruction.
|
||||
instruct vreverseBytes_masked(vReg dst, vReg src, pRegGov pg) %{
|
||||
predicate(UseSVE > 0);
|
||||
match(Set dst_src (ReverseBytesV dst_src pg));
|
||||
format %{ "vreverseBytes_masked $dst_src, $pg, $dst_src" %}
|
||||
match(Set dst (ReverseBytesV src pg));
|
||||
format %{ "vreverseBytes_masked $dst, $pg, $src" %}
|
||||
ins_encode %{
|
||||
BasicType bt = Matcher::vector_element_basic_type(this);
|
||||
if (bt == T_BYTE) {
|
||||
// do nothing
|
||||
if ($dst$$FloatRegister != $src$$FloatRegister) {
|
||||
__ sve_orr($dst$$FloatRegister, $src$$FloatRegister, $src$$FloatRegister);
|
||||
}
|
||||
} else {
|
||||
__ sve_revb($dst_src$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $dst_src$$FloatRegister);
|
||||
__ maybe_movprfx($dst$$FloatRegister, $src$$FloatRegister);
|
||||
// Although dst and src hold the same value after movprfx, we must use src
|
||||
// (not dst) as the source of the following instruction. The movprfx
|
||||
// destination register must not appear in any source operand of the
|
||||
// following instruction except as the destructive operand.
|
||||
__ sve_revb($dst$$FloatRegister, __ elemType_to_regVariant(bt),
|
||||
$pg$$PRegister, $src$$FloatRegister);
|
||||
}
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
|
||||
@ -2494,8 +2494,12 @@ void C2_MacroAssembler::sve_extract_integral(Register dst, BasicType bt, FloatRe
|
||||
smov(dst, src, size, idx);
|
||||
}
|
||||
} else {
|
||||
sve_orr(vtmp, src, src);
|
||||
sve_ext(vtmp, vtmp, idx << size);
|
||||
sve_movprfx(vtmp, src);
|
||||
// Although vtmp and src hold the same value after movprfx, we must use src
|
||||
// (not vtmp) as the second source of ext. The movprfx destination register
|
||||
// must not appear in any source operand of the following instruction except
|
||||
// as the destructive operand.
|
||||
sve_ext(vtmp, src, idx << size);
|
||||
if (bt == T_INT || bt == T_LONG) {
|
||||
umov(dst, vtmp, size, 0);
|
||||
} else {
|
||||
|
||||
@ -28,7 +28,6 @@
|
||||
#include "gc/shenandoah/mode/shenandoahMode.hpp"
|
||||
#include "gc/shenandoah/shenandoahBarrierSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
|
||||
#include "gc/shenandoah/shenandoahForwarding.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
|
||||
#include "gc/shenandoah/shenandoahRuntime.hpp"
|
||||
@ -174,53 +173,6 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm,
|
||||
__ bind(done);
|
||||
}
|
||||
|
||||
void ShenandoahBarrierSetAssembler::resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp) {
|
||||
assert(ShenandoahLoadRefBarrier || ShenandoahCASBarrier, "Should be enabled");
|
||||
Label is_null;
|
||||
__ cbz(dst, is_null);
|
||||
resolve_forward_pointer_not_null(masm, dst, tmp);
|
||||
__ bind(is_null);
|
||||
}
|
||||
|
||||
// IMPORTANT: This must preserve all registers, even rscratch1 and rscratch2, except those explicitly
|
||||
// passed in.
|
||||
void ShenandoahBarrierSetAssembler::resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp) {
|
||||
assert(ShenandoahLoadRefBarrier || ShenandoahCASBarrier, "Should be enabled");
|
||||
// The below loads the mark word, checks if the lowest two bits are
|
||||
// set, and if so, clear the lowest two bits and copy the result
|
||||
// to dst. Otherwise it leaves dst alone.
|
||||
// Implementing this is surprisingly awkward. I do it here by:
|
||||
// - Inverting the mark word
|
||||
// - Test lowest two bits == 0
|
||||
// - If so, set the lowest two bits
|
||||
// - Invert the result back, and copy to dst
|
||||
|
||||
bool borrow_reg = (tmp == noreg);
|
||||
if (borrow_reg) {
|
||||
// No free registers available. Make one useful.
|
||||
tmp = rscratch1;
|
||||
if (tmp == dst) {
|
||||
tmp = rscratch2;
|
||||
}
|
||||
__ push(RegSet::of(tmp), sp);
|
||||
}
|
||||
|
||||
assert_different_registers(tmp, dst);
|
||||
|
||||
Label done;
|
||||
__ ldr(tmp, Address(dst, oopDesc::mark_offset_in_bytes()));
|
||||
__ eon(tmp, tmp, zr);
|
||||
__ ands(zr, tmp, markWord::lock_mask_in_place);
|
||||
__ br(Assembler::NE, done);
|
||||
__ orr(tmp, tmp, markWord::marked_value);
|
||||
__ eon(dst, tmp, zr);
|
||||
__ bind(done);
|
||||
|
||||
if (borrow_reg) {
|
||||
__ pop(RegSet::of(tmp), sp);
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, DecoratorSet decorators) {
|
||||
assert(ShenandoahLoadRefBarrier, "Should be enabled");
|
||||
assert(dst != rscratch2, "need rscratch2");
|
||||
@ -468,166 +420,6 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl
|
||||
__ bind(done);
|
||||
}
|
||||
|
||||
// Special Shenandoah CAS implementation that handles false negatives due
|
||||
// to concurrent evacuation. The service is more complex than a
|
||||
// traditional CAS operation because the CAS operation is intended to
|
||||
// succeed if the reference at addr exactly matches expected or if the
|
||||
// reference at addr holds a pointer to a from-space object that has
|
||||
// been relocated to the location named by expected. There are two
|
||||
// races that must be addressed:
|
||||
// a) A parallel thread may mutate the contents of addr so that it points
|
||||
// to a different object. In this case, the CAS operation should fail.
|
||||
// b) A parallel thread may heal the contents of addr, replacing a
|
||||
// from-space pointer held in addr with the to-space pointer
|
||||
// representing the new location of the object.
|
||||
// Upon entry to cmpxchg_oop, it is assured that new_val equals null
|
||||
// or it refers to an object that is not being evacuated out of
|
||||
// from-space, or it refers to the to-space version of an object that
|
||||
// is being evacuated out of from-space.
|
||||
//
|
||||
// By default the value held in the result register following execution
|
||||
// of the generated code sequence is 0 to indicate failure of CAS,
|
||||
// non-zero to indicate success. If is_cae, the result is the value most
|
||||
// recently fetched from addr rather than a boolean success indicator.
|
||||
//
|
||||
// Clobbers rscratch1, rscratch2
|
||||
void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm,
|
||||
Register addr,
|
||||
Register expected,
|
||||
Register new_val,
|
||||
bool acquire, bool release,
|
||||
bool is_cae,
|
||||
Register result) {
|
||||
Register tmp1 = rscratch1;
|
||||
Register tmp2 = rscratch2;
|
||||
bool is_narrow = UseCompressedOops;
|
||||
Assembler::operand_size size = is_narrow ? Assembler::word : Assembler::xword;
|
||||
|
||||
assert_different_registers(addr, expected, tmp1, tmp2);
|
||||
assert_different_registers(addr, new_val, tmp1, tmp2);
|
||||
|
||||
Label step4, done;
|
||||
|
||||
// There are two ways to reach this label. Initial entry into the
|
||||
// cmpxchg_oop code expansion starts at step1 (which is equivalent
|
||||
// to label step4). Additionally, in the rare case that four steps
|
||||
// are required to perform the requested operation, the fourth step
|
||||
// is the same as the first. On a second pass through step 1,
|
||||
// control may flow through step 2 on its way to failure. It will
|
||||
// not flow from step 2 to step 3 since we are assured that the
|
||||
// memory at addr no longer holds a from-space pointer.
|
||||
//
|
||||
// The comments that immediately follow the step4 label apply only
|
||||
// to the case in which control reaches this label by branch from
|
||||
// step 3.
|
||||
|
||||
__ bind (step4);
|
||||
|
||||
// Step 4. CAS has failed because the value most recently fetched
|
||||
// from addr is no longer the from-space pointer held in tmp2. If a
|
||||
// different thread replaced the in-memory value with its equivalent
|
||||
// to-space pointer, then CAS may still be able to succeed. The
|
||||
// value held in the expected register has not changed.
|
||||
//
|
||||
// It is extremely rare we reach this point. For this reason, the
|
||||
// implementation opts for smaller rather than potentially faster
|
||||
// code. Ultimately, smaller code for this rare case most likely
|
||||
// delivers higher overall throughput by enabling improved icache
|
||||
// performance.
|
||||
|
||||
// Step 1. Fast-path.
|
||||
//
|
||||
// Try to CAS with given arguments. If successful, then we are done.
|
||||
//
|
||||
// No label required for step 1.
|
||||
|
||||
__ cmpxchg(addr, expected, new_val, size, acquire, release, false, tmp2);
|
||||
// EQ flag set iff success. tmp2 holds value fetched.
|
||||
|
||||
// If expected equals null but tmp2 does not equal null, the
|
||||
// following branches to done to report failure of CAS. If both
|
||||
// expected and tmp2 equal null, the following branches to done to
|
||||
// report success of CAS. There's no need for a special test of
|
||||
// expected equal to null.
|
||||
|
||||
__ br(Assembler::EQ, done);
|
||||
// if CAS failed, fall through to step 2
|
||||
|
||||
// Step 2. CAS has failed because the value held at addr does not
|
||||
// match expected. This may be a false negative because the value fetched
|
||||
// from addr (now held in tmp2) may be a from-space pointer to the
|
||||
// original copy of same object referenced by to-space pointer expected.
|
||||
//
|
||||
// To resolve this, it suffices to find the forward pointer associated
|
||||
// with fetched value. If this matches expected, retry CAS with new
|
||||
// parameters. If this mismatches, then we have a legitimate
|
||||
// failure, and we're done.
|
||||
//
|
||||
// No need for step2 label.
|
||||
|
||||
// overwrite tmp1 with from-space pointer fetched from memory
|
||||
__ mov(tmp1, tmp2);
|
||||
|
||||
if (is_narrow) {
|
||||
// Decode tmp1 in order to resolve its forward pointer
|
||||
__ decode_heap_oop(tmp1, tmp1);
|
||||
}
|
||||
resolve_forward_pointer(masm, tmp1);
|
||||
// Encode tmp1 to compare against expected.
|
||||
__ encode_heap_oop(tmp1, tmp1);
|
||||
|
||||
// Does forwarded value of fetched from-space pointer match original
|
||||
// value of expected? If tmp1 holds null, this comparison will fail
|
||||
// because we know from step1 that expected is not null. There is
|
||||
// no need for a separate test for tmp1 (the value originally held
|
||||
// in memory) equal to null.
|
||||
__ cmp(tmp1, expected);
|
||||
|
||||
// If not, then the failure was legitimate and we're done.
|
||||
// Branching to done with NE condition denotes failure.
|
||||
__ br(Assembler::NE, done);
|
||||
|
||||
// Fall through to step 3. No need for step3 label.
|
||||
|
||||
// Step 3. We've confirmed that the value originally held in memory
|
||||
// (now held in tmp2) pointed to from-space version of original
|
||||
// expected value. Try the CAS again with the from-space expected
|
||||
// value. If it now succeeds, we're good.
|
||||
//
|
||||
// Note: tmp2 holds encoded from-space pointer that matches to-space
|
||||
// object residing at expected. tmp2 is the new "expected".
|
||||
|
||||
// Note that macro implementation of __cmpxchg cannot use same register
|
||||
// tmp2 for result and expected since it overwrites result before it
|
||||
// compares result with expected.
|
||||
__ cmpxchg(addr, tmp2, new_val, size, acquire, release, false, noreg);
|
||||
// EQ flag set iff success. tmp2 holds value fetched, tmp1 (rscratch1) clobbered.
|
||||
|
||||
// If fetched value did not equal the new expected, this could
|
||||
// still be a false negative because some other thread may have
|
||||
// newly overwritten the memory value with its to-space equivalent.
|
||||
__ br(Assembler::NE, step4);
|
||||
|
||||
if (is_cae) {
|
||||
// We're falling through to done to indicate success. Success
|
||||
// with is_cae is denoted by returning the value of expected as
|
||||
// result.
|
||||
__ mov(tmp2, expected);
|
||||
}
|
||||
|
||||
__ bind(done);
|
||||
// At entry to done, the Z (EQ) flag is on iff if the CAS
|
||||
// operation was successful. Additionally, if is_cae, tmp2 holds
|
||||
// the value most recently fetched from addr. In this case, success
|
||||
// is denoted by tmp2 matching expected.
|
||||
|
||||
if (is_cae) {
|
||||
__ mov(result, tmp2);
|
||||
} else {
|
||||
__ cset(result, Assembler::EQ);
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
|
||||
Register start, Register count, Register scratch) {
|
||||
assert(ShenandoahCardBarrier, "Should have been checked by caller");
|
||||
@ -868,7 +660,7 @@ void ShenandoahBarrierSetAssembler::load_c2(const MachNode* node, MacroAssembler
|
||||
void ShenandoahBarrierSetAssembler::store_c2(const MachNode* node, MacroAssembler* masm, Address dst, bool dst_narrow,
|
||||
Register src, bool src_narrow, Register tmp1, Register tmp2, Register tmp3, bool is_volatile) {
|
||||
|
||||
ShenandoahBarrierStubC2::store_pre(masm, node, tmp1, dst, tmp2, tmp3, dst_narrow);
|
||||
ShenandoahBarrierStubC2::store_pre(masm, node, dst, tmp1, tmp2, tmp3, dst_narrow);
|
||||
|
||||
// Do the actual store
|
||||
if (dst_narrow) {
|
||||
@ -906,7 +698,7 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac
|
||||
Register oldval, Register newval, Register tmp1, Register tmp2, Register tmp3, bool exchange, bool narrow, bool weak, bool acquire) {
|
||||
Assembler::operand_size op_size = narrow ? Assembler::word : Assembler::xword;
|
||||
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, tmp1, addr, tmp2, tmp3, narrow);
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp1, tmp2, tmp3, narrow);
|
||||
|
||||
// CAS!
|
||||
__ cmpxchg(addr, oldval, newval, op_size, acquire, /* release */ true, weak, exchange ? res : noreg);
|
||||
@ -924,7 +716,7 @@ void ShenandoahBarrierSetAssembler::get_and_set_c2(const MachNode* node, MacroAs
|
||||
Register newval, Register addr, Register tmp1, Register tmp2, Register tmp3, bool is_acquire) {
|
||||
bool is_narrow = node->bottom_type()->isa_narrowoop();
|
||||
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, tmp1, addr, tmp2, tmp3, is_narrow);
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp1, tmp2, tmp3, is_narrow);
|
||||
|
||||
if (is_narrow) {
|
||||
if (is_acquire) {
|
||||
@ -1184,12 +976,9 @@ void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) {
|
||||
if (c_rarg0 == _obj) {
|
||||
__ lea(c_rarg1, _addr);
|
||||
} else if (c_rarg1 == _obj) {
|
||||
// Set up arguments in reverse, and then flip them
|
||||
__ lea(c_rarg0, _addr);
|
||||
// flip them
|
||||
__ mov(_tmp1, c_rarg0);
|
||||
__ mov(c_rarg0, c_rarg1);
|
||||
__ mov(c_rarg1, _tmp1);
|
||||
__ mov(_tmp1, c_rarg1);
|
||||
__ lea(c_rarg1, _addr);
|
||||
__ mov(c_rarg0, _tmp1);
|
||||
} else {
|
||||
assert_different_registers(c_rarg1, _obj);
|
||||
__ lea(c_rarg1, _addr);
|
||||
|
||||
@ -53,8 +53,6 @@ private:
|
||||
|
||||
void card_barrier(MacroAssembler* masm, Register obj);
|
||||
|
||||
void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg);
|
||||
void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg);
|
||||
void load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, DecoratorSet decorators);
|
||||
|
||||
void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
|
||||
@ -76,8 +74,6 @@ public:
|
||||
Register obj, Register tmp, Label& slowpath);
|
||||
virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj,
|
||||
Register tmp, Label& slow_path);
|
||||
void cmpxchg_oop(MacroAssembler* masm, Register addr, Register expected, Register new_val,
|
||||
bool acquire, bool release, bool is_cae, Register result);
|
||||
|
||||
#ifdef COMPILER1
|
||||
void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub);
|
||||
|
||||
@ -2147,7 +2147,7 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
Register offset = rscratch2;
|
||||
|
||||
Label L_loop_search_receiver, L_loop_search_empty;
|
||||
Label L_restart, L_found_recv, L_found_empty, L_polymorphic, L_count_update;
|
||||
Label L_restart, L_found_recv, L_found_empty, L_count_update;
|
||||
|
||||
// The code here recognizes three major cases:
|
||||
// A. Fastest: receiver found in the table
|
||||
@ -2177,21 +2177,20 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
// if (receiver(i) == recv) goto found_recv(i);
|
||||
// }
|
||||
//
|
||||
// // Fast: no receiver, but profile is full
|
||||
// // Fast: no receiver, but profile is not full
|
||||
// for (i = 0; i < receiver_count(); i++) {
|
||||
// if (receiver(i) == null) goto found_null(i);
|
||||
// }
|
||||
// goto polymorphic
|
||||
//
|
||||
// // Slow: profile is full, polymorphic case
|
||||
// count++;
|
||||
// return
|
||||
//
|
||||
// // Slow: try to install receiver
|
||||
// found_null(i):
|
||||
// CAS(&receiver(i), null, recv);
|
||||
// goto restart
|
||||
//
|
||||
// polymorphic:
|
||||
// count++;
|
||||
// return
|
||||
//
|
||||
// found_recv(i):
|
||||
// *receiver_count(i)++
|
||||
//
|
||||
@ -2208,7 +2207,7 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
sub(rscratch1, offset, end_receiver_offset);
|
||||
cbnz(rscratch1, L_loop_search_receiver);
|
||||
|
||||
// Fast: no receiver, but profile is full
|
||||
// Fast: no receiver, but profile is not full
|
||||
mov(offset, base_receiver_offset);
|
||||
bind(L_loop_search_empty);
|
||||
ldr(rscratch1, Address(mdp, offset));
|
||||
@ -2216,9 +2215,13 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
add(offset, offset, receiver_step);
|
||||
sub(rscratch1, offset, end_receiver_offset);
|
||||
cbnz(rscratch1, L_loop_search_empty);
|
||||
b(L_polymorphic);
|
||||
|
||||
// Slow: try to install receiver
|
||||
// Slow: Receiver is not found and table is full.
|
||||
// Increment polymorphic counter instead of receiver slot.
|
||||
mov(offset, poly_count_offset);
|
||||
b(L_count_update);
|
||||
|
||||
// Slowest: try to install receiver
|
||||
bind(L_found_empty);
|
||||
|
||||
// Atomically swing receiver slot: null -> recv.
|
||||
@ -2237,17 +2240,11 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
// and just restart the search from the beginning.
|
||||
b(L_restart);
|
||||
|
||||
// Counter updates:
|
||||
|
||||
// Increment polymorphic counter instead of receiver slot.
|
||||
bind(L_polymorphic);
|
||||
mov(offset, poly_count_offset);
|
||||
b(L_count_update);
|
||||
|
||||
// Found a receiver, convert its slot offset to corresponding count offset.
|
||||
bind(L_found_recv);
|
||||
add(offset, offset, receiver_to_count_step);
|
||||
|
||||
// Finally, update the counter
|
||||
bind(L_count_update);
|
||||
increment(Address(mdp, offset), DataLayout::counter_increment);
|
||||
}
|
||||
@ -2816,7 +2813,7 @@ void MacroAssembler::decrementw(Register reg, int value)
|
||||
{
|
||||
if (value < 0) { incrementw(reg, -value); return; }
|
||||
if (value == 0) { return; }
|
||||
if (value < (1 << 12)) { subw(reg, reg, value); return; }
|
||||
if (value < (1 << 24)) { subw(reg, reg, value); return; }
|
||||
/* else */ {
|
||||
guarantee(reg != rscratch2, "invalid dst for register decrement");
|
||||
movw(rscratch2, (unsigned)value);
|
||||
@ -2828,7 +2825,7 @@ void MacroAssembler::decrement(Register reg, int value)
|
||||
{
|
||||
if (value < 0) { increment(reg, -value); return; }
|
||||
if (value == 0) { return; }
|
||||
if (value < (1 << 12)) { sub(reg, reg, value); return; }
|
||||
if (value < (1 << 24)) { sub(reg, reg, value); return; }
|
||||
/* else */ {
|
||||
assert(reg != rscratch2, "invalid dst for register decrement");
|
||||
mov(rscratch2, (uint64_t)value);
|
||||
@ -2840,7 +2837,7 @@ void MacroAssembler::decrementw(Address dst, int value)
|
||||
{
|
||||
assert(!dst.uses(rscratch1), "invalid dst for address decrement");
|
||||
if (dst.getMode() == Address::literal) {
|
||||
assert(abs(value) < (1 << 12), "invalid value and address mode combination");
|
||||
assert(abs(value) < (1 << 24), "invalid value and address mode combination");
|
||||
lea(rscratch2, dst);
|
||||
dst = Address(rscratch2);
|
||||
}
|
||||
@ -2853,7 +2850,7 @@ void MacroAssembler::decrement(Address dst, int value)
|
||||
{
|
||||
assert(!dst.uses(rscratch1), "invalid address for decrement");
|
||||
if (dst.getMode() == Address::literal) {
|
||||
assert(abs(value) < (1 << 12), "invalid value and address mode combination");
|
||||
assert(abs(value) < (1 << 24), "invalid value and address mode combination");
|
||||
lea(rscratch2, dst);
|
||||
dst = Address(rscratch2);
|
||||
}
|
||||
@ -2866,7 +2863,7 @@ void MacroAssembler::incrementw(Register reg, int value)
|
||||
{
|
||||
if (value < 0) { decrementw(reg, -value); return; }
|
||||
if (value == 0) { return; }
|
||||
if (value < (1 << 12)) { addw(reg, reg, value); return; }
|
||||
if (value < (1 << 24)) { addw(reg, reg, value); return; }
|
||||
/* else */ {
|
||||
assert(reg != rscratch2, "invalid dst for register increment");
|
||||
movw(rscratch2, (unsigned)value);
|
||||
@ -2878,7 +2875,7 @@ void MacroAssembler::increment(Register reg, int value)
|
||||
{
|
||||
if (value < 0) { decrement(reg, -value); return; }
|
||||
if (value == 0) { return; }
|
||||
if (value < (1 << 12)) { add(reg, reg, value); return; }
|
||||
if (value < (1 << 24)) { add(reg, reg, value); return; }
|
||||
/* else */ {
|
||||
assert(reg != rscratch2, "invalid dst for register increment");
|
||||
movw(rscratch2, (unsigned)value);
|
||||
@ -2886,30 +2883,34 @@ void MacroAssembler::increment(Register reg, int value)
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::incrementw(Address dst, int value)
|
||||
void MacroAssembler::incrementw(Address dst, int value, Register result)
|
||||
{
|
||||
assert(!dst.uses(rscratch1), "invalid dst for address increment");
|
||||
assert(!dst.uses(result), "invalid dst for address increment");
|
||||
assert(result->is_valid(), "must be");
|
||||
assert_different_registers(result, rscratch2);
|
||||
if (dst.getMode() == Address::literal) {
|
||||
assert(abs(value) < (1 << 12), "invalid value and address mode combination");
|
||||
assert(abs(value) < (1 << 24), "invalid value and address mode combination");
|
||||
lea(rscratch2, dst);
|
||||
dst = Address(rscratch2);
|
||||
}
|
||||
ldrw(rscratch1, dst);
|
||||
incrementw(rscratch1, value);
|
||||
strw(rscratch1, dst);
|
||||
ldrw(result, dst);
|
||||
incrementw(result, value);
|
||||
strw(result, dst);
|
||||
}
|
||||
|
||||
void MacroAssembler::increment(Address dst, int value)
|
||||
void MacroAssembler::increment(Address dst, int value, Register result)
|
||||
{
|
||||
assert(!dst.uses(rscratch1), "invalid dst for address increment");
|
||||
assert(!dst.uses(result), "invalid dst for address increment");
|
||||
assert(result->is_valid(), "must be");
|
||||
assert_different_registers(result, rscratch2);
|
||||
if (dst.getMode() == Address::literal) {
|
||||
assert(abs(value) < (1 << 12), "invalid value and address mode combination");
|
||||
assert(abs(value) < (1 << 24), "invalid value and address mode combination");
|
||||
lea(rscratch2, dst);
|
||||
dst = Address(rscratch2);
|
||||
}
|
||||
ldr(rscratch1, dst);
|
||||
increment(rscratch1, value);
|
||||
str(rscratch1, dst);
|
||||
ldr(result, dst);
|
||||
increment(result, value);
|
||||
str(result, dst);
|
||||
}
|
||||
|
||||
// Push lots of registers in the bit set supplied. Don't push sp.
|
||||
@ -7278,3 +7279,26 @@ void MacroAssembler::neon_vector_rotate(FloatRegister dst, SIMD_Arrangement T,
|
||||
sli(dst, T, src, lshift);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::try_to_replace_prev_vector_copy_with_movprfx(FloatRegister dst) {
|
||||
if (code_section()->is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
address prev = pc() - NativeInstruction::instruction_size;
|
||||
uint32_t insn = nativeInstruction_at(prev)->encoding();
|
||||
if (!NativeInstruction::is_neon_vector_mov_alias(insn) &&
|
||||
!NativeInstruction::is_sve_vector_mov_alias(insn)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The destructive instruction must reuse the mov alias destination.
|
||||
uint32_t rd = Instruction_aarch64::extract(insn, 4, 0);
|
||||
if (rd != (uint32_t)dst->encoding()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t rn = Instruction_aarch64::extract(insn, 9, 5);
|
||||
Instruction_aarch64::patch(prev, 31, 0,
|
||||
NativeInstruction::encode_sve_movprfx(rd, rn));
|
||||
}
|
||||
|
||||
@ -482,6 +482,25 @@ class MacroAssembler: public Assembler {
|
||||
WRAP(smaddl) WRAP(smsubl) WRAP(umaddl) WRAP(umsubl)
|
||||
#undef WRAP
|
||||
|
||||
using Assembler::andw, Assembler::andr;
|
||||
void andw(Register Rd, Register Rn, uint64_t imm) {
|
||||
if (operand_valid_for_logical_immediate(/*is32*/true, imm)) {
|
||||
Assembler::andw(Rd, Rn, imm);
|
||||
} else {
|
||||
assert(Rd != Rn, "must be");
|
||||
movw(Rd, imm);
|
||||
andw(Rd, Rn, Rd);
|
||||
}
|
||||
}
|
||||
void andr(Register Rd, Register Rn, uint64_t imm) {
|
||||
if (operand_valid_for_logical_immediate(/*is32*/false, imm)) {
|
||||
Assembler::andr(Rd, Rn, imm);
|
||||
} else {
|
||||
assert(Rd != Rn, "must be");
|
||||
mov(Rd, imm);
|
||||
andr(Rd, Rn, Rd);
|
||||
}
|
||||
}
|
||||
|
||||
// macro assembly operations needed for aarch64
|
||||
|
||||
@ -743,7 +762,7 @@ public:
|
||||
// n.b. increment/decrement calls with an Address destination will
|
||||
// need to use a scratch register to load the value to be
|
||||
// incremented. increment/decrement calls which add or subtract a
|
||||
// constant value greater than 2^12 will need to use a 2nd scratch
|
||||
// constant value greater than 2^24 will need to use a 2nd scratch
|
||||
// register to hold the constant. so, a register increment/decrement
|
||||
// may trash rscratch2 and an address increment/decrement trash
|
||||
// rscratch and rscratch2
|
||||
@ -754,11 +773,11 @@ public:
|
||||
void decrement(Register reg, int value = 1);
|
||||
void decrement(Address dst, int value = 1);
|
||||
|
||||
void incrementw(Address dst, int value = 1);
|
||||
void incrementw(Address dst, int value = 1, Register result = rscratch1);
|
||||
void incrementw(Register reg, int value = 1);
|
||||
|
||||
void increment(Register reg, int value = 1);
|
||||
void increment(Address dst, int value = 1);
|
||||
void increment(Address dst, int value = 1, Register result = rscratch1);
|
||||
|
||||
|
||||
// Alignment
|
||||
@ -1734,7 +1753,103 @@ public:
|
||||
private:
|
||||
// Check the current thread doesn't need a cross modify fence.
|
||||
void verify_cross_modify_fence_not_required() PRODUCT_RETURN;
|
||||
void try_to_replace_prev_vector_copy_with_movprfx(FloatRegister dst);
|
||||
|
||||
public:
|
||||
void maybe_movprfx(FloatRegister dst, FloatRegister src) {
|
||||
if (dst != src) {
|
||||
sve_movprfx(dst, src);
|
||||
}
|
||||
}
|
||||
|
||||
// Wrappers for SVE explicit destructive instructions, overriding the
|
||||
// same-signature Assembler entry points to enable movprfx fusion optimization.
|
||||
//
|
||||
// Implicit destructive instructions (e.g. predicated unary ops like sve_abs/
|
||||
// sve_neg/sve_not, whose ISA encoding allows Zd != Zn but whose use as a Java
|
||||
// Vector API masked operation requires pass-through of the first source) are
|
||||
// not covered here. For those, the .ad file is responsible for emitting
|
||||
// movprfx explicitly via maybe_movprfx() before the destructive op.
|
||||
#define SVE_DESTRUCTIVE_BINARY_INS(NAME) \
|
||||
using Assembler::NAME; \
|
||||
void NAME(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, \
|
||||
FloatRegister Zm) { \
|
||||
if (Zd != Zm) { \
|
||||
try_to_replace_prev_vector_copy_with_movprfx(Zd); \
|
||||
} \
|
||||
Assembler::NAME(Zd, T, Pg, Zm); \
|
||||
}
|
||||
|
||||
#define SVE_DESTRUCTIVE_BINARY_5(I1, I2, I3, I4, I5) \
|
||||
SVE_DESTRUCTIVE_BINARY_INS(I1); SVE_DESTRUCTIVE_BINARY_INS(I2); \
|
||||
SVE_DESTRUCTIVE_BINARY_INS(I3); SVE_DESTRUCTIVE_BINARY_INS(I4); \
|
||||
SVE_DESTRUCTIVE_BINARY_INS(I5);
|
||||
|
||||
SVE_DESTRUCTIVE_BINARY_5(sve_add, sve_and, sve_asr, sve_bic, sve_eor)
|
||||
SVE_DESTRUCTIVE_BINARY_5(sve_fabd, sve_fadd, sve_fdiv, sve_fmax, sve_fmin)
|
||||
SVE_DESTRUCTIVE_BINARY_5(sve_fmul, sve_fsub, sve_lsl, sve_lsr, sve_mul)
|
||||
SVE_DESTRUCTIVE_BINARY_5(sve_orr, sve_smax, sve_smin, sve_sqadd, sve_sqsub)
|
||||
SVE_DESTRUCTIVE_BINARY_5(sve_sub, sve_uqadd, sve_uqsub, sve_umax, sve_umin)
|
||||
|
||||
#undef SVE_DESTRUCTIVE_BINARY_INS
|
||||
#undef SVE_DESTRUCTIVE_BINARY_5
|
||||
|
||||
#define SVE_DESTRUCTIVE_SHIFT_IMM_INS(NAME) \
|
||||
void NAME(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, int shift) { \
|
||||
try_to_replace_prev_vector_copy_with_movprfx(Zd); \
|
||||
Assembler::NAME(Zd, T, Pg, shift); \
|
||||
}
|
||||
|
||||
SVE_DESTRUCTIVE_SHIFT_IMM_INS(sve_asr);
|
||||
SVE_DESTRUCTIVE_SHIFT_IMM_INS(sve_lsl);
|
||||
SVE_DESTRUCTIVE_SHIFT_IMM_INS(sve_lsr);
|
||||
|
||||
#undef SVE_DESTRUCTIVE_SHIFT_IMM_INS
|
||||
|
||||
#define SVE_DESTRUCTIVE_UNPRED_IMM_INS(NAME, IMM_TYPE) \
|
||||
void NAME(FloatRegister Zd, SIMD_RegVariant T, IMM_TYPE imm) { \
|
||||
try_to_replace_prev_vector_copy_with_movprfx(Zd); \
|
||||
Assembler::NAME(Zd, T, imm); \
|
||||
}
|
||||
|
||||
SVE_DESTRUCTIVE_UNPRED_IMM_INS(sve_add, unsigned);
|
||||
SVE_DESTRUCTIVE_UNPRED_IMM_INS(sve_sub, unsigned);
|
||||
SVE_DESTRUCTIVE_UNPRED_IMM_INS(sve_and, uint64_t);
|
||||
SVE_DESTRUCTIVE_UNPRED_IMM_INS(sve_eor, uint64_t);
|
||||
SVE_DESTRUCTIVE_UNPRED_IMM_INS(sve_orr, uint64_t);
|
||||
|
||||
#undef SVE_DESTRUCTIVE_UNPRED_IMM_INS
|
||||
|
||||
#define SVE_DESTRUCTIVE_TERNARY_INS(NAME) \
|
||||
using Assembler::NAME; \
|
||||
void NAME(FloatRegister Zd, SIMD_RegVariant T, PRegister Pg, \
|
||||
FloatRegister Zn, FloatRegister Zm) { \
|
||||
if (Zd != Zn && Zd != Zm) { \
|
||||
try_to_replace_prev_vector_copy_with_movprfx(Zd); \
|
||||
} \
|
||||
Assembler::NAME(Zd, T, Pg, Zn, Zm); \
|
||||
}
|
||||
|
||||
SVE_DESTRUCTIVE_TERNARY_INS(sve_fmad);
|
||||
SVE_DESTRUCTIVE_TERNARY_INS(sve_fmla);
|
||||
SVE_DESTRUCTIVE_TERNARY_INS(sve_fmls);
|
||||
SVE_DESTRUCTIVE_TERNARY_INS(sve_fmsb);
|
||||
SVE_DESTRUCTIVE_TERNARY_INS(sve_fnmad);
|
||||
SVE_DESTRUCTIVE_TERNARY_INS(sve_fnmla);
|
||||
SVE_DESTRUCTIVE_TERNARY_INS(sve_fnmls);
|
||||
SVE_DESTRUCTIVE_TERNARY_INS(sve_fnmsb);
|
||||
SVE_DESTRUCTIVE_TERNARY_INS(sve_mla);
|
||||
SVE_DESTRUCTIVE_TERNARY_INS(sve_mls);
|
||||
|
||||
#undef SVE_DESTRUCTIVE_TERNARY_INS
|
||||
|
||||
using Assembler::sve_eor3;
|
||||
void sve_eor3(FloatRegister Zd, FloatRegister Zm, FloatRegister Zk) {
|
||||
if (Zd != Zm && Zd != Zk) {
|
||||
try_to_replace_prev_vector_copy_with_movprfx(Zd);
|
||||
}
|
||||
Assembler::sve_eor3(Zd, Zm, Zk);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef ASSERT
|
||||
|
||||
@ -140,6 +140,29 @@ public:
|
||||
Instruction_aarch64::extract(insn, 23, 23) == 0b0 &&
|
||||
Instruction_aarch64::extract(insn, 26, 25) == 0b00;
|
||||
}
|
||||
|
||||
static bool is_neon_vector_mov_alias(uint32_t insn) {
|
||||
if (Instruction_aarch64::extract(insn, 31, 31) != 0 ||
|
||||
Instruction_aarch64::extract(insn, 29, 21) != 0b001110101 ||
|
||||
Instruction_aarch64::extract(insn, 15, 10) != 0b000111) {
|
||||
return false;
|
||||
}
|
||||
return Instruction_aarch64::extract(insn, 9, 5) ==
|
||||
Instruction_aarch64::extract(insn, 20, 16);
|
||||
}
|
||||
|
||||
static bool is_sve_vector_mov_alias(uint32_t insn) {
|
||||
if (Instruction_aarch64::extract(insn, 31, 21) != 0b00000100011 ||
|
||||
Instruction_aarch64::extract(insn, 15, 10) != 0b001100) {
|
||||
return false;
|
||||
}
|
||||
return Instruction_aarch64::extract(insn, 9, 5) ==
|
||||
Instruction_aarch64::extract(insn, 20, 16);
|
||||
}
|
||||
|
||||
static uint32_t encode_sve_movprfx(uint32_t dst, uint32_t src) {
|
||||
return 0x1082f << 10 | (src << 5) | dst;
|
||||
}
|
||||
};
|
||||
|
||||
inline NativeInstruction* nativeInstruction_at(address address) {
|
||||
|
||||
@ -6276,6 +6276,24 @@ class StubGenerator: public StubCodeGenerator {
|
||||
// static int implKyberNttMult(
|
||||
// short[] result, short[] ntta, short[] nttb, short[] zetas) {}
|
||||
//
|
||||
// The actual algorithm that is used here differs from the one in the Java
|
||||
// implementation, it uses Montgomery multiplications instead of Barrett
|
||||
// reduction, but the end result modulo MLKEM_Q is the same. This is the
|
||||
// Java equivalent of this intrinsic implementation:
|
||||
// static void implKyberNttMultJava(short[] result, short[] ntta, short[] nttb) {
|
||||
// for (int m = 0; m < ML_KEM_N / 2; m++) {
|
||||
// int a0 = ntta[2 * m];
|
||||
// int a1 = ntta[2 * m + 1];
|
||||
// int b0 = nttb[2 * m];
|
||||
// int b1 = nttb[2 * m + 1];
|
||||
// int r = montMul(a0, b0) +
|
||||
// montMul(montMul(a1, b1), MONT_ZETAS_FOR_NTT_MULT[m]);
|
||||
// result[2 * m] = (short) montMul(r, MONT_R_SQUARE_MOD_Q);
|
||||
// result[2 * m + 1] = (short) montMul(
|
||||
// (montMul(a0, b1) + montMul(a1, b0)), MONT_R_SQUARE_MOD_Q);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// result (short[256]) = c_rarg0
|
||||
// ntta (short[256]) = c_rarg1
|
||||
// nttb (short[256]) = c_rarg2
|
||||
|
||||
@ -2226,39 +2226,12 @@ void LIR_Assembler::emit_alloc_array(LIR_OpAllocArray* op) {
|
||||
}
|
||||
|
||||
|
||||
// kills recv
|
||||
void LIR_Assembler::type_profile_helper(Register mdo, int mdo_offset_bias,
|
||||
ciMethodData *md, ciProfileData *data,
|
||||
Register recv, Register tmp1, Label* update_done) {
|
||||
uint i;
|
||||
for (i = 0; i < VirtualCallData::row_limit(); i++) {
|
||||
Label next_test;
|
||||
// See if the receiver is receiver[n].
|
||||
__ ld(tmp1, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i)) - mdo_offset_bias, mdo);
|
||||
__ verify_klass_ptr(tmp1);
|
||||
__ cmpd(CR0, recv, tmp1);
|
||||
__ bne(CR0, next_test);
|
||||
|
||||
__ ld(tmp1, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i)) - mdo_offset_bias, mdo);
|
||||
__ addi(tmp1, tmp1, DataLayout::counter_increment);
|
||||
__ std(tmp1, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i)) - mdo_offset_bias, mdo);
|
||||
__ b(*update_done);
|
||||
|
||||
__ bind(next_test);
|
||||
}
|
||||
|
||||
// Didn't find receiver; find next empty slot and fill it in.
|
||||
for (i = 0; i < VirtualCallData::row_limit(); i++) {
|
||||
Label next_test;
|
||||
__ ld(tmp1, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i)) - mdo_offset_bias, mdo);
|
||||
__ cmpdi(CR0, tmp1, 0);
|
||||
__ bne(CR0, next_test);
|
||||
__ li(tmp1, DataLayout::counter_increment);
|
||||
__ std(recv, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_offset(i)) - mdo_offset_bias, mdo);
|
||||
__ std(tmp1, md->byte_offset_of_slot(data, ReceiverTypeData::receiver_count_offset(i)) - mdo_offset_bias, mdo);
|
||||
__ b(*update_done);
|
||||
|
||||
__ bind(next_test);
|
||||
}
|
||||
Register recv, Register tmp) {
|
||||
int mdp_offset = md->byte_offset_of_slot(data, in_ByteSize(0)) - mdo_offset_bias;
|
||||
__ profile_receiver_type(recv, mdo, mdp_offset, tmp, noreg);
|
||||
}
|
||||
|
||||
|
||||
@ -2320,15 +2293,9 @@ void LIR_Assembler::emit_typecheck_helper(LIR_OpTypeCheck *op, Label* success, L
|
||||
__ b(*obj_is_null);
|
||||
__ bind(not_null);
|
||||
|
||||
Label update_done;
|
||||
Register recv = klass_RInfo;
|
||||
__ load_klass(recv, obj);
|
||||
type_profile_helper(mdo, mdo_offset_bias, md, data, recv, Rtmp1, &update_done);
|
||||
const int slot_offset = md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias;
|
||||
__ ld(Rtmp1, slot_offset, mdo);
|
||||
__ addi(Rtmp1, Rtmp1, DataLayout::counter_increment);
|
||||
__ std(Rtmp1, slot_offset, mdo);
|
||||
__ bind(update_done);
|
||||
type_profile_helper(mdo, mdo_offset_bias, md, data, recv, Rtmp1); // kills recv
|
||||
} else {
|
||||
__ cmpdi(CR0, obj, 0);
|
||||
__ beq(CR0, *obj_is_null);
|
||||
@ -2427,15 +2394,9 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) {
|
||||
__ b(done);
|
||||
__ bind(not_null);
|
||||
|
||||
Label update_done;
|
||||
Register recv = klass_RInfo;
|
||||
__ load_klass(recv, value);
|
||||
type_profile_helper(mdo, mdo_offset_bias, md, data, recv, Rtmp1, &update_done);
|
||||
const int slot_offset = md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias;
|
||||
__ ld(Rtmp1, slot_offset, mdo);
|
||||
__ addi(Rtmp1, Rtmp1, DataLayout::counter_increment);
|
||||
__ std(Rtmp1, slot_offset, mdo);
|
||||
__ bind(update_done);
|
||||
type_profile_helper(mdo, mdo_offset_bias, md, data, recv, Rtmp1); // kills recv
|
||||
} else {
|
||||
__ cmpdi(CR0, value, 0);
|
||||
__ beq(CR0, done);
|
||||
@ -2648,55 +2609,27 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) {
|
||||
// We know the type that will be seen at this call site; we can
|
||||
// statically update the MethodData* rather than needing to do
|
||||
// dynamic tests on the receiver type.
|
||||
|
||||
// NOTE: we should probably put a lock around this search to
|
||||
// avoid collisions by concurrent compilations.
|
||||
ciVirtualCallData* vc_data = (ciVirtualCallData*) data;
|
||||
uint i;
|
||||
for (i = 0; i < VirtualCallData::row_limit(); i++) {
|
||||
for (uint i = 0; i < VirtualCallData::row_limit(); i++) {
|
||||
ciKlass* receiver = vc_data->receiver(i);
|
||||
if (known_klass->equals(receiver)) {
|
||||
__ ld(tmp1, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias, mdo);
|
||||
__ addi(tmp1, tmp1, DataLayout::counter_increment);
|
||||
__ std(tmp1, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias, mdo);
|
||||
__ increment_mem64(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias,
|
||||
DataLayout::counter_increment, tmp1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Receiver type not found in profile data; select an empty slot.
|
||||
|
||||
// Note that this is less efficient than it should be because it
|
||||
// always does a write to the receiver part of the
|
||||
// VirtualCallData rather than just the first time.
|
||||
for (i = 0; i < VirtualCallData::row_limit(); i++) {
|
||||
ciKlass* receiver = vc_data->receiver(i);
|
||||
if (receiver == nullptr) {
|
||||
metadata2reg(known_klass->constant_encoding(), tmp1);
|
||||
__ std(tmp1, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)) - mdo_offset_bias, mdo);
|
||||
|
||||
__ ld(tmp1, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias, mdo);
|
||||
__ addi(tmp1, tmp1, DataLayout::counter_increment);
|
||||
__ std(tmp1, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)) - mdo_offset_bias, mdo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Receiver type is not found in profile data.
|
||||
// Fall back to runtime helper to handle the rest at runtime.
|
||||
metadata2reg(known_klass->constant_encoding(), recv);
|
||||
} else {
|
||||
__ load_klass(recv, recv);
|
||||
Label update_done;
|
||||
type_profile_helper(mdo, mdo_offset_bias, md, data, recv, tmp1, &update_done);
|
||||
// Receiver did not match any saved receiver and there is no empty row for it.
|
||||
// Increment total counter to indicate polymorphic case.
|
||||
__ ld(tmp1, md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias, mdo);
|
||||
__ addi(tmp1, tmp1, DataLayout::counter_increment);
|
||||
__ std(tmp1, md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias, mdo);
|
||||
|
||||
__ bind(update_done);
|
||||
}
|
||||
type_profile_helper(mdo, mdo_offset_bias, md, data, recv, tmp1); // kills recv
|
||||
} else {
|
||||
// Static call
|
||||
__ ld(tmp1, md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias, mdo);
|
||||
__ addi(tmp1, tmp1, DataLayout::counter_increment);
|
||||
__ std(tmp1, md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias, mdo);
|
||||
__ increment_mem64(mdo, md->byte_offset_of_slot(data, CounterData::count_offset()) - mdo_offset_bias,
|
||||
DataLayout::counter_increment, tmp1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ friend class ArrayCopyStub;
|
||||
// Record the type of the receiver in ReceiverTypeData.
|
||||
void type_profile_helper(Register mdo, int mdo_offset_bias,
|
||||
ciMethodData *md, ciProfileData *data,
|
||||
Register recv, Register tmp1, Label* update_done);
|
||||
Register recv, Register tmp);
|
||||
// Setup pointers to MDO, MDO slot, also compute offset bias to access the slot.
|
||||
void setup_md_access(ciMethod* method, int bci,
|
||||
ciMethodData*& md, ciProfileData*& data, int& mdo_offset_bias);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2000, 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
|
||||
@ -319,7 +319,7 @@ void frame::patch_pc(Thread* thread, address pc) {
|
||||
|
||||
#ifdef ASSERT
|
||||
{
|
||||
frame f(this->sp(), pc, this->unextended_sp());
|
||||
frame f(sp(), unextended_sp(), fp(), pc, cb(), oop_map(), is_heap_frame());
|
||||
assert(f.is_deoptimized_frame() == this->is_deoptimized_frame() && f.pc() == this->pc() && f.raw_pc() == this->raw_pc(),
|
||||
"must be (f.is_deoptimized_frame(): %d this->is_deoptimized_frame(): %d "
|
||||
"f.pc(): " INTPTR_FORMAT " this->pc(): " INTPTR_FORMAT " f.raw_pc(): " INTPTR_FORMAT " this->raw_pc(): " INTPTR_FORMAT ")",
|
||||
|
||||
@ -81,7 +81,7 @@ inline void frame::setup(kind knd) {
|
||||
// Continuation frames on the java heap are not aligned.
|
||||
// When thawing interpreted frames the sp can be unaligned (see new_stack_frame()).
|
||||
assert(_on_heap ||
|
||||
((is_aligned(_sp, alignment_in_bytes) || is_interpreted_frame() || is_deoptimized_frame()) &&
|
||||
((is_aligned(_sp, alignment_in_bytes) || is_interpreted_frame()) &&
|
||||
(is_aligned(_fp, alignment_in_bytes) || !is_fully_initialized())),
|
||||
"invalid alignment sp:" PTR_FORMAT " unextended_sp:" PTR_FORMAT " fp:" PTR_FORMAT, p2i(_sp), p2i(_unextended_sp), p2i(_fp));
|
||||
}
|
||||
|
||||
@ -31,7 +31,6 @@
|
||||
#include "gc/shenandoah/mode/shenandoahMode.hpp"
|
||||
#include "gc/shenandoah/shenandoahBarrierSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
|
||||
#include "gc/shenandoah/shenandoahForwarding.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
|
||||
@ -335,43 +334,6 @@ void ShenandoahBarrierSetAssembler::satb_barrier_impl(MacroAssembler *masm, Deco
|
||||
__ bind(skip_barrier);
|
||||
}
|
||||
|
||||
void ShenandoahBarrierSetAssembler::resolve_forward_pointer_not_null(MacroAssembler *masm, Register dst, Register tmp) {
|
||||
__ block_comment("resolve_forward_pointer_not_null (shenandoahgc) {");
|
||||
|
||||
Register tmp1 = tmp,
|
||||
R0_tmp2 = R0;
|
||||
assert_different_registers(dst, tmp1, R0_tmp2, noreg);
|
||||
|
||||
// If the object has been evacuated, the mark word layout is as follows:
|
||||
// | forwarding pointer (62-bit) | '11' (2-bit) |
|
||||
|
||||
// The invariant that stack/thread pointers have the lowest two bits cleared permits retrieving
|
||||
// the forwarding pointer solely by inversing the lowest two bits.
|
||||
// This invariant follows inevitably from hotspot's minimal alignment.
|
||||
assert(markWord::marked_value <= (unsigned long) MinObjAlignmentInBytes,
|
||||
"marked value must not be higher than hotspot's minimal alignment");
|
||||
|
||||
Label done;
|
||||
|
||||
// Load the object's mark word.
|
||||
__ ld(tmp1, oopDesc::mark_offset_in_bytes(), dst);
|
||||
|
||||
// Load the bit mask for the lock bits.
|
||||
__ li(R0_tmp2, markWord::lock_mask_in_place);
|
||||
|
||||
// Check whether all bits matching the bit mask are set.
|
||||
// If that is the case, the object has been evacuated and the most significant bits form the forward pointer.
|
||||
__ andc_(R0_tmp2, R0_tmp2, tmp1);
|
||||
|
||||
assert(markWord::lock_mask_in_place == markWord::marked_value,
|
||||
"marked value must equal the value obtained when all lock bits are being set");
|
||||
__ xori(tmp1, tmp1, markWord::lock_mask_in_place);
|
||||
__ isel(dst, CR0, Assembler::equal, false, tmp1);
|
||||
|
||||
__ bind(done);
|
||||
__ block_comment("} resolve_forward_pointer_not_null (shenandoahgc)");
|
||||
}
|
||||
|
||||
// base: Base register of the reference's address.
|
||||
// ind_or_offs: Index or offset of the reference's address (load mode).
|
||||
// dst: Reference's address. In case the object has been evacuated, this is the to-space version
|
||||
@ -693,125 +655,6 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl
|
||||
__ block_comment("} try_peek_weak_handle_in_nmethod (shenandoahgc)");
|
||||
}
|
||||
|
||||
// Special shenandoah CAS implementation that handles false negatives due
|
||||
// to concurrent evacuation. That is, the CAS operation is intended to succeed in
|
||||
// the following scenarios (success criteria):
|
||||
// s1) The reference pointer ('base_addr') equals the expected ('expected') pointer.
|
||||
// s2) The reference pointer refers to the from-space version of an already-evacuated
|
||||
// object, whereas the expected pointer refers to the to-space version of the same object.
|
||||
// Situations in which the reference pointer refers to the to-space version of an object
|
||||
// and the expected pointer refers to the from-space version of the same object can not occur due to
|
||||
// shenandoah's strong to-space invariant. This also implies that the reference stored in 'new_val'
|
||||
// can not refer to the from-space version of an already-evacuated object.
|
||||
//
|
||||
// To guarantee correct behavior in concurrent environments, two races must be addressed:
|
||||
// r1) A concurrent thread may heal the reference pointer (i.e., it is no longer referring to the
|
||||
// from-space version but to the to-space version of the object in question).
|
||||
// In this case, the CAS operation should succeed.
|
||||
// r2) A concurrent thread may mutate the reference (i.e., the reference pointer refers to an entirely different object).
|
||||
// In this case, the CAS operation should fail.
|
||||
//
|
||||
// By default, the value held in the 'result' register is zero to indicate failure of CAS,
|
||||
// non-zero to indicate success. If 'is_cae' is set, the result is the most recently fetched
|
||||
// value from 'base_addr' rather than a boolean success indicator.
|
||||
void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler *masm, Register base_addr,
|
||||
Register expected, Register new_val, Register tmp1, Register tmp2,
|
||||
bool is_cae, Register result) {
|
||||
__ block_comment("cmpxchg_oop (shenandoahgc) {");
|
||||
|
||||
assert_different_registers(base_addr, new_val, tmp1, tmp2, result, R0);
|
||||
assert_different_registers(base_addr, expected, tmp1, tmp2, result, R0);
|
||||
|
||||
// Potential clash of 'success_flag' and 'tmp' is being accounted for.
|
||||
Register success_flag = is_cae ? noreg : result,
|
||||
current_value = is_cae ? result : tmp1,
|
||||
tmp = is_cae ? tmp1 : result,
|
||||
initial_value = tmp2;
|
||||
|
||||
Label done, step_four;
|
||||
|
||||
__ bind(step_four);
|
||||
|
||||
/* ==== Step 1 ("Standard" CAS) ==== */
|
||||
// Fast path: The values stored in 'expected' and 'base_addr' are equal.
|
||||
// Given that 'expected' must refer to the to-space object of an evacuated object (strong to-space invariant),
|
||||
// no special processing is required.
|
||||
if (UseCompressedOops) {
|
||||
__ cmpxchgw(CR0, current_value, expected, new_val, base_addr, MacroAssembler::MemBarNone,
|
||||
false, success_flag, nullptr, true);
|
||||
} else {
|
||||
__ cmpxchgd(CR0, current_value, expected, new_val, base_addr, MacroAssembler::MemBarNone,
|
||||
false, success_flag, nullptr, true);
|
||||
}
|
||||
|
||||
// Skip the rest of the barrier if the CAS operation succeeds immediately.
|
||||
// If it does not, the value stored at the address is either the from-space pointer of the
|
||||
// referenced object (success criteria s2)) or simply another object.
|
||||
__ beq(CR0, done);
|
||||
|
||||
/* ==== Step 2 (Null check) ==== */
|
||||
// The success criteria s2) cannot be matched with a null pointer
|
||||
// (null pointers cannot be subject to concurrent evacuation). The failure of the CAS operation is thus legitimate.
|
||||
__ cmpdi(CR0, current_value, 0);
|
||||
__ beq(CR0, done);
|
||||
|
||||
/* ==== Step 3 (reference pointer refers to from-space version; success criteria s2)) ==== */
|
||||
// To check whether the reference pointer refers to the from-space version, the forward
|
||||
// pointer of the object referred to by the reference is resolved and compared against the expected pointer.
|
||||
// If this check succeed, another CAS operation is issued with the from-space pointer being the expected pointer.
|
||||
//
|
||||
// Save the potential from-space pointer.
|
||||
__ mr(initial_value, current_value);
|
||||
|
||||
// Resolve forward pointer.
|
||||
if (UseCompressedOops) { __ decode_heap_oop_not_null(current_value); }
|
||||
resolve_forward_pointer_not_null(masm, current_value, tmp);
|
||||
if (UseCompressedOops) { __ encode_heap_oop_not_null(current_value); }
|
||||
|
||||
if (!is_cae) {
|
||||
// 'success_flag' was overwritten by call to 'resovle_forward_pointer_not_null'.
|
||||
// Load zero into register for the potential failure case.
|
||||
__ li(success_flag, 0);
|
||||
}
|
||||
__ cmpd(CR0, current_value, expected);
|
||||
__ bne(CR0, done);
|
||||
|
||||
// Discard fetched value as it might be a reference to the from-space version of an object.
|
||||
if (UseCompressedOops) {
|
||||
__ cmpxchgw(CR0, R0, initial_value, new_val, base_addr, MacroAssembler::MemBarNone,
|
||||
false, success_flag);
|
||||
} else {
|
||||
__ cmpxchgd(CR0, R0, initial_value, new_val, base_addr, MacroAssembler::MemBarNone,
|
||||
false, success_flag);
|
||||
}
|
||||
|
||||
/* ==== Step 4 (Retry CAS with to-space pointer (success criteria s2) under race r1)) ==== */
|
||||
// The reference pointer could have been healed whilst the previous CAS operation was being performed.
|
||||
// Another CAS operation must thus be issued with the to-space pointer being the expected pointer.
|
||||
// If that CAS operation fails as well, race r2) must have occurred, indicating that
|
||||
// the operation failure is legitimate.
|
||||
//
|
||||
// To keep the code's size small and thus improving cache (icache) performance, this highly
|
||||
// unlikely case should be handled by the smallest possible code. Instead of emitting a third,
|
||||
// explicit CAS operation, the code jumps back and reuses the first CAS operation (step 1)
|
||||
// (passed arguments are identical).
|
||||
//
|
||||
// A failure of the CAS operation in step 1 would imply that the overall CAS operation is supposed
|
||||
// to fail. Jumping back to step 1 requires, however, that step 2 and step 3 are re-executed as well.
|
||||
// It is thus important to ensure that a re-execution of those steps does not put program correctness
|
||||
// at risk:
|
||||
// - Step 2: Either terminates in failure (desired result) or falls through to step 3.
|
||||
// - Step 3: Terminates if the comparison between the forwarded, fetched pointer and the expected value
|
||||
// fails. Unless the reference has been updated in the meanwhile once again, this is
|
||||
// guaranteed to be the case.
|
||||
// In case of a concurrent update, the CAS would be retried again. This is legitimate
|
||||
// in terms of program correctness (even though it is not desired).
|
||||
__ bne(CR0, step_four);
|
||||
|
||||
__ bind(done);
|
||||
__ block_comment("} cmpxchg_oop (shenandoahgc)");
|
||||
}
|
||||
|
||||
void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
|
||||
Register addr, Register count, Register preserve) {
|
||||
assert(ShenandoahCardBarrier, "Should have been checked by caller");
|
||||
@ -1115,7 +958,7 @@ void ShenandoahBarrierSetAssembler::load_c2(const MachNode* node, MacroAssembler
|
||||
void ShenandoahBarrierSetAssembler::store_c2(const MachNode* node, MacroAssembler* masm,
|
||||
Register dst, int disp, bool dst_narrow, Register src, bool src_narrow, Register tmp1, Register tmp2, Register tmp3) {
|
||||
|
||||
ShenandoahBarrierStubC2::store_pre(masm, node, tmp1, Address(dst, disp), tmp2, tmp3, dst_narrow);
|
||||
ShenandoahBarrierStubC2::store_pre(masm, node, Address(dst, disp), tmp1, tmp2, tmp3, dst_narrow);
|
||||
|
||||
if (dst_narrow && !src_narrow) {
|
||||
// Need to encode into tmp, because we cannot clobber src.
|
||||
@ -1134,39 +977,41 @@ void ShenandoahBarrierSetAssembler::store_c2(const MachNode* node, MacroAssemble
|
||||
ShenandoahBarrierStubC2::store_post(masm, node, Address(dst, disp), tmp1, tmp2);
|
||||
}
|
||||
|
||||
void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, MacroAssembler* masm, Register res, Register addr, Register oldval,
|
||||
Register newval, Register tmp1, Register tmp2, Register tmp3, bool exchange, bool narrow, bool weak, bool acquire) {
|
||||
void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, MacroAssembler* masm, Register res, Register addr,
|
||||
Register oldval, Register newval, Register tmp1, Register tmp2, bool exchange, bool narrow, bool weak, bool acquire) {
|
||||
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, tmp1, addr, tmp2, tmp3, narrow);
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, res, tmp1, tmp2, narrow);
|
||||
|
||||
Register dest_current = exchange ? res : R0;
|
||||
Register int_flag = exchange ? noreg : res;
|
||||
int semantics = MacroAssembler::MemBarNone;
|
||||
Register dest_current = exchange ? res : R0;
|
||||
Label no_update;
|
||||
int semantics = MacroAssembler::MemBarNone;
|
||||
|
||||
if (acquire) {
|
||||
semantics = support_IRIW_for_not_multiple_copy_atomic_cpu ?
|
||||
MacroAssembler::MemBarAcq : MacroAssembler::MemBarFenceAfter;
|
||||
}
|
||||
|
||||
if (!exchange) { __ li(res, 0); }
|
||||
if (narrow) {
|
||||
// CmpxchgX sets CR0 to cmpX(src1, src2) and Rres to 'true'/'false'.
|
||||
__ cmpxchgw(CR0, dest_current, oldval, newval, addr,
|
||||
semantics, MacroAssembler::cmpxchgx_hint_atomic_update(),
|
||||
int_flag, nullptr, true, weak);
|
||||
noreg, &no_update, true, weak);
|
||||
} else {
|
||||
// CmpxchgX sets CR0 to cmpX(src1, src2) and Rres to 'true'/'false'.
|
||||
__ cmpxchgd(CR0, dest_current, oldval, newval, addr,
|
||||
semantics, MacroAssembler::cmpxchgx_hint_atomic_update(),
|
||||
int_flag, nullptr, true, weak);
|
||||
noreg, &no_update, true, weak);
|
||||
}
|
||||
if (!exchange) { __ li(res, 1); }
|
||||
|
||||
ShenandoahBarrierStubC2::load_store_post(masm, node, Address(addr, 0), tmp1, tmp2);
|
||||
|
||||
__ bind(no_update);
|
||||
}
|
||||
|
||||
void ShenandoahBarrierSetAssembler::get_and_set_c2(const MachNode* node, MacroAssembler* masm, Register preval, Register newval, Register addr, Register tmp1, Register tmp2, Register tmp3) {
|
||||
void ShenandoahBarrierSetAssembler::get_and_set_c2(const MachNode* node, MacroAssembler* masm, Register preval, Register newval, Register addr, Register tmp1, Register tmp2) {
|
||||
bool is_narrow = node->bottom_type()->isa_narrowoop();
|
||||
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, tmp1, addr, tmp2, tmp3, is_narrow);
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, preval, tmp1, tmp2, is_narrow);
|
||||
|
||||
if (is_narrow) {
|
||||
__ getandsetw(preval, newval, addr, MacroAssembler::cmpxchgx_hint_atomic_update());
|
||||
@ -1365,12 +1210,9 @@ void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) {
|
||||
if (c_rarg0 == _obj) {
|
||||
__ addi(c_rarg1, _addr.base(), _addr.disp());
|
||||
} else if (c_rarg1 == _obj) {
|
||||
// Set up arguments in reverse, and then flip them
|
||||
__ addi(c_rarg0, _addr.base(), _addr.disp());
|
||||
// flip them
|
||||
__ mr(_tmp1, c_rarg0);
|
||||
__ mr(c_rarg0, c_rarg1);
|
||||
__ mr(c_rarg1, _tmp1);
|
||||
__ mr(_tmp1, c_rarg1);
|
||||
__ addi(c_rarg1, _addr.base(), _addr.disp());
|
||||
__ mr(c_rarg0, _tmp1);
|
||||
} else {
|
||||
assert_different_registers(c_rarg1, _obj);
|
||||
__ addi(c_rarg1, _addr.base(), _addr.disp());
|
||||
|
||||
@ -69,8 +69,6 @@ private:
|
||||
MacroAssembler::PreservationLevel preservation_level);
|
||||
|
||||
/* ==== Helper methods for barrier implementations ==== */
|
||||
void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp);
|
||||
|
||||
void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
|
||||
Register addr, Register count,
|
||||
Register preserve);
|
||||
@ -103,11 +101,6 @@ public:
|
||||
Register tmp1, Register tmp2,
|
||||
MacroAssembler::PreservationLevel preservation_level);
|
||||
|
||||
/* ==== Helper methods used by C1 and C2 ==== */
|
||||
void cmpxchg_oop(MacroAssembler* masm, Register base_addr, Register expected, Register new_val,
|
||||
Register tmp1, Register tmp2,
|
||||
bool is_cae, Register result);
|
||||
|
||||
/* ==== Access api ==== */
|
||||
virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
|
||||
Register src, Register dst, Register count,
|
||||
@ -140,10 +133,10 @@ public:
|
||||
Register dst, int disp, bool dst_narrow, Register src, bool src_narrow, Register tmp1, Register tmp2, Register tmp3);
|
||||
|
||||
void compare_and_set_c2(const MachNode* node, MacroAssembler* masm, Register res, Register addr, Register oldval,
|
||||
Register newval, Register tmp1, Register tmp2, Register tmp3, bool exchange, bool narrow, bool weak, bool acquire);
|
||||
Register newval, Register tmp1, Register tmp2, bool exchange, bool narrow, bool weak, bool acquire);
|
||||
|
||||
void get_and_set_c2(const MachNode* node, MacroAssembler* masm,
|
||||
Register preval, Register newval, Register addr, Register tmp1, Register tmp2, Register tmp3);
|
||||
Register preval, Register newval, Register addr, Register tmp1, Register tmp2);
|
||||
#endif // COMPILER2
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//
|
||||
// Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved.
|
||||
// Copyright (c) 2012, 2021 SAP SE. 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
|
||||
@ -201,10 +201,12 @@ instruct encodePAndStoreN_shenandoah(memory dst, iRegPsrc src, iRegPdstNoScratch
|
||||
// ---------------------- LOAD-STORES -----------------------------------
|
||||
//
|
||||
|
||||
instruct compareAndSwapN_regP_regN_regN_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
// Strong CAS also handles WeakCompareAndSwap* on PPC, see JDK-8385633.
|
||||
instruct compareAndSwapN_regP_regN_regN_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, flagsRegCR0 cr0) %{
|
||||
match(Set res (CompareAndSwapN mem_ptr (Binary src1 src2)));
|
||||
match(Set res (WeakCompareAndSwapN mem_ptr (Binary src1 src2)));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && !need_acquire_load_store(n));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0); // TEMP_DEF to avoid jump
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0); // TEMP_DEF to avoid jump
|
||||
format %{ "CMPXCHGW $res, $mem_ptr, $src1, $src2; as bool" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm,
|
||||
@ -214,7 +216,6 @@ instruct compareAndSwapN_regP_regN_regN_shenandoah(iRegIdst res, iRegPdst mem_pt
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ false,
|
||||
/* is_narrow */ true,
|
||||
/* weak */ false,
|
||||
@ -223,10 +224,11 @@ instruct compareAndSwapN_regP_regN_regN_shenandoah(iRegIdst res, iRegPdst mem_pt
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapN_acq_regP_regN_regN_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
instruct compareAndSwapN_acq_regP_regN_regN_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, flagsRegCR0 cr0) %{
|
||||
match(Set res (CompareAndSwapN mem_ptr (Binary src1 src2)));
|
||||
match(Set res (WeakCompareAndSwapN mem_ptr (Binary src1 src2)));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && need_acquire_load_store(n));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0); // TEMP_DEF to avoid jump
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0); // TEMP_DEF to avoid jump
|
||||
format %{ "CMPXCHGW $res, $mem_ptr, $src1, $src2; as bool" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm,
|
||||
@ -236,7 +238,6 @@ instruct compareAndSwapN_acq_regP_regN_regN_shenandoah(iRegIdst res, iRegPdst me
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ false,
|
||||
/* is_narrow */ true,
|
||||
/* weak */ false,
|
||||
@ -245,9 +246,10 @@ instruct compareAndSwapN_acq_regP_regN_regN_shenandoah(iRegIdst res, iRegPdst me
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapP_regP_regP_regP_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
instruct compareAndSwapP_regP_regP_regP_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, flagsRegCR0 cr0) %{
|
||||
match(Set res (CompareAndSwapP mem_ptr (Binary src1 src2)));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0); // TEMP_DEF to avoid jump
|
||||
match(Set res (WeakCompareAndSwapP mem_ptr (Binary src1 src2)));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0); // TEMP_DEF to avoid jump
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && !need_acquire_load_store(n));
|
||||
format %{ "CMPXCHGD $res, $mem_ptr, $src1, $src2; as bool; ptr" %}
|
||||
ins_encode %{
|
||||
@ -258,7 +260,6 @@ instruct compareAndSwapP_regP_regP_regP_shenandoah(iRegIdst res, iRegPdst mem_pt
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ false,
|
||||
/* is_narrow */ false,
|
||||
/* weak */ false,
|
||||
@ -267,9 +268,10 @@ instruct compareAndSwapP_regP_regP_regP_shenandoah(iRegIdst res, iRegPdst mem_pt
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct compareAndSwapP_acq_regP_regP_regP_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
instruct compareAndSwapP_acq_regP_regP_regP_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, flagsRegCR0 cr0) %{
|
||||
match(Set res (CompareAndSwapP mem_ptr (Binary src1 src2)));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0); // TEMP_DEF to avoid jump
|
||||
match(Set res (WeakCompareAndSwapP mem_ptr (Binary src1 src2)));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0); // TEMP_DEF to avoid jump
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && need_acquire_load_store(n));
|
||||
format %{ "CMPXCHGD $res, $mem_ptr, $src1, $src2; as bool; ptr" %}
|
||||
ins_encode %{
|
||||
@ -280,7 +282,6 @@ instruct compareAndSwapP_acq_regP_regP_regP_shenandoah(iRegIdst res, iRegPdst me
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ false,
|
||||
/* is_narrow */ false,
|
||||
/* weak */ false,
|
||||
@ -289,98 +290,10 @@ instruct compareAndSwapP_acq_regP_regP_regP_shenandoah(iRegIdst res, iRegPdst me
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct weakCompareAndSwapN_regP_regN_regN_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
match(Set res (WeakCompareAndSwapN mem_ptr (Binary src1 src2)));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && !need_acquire_load_store(n));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0); // TEMP_DEF to avoid jump
|
||||
format %{ "weak CMPXCHGW acq $res, $mem_ptr, $src1, $src2; as bool" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm,
|
||||
$res$$Register,
|
||||
$mem_ptr$$Register,
|
||||
$src1$$Register,
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ false,
|
||||
/* is_narrow */ true,
|
||||
/* weak */ true,
|
||||
/* acquire */ false);
|
||||
%}
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct weakCompareAndSwapN_acq_regP_regN_regN_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
match(Set res (WeakCompareAndSwapN mem_ptr (Binary src1 src2)));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && need_acquire_load_store(n));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0); // TEMP_DEF to avoid jump
|
||||
format %{ "weak CMPXCHGW acq $res, $mem_ptr, $src1, $src2; as bool" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm,
|
||||
$res$$Register,
|
||||
$mem_ptr$$Register,
|
||||
$src1$$Register,
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ false,
|
||||
/* is_narrow */ true,
|
||||
/* weak */ true,
|
||||
/* acquire */ true);
|
||||
%}
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct weakCompareAndSwapP_regP_regP_regP_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
match(Set res (WeakCompareAndSwapP mem_ptr (Binary src1 src2)));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && !need_acquire_load_store(n));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0); // TEMP_DEF to avoid jump
|
||||
format %{ "weak CMPXCHGD $res, $mem_ptr, $src1, $src2; as bool; ptr" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm,
|
||||
$res$$Register,
|
||||
$mem_ptr$$Register,
|
||||
$src1$$Register,
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ false,
|
||||
/* is_narrow */ false,
|
||||
/* weak */ true,
|
||||
/* acquire */ false);
|
||||
%}
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct weakCompareAndSwapP_acq_regP_regP_regP_shenandoah(iRegIdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
match(Set res (WeakCompareAndSwapP mem_ptr (Binary src1 src2)));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && need_acquire_load_store(n));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0); // TEMP_DEF to avoid jump
|
||||
format %{ "weak CMPXCHGD $res, $mem_ptr, $src1, $src2; as bool; ptr" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm,
|
||||
$res$$Register,
|
||||
$mem_ptr$$Register,
|
||||
$src1$$Register,
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ false,
|
||||
/* is_narrow */ false,
|
||||
/* weak */ true,
|
||||
/* acquire */ true);
|
||||
%}
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct compareAndExchangeN_regP_regN_regN_shenandoah(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
instruct compareAndExchangeN_regP_regN_regN_shenandoah(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, flagsRegCR0 cr0) %{
|
||||
match(Set res (CompareAndExchangeN mem_ptr (Binary src1 src2)));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && !need_acquire_load_store(n));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0);
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0);
|
||||
format %{ "CMPXCHGW $res, $mem_ptr, $src1, $src2; as narrow oop" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm,
|
||||
@ -390,7 +303,6 @@ instruct compareAndExchangeN_regP_regN_regN_shenandoah(iRegNdst res, iRegPdst me
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ true,
|
||||
/* is_narrow */ true,
|
||||
/* weak */ false,
|
||||
@ -399,10 +311,10 @@ instruct compareAndExchangeN_regP_regN_regN_shenandoah(iRegNdst res, iRegPdst me
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct compareAndExchangeN_acq_regP_regN_regN_shenandoah(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
instruct compareAndExchangeN_acq_regP_regN_regN_shenandoah(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src1, iRegNsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, flagsRegCR0 cr0) %{
|
||||
match(Set res (CompareAndExchangeN mem_ptr (Binary src1 src2)));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && need_acquire_load_store(n));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0);
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0);
|
||||
format %{ "CMPXCHGW $res, $mem_ptr, $src1, $src2; as narrow oop" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm,
|
||||
@ -412,7 +324,6 @@ instruct compareAndExchangeN_acq_regP_regN_regN_shenandoah(iRegNdst res, iRegPds
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ true,
|
||||
/* is_narrow */ true,
|
||||
/* weak */ false,
|
||||
@ -421,10 +332,10 @@ instruct compareAndExchangeN_acq_regP_regN_regN_shenandoah(iRegNdst res, iRegPds
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct compareAndExchangeP_regP_regP_regP_shenandoah(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
instruct compareAndExchangeP_regP_regP_regP_shenandoah(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, flagsRegCR0 cr0) %{
|
||||
match(Set res (CompareAndExchangeP mem_ptr (Binary src1 src2)));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && !need_acquire_load_store(n));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0);
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0);
|
||||
format %{ "CMPXCHGD $res, $mem_ptr, $src1, $src2; as ptr; ptr" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm,
|
||||
@ -434,7 +345,6 @@ instruct compareAndExchangeP_regP_regP_regP_shenandoah(iRegPdst res, iRegPdst me
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ true,
|
||||
/* is_narrow */ false,
|
||||
/* weak */ false,
|
||||
@ -443,10 +353,10 @@ instruct compareAndExchangeP_regP_regP_regP_shenandoah(iRegPdst res, iRegPdst me
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct compareAndExchangeP_acq_regP_regP_regP_shenandoah(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
instruct compareAndExchangeP_acq_regP_regP_regP_shenandoah(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src1, iRegPsrc src2, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, flagsRegCR0 cr0) %{
|
||||
match(Set res (CompareAndExchangeP mem_ptr (Binary src1 src2)));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0) && need_acquire_load_store(n));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0);
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0);
|
||||
format %{ "CMPXCHGD $res, $mem_ptr, $src1, $src2; as ptr; ptr" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->compare_and_set_c2(this, masm,
|
||||
@ -456,7 +366,6 @@ instruct compareAndExchangeP_acq_regP_regP_regP_shenandoah(iRegPdst res, iRegPds
|
||||
$src2$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register,
|
||||
/* exchange */ true,
|
||||
/* is_narrow */ false,
|
||||
/* weak */ false,
|
||||
@ -465,10 +374,10 @@ instruct compareAndExchangeP_acq_regP_regP_regP_shenandoah(iRegPdst res, iRegPds
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct getAndSetP_shenandoah(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
instruct getAndSetP_shenandoah(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, flagsRegCR0 cr0) %{
|
||||
match(Set res (GetAndSetP mem_ptr src));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0);
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0);
|
||||
format %{ "GetAndSetP $res, $mem_ptr, $src" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->get_and_set_c2(this, masm,
|
||||
@ -476,16 +385,15 @@ instruct getAndSetP_shenandoah(iRegPdst res, iRegPdst mem_ptr, iRegPsrc src, iRe
|
||||
$src$$Register,
|
||||
$mem_ptr$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register);
|
||||
$tmp2$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct getAndSetN_shenandoah(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, iRegPdstNoScratch tmp3, flagsRegCR0 cr0) %{
|
||||
instruct getAndSetN_shenandoah(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src, iRegPdstNoScratch tmp1, iRegPdstNoScratch tmp2, flagsRegCR0 cr0) %{
|
||||
match(Set res (GetAndSetN mem_ptr src));
|
||||
predicate(UseShenandoahGC && (n->as_LoadStore()->barrier_data() != 0));
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr0);
|
||||
effect(TEMP_DEF res, TEMP tmp1, TEMP tmp2, KILL cr0);
|
||||
format %{ "GetAndSetN $res, $mem_ptr, $src" %}
|
||||
ins_encode %{
|
||||
ShenandoahBarrierSet::assembler()->get_and_set_c2(this, masm,
|
||||
@ -493,8 +401,7 @@ instruct getAndSetN_shenandoah(iRegNdst res, iRegPdst mem_ptr, iRegNsrc src, iRe
|
||||
$src$$Register,
|
||||
$mem_ptr$$Register,
|
||||
$tmp1$$Register,
|
||||
$tmp2$$Register,
|
||||
$tmp3$$Register);
|
||||
$tmp2$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025 SAP SE. 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
|
||||
@ -264,8 +264,6 @@ class InterpreterMacroAssembler: public MacroAssembler {
|
||||
void profile_switch_default(Register scratch1, Register scratch2);
|
||||
void profile_switch_case(Register index, Register scratch1,Register scratch2, Register scratch3);
|
||||
void profile_null_seen(Register Rscratch1, Register Rscratch2);
|
||||
void record_klass_in_profile(Register receiver, Register scratch1, Register scratch2);
|
||||
void record_klass_in_profile_helper(Register receiver, Register scratch1, Register scratch2, int start_row, Label& done);
|
||||
|
||||
// Argument and return type profiling.
|
||||
void profile_obj_type(Register obj, Register mdo_addr_base, RegisterOrConstant mdo_addr_offs, Register tmp, Register tmp2);
|
||||
|
||||
@ -1348,7 +1348,7 @@ void InterpreterMacroAssembler::profile_virtual_call(Register Rreceiver,
|
||||
test_method_data_pointer(profile_continue);
|
||||
|
||||
// Record the receiver type.
|
||||
record_klass_in_profile(Rreceiver, Rscratch1, Rscratch2);
|
||||
profile_receiver_type(Rreceiver, R28_mdx, 0, Rscratch1, Rscratch2);
|
||||
|
||||
// The method data pointer needs to be updated to reflect the new target.
|
||||
update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size()));
|
||||
@ -1367,7 +1367,7 @@ void InterpreterMacroAssembler::profile_typecheck(Register Rklass, Register Rscr
|
||||
mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());
|
||||
|
||||
// Record the object type.
|
||||
record_klass_in_profile(Rklass, Rscratch1, Rscratch2);
|
||||
profile_receiver_type(Rklass, R28_mdx, 0, Rscratch1, Rscratch2);
|
||||
}
|
||||
|
||||
// The method data pointer needs to be updated.
|
||||
@ -1481,88 +1481,6 @@ void InterpreterMacroAssembler::profile_null_seen(Register Rscratch1, Register R
|
||||
}
|
||||
}
|
||||
|
||||
void InterpreterMacroAssembler::record_klass_in_profile(Register Rreceiver,
|
||||
Register Rscratch1, Register Rscratch2) {
|
||||
assert(ProfileInterpreter, "must be profiling");
|
||||
assert_different_registers(Rreceiver, Rscratch1, Rscratch2);
|
||||
|
||||
Label done;
|
||||
record_klass_in_profile_helper(Rreceiver, Rscratch1, Rscratch2, 0, done);
|
||||
bind (done);
|
||||
}
|
||||
|
||||
void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
Register receiver, Register scratch1, Register scratch2,
|
||||
int start_row, Label& done) {
|
||||
if (TypeProfileWidth == 0) {
|
||||
increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2);
|
||||
return;
|
||||
}
|
||||
|
||||
int last_row = VirtualCallData::row_limit() - 1;
|
||||
assert(start_row <= last_row, "must be work left to do");
|
||||
// Test this row for both the receiver and for null.
|
||||
// Take any of three different outcomes:
|
||||
// 1. found receiver => increment count and goto done
|
||||
// 2. found null => keep looking for case 1, maybe allocate this cell
|
||||
// 3. found something else => keep looking for cases 1 and 2
|
||||
// Case 3 is handled by a recursive call.
|
||||
for (int row = start_row; row <= last_row; row++) {
|
||||
Label next_test;
|
||||
bool test_for_null_also = (row == start_row);
|
||||
|
||||
// See if the receiver is receiver[n].
|
||||
int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row));
|
||||
test_mdp_data_at(recvr_offset, receiver, next_test, scratch1);
|
||||
// delayed()->tst(scratch);
|
||||
|
||||
// The receiver is receiver[n]. Increment count[n].
|
||||
int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row));
|
||||
increment_mdp_data_at(count_offset, scratch1, scratch2);
|
||||
b(done);
|
||||
bind(next_test);
|
||||
|
||||
if (test_for_null_also) {
|
||||
Label found_null;
|
||||
// Failed the equality check on receiver[n]... Test for null.
|
||||
if (start_row == last_row) {
|
||||
// The only thing left to do is handle the null case.
|
||||
// Scratch1 contains test_out from test_mdp_data_at.
|
||||
cmpdi(CR0, scratch1, 0);
|
||||
beq(CR0, found_null);
|
||||
// Receiver did not match any saved receiver and there is no empty row for it.
|
||||
// Increment total counter to indicate polymorphic case.
|
||||
increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2);
|
||||
b(done);
|
||||
bind(found_null);
|
||||
break;
|
||||
}
|
||||
// Since null is rare, make it be the branch-taken case.
|
||||
cmpdi(CR0, scratch1, 0);
|
||||
beq(CR0, found_null);
|
||||
|
||||
// Put all the "Case 3" tests here.
|
||||
record_klass_in_profile_helper(receiver, scratch1, scratch2, start_row + 1, done);
|
||||
|
||||
// Found a null. Keep searching for a matching receiver,
|
||||
// but remember that this is an empty (unused) slot.
|
||||
bind(found_null);
|
||||
}
|
||||
}
|
||||
|
||||
// In the fall-through case, we found no matching receiver, but we
|
||||
// observed the receiver[start_row] is null.
|
||||
|
||||
// Fill in the receiver field and increment the count.
|
||||
int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row));
|
||||
set_mdp_data_at(recvr_offset, receiver);
|
||||
int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row));
|
||||
li(scratch1, DataLayout::counter_increment);
|
||||
set_mdp_data_at(count_offset, scratch1);
|
||||
if (start_row > 0) {
|
||||
b(done);
|
||||
}
|
||||
}
|
||||
|
||||
// Argument and return type profilig.
|
||||
// kills: tmp, tmp2, R0, CR0, CR1
|
||||
|
||||
@ -4329,6 +4329,173 @@ void MacroAssembler::multiply_to_len(Register x, Register xlen,
|
||||
bind(L_done);
|
||||
} // multiply_to_len
|
||||
|
||||
void MacroAssembler::increment_mem64(Register base, RegisterOrConstant ind_or_offs, int val, Register tmp) {
|
||||
ld(tmp, ind_or_offs, base);
|
||||
addi(tmp, tmp, val);
|
||||
std(tmp, ind_or_offs, base);
|
||||
}
|
||||
|
||||
// Handle the receiver type profile update given the "recv" klass.
|
||||
//
|
||||
// Normally updates the ReceiverData (RD) that starts at "mdp" + "mdp_offset".
|
||||
// If there are no matching or claimable receiver entries in RD, updates
|
||||
// the polymorphic counter.
|
||||
//
|
||||
// This code expected to run by either the interpreter or JIT-ed code, without
|
||||
// extra synchronization. For safety, receiver cells are claimed atomically, which
|
||||
// avoids grossly misrepresenting the profiles under concurrent updates. For speed,
|
||||
// counter updates are not atomic.
|
||||
//
|
||||
void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_offset, Register tmp1, Register tmp2) {
|
||||
assert_different_registers(recv, mdp, tmp1, tmp2);
|
||||
|
||||
int base_receiver_offset = in_bytes(ReceiverTypeData::receiver_offset(0));
|
||||
int poly_count_offset = in_bytes(CounterData::count_offset());
|
||||
int receiver_step = in_bytes(ReceiverTypeData::receiver_offset(1)) - base_receiver_offset;
|
||||
int receiver_to_count_step = in_bytes(ReceiverTypeData::receiver_count_offset(0)) - base_receiver_offset;
|
||||
|
||||
// Adjust for MDP offsets.
|
||||
base_receiver_offset += mdp_offset;
|
||||
poly_count_offset += mdp_offset;
|
||||
|
||||
#ifdef ASSERT
|
||||
// We are about to walk the MDO slots without asking for offsets.
|
||||
// Check that our math hits all the right spots.
|
||||
for (uint c = 0; c < ReceiverTypeData::row_limit(); c++) {
|
||||
int real_recv_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_offset(c));
|
||||
int real_count_offset = mdp_offset + in_bytes(ReceiverTypeData::receiver_count_offset(c));
|
||||
int offset = base_receiver_offset + receiver_step*c;
|
||||
int count_offset = offset + receiver_to_count_step;
|
||||
assert(offset == real_recv_offset, "receiver slot math");
|
||||
assert(count_offset == real_count_offset, "receiver count math");
|
||||
}
|
||||
int real_poly_count_offset = mdp_offset + in_bytes(CounterData::count_offset());
|
||||
assert(poly_count_offset == real_poly_count_offset, "poly counter math");
|
||||
#endif
|
||||
|
||||
// Corner case: no profile table. Increment poly counter and exit.
|
||||
if (ReceiverTypeData::row_limit() == 0) {
|
||||
increment_mem64(mdp, poly_count_offset, DataLayout::counter_increment, tmp1);
|
||||
return;
|
||||
}
|
||||
|
||||
Label L_loop_search_receiver, L_loop_search_empty;
|
||||
Label L_restart, L_found_recv, L_found_empty, L_count_update;
|
||||
Register offset = tmp1, count = tmp2;
|
||||
|
||||
// The code here recognizes three major cases:
|
||||
// A. Fastest: receiver found in the table
|
||||
// B. Fast: no receiver in the table, and the table is full
|
||||
// C. Slow: no receiver in the table, free slots in the table
|
||||
//
|
||||
// The case A performance is most important, as perfectly-behaved code would end up
|
||||
// there, especially with larger TypeProfileWidth. The case B performance is
|
||||
// important as well, this is where bulk of code would land for normally megamorphic
|
||||
// cases. The case C performance is not essential, its job is to deal with installation
|
||||
// races, we optimize for code density instead. Case C needs to make sure that receiver
|
||||
// rows are only claimed once. This makes sure we never overwrite a row for another
|
||||
// receiver and never duplicate the receivers in the list, making profile type-accurate.
|
||||
//
|
||||
// It is very tempting to handle these cases in a single loop, and claim the first slot
|
||||
// without checking the rest of the table. But, profiling code should tolerate free slots
|
||||
// in the table, as class unloading can clear them. After such cleanup, the receiver
|
||||
// we need might be _after_ the free slot. Therefore, we need to let at least full scan
|
||||
// to complete, before trying to install new slots. Splitting the code in several tight
|
||||
// loops also helpfully optimizes for cases A and B.
|
||||
//
|
||||
// This code is effectively:
|
||||
//
|
||||
// restart:
|
||||
// // Fastest: receiver is already installed
|
||||
// for (i = 0; i < receiver_count(); i++) {
|
||||
// if (receiver(i) == recv) goto found_recv(i);
|
||||
// }
|
||||
//
|
||||
// // Fast: no receiver, but profile is not full
|
||||
// for (i = 0; i < receiver_count(); i++) {
|
||||
// if (receiver(i) == null) goto found_null(i);
|
||||
// }
|
||||
//
|
||||
// // Slow: profile is full, polymorphic case
|
||||
// count++;
|
||||
// return
|
||||
//
|
||||
// // Slow: try to install receiver
|
||||
// found_null(i):
|
||||
// CAS(&receiver(i), null, recv);
|
||||
// goto restart
|
||||
//
|
||||
// found_recv(i):
|
||||
// *receiver_count(i)++
|
||||
//
|
||||
|
||||
if (count != noreg) {
|
||||
li(count, ReceiverTypeData::row_limit());
|
||||
}
|
||||
|
||||
bind(L_restart);
|
||||
|
||||
// Fastest: receiver is already installed
|
||||
if (count != noreg) {
|
||||
mtctr(count);
|
||||
} else {
|
||||
li(R0, ReceiverTypeData::row_limit());
|
||||
mtctr(R0);
|
||||
}
|
||||
li(offset, base_receiver_offset);
|
||||
bind(L_loop_search_receiver);
|
||||
ldx(R0, offset, mdp);
|
||||
cmpd(CR0, R0, recv);
|
||||
beq(CR0, L_found_recv);
|
||||
addi(offset, offset, receiver_step);
|
||||
bdnz(L_loop_search_receiver);
|
||||
|
||||
// Fast: no receiver, but profile is full
|
||||
if (count != noreg) {
|
||||
mtctr(count);
|
||||
} else {
|
||||
li(R0, ReceiverTypeData::row_limit());
|
||||
mtctr(R0);
|
||||
}
|
||||
li(offset, base_receiver_offset);
|
||||
bind(L_loop_search_empty);
|
||||
ldx(R0, offset, mdp);
|
||||
cmpdi(CR0, R0, 0);
|
||||
beq(CR0, L_found_empty);
|
||||
addi(offset, offset, receiver_step);
|
||||
bdnz(L_loop_search_empty);
|
||||
|
||||
// Slow: Receiver is not found and table is full.
|
||||
// Increment polymorphic counter instead of receiver slot.
|
||||
li(offset, poly_count_offset);
|
||||
b(L_count_update);
|
||||
|
||||
// Slowest: try to install receiver
|
||||
bind(L_found_empty);
|
||||
|
||||
// Atomically swing receiver slot: null -> recv.
|
||||
{
|
||||
Register receiver_addr = offset;
|
||||
add(receiver_addr, mdp, offset); // kills offset
|
||||
cmpxchgd(CR0, R0, RegisterOrConstant(0), recv, receiver_addr, MemBarNone, cmpxchgx_hint_atomic_update(),
|
||||
noreg, nullptr, /* check without ldarx first */ false, /* weak */ true);
|
||||
}
|
||||
|
||||
// CAS success means the slot now has the receiver we want. CAS failure means
|
||||
// something had claimed the slot concurrently: it can be the same receiver we want,
|
||||
// or something else. Since this is a slow path, we can optimize for code density,
|
||||
// and just restart the search from the beginning.
|
||||
b(L_restart);
|
||||
|
||||
// Found a receiver, convert its slot offset to corresponding count offset.
|
||||
bind(L_found_recv);
|
||||
addi(offset, offset, receiver_to_count_step);
|
||||
|
||||
// Finally, update the counter
|
||||
bind(L_count_update);
|
||||
increment_mem64(mdp, offset, DataLayout::counter_increment, /* temp */ (count != noreg) ? count : recv);
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
void MacroAssembler::asm_assert(AsmAssertCond cond, const char *msg) {
|
||||
Label ok;
|
||||
|
||||
@ -870,6 +870,12 @@ class MacroAssembler: public Assembler {
|
||||
Register tmp6, Register tmp7, Register tmp8, Register tmp9, Register tmp10,
|
||||
Register tmp11, Register tmp12, Register tmp13);
|
||||
|
||||
// non-atomic 64-bit memory increment by simm16
|
||||
void increment_mem64(Register base, RegisterOrConstant ind_or_offs, int val, Register tmp);
|
||||
|
||||
// Bytecode profiling (tmp2 = noreg is allowed, but then recv is killed)
|
||||
void profile_receiver_type(Register recv, Register mdp, int mdp_offset, Register tmp1, Register tmp2);
|
||||
|
||||
// Emitters for CRC32 calculation.
|
||||
// A note on invertCRC:
|
||||
// Unfortunately, internal representation of crc differs between CRC32 and CRC32C.
|
||||
|
||||
@ -3475,30 +3475,34 @@ frame %{
|
||||
// 4 what apparently works and saves us some spills.
|
||||
return_addr(STACK 4);
|
||||
|
||||
// Location of native (C/C++) and interpreter return values. This
|
||||
// is specified to be the same as Java. In the 32-bit VM, long
|
||||
// values are actually returned from native calls in O0:O1 and
|
||||
// returned to the interpreter in I0:I1. The copying to and from
|
||||
// the register pairs is done by the appropriate call and epilog
|
||||
// opcodes. This simplifies the register allocator.
|
||||
c_return_value %{
|
||||
assert((ideal_reg >= Op_RegI && ideal_reg <= Op_RegL) ||
|
||||
(ideal_reg == Op_RegN && CompressedOops::base() == nullptr && CompressedOops::shift() == 0),
|
||||
"only return normal values");
|
||||
// enum names from opcodes.hpp: Op_Node Op_Set Op_RegN Op_RegI Op_RegP Op_RegF Op_RegD Op_RegL
|
||||
static int typeToRegLo[Op_RegL+1] = { 0, 0, R3_num, R3_num, R3_num, F1_num, F1_num, R3_num };
|
||||
static int typeToRegHi[Op_RegL+1] = { 0, 0, OptoReg::Bad, R3_H_num, R3_H_num, OptoReg::Bad, F1_H_num, R3_H_num };
|
||||
return OptoRegPair(typeToRegHi[ideal_reg], typeToRegLo[ideal_reg]);
|
||||
%}
|
||||
|
||||
// Location of compiled Java return values. Same as C
|
||||
return_value %{
|
||||
assert((ideal_reg >= Op_RegI && ideal_reg <= Op_RegL) ||
|
||||
(ideal_reg == Op_RegN && CompressedOops::base() == nullptr && CompressedOops::shift() == 0),
|
||||
"only return normal values");
|
||||
// enum names from opcodes.hpp: Op_Node Op_Set Op_RegN Op_RegI Op_RegP Op_RegF Op_RegD Op_RegL
|
||||
static int typeToRegLo[Op_RegL+1] = { 0, 0, R3_num, R3_num, R3_num, F1_num, F1_num, R3_num };
|
||||
static int typeToRegHi[Op_RegL+1] = { 0, 0, OptoReg::Bad, R3_H_num, R3_H_num, OptoReg::Bad, F1_H_num, R3_H_num };
|
||||
// enum names from opcodes.hpp
|
||||
static int typeToRegLo[Op_RegL+1] = {
|
||||
0, // Op_Node
|
||||
0, // Op_Set
|
||||
R3_num, // Op_RegN
|
||||
R3_num, // Op_RegI
|
||||
R3_num, // Op_RegP
|
||||
F1_num, // Op_RegF
|
||||
F1_num, // Op_RegD
|
||||
R3_num, // Op_RegL
|
||||
};
|
||||
|
||||
static int typeToRegHi[Op_RegL+1] = {
|
||||
0, // Op_Node
|
||||
0, // Op_Set
|
||||
OptoReg::Bad, // Op_RegN
|
||||
OptoReg::Bad, // Op_RegI
|
||||
R3_H_num, // Op_RegP
|
||||
OptoReg::Bad, // Op_RegF
|
||||
F1_H_num, // Op_RegD
|
||||
R3_H_num // Op_RegL
|
||||
};
|
||||
|
||||
return OptoRegPair(typeToRegHi[ideal_reg], typeToRegLo[ideal_reg]);
|
||||
%}
|
||||
%}
|
||||
|
||||
@ -28,7 +28,6 @@
|
||||
#include "gc/shenandoah/mode/shenandoahMode.hpp"
|
||||
#include "gc/shenandoah/shenandoahBarrierSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
|
||||
#include "gc/shenandoah/shenandoahForwarding.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
|
||||
#include "gc/shenandoah/shenandoahRuntime.hpp"
|
||||
@ -174,53 +173,6 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm,
|
||||
__ bind(done);
|
||||
}
|
||||
|
||||
void ShenandoahBarrierSetAssembler::resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp) {
|
||||
assert(ShenandoahLoadRefBarrier || ShenandoahCASBarrier, "Should be enabled");
|
||||
|
||||
Label is_null;
|
||||
__ beqz(dst, is_null);
|
||||
resolve_forward_pointer_not_null(masm, dst, tmp);
|
||||
__ bind(is_null);
|
||||
}
|
||||
|
||||
// IMPORTANT: This must preserve all registers, even t0 and t1, except those explicitly
|
||||
// passed in.
|
||||
void ShenandoahBarrierSetAssembler::resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp) {
|
||||
assert(ShenandoahLoadRefBarrier || ShenandoahCASBarrier, "Should be enabled");
|
||||
// The below loads the mark word, checks if the lowest two bits are
|
||||
// set, and if so, clear the lowest two bits and copy the result
|
||||
// to dst. Otherwise it leaves dst alone.
|
||||
// Implementing this is surprisingly awkward. I do it here by:
|
||||
// - Inverting the mark word
|
||||
// - Test lowest two bits == 0
|
||||
// - If so, set the lowest two bits
|
||||
// - Invert the result back, and copy to dst
|
||||
RegSet saved_regs = RegSet::of(t2);
|
||||
bool borrow_reg = (tmp == noreg);
|
||||
if (borrow_reg) {
|
||||
// No free registers available. Make one useful.
|
||||
tmp = t0;
|
||||
if (tmp == dst) {
|
||||
tmp = t1;
|
||||
}
|
||||
saved_regs += RegSet::of(tmp);
|
||||
}
|
||||
|
||||
assert_different_registers(tmp, dst, t2);
|
||||
__ push_reg(saved_regs, sp);
|
||||
|
||||
Label done;
|
||||
__ ld(tmp, Address(dst, oopDesc::mark_offset_in_bytes()));
|
||||
__ xori(tmp, tmp, -1); // eon with 0 is equivalent to XOR with -1
|
||||
__ andi(t2, tmp, markWord::lock_mask_in_place);
|
||||
__ bnez(t2, done);
|
||||
__ ori(tmp, tmp, markWord::marked_value);
|
||||
__ xori(dst, tmp, -1); // eon with 0 is equivalent to XOR with -1
|
||||
__ bind(done);
|
||||
|
||||
__ pop_reg(saved_regs, sp);
|
||||
}
|
||||
|
||||
void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm,
|
||||
Register dst,
|
||||
Address load_addr,
|
||||
@ -481,90 +433,6 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl
|
||||
__ bind(done);
|
||||
}
|
||||
|
||||
// Special Shenandoah CAS implementation that handles false negatives due
|
||||
// to concurrent evacuation. The service is more complex than a
|
||||
// traditional CAS operation because the CAS operation is intended to
|
||||
// succeed if the reference at addr exactly matches expected or if the
|
||||
// reference at addr holds a pointer to a from-space object that has
|
||||
// been relocated to the location named by expected. There are two
|
||||
// races that must be addressed:
|
||||
// a) A parallel thread may mutate the contents of addr so that it points
|
||||
// to a different object. In this case, the CAS operation should fail.
|
||||
// b) A parallel thread may heal the contents of addr, replacing a
|
||||
// from-space pointer held in addr with the to-space pointer
|
||||
// representing the new location of the object.
|
||||
// Upon entry to cmpxchg_oop, it is assured that new_val equals null
|
||||
// or it refers to an object that is not being evacuated out of
|
||||
// from-space, or it refers to the to-space version of an object that
|
||||
// is being evacuated out of from-space.
|
||||
//
|
||||
// By default the value held in the result register following execution
|
||||
// of the generated code sequence is 0 to indicate failure of CAS,
|
||||
// non-zero to indicate success. If is_cae, the result is the value most
|
||||
// recently fetched from addr rather than a boolean success indicator.
|
||||
//
|
||||
// Clobbers t0, t1
|
||||
void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm,
|
||||
Register addr,
|
||||
Register expected,
|
||||
Register new_val,
|
||||
Assembler::Aqrl acquire,
|
||||
Assembler::Aqrl release,
|
||||
bool is_cae,
|
||||
Register result) {
|
||||
bool is_narrow = UseCompressedOops;
|
||||
Assembler::operand_size size = is_narrow ? Assembler::uint32 : Assembler::int64;
|
||||
|
||||
assert_different_registers(addr, expected, t0, t1);
|
||||
assert_different_registers(addr, new_val, t0, t1);
|
||||
|
||||
Label retry, success, fail, done;
|
||||
|
||||
__ bind(retry);
|
||||
|
||||
// Step1: Try to CAS.
|
||||
__ cmpxchg(addr, expected, new_val, size, acquire, release, /* result */ t1);
|
||||
|
||||
// If success, then we are done.
|
||||
__ beq(expected, t1, success);
|
||||
|
||||
// Step2: CAS failed, check the forwarded pointer.
|
||||
__ mv(t0, t1);
|
||||
|
||||
if (is_narrow) {
|
||||
__ decode_heap_oop(t0, t0);
|
||||
}
|
||||
resolve_forward_pointer(masm, t0);
|
||||
|
||||
__ encode_heap_oop(t0, t0);
|
||||
|
||||
// Report failure when the forwarded oop was not expected.
|
||||
__ bne(t0, expected, fail);
|
||||
|
||||
// Step 3: CAS again using the forwarded oop.
|
||||
__ cmpxchg(addr, t1, new_val, size, acquire, release, /* result */ t0);
|
||||
|
||||
// Retry when failed.
|
||||
__ bne(t0, t1, retry);
|
||||
|
||||
__ bind(success);
|
||||
if (is_cae) {
|
||||
__ mv(result, expected);
|
||||
} else {
|
||||
__ mv(result, 1);
|
||||
}
|
||||
__ j(done);
|
||||
|
||||
__ bind(fail);
|
||||
if (is_cae) {
|
||||
__ mv(result, t0);
|
||||
} else {
|
||||
__ mv(result, zr);
|
||||
}
|
||||
|
||||
__ bind(done);
|
||||
}
|
||||
|
||||
void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
|
||||
Register start, Register count, Register tmp) {
|
||||
assert(ShenandoahCardBarrier, "Did you mean to enable ShenandoahCardBarrier?");
|
||||
@ -792,7 +660,7 @@ void ShenandoahBarrierSetAssembler::load_c2(const MachNode* node, MacroAssembler
|
||||
void ShenandoahBarrierSetAssembler::store_c2(const MachNode* node, MacroAssembler* masm, Address dst, bool dst_narrow,
|
||||
Register src, bool src_narrow, Register tmp1, Register tmp2, Register tmp3) {
|
||||
|
||||
ShenandoahBarrierStubC2::store_pre(masm, node, tmp1, dst, tmp2, tmp3, dst_narrow);
|
||||
ShenandoahBarrierStubC2::store_pre(masm, node, dst, tmp1, tmp2, tmp3, dst_narrow);
|
||||
|
||||
// Do the actual store
|
||||
if (dst_narrow) {
|
||||
@ -820,7 +688,7 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac
|
||||
const Assembler::Aqrl release = Assembler::rl;
|
||||
const Assembler::operand_size size = narrow ? Assembler::uint32 : Assembler::int64;
|
||||
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, tmp1, Address(addr), tmp2, tmp3, narrow);
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, Address(addr), tmp1, tmp2, tmp3, narrow);
|
||||
|
||||
// CAS!
|
||||
__ cmpxchg(addr, oldval, newval, size, acquire, release, /* result */ res, !exchange /* result_as_bool */);
|
||||
@ -832,7 +700,7 @@ void ShenandoahBarrierSetAssembler::get_and_set_c2(const MachNode* node, MacroAs
|
||||
Register newval, Register addr, Register tmp1, Register tmp2, Register tmp3, bool is_acquire) {
|
||||
const bool is_narrow = node->bottom_type()->isa_narrowoop();
|
||||
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, tmp1, Address(addr, 0), tmp2, tmp3, is_narrow);
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, Address(addr, 0), tmp1, tmp2, tmp3, is_narrow);
|
||||
|
||||
if (is_narrow) {
|
||||
if (is_acquire) {
|
||||
@ -1044,12 +912,9 @@ void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) {
|
||||
if (c_rarg0 == _obj) {
|
||||
__ la(c_rarg1, _addr);
|
||||
} else if (c_rarg1 == _obj) {
|
||||
// Set up arguments in reverse, and then flip them
|
||||
__ la(c_rarg0, _addr);
|
||||
// flip them
|
||||
__ mv(_tmp1, c_rarg0);
|
||||
__ mv(c_rarg0, c_rarg1);
|
||||
__ mv(c_rarg1, _tmp1);
|
||||
__ mv(_tmp1, c_rarg1);
|
||||
__ la(c_rarg1, _addr);
|
||||
__ mv(c_rarg0, _tmp1);
|
||||
} else {
|
||||
assert_different_registers(c_rarg1, _obj);
|
||||
__ la(c_rarg1, _addr);
|
||||
|
||||
@ -54,8 +54,6 @@ private:
|
||||
|
||||
void card_barrier(MacroAssembler* masm, Register obj);
|
||||
|
||||
void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg);
|
||||
void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg);
|
||||
void load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, DecoratorSet decorators);
|
||||
|
||||
void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
|
||||
@ -81,8 +79,6 @@ public:
|
||||
Register obj, Register tmp, Label& slowpath);
|
||||
virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj,
|
||||
Register tmp, Label& slow_path);
|
||||
void cmpxchg_oop(MacroAssembler* masm, Register addr, Register expected, Register new_val,
|
||||
Assembler::Aqrl acquire, Assembler::Aqrl release, bool is_cae, Register result);
|
||||
|
||||
#ifdef COMPILER1
|
||||
void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub);
|
||||
|
||||
@ -120,10 +120,10 @@ define_pd_global(intx, InlineSmallCode, 1000);
|
||||
product(bool, UseZvbb, false, EXPERIMENTAL, "Use Zvbb instructions") \
|
||||
product(bool, UseZvbc, false, EXPERIMENTAL, "Use Zvbc instructions") \
|
||||
product(bool, UseZvfh, false, DIAGNOSTIC, "Use Zvfh instructions") \
|
||||
product(bool, UseZvkn, false, EXPERIMENTAL, \
|
||||
product(bool, UseZvkg, false, DIAGNOSTIC, "Use Zvkg instructions") \
|
||||
product(bool, UseZvkn, false, DIAGNOSTIC, \
|
||||
"Use Zvkn group extension, Zvkned, Zvknhb, Zvkb, Zvkt") \
|
||||
product(bool, UseCtxFencei, false, EXPERIMENTAL, \
|
||||
"Use PR_RISCV_CTX_SW_FENCEI_ON to avoid explicit icache flush") \
|
||||
product(bool, UseZvkg, false, EXPERIMENTAL, "Use Zvkg instructions")
|
||||
"Use PR_RISCV_CTX_SW_FENCEI_ON to avoid explicit icache flush")
|
||||
|
||||
#endif // CPU_RISCV_GLOBALS_RISCV_HPP
|
||||
|
||||
@ -1221,6 +1221,7 @@ void InterpreterMacroAssembler::notify_method_exit(
|
||||
// track stack depth. If it is possible to enter interp_only_mode we add
|
||||
// the code to check if the event should be sent.
|
||||
if (mode == NotifyJVMTI && (JvmtiExport::can_post_interpreter_events() || JvmtiExport::can_post_frame_pop())) {
|
||||
Label L;
|
||||
// Note: frame::interpreter_frame_result has a dependency on how the
|
||||
// method result is saved across the call to post_method_exit. If this
|
||||
// is changed then the interpreter_frame_result implementation will
|
||||
@ -1228,8 +1229,18 @@ void InterpreterMacroAssembler::notify_method_exit(
|
||||
|
||||
// template interpreter will leave the result on the top of the stack.
|
||||
push(state);
|
||||
|
||||
ld(t1, Address(xthread, JavaThread::jvmti_thread_state_offset()));
|
||||
beqz(t1, L); // if (thread->jvmti_thread_state() == nullptr) exit;
|
||||
|
||||
lwu(t1, Address(t1, JvmtiThreadState::frame_pop_cnt_offset()));
|
||||
lwu(t0, Address(xthread, JavaThread::interp_only_mode_offset()));
|
||||
orr(t0, t0, t1);
|
||||
beqz(t0, L);
|
||||
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit));
|
||||
bind(L);
|
||||
pop(state);
|
||||
}
|
||||
|
||||
|
||||
@ -593,7 +593,7 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
Register offset = t1;
|
||||
|
||||
Label L_loop_search_receiver, L_loop_search_empty;
|
||||
Label L_restart, L_found_recv, L_found_empty, L_polymorphic, L_count_update;
|
||||
Label L_restart, L_found_recv, L_found_empty, L_count_update;
|
||||
|
||||
// The code here recognizes three major cases:
|
||||
// A. Fastest: receiver found in the table
|
||||
@ -623,21 +623,20 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
// if (receiver(i) == recv) goto found_recv(i);
|
||||
// }
|
||||
//
|
||||
// // Fast: no receiver, but profile is full
|
||||
// // Fast: no receiver, but profile is not full
|
||||
// for (i = 0; i < receiver_count(); i++) {
|
||||
// if (receiver(i) == null) goto found_null(i);
|
||||
// }
|
||||
// goto polymorphic
|
||||
//
|
||||
// // Slow: profile is full, polymorphic case
|
||||
// count++;
|
||||
// return
|
||||
//
|
||||
// // Slow: try to install receiver
|
||||
// found_null(i):
|
||||
// CAS(&receiver(i), null, recv);
|
||||
// goto restart
|
||||
//
|
||||
// polymorphic:
|
||||
// count++;
|
||||
// return
|
||||
//
|
||||
// found_recv(i):
|
||||
// *receiver_count(i)++
|
||||
//
|
||||
@ -654,7 +653,7 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
sub(t0, offset, end_receiver_offset);
|
||||
bnez(t0, L_loop_search_receiver);
|
||||
|
||||
// Fast: no receiver, but profile is full
|
||||
// Fast: no receiver, but profile is not full
|
||||
mv(offset, base_receiver_offset);
|
||||
bind(L_loop_search_empty);
|
||||
add(t0, mdp, offset);
|
||||
@ -663,9 +662,13 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
add(offset, offset, receiver_step);
|
||||
sub(t0, offset, end_receiver_offset);
|
||||
bnez(t0, L_loop_search_empty);
|
||||
j(L_polymorphic);
|
||||
|
||||
// Slow: try to install receiver
|
||||
// Slow: Receiver is not found and table is full.
|
||||
// Increment polymorphic counter instead of receiver slot.
|
||||
mv(offset, poly_count_offset);
|
||||
j(L_count_update);
|
||||
|
||||
// Slowest: try to install receiver
|
||||
bind(L_found_empty);
|
||||
|
||||
// Atomically swing receiver slot: null -> recv.
|
||||
@ -683,16 +686,11 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
// and just restart the search from the beginning.
|
||||
j(L_restart);
|
||||
|
||||
// Counter updates:
|
||||
// Increment polymorphic counter instead of receiver slot.
|
||||
bind(L_polymorphic);
|
||||
mv(offset, poly_count_offset);
|
||||
j(L_count_update);
|
||||
|
||||
// Found a receiver, convert its slot offset to corresponding count offset.
|
||||
bind(L_found_recv);
|
||||
add(offset, offset, receiver_to_count_step);
|
||||
|
||||
// Finally, update the counter
|
||||
bind(L_count_update);
|
||||
add(t1, mdp, offset);
|
||||
increment(Address(t1), DataLayout::counter_increment);
|
||||
@ -4178,50 +4176,6 @@ void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool in_nm
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::cmpxchgptr(Register oldv, Register newv, Register addr, Register tmp,
|
||||
Label &succeed, Label *fail) {
|
||||
assert_different_registers(addr, tmp, t0);
|
||||
assert_different_registers(newv, tmp, t0);
|
||||
assert_different_registers(oldv, tmp, t0);
|
||||
|
||||
// oldv holds comparison value
|
||||
// newv holds value to write in exchange
|
||||
// addr identifies memory word to compare against/update
|
||||
if (UseZacas) {
|
||||
mv(tmp, oldv);
|
||||
atomic_cas(tmp, newv, addr, Assembler::int64, Assembler::aq, Assembler::rl);
|
||||
beq(tmp, oldv, succeed);
|
||||
} else {
|
||||
Label retry_load, nope;
|
||||
bind(retry_load);
|
||||
// Load reserved from the memory location
|
||||
load_reserved(tmp, addr, int64, Assembler::aqrl);
|
||||
// Fail and exit if it is not what we expect
|
||||
bne(tmp, oldv, nope);
|
||||
// If the store conditional succeeds, tmp will be zero
|
||||
store_conditional(tmp, newv, addr, int64, Assembler::rl);
|
||||
beqz(tmp, succeed);
|
||||
// Retry only when the store conditional failed
|
||||
j(retry_load);
|
||||
|
||||
bind(nope);
|
||||
}
|
||||
|
||||
// neither amocas nor lr/sc have an implied barrier in the failing case
|
||||
membar(AnyAny);
|
||||
|
||||
mv(oldv, tmp);
|
||||
if (fail != nullptr) {
|
||||
j(*fail);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::cmpxchg_obj_header(Register oldv, Register newv, Register obj, Register tmp,
|
||||
Label &succeed, Label *fail) {
|
||||
assert(oopDesc::mark_offset_in_bytes() == 0, "assumption");
|
||||
cmpxchgptr(oldv, newv, obj, tmp, succeed, fail);
|
||||
}
|
||||
|
||||
void MacroAssembler::load_reserved(Register dst,
|
||||
Register addr,
|
||||
Assembler::operand_size size,
|
||||
|
||||
@ -1203,8 +1203,6 @@ public:
|
||||
|
||||
#undef INSN_ENTRY_RELOC
|
||||
|
||||
void cmpxchg_obj_header(Register oldv, Register newv, Register obj, Register tmp, Label &succeed, Label *fail);
|
||||
void cmpxchgptr(Register oldv, Register newv, Register addr, Register tmp, Label &succeed, Label *fail);
|
||||
void cmpxchg(Register addr, Register expected,
|
||||
Register new_val,
|
||||
Assembler::operand_size size,
|
||||
|
||||
@ -2003,9 +2003,22 @@ void InterpreterMacroAssembler::notify_method_exit(bool native_method,
|
||||
// depth. If it is possible to enter interp_only_mode we add
|
||||
// the code to check if the event should be sent.
|
||||
if (mode == NotifyJVMTI && (JvmtiExport::can_post_interpreter_events() || JvmtiExport::can_post_frame_pop())) {
|
||||
NearLabel jvmti_post_done;
|
||||
|
||||
// if (thread->jvmti_thread_state() == nullptr) exit;
|
||||
z_ltg(Z_R1_scratch, Address(Z_thread, JavaThread::jvmti_thread_state_offset()));
|
||||
z_brz(jvmti_post_done);
|
||||
|
||||
// if (interp_only_mode() == false && frame_pop_cnt() == 0) exit;
|
||||
z_lgf(Z_R1_scratch, Address(Z_R1_scratch, JvmtiThreadState::frame_pop_cnt_offset()));
|
||||
z_o(Z_R1_scratch, Address(Z_thread, JavaThread::interp_only_mode_offset()));
|
||||
z_brz(jvmti_post_done);
|
||||
|
||||
if (!native_method) push(state); // see frame::interpreter_frame_result()
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit));
|
||||
if (!native_method) pop(state);
|
||||
|
||||
bind(jvmti_post_done);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -298,7 +298,7 @@ void frame::patch_pc(Thread* thread, address pc) {
|
||||
|
||||
#ifdef ASSERT
|
||||
{
|
||||
frame f(this->sp(), this->unextended_sp(), this->fp(), pc);
|
||||
frame f(sp(), unextended_sp(), fp(), pc, cb(), oop_map(), is_heap_frame());
|
||||
assert(f.is_deoptimized_frame() == this->is_deoptimized_frame() && f.pc() == this->pc() && f.raw_pc() == this->raw_pc(),
|
||||
"must be (f.is_deoptimized_frame(): %d this->is_deoptimized_frame(): %d "
|
||||
"f.pc(): " INTPTR_FORMAT " this->pc(): " INTPTR_FORMAT " f.raw_pc(): " INTPTR_FORMAT " this->raw_pc(): " INTPTR_FORMAT ")",
|
||||
|
||||
@ -28,7 +28,6 @@
|
||||
#include "gc/shenandoah/mode/shenandoahMode.hpp"
|
||||
#include "gc/shenandoah/shenandoahBarrierSet.hpp"
|
||||
#include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
|
||||
#include "gc/shenandoah/shenandoahForwarding.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
|
||||
#include "gc/shenandoah/shenandoahRuntime.hpp"
|
||||
@ -512,151 +511,6 @@ void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembl
|
||||
__ bind(done);
|
||||
}
|
||||
|
||||
// Special Shenandoah CAS implementation that handles false negatives
|
||||
// due to concurrent evacuation.
|
||||
void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm,
|
||||
Register res, Address addr, Register oldval, Register newval,
|
||||
bool exchange, Register tmp1, Register tmp2) {
|
||||
assert(ShenandoahCASBarrier, "Should only be used when CAS barrier is enabled");
|
||||
assert(oldval == rax, "must be in rax for implicit use in cmpxchg");
|
||||
assert_different_registers(oldval, tmp1, tmp2);
|
||||
assert_different_registers(newval, tmp1, tmp2);
|
||||
|
||||
Label L_success, L_failure;
|
||||
|
||||
// Remember oldval for retry logic below
|
||||
if (UseCompressedOops) {
|
||||
__ movl(tmp1, oldval);
|
||||
} else {
|
||||
__ movptr(tmp1, oldval);
|
||||
}
|
||||
|
||||
// Step 1. Fast-path.
|
||||
//
|
||||
// Try to CAS with given arguments. If successful, then we are done.
|
||||
|
||||
if (UseCompressedOops) {
|
||||
__ lock();
|
||||
__ cmpxchgl(newval, addr);
|
||||
} else {
|
||||
__ lock();
|
||||
__ cmpxchgptr(newval, addr);
|
||||
}
|
||||
__ jcc(Assembler::equal, L_success);
|
||||
|
||||
// Step 2. CAS had failed. This may be a false negative.
|
||||
//
|
||||
// The trouble comes when we compare the to-space pointer with the from-space
|
||||
// pointer to the same object. To resolve this, it will suffice to resolve
|
||||
// the value from memory -- this will give both to-space pointers.
|
||||
// If they mismatch, then it was a legitimate failure.
|
||||
//
|
||||
// Before reaching to resolve sequence, see if we can avoid the whole shebang
|
||||
// with filters.
|
||||
|
||||
// Filter: when offending in-memory value is null, the failure is definitely legitimate
|
||||
__ testptr(oldval, oldval);
|
||||
__ jcc(Assembler::zero, L_failure);
|
||||
|
||||
// Filter: when heap is stable, the failure is definitely legitimate
|
||||
const Register thread = r15_thread;
|
||||
Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
|
||||
__ testb(gc_state, ShenandoahHeap::HAS_FORWARDED);
|
||||
__ jcc(Assembler::zero, L_failure);
|
||||
|
||||
if (UseCompressedOops) {
|
||||
__ movl(tmp2, oldval);
|
||||
__ decode_heap_oop(tmp2);
|
||||
} else {
|
||||
__ movptr(tmp2, oldval);
|
||||
}
|
||||
|
||||
// Decode offending in-memory value.
|
||||
// Test if-forwarded
|
||||
__ testb(Address(tmp2, oopDesc::mark_offset_in_bytes()), markWord::marked_value);
|
||||
__ jcc(Assembler::noParity, L_failure); // When odd number of bits, then not forwarded
|
||||
__ jcc(Assembler::zero, L_failure); // When it is 00, then also not forwarded
|
||||
|
||||
// Load and mask forwarding pointer
|
||||
__ movptr(tmp2, Address(tmp2, oopDesc::mark_offset_in_bytes()));
|
||||
__ shrptr(tmp2, 2);
|
||||
__ shlptr(tmp2, 2);
|
||||
|
||||
if (UseCompressedOops) {
|
||||
__ decode_heap_oop(tmp1); // decode for comparison
|
||||
}
|
||||
|
||||
// Now we have the forwarded offender in tmp2.
|
||||
// Compare and if they don't match, we have legitimate failure
|
||||
__ cmpptr(tmp1, tmp2);
|
||||
__ jcc(Assembler::notEqual, L_failure);
|
||||
|
||||
// Step 3. Need to fix the memory ptr before continuing.
|
||||
//
|
||||
// At this point, we have from-space oldval in the register, and its to-space
|
||||
// address is in tmp2. Let's try to update it into memory. We don't care if it
|
||||
// succeeds or not. If it does, then the retrying CAS would see it and succeed.
|
||||
// If this fixup fails, this means somebody else beat us to it, and necessarily
|
||||
// with to-space ptr store. We still have to do the retry, because the GC might
|
||||
// have updated the reference for us.
|
||||
|
||||
if (UseCompressedOops) {
|
||||
__ encode_heap_oop(tmp2); // previously decoded at step 2.
|
||||
}
|
||||
|
||||
if (UseCompressedOops) {
|
||||
__ lock();
|
||||
__ cmpxchgl(tmp2, addr);
|
||||
} else {
|
||||
__ lock();
|
||||
__ cmpxchgptr(tmp2, addr);
|
||||
}
|
||||
|
||||
// Step 4. Try to CAS again.
|
||||
//
|
||||
// This is guaranteed not to have false negatives, because oldval is definitely
|
||||
// to-space, and memory pointer is to-space as well. Nothing is able to store
|
||||
// from-space ptr into memory anymore. Make sure oldval is restored, after being
|
||||
// garbled during retries.
|
||||
//
|
||||
if (UseCompressedOops) {
|
||||
__ movl(oldval, tmp2);
|
||||
} else {
|
||||
__ movptr(oldval, tmp2);
|
||||
}
|
||||
|
||||
if (UseCompressedOops) {
|
||||
__ lock();
|
||||
__ cmpxchgl(newval, addr);
|
||||
} else {
|
||||
__ lock();
|
||||
__ cmpxchgptr(newval, addr);
|
||||
}
|
||||
if (!exchange) {
|
||||
__ jccb(Assembler::equal, L_success); // fastpath, peeking into Step 5, no need to jump
|
||||
}
|
||||
|
||||
// Step 5. If we need a boolean result out of CAS, set the flag appropriately.
|
||||
// and promote the result. Note that we handle the flag from both the 1st and 2nd CAS.
|
||||
// Otherwise, failure witness for CAE is in oldval on all paths, and we can return.
|
||||
|
||||
if (exchange) {
|
||||
__ bind(L_failure);
|
||||
__ bind(L_success);
|
||||
} else {
|
||||
assert(res != noreg, "need result register");
|
||||
|
||||
Label exit;
|
||||
__ bind(L_failure);
|
||||
__ xorptr(res, res);
|
||||
__ jmpb(exit);
|
||||
|
||||
__ bind(L_success);
|
||||
__ movptr(res, 1);
|
||||
__ bind(exit);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PRODUCT
|
||||
#define BLOCK_COMMENT(str) /* nothing */
|
||||
#else
|
||||
@ -926,7 +780,7 @@ void ShenandoahBarrierSetAssembler::store_c2(const MachNode* node, MacroAssemble
|
||||
Register src, bool src_narrow,
|
||||
Register tmp) {
|
||||
|
||||
ShenandoahBarrierStubC2::store_pre(masm, node, tmp, dst, noreg, noreg, dst_narrow);
|
||||
ShenandoahBarrierStubC2::store_pre(masm, node, dst, tmp, noreg, noreg, dst_narrow);
|
||||
|
||||
// Need to encode into tmp, because we cannot clobber src.
|
||||
if (dst_narrow && !src_narrow) {
|
||||
@ -961,7 +815,7 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac
|
||||
assert_different_registers(oldval, tmp, addr.base(), addr.index());
|
||||
assert_different_registers(newval, tmp, addr.base(), addr.index());
|
||||
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, tmp, addr, noreg, noreg, narrow);
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp, noreg, noreg, narrow);
|
||||
|
||||
// CAS!
|
||||
__ lock();
|
||||
@ -982,7 +836,7 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac
|
||||
void ShenandoahBarrierSetAssembler::get_and_set_c2(const MachNode* node, MacroAssembler* masm, Register newval, Address addr, Register tmp, bool narrow) {
|
||||
assert_different_registers(newval, tmp, addr.base(), addr.index());
|
||||
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, tmp, addr, noreg, noreg, narrow);
|
||||
ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp, noreg, noreg, narrow);
|
||||
|
||||
if (narrow) {
|
||||
__ xchgl(newval, addr);
|
||||
|
||||
@ -60,9 +60,6 @@ public:
|
||||
|
||||
void load_reference_barrier(MacroAssembler* masm, Register dst, Address src, DecoratorSet decorators);
|
||||
|
||||
void cmpxchg_oop(MacroAssembler* masm,
|
||||
Register res, Address addr, Register oldval, Register newval,
|
||||
bool exchange, Register tmp1, Register tmp2);
|
||||
virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
|
||||
Register src, Register dst, Register count);
|
||||
virtual void arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
|
||||
|
||||
@ -4901,7 +4901,7 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
Register offset = rscratch1;
|
||||
|
||||
Label L_loop_search_receiver, L_loop_search_empty;
|
||||
Label L_restart, L_found_recv, L_found_empty, L_polymorphic, L_count_update;
|
||||
Label L_restart, L_found_recv, L_found_empty, L_count_update;
|
||||
|
||||
// The code here recognizes three major cases:
|
||||
// A. Fastest: receiver found in the table
|
||||
@ -4931,21 +4931,20 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
// if (receiver(i) == recv) goto found_recv(i);
|
||||
// }
|
||||
//
|
||||
// // Fast: no receiver, but profile is full
|
||||
// // Fast: no receiver, but profile is not full
|
||||
// for (i = 0; i < receiver_count(); i++) {
|
||||
// if (receiver(i) == null) goto found_null(i);
|
||||
// }
|
||||
// goto polymorphic
|
||||
//
|
||||
// // Slow: profile is full, polymorphic case
|
||||
// count++;
|
||||
// return
|
||||
//
|
||||
// // Slow: try to install receiver
|
||||
// found_null(i):
|
||||
// CAS(&receiver(i), null, recv);
|
||||
// goto restart
|
||||
//
|
||||
// polymorphic:
|
||||
// count++;
|
||||
// return
|
||||
//
|
||||
// found_recv(i):
|
||||
// *receiver_count(i)++
|
||||
//
|
||||
@ -4961,7 +4960,7 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
cmpptr(offset, end_receiver_offset);
|
||||
jccb(Assembler::notEqual, L_loop_search_receiver);
|
||||
|
||||
// Fast: no receiver, but profile is full
|
||||
// Fast: no receiver, but profile is not full
|
||||
movptr(offset, base_receiver_offset);
|
||||
bind(L_loop_search_empty);
|
||||
cmpptr(Address(mdp, offset, Address::times_ptr), NULL_WORD);
|
||||
@ -4969,9 +4968,13 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
addptr(offset, receiver_step);
|
||||
cmpptr(offset, end_receiver_offset);
|
||||
jccb(Assembler::notEqual, L_loop_search_empty);
|
||||
jmpb(L_polymorphic);
|
||||
|
||||
// Slow: try to install receiver
|
||||
// Slow: Receiver is not found and table is full.
|
||||
// Increment polymorphic counter instead of receiver slot.
|
||||
movptr(offset, poly_count_offset);
|
||||
jmpb(L_count_update);
|
||||
|
||||
// Slowest: try to install receiver
|
||||
bind(L_found_empty);
|
||||
|
||||
// Atomically swing receiver slot: null -> recv.
|
||||
@ -5023,17 +5026,11 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_
|
||||
// and just restart the search from the beginning.
|
||||
jmpb(L_restart);
|
||||
|
||||
// Counter updates:
|
||||
|
||||
// Increment polymorphic counter instead of receiver slot.
|
||||
bind(L_polymorphic);
|
||||
movptr(offset, poly_count_offset);
|
||||
jmpb(L_count_update);
|
||||
|
||||
// Found a receiver, convert its slot offset to corresponding count offset.
|
||||
bind(L_found_recv);
|
||||
addptr(offset, receiver_to_count_step);
|
||||
|
||||
// Finally, update the counter
|
||||
bind(L_count_update);
|
||||
addptr(Address(mdp, offset, Address::times_ptr), DataLayout::counter_increment);
|
||||
}
|
||||
|
||||
@ -4904,6 +4904,11 @@ void StubGenerator::generate_compiler_stubs() {
|
||||
StubRoutines::_intpoly_assign = generate_intpoly_assign();
|
||||
}
|
||||
|
||||
if (UseIntPoly25519Intrinsics) {
|
||||
StubRoutines::_intpoly_mult_25519 = generate_intpoly_mult_25519();
|
||||
StubRoutines::_intpoly_square_25519 = generate_intpoly_square_25519();
|
||||
}
|
||||
|
||||
if (UseMD5Intrinsics) {
|
||||
StubRoutines::_md5_implCompress = generate_md5_implCompress(StubId::stubgen_md5_implCompress_id);
|
||||
StubRoutines::_md5_implCompressMB = generate_md5_implCompress(StubId::stubgen_md5_implCompressMB_id);
|
||||
|
||||
@ -496,6 +496,9 @@ class StubGenerator: public StubCodeGenerator {
|
||||
address generate_intpoly_montgomeryMult_P256();
|
||||
address generate_intpoly_assign();
|
||||
|
||||
address generate_intpoly_mult_25519();
|
||||
address generate_intpoly_square_25519();
|
||||
|
||||
// SHA3 stubs
|
||||
void generate_sha3_stubs();
|
||||
|
||||
|
||||
@ -631,6 +631,27 @@ address generate_kyberInverseNtt_avx512(StubGenerator *stubgen,
|
||||
}
|
||||
|
||||
// Kyber multiply polynomials in the NTT domain.
|
||||
// Implements
|
||||
// static int implKyberNttMult(
|
||||
// short[] result, short[] ntta, short[] nttb, short[] zetas) {}
|
||||
//
|
||||
// The actual algorithm that is used here differs from the one in the Java
|
||||
// implementation, it uses Montgomery multiplications instead of Barrett
|
||||
// reduction, but the end result modulo MLKEM_Q is the same. This is the
|
||||
// Java equivalent of this intrinsic implementation:
|
||||
// static void implKyberNttMultJava(short[] result, short[] ntta, short[] nttb) {
|
||||
// for (int m = 0; m < ML_KEM_N / 2; m++) {
|
||||
// int a0 = ntta[2 * m];
|
||||
// int a1 = ntta[2 * m + 1];
|
||||
// int b0 = nttb[2 * m];
|
||||
// int b1 = nttb[2 * m + 1];
|
||||
// int r = montMul(a0, b0) +
|
||||
// montMul(montMul(a1, b1), MONT_ZETAS_FOR_NTT_MULT[m]);
|
||||
// result[2 * m] = (short) montMul(r, MONT_R_SQUARE_MOD_Q);
|
||||
// result[2 * m + 1] = (short) montMul(
|
||||
// (montMul(a0, b1) + montMul(a1, b0)), MONT_R_SQUARE_MOD_Q);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// result (short[256]) = c_rarg0
|
||||
// ntta (short[256]) = c_rarg1
|
||||
|
||||
306
src/hotspot/cpu/x86/stubGenerator_x86_64_poly25519.cpp
Normal file
306
src/hotspot/cpu/x86/stubGenerator_x86_64_poly25519.cpp
Normal file
@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
#include "macroAssembler_x86.hpp"
|
||||
#include "stubGenerator_x86_64.hpp"
|
||||
|
||||
#define __ _masm->
|
||||
|
||||
const int32_t term = 19;
|
||||
const int32_t limbs = 5;
|
||||
const int32_t bpl = 51;
|
||||
const int32_t rem = 64 - bpl;
|
||||
const uint64_t MASK = 0x7FFFFFFFFFFFF;
|
||||
const uint64_t CARRY_ADD = 0x4000000000000;
|
||||
|
||||
// Multiplication operation for polynomial arithmetic in Curve25519.
|
||||
//
|
||||
// This is the same algorithm as used in Java, except we use pseudo-Mersenne
|
||||
// reduction to reduce register pressure instead of using the full 10 columns
|
||||
// in Java.
|
||||
void multiply_25519_scalar(const Register aLimbs, const Register bLimbs, const Register rLimbs, Register c[], Register bArg, Register d, Register b, Register mask, MacroAssembler* _masm) {
|
||||
|
||||
for (int i = 0; i < limbs; i++) {
|
||||
__ xorq(c[i], c[i]);
|
||||
}
|
||||
__ mov64(mask, MASK);
|
||||
__ movq(bArg, bLimbs);
|
||||
|
||||
// Perform high/low multiplication with signed 5x51 bit limbs
|
||||
for (int i = 0; i < limbs; i++) {
|
||||
__ movq(b, Address(bArg, i * 8));
|
||||
for (int j = 0; j < limbs; j++) {
|
||||
__ movq(rax, Address(aLimbs, j * 8));
|
||||
__ imulq(b); // rdx:rax = a * b
|
||||
__ movq(d, rax);
|
||||
__ andq(d, mask);
|
||||
__ shrq(rax, bpl);
|
||||
__ shlq(rdx, rem);
|
||||
__ orq(rax, rdx);
|
||||
// Fold in pseudo-Mersenne reduction
|
||||
if ((i + j + 1) >= limbs) {
|
||||
__ imulq(rax, rax, term);
|
||||
}
|
||||
if ((i + j) >= limbs) {
|
||||
__ imulq(d, d, term);
|
||||
}
|
||||
__ addq(c[(i + j) % limbs], d);
|
||||
__ addq(c[(i + j + 1) % limbs], rax);
|
||||
}
|
||||
}
|
||||
|
||||
// Carry-add with reduction from high limb
|
||||
Register carry = bArg;
|
||||
__ mov64(mask, CARRY_ADD);
|
||||
__ movq(carry, mask);
|
||||
|
||||
// Limb 3
|
||||
__ addq(carry, c[3]);
|
||||
__ sarq(carry, bpl);
|
||||
__ addq(c[4], carry);
|
||||
__ shlq(carry, bpl);
|
||||
__ subq(c[3], carry);
|
||||
|
||||
// Limb 4
|
||||
__ movq(carry, mask);
|
||||
__ addq(carry, c[4]);
|
||||
__ sarq(carry, bpl);
|
||||
|
||||
// Reduce high order limb and fold back into low order limb
|
||||
__ mov64(rax, term);
|
||||
__ imulq(carry);
|
||||
__ addq(c[0], rax);
|
||||
|
||||
__ shlq(carry, bpl);
|
||||
__ subq(c[4], carry);
|
||||
|
||||
// Limbs 0 - 3
|
||||
for (int i = 0; i < (limbs - 1); i++) {
|
||||
__ movq(carry, mask);
|
||||
__ addq(carry, c[i]);
|
||||
__ sarq(carry, bpl);
|
||||
__ addq(c[i + 1], carry);
|
||||
__ shlq(carry, bpl);
|
||||
__ subq(c[i], carry);
|
||||
}
|
||||
|
||||
__ pop_ppx(rdx);
|
||||
|
||||
for (int i = 0; i < limbs; i++) {
|
||||
__ movq(Address(rLimbs, i * 8), c[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Squaring operation for polynomial arithmetic in Curve25519.
|
||||
//
|
||||
// This is the same algorithm as used in Java, except we use pseudo-Mersenne
|
||||
// reduction to reduce register pressure instead of using the full 10 columns
|
||||
// in Java.
|
||||
void square_25519_scalar(const Register aLimbs, const Register rLimbs, Register c[], Register aArg, Register d, Register carry, Register mask, MacroAssembler* _masm) {
|
||||
|
||||
for (int i = 0; i < limbs; i++) {
|
||||
__ xorq(c[i], c[i]);
|
||||
}
|
||||
__ mov64(mask, MASK);
|
||||
|
||||
// Perform high/low multiplication with signed 5x51 bit limbs
|
||||
for (int i = 0; i < limbs; i++) {
|
||||
__ movq(aArg, Address(aLimbs, i * 8));
|
||||
__ movq(rax, aArg);
|
||||
__ imulq(aArg); // rdx:rax = a[j] * a[i]
|
||||
__ movq(d, rax);
|
||||
__ andq(d, mask);
|
||||
__ shrq(rax, bpl);
|
||||
__ shlq(rdx, rem);
|
||||
__ orq(rax, rdx); // rax = dd
|
||||
if ((i * 2 + 1) >= limbs) {
|
||||
__ imulq(rax, rax, term);
|
||||
}
|
||||
if ((i * 2) >= limbs) {
|
||||
__ imulq(d, d, term);
|
||||
}
|
||||
__ addq(c[(i * 2) % limbs], d);
|
||||
__ addq(c[(i * 2 + 1) % limbs], rax);
|
||||
for (int j = i + 1; j < limbs; j++) {
|
||||
__ movq(rax, Address(aLimbs, j * 8));
|
||||
__ imulq(aArg); // rdx:rax = a * a
|
||||
__ movq(d, rax);
|
||||
__ andq(d, mask);
|
||||
__ shlq(d, 1);
|
||||
__ shrq(rax, bpl);
|
||||
__ shlq(rdx, rem);
|
||||
__ orq(rax, rdx); // rax = dd
|
||||
__ shlq(rax, 1);
|
||||
if ((j + i + 1) >= limbs) {
|
||||
__ imulq(rax, rax, term);
|
||||
}
|
||||
if ((j + i) >= limbs) {
|
||||
__ imulq(d, d, term);
|
||||
}
|
||||
__ addq(c[(i + j) % limbs], d);
|
||||
__ addq(c[(i + j + 1) % limbs], rax);
|
||||
}
|
||||
}
|
||||
|
||||
// Carry-add with reduction from high limb
|
||||
// Limb 3
|
||||
__ mov64(mask, CARRY_ADD);
|
||||
__ movq(carry, mask);
|
||||
__ addq(carry, c[3]);
|
||||
__ sarq(carry, bpl);
|
||||
__ addq(c[4], carry);
|
||||
__ shlq(carry, bpl);
|
||||
__ subq(c[3], carry);
|
||||
|
||||
// Limb 4
|
||||
__ movq(carry, mask);
|
||||
__ addq(carry, c[4]);
|
||||
__ sarq(carry, bpl);
|
||||
|
||||
// Reduce high order limb and fold back into low order limb
|
||||
__ mov64(rax, term);
|
||||
__ imulq(carry);
|
||||
__ addq(c[0], rax);
|
||||
|
||||
__ shlq(carry, bpl);
|
||||
__ subq(c[4], carry);
|
||||
|
||||
// Limbs 0 - 3
|
||||
for (int i = 0; i < (limbs - 1); i++) {
|
||||
__ movq(carry, mask);
|
||||
__ addq(carry, c[i]);
|
||||
__ sarq(carry, bpl);
|
||||
__ addq(c[i + 1], carry);
|
||||
__ shlq(carry, bpl);
|
||||
__ subq(c[i], carry);
|
||||
}
|
||||
|
||||
__ pop_ppx(rdx);
|
||||
|
||||
for (int i = 0; i < limbs; i++) {
|
||||
__ movq(Address(rLimbs, i * 8), c[i]);
|
||||
}
|
||||
}
|
||||
|
||||
address StubGenerator::generate_intpoly_mult_25519() {
|
||||
StubId stub_id = StubId::stubgen_intpoly_mult_25519_id;
|
||||
int entry_count = StubInfo::entry_count(stub_id);
|
||||
assert(entry_count == 1, "sanity check");
|
||||
address start = load_archive_data(stub_id);
|
||||
if (start != nullptr) {
|
||||
return start;
|
||||
}
|
||||
__ align(CodeEntryAlignment);
|
||||
StubCodeMark mark(this, stub_id);
|
||||
start = __ pc();
|
||||
__ enter();
|
||||
|
||||
// Register Map
|
||||
const Register aLimbs = c_rarg0; // rdi | rcx
|
||||
const Register bLimbs = c_rarg1; // rsi | rdx
|
||||
const Register rLimbs = c_rarg2; // rdx | r8
|
||||
|
||||
Register c[] = {r9, r10, r11, r12, r13};
|
||||
Register bArg = r14;
|
||||
Register d = r15;
|
||||
Register b = rbp;
|
||||
Register mask = rbx;
|
||||
|
||||
__ push_ppx(rbp);
|
||||
__ push_ppx(rbx);
|
||||
__ push_ppx(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r14);
|
||||
__ push_ppx(r15);
|
||||
__ push_ppx(rdx);
|
||||
|
||||
multiply_25519_scalar(aLimbs, bLimbs, rLimbs, c, bArg, d, b, mask, _masm);
|
||||
|
||||
// __ pop_ppx(rdx); // restored in the helper already
|
||||
__ pop_ppx(r15);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r13);
|
||||
__ pop_ppx(r12);
|
||||
__ pop_ppx(rbx);
|
||||
__ pop_ppx(rbp);
|
||||
|
||||
__ leave();
|
||||
__ ret(0);
|
||||
|
||||
// Record the stub entry and end
|
||||
store_archive_data(stub_id, start, __ pc());
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
address StubGenerator::generate_intpoly_square_25519() {
|
||||
StubId stub_id = StubId::stubgen_intpoly_square_25519_id;
|
||||
int entry_count = StubInfo::entry_count(stub_id);
|
||||
assert(entry_count == 1, "sanity check");
|
||||
address start = load_archive_data(stub_id);
|
||||
if (start != nullptr) {
|
||||
return start;
|
||||
}
|
||||
__ align(CodeEntryAlignment);
|
||||
StubCodeMark mark(this, stub_id);
|
||||
start = __ pc();
|
||||
__ enter();
|
||||
|
||||
// Register Map
|
||||
const Register aLimbs = c_rarg0; // rdi | rcx
|
||||
const Register rLimbs = c_rarg1; // rsi | rdx
|
||||
Register c[] = {r9, r10, r11, r12, r13};
|
||||
Register aArg = r14;
|
||||
Register d = r15;
|
||||
Register carry = rbp;
|
||||
Register mask = rbx;
|
||||
|
||||
__ push_ppx(rbp);
|
||||
__ push_ppx(rbx);
|
||||
__ push_ppx(r12);
|
||||
__ push_ppx(r13);
|
||||
__ push_ppx(r14);
|
||||
__ push_ppx(r15);
|
||||
__ push_ppx(rdx);
|
||||
|
||||
square_25519_scalar(aLimbs, rLimbs, c, aArg, d, carry, mask, _masm);
|
||||
|
||||
// __ pop_ppx(rdx); // restored in the helper already
|
||||
__ pop_ppx(r15);
|
||||
__ pop_ppx(r14);
|
||||
__ pop_ppx(r13);
|
||||
__ pop_ppx(r12);
|
||||
__ pop_ppx(rbx);
|
||||
__ pop_ppx(rbp);
|
||||
|
||||
__ leave();
|
||||
__ ret(0);
|
||||
|
||||
// Record the stub entry and end
|
||||
store_archive_data(stub_id, start, __ pc());
|
||||
|
||||
return start;
|
||||
}
|
||||
#undef __
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 2025, 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.
|
||||
*
|
||||
@ -676,7 +676,7 @@ address StubGenerator::generate_intpoly_assign() {
|
||||
// KNOWN Lengths:
|
||||
// MontgomeryIntPolynP256: 5 = 4 + 1
|
||||
// IntegerPolynomial1305: 5 = 4 + 1
|
||||
// IntegerPolynomial25519: 10 = 8 + 2
|
||||
// IntegerPolynomial25519: 5 = 4 + 1
|
||||
// IntegerPolynomialP256: 10 = 8 + 2
|
||||
// Curve25519OrderField: 10 = 8 + 2
|
||||
// Curve25519OrderField: 10 = 8 + 2
|
||||
|
||||
@ -1407,6 +1407,10 @@ void VM_Version::get_processor_features() {
|
||||
FLAG_SET_DEFAULT(UseIntPolyIntrinsics, false);
|
||||
}
|
||||
|
||||
if (FLAG_IS_DEFAULT(UseIntPoly25519Intrinsics)) {
|
||||
UseIntPoly25519Intrinsics = true;
|
||||
}
|
||||
|
||||
if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) {
|
||||
UseMultiplyToLenIntrinsic = true;
|
||||
}
|
||||
|
||||
@ -13959,6 +13959,7 @@ instruct orL_rReg_ndd(rRegL dst, rRegL src1, rRegL src2, rFlagsReg cr)
|
||||
|
||||
// Use any_RegP to match R15 (TLS register) without spilling.
|
||||
instruct orL_rReg_castP2X(rRegL dst, any_RegP src, rFlagsReg cr) %{
|
||||
predicate(!UseAPX);
|
||||
match(Set dst (OrL dst (CastP2X src)));
|
||||
effect(KILL cr);
|
||||
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
||||
@ -13971,6 +13972,7 @@ instruct orL_rReg_castP2X(rRegL dst, any_RegP src, rFlagsReg cr) %{
|
||||
%}
|
||||
|
||||
instruct orL_rReg_castP2X_ndd(rRegL dst, any_RegP src1, any_RegP src2, rFlagsReg cr) %{
|
||||
predicate(UseAPX);
|
||||
match(Set dst (OrL src1 (CastP2X src2)));
|
||||
effect(KILL cr);
|
||||
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
|
||||
|
||||
@ -2183,6 +2183,10 @@ void os::print_os_info(outputStream* st) {
|
||||
st->cr();
|
||||
}
|
||||
|
||||
if (os::Linux::print_numa_info(st)) {
|
||||
st->cr();
|
||||
}
|
||||
|
||||
VM_Version::print_platform_virtualization_info(st);
|
||||
|
||||
os::Linux::print_steal_info(st);
|
||||
@ -2622,6 +2626,97 @@ bool os::Linux::print_container_info(outputStream* st) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#define SYS_DEVICES_NODE "/sys/devices/system/node"
|
||||
|
||||
static size_t read_sysfs_file(const char* path, char* buf, size_t sz) {
|
||||
FILE* f = os::fopen(path, "r");
|
||||
if (f == nullptr) return 0;
|
||||
size_t n = fread(buf, 1, sz - 1, f);
|
||||
fclose(f);
|
||||
buf[n] = '\0';
|
||||
while (n > 0 && (buf[n-1] == '\n' || buf[n-1] == '\r')) buf[--n] = '\0';
|
||||
return n;
|
||||
}
|
||||
|
||||
static void print_numa_memory_info(outputStream* st, int node) {
|
||||
char path[256];
|
||||
char line[256];
|
||||
long long mem_total = -1;
|
||||
long long mem_free = -1;
|
||||
os::snprintf_checked(path, sizeof(path), SYS_DEVICES_NODE "/node%d/meminfo", node);
|
||||
FILE* f = os::fopen(path, "r");
|
||||
if (f == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), f) != nullptr) {
|
||||
long long mval;
|
||||
if (sscanf(line, "Node %*d MemTotal: %lld kB", &mval) == 1) mem_total = mval;
|
||||
if (sscanf(line, "Node %*d MemFree: %lld kB", &mval) == 1) mem_free = mval;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (mem_total >= 0) { st->print_cr("mem size: %lld kB", mem_total); }
|
||||
if (mem_free >= 0) { st->print_cr("mem free: %lld kB", mem_free); }
|
||||
}
|
||||
|
||||
static void print_numa_cpu_list(outputStream* st, int node) {
|
||||
char path[256];
|
||||
char buf[1024];
|
||||
os::snprintf_checked(path, sizeof(path), SYS_DEVICES_NODE "/node%d/cpulist", node);
|
||||
if (read_sysfs_file(path, buf, sizeof(buf)) > 0) {
|
||||
st->print_cr("cpus: %s", buf);
|
||||
} else {
|
||||
st->print_cr("cpus: (unavailable)");
|
||||
}
|
||||
}
|
||||
|
||||
bool os::Linux::print_numa_info(outputStream* st) {
|
||||
if (!UseNUMA) {
|
||||
// If NUMA optimizations are not enabled we don't print anything
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
if (read_sysfs_file("/sys/devices/system/node/online", buf, sizeof(buf)) > 0) {
|
||||
st->print_cr("NUMA nodes online: %s", buf);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
int node_count = 0;
|
||||
|
||||
if (nindex_to_node() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int node: *nindex_to_node()) {
|
||||
char nodepath[256];
|
||||
os::snprintf_checked(nodepath, sizeof(nodepath), SYS_DEVICES_NODE "/node%d", node);
|
||||
DIR* currd = os::opendir(nodepath);
|
||||
if (currd == nullptr) continue;
|
||||
if (first) {
|
||||
st->cr();
|
||||
first = false;
|
||||
}
|
||||
os::closedir(currd);
|
||||
|
||||
st->print_cr("NUMA node %d", node);
|
||||
StreamIndentor si(st);
|
||||
print_numa_cpu_list(st, node);
|
||||
print_numa_memory_info(st, node);
|
||||
node_count++;
|
||||
}
|
||||
|
||||
if (node_count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
st->print_cr("Total NUMA node count: %d", node_count);
|
||||
return true;
|
||||
}
|
||||
|
||||
void os::Linux::print_steal_info(outputStream* st) {
|
||||
if (has_initial_tick_info) {
|
||||
CPUPerfTicks pticks;
|
||||
|
||||
@ -77,6 +77,7 @@ class os::Linux {
|
||||
static void print_proc_sys_info(outputStream* st);
|
||||
static bool print_ld_preload_file(outputStream* st);
|
||||
static void print_uptime_info(outputStream* st);
|
||||
static bool print_numa_info(outputStream* st);
|
||||
|
||||
public:
|
||||
struct CPUPerfTicks {
|
||||
|
||||
@ -167,11 +167,11 @@ void os::check_core_dump_prerequisites(char* buffer, size_t bufferSize, bool che
|
||||
}
|
||||
}
|
||||
|
||||
bool os::committed_in_range(address start, size_t size, address& committed_start, size_t& committed_size) {
|
||||
bool os::first_resident_in_range(address start, size_t size, address& resident_start, size_t& resident_size) {
|
||||
|
||||
#ifdef _AIX
|
||||
committed_start = start;
|
||||
committed_size = size;
|
||||
resident_start = start;
|
||||
resident_size = size;
|
||||
return true;
|
||||
#else
|
||||
|
||||
@ -188,10 +188,10 @@ bool os::committed_in_range(address start, size_t size, address& committed_start
|
||||
assert(is_aligned(start, page_sz), "Start address must be page aligned");
|
||||
assert(is_aligned(size, page_sz), "Size must be page aligned");
|
||||
|
||||
committed_start = nullptr;
|
||||
resident_start = nullptr;
|
||||
|
||||
int loops = checked_cast<int>((pages + stripe - 1) / stripe);
|
||||
int committed_pages = 0;
|
||||
int resident_pages = 0;
|
||||
address loop_base = start;
|
||||
bool found_range = false;
|
||||
|
||||
@ -210,7 +210,7 @@ bool os::committed_in_range(address start, size_t size, address& committed_start
|
||||
|
||||
// During shutdown, some memory goes away without properly notifying NMT,
|
||||
// E.g. ConcurrentGCThread/WatcherThread can exit without deleting thread object.
|
||||
// Bailout and return as not committed for now.
|
||||
// Bailout and return as not resident for now.
|
||||
if (mincore_return_value == -1 && errno == ENOMEM) {
|
||||
return false;
|
||||
}
|
||||
@ -224,32 +224,32 @@ bool os::committed_in_range(address start, size_t size, address& committed_start
|
||||
assert(mincore_return_value == 0, "Range must be valid");
|
||||
// Process this stripe
|
||||
for (uintx vecIdx = 0; vecIdx < pages_to_query; vecIdx ++) {
|
||||
if ((vec[vecIdx] & 0x01) == 0) { // not committed
|
||||
if ((vec[vecIdx] & 0x01) == 0) { // not resident
|
||||
// End of current contiguous region
|
||||
if (committed_start != nullptr) {
|
||||
if (resident_start != nullptr) {
|
||||
found_range = true;
|
||||
break;
|
||||
}
|
||||
} else { // committed
|
||||
} else { // resident
|
||||
// Start of region
|
||||
if (committed_start == nullptr) {
|
||||
committed_start = loop_base + page_sz * vecIdx;
|
||||
if (resident_start == nullptr) {
|
||||
resident_start = loop_base + page_sz * vecIdx;
|
||||
}
|
||||
committed_pages ++;
|
||||
resident_pages ++;
|
||||
}
|
||||
}
|
||||
|
||||
loop_base += pages_to_query * page_sz;
|
||||
}
|
||||
|
||||
if (committed_start != nullptr) {
|
||||
assert(committed_pages > 0, "Must have committed region");
|
||||
assert(committed_pages <= int(size / page_sz), "Can not commit more than it has");
|
||||
assert(committed_start >= start && committed_start < start + size, "Out of range");
|
||||
committed_size = page_sz * committed_pages;
|
||||
if (resident_start != nullptr) {
|
||||
assert(resident_pages > 0, "Must have a resident region");
|
||||
assert(resident_pages <= int(size / page_sz), "Resident size exceeds region size");
|
||||
assert(resident_start >= start && resident_start < start + size, "Out of range");
|
||||
resident_size = page_sz * resident_pages;
|
||||
return true;
|
||||
} else {
|
||||
assert(committed_pages == 0, "Should not have committed region");
|
||||
assert(resident_pages == 0, "Should not have a resident region");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -243,6 +243,16 @@ static LPVOID virtualAllocExNuma(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSiz
|
||||
return result;
|
||||
}
|
||||
|
||||
void* os::win32::lookup_kernelbase_symbol(const char* name) {
|
||||
// Pass a small ebuf so dll_load logs failures, but don't use it here to avoid redundancy.
|
||||
char ebuf[1024];
|
||||
static void* const handle = os::dll_load("KernelBase", ebuf, sizeof(ebuf));
|
||||
if (handle == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return os::dll_lookup(handle, name);
|
||||
}
|
||||
|
||||
// Logging wrapper for MapViewOfFileEx
|
||||
static LPVOID mapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh,
|
||||
DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap, LPVOID lpBaseAddress) {
|
||||
@ -465,36 +475,63 @@ void os::current_stack_base_and_size(address* stack_base, size_t* stack_size) {
|
||||
*stack_size = size;
|
||||
}
|
||||
|
||||
bool os::committed_in_range(address start, size_t size, address& committed_start, size_t& committed_size) {
|
||||
MEMORY_BASIC_INFORMATION minfo;
|
||||
committed_start = nullptr;
|
||||
committed_size = 0;
|
||||
address top = start + size;
|
||||
const address start_addr = start;
|
||||
while (start < top) {
|
||||
VirtualQuery(start, &minfo, sizeof(minfo));
|
||||
if ((minfo.State & MEM_COMMIT) == 0) { // not committed
|
||||
if (committed_start != nullptr) {
|
||||
break;
|
||||
}
|
||||
} else { // committed
|
||||
if (committed_start == nullptr) {
|
||||
committed_start = start;
|
||||
}
|
||||
size_t offset = start - (address)minfo.BaseAddress;
|
||||
committed_size += minfo.RegionSize - offset;
|
||||
}
|
||||
start = (address)minfo.BaseAddress + minfo.RegionSize;
|
||||
}
|
||||
bool os::first_resident_in_range(address start, size_t size, address& resident_start, size_t& resident_size) {
|
||||
constexpr size_t stripe = 1024; // query this many pages each time
|
||||
PSAPI_WORKING_SET_EX_INFORMATION wsinfo[stripe];
|
||||
|
||||
if (committed_start == nullptr) {
|
||||
assert(committed_size == 0, "Sanity");
|
||||
return false;
|
||||
} else {
|
||||
assert(committed_start >= start_addr && committed_start < top, "Out of range");
|
||||
// current region may go beyond the limit, trim to the limit
|
||||
committed_size = MIN2(committed_size, size_t(top - committed_start));
|
||||
size_t page_sz = os::vm_page_size();
|
||||
uintx pages_left = size / page_sz;
|
||||
|
||||
assert(is_aligned(start, page_sz), "Start address must be page aligned");
|
||||
assert(is_aligned(size, page_sz), "Size must be page aligned");
|
||||
|
||||
resident_start = nullptr;
|
||||
|
||||
uintx loops = (pages_left + stripe - 1) / stripe;
|
||||
uintx resident_pages = 0;
|
||||
address pos = start;
|
||||
bool found_range = false;
|
||||
|
||||
for (uintx index = 0; index < loops && !found_range; index++) {
|
||||
assert(pages_left > 0, "Nothing to do");
|
||||
uintx pages_to_query = MIN2(pages_left, stripe);
|
||||
pages_left -= pages_to_query;
|
||||
|
||||
for (uintx i = 0; i < pages_to_query; i++) {
|
||||
wsinfo[i].VirtualAddress = (PVOID)(pos + i * page_sz);
|
||||
}
|
||||
|
||||
BOOL success = QueryWorkingSetEx(GetCurrentProcess(), wsinfo, pages_to_query * sizeof(PSAPI_WORKING_SET_EX_INFORMATION));
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uintx i = 0; i < pages_to_query; i++) {
|
||||
if (wsinfo[i].VirtualAttributes.Valid == 0) {
|
||||
if (resident_start != nullptr) {
|
||||
found_range = true;
|
||||
break;
|
||||
}
|
||||
// Still searching for start of resident region
|
||||
} else {
|
||||
if (resident_start == nullptr) {
|
||||
// Found first resident page in region
|
||||
resident_start = pos + i * page_sz;
|
||||
}
|
||||
resident_pages++;
|
||||
}
|
||||
}
|
||||
pos += pages_to_query * page_sz;
|
||||
}
|
||||
if (resident_start != nullptr) {
|
||||
assert(resident_pages > 0, "Must have a resident region");
|
||||
assert(resident_pages <= size / page_sz, "Resident size exceeds region size");
|
||||
assert(resident_start >= start && resident_start < start + size, "Out of range");
|
||||
resident_size = page_sz * resident_pages;
|
||||
return true;
|
||||
} else {
|
||||
assert(resident_pages == 0, "Should not have a resident region");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3223,9 +3260,9 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) {
|
||||
assert(fd != -1, "File descriptor is not valid");
|
||||
|
||||
HANDLE fh = (HANDLE)_get_osfhandle(fd);
|
||||
HANDLE fileMapping = CreateFileMapping(fh, nullptr, PAGE_READWRITE,
|
||||
HANDLE file_mapping = CreateFileMapping(fh, nullptr, PAGE_READWRITE,
|
||||
(DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), nullptr);
|
||||
if (fileMapping == nullptr) {
|
||||
if (file_mapping == nullptr) {
|
||||
if (GetLastError() == ERROR_DISK_FULL) {
|
||||
vm_exit_during_initialization(err_msg("Could not allocate sufficient disk space for Java heap"));
|
||||
}
|
||||
@ -3236,9 +3273,9 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LPVOID addr = mapViewOfFileEx(fileMapping, FILE_MAP_WRITE, 0, 0, size, base);
|
||||
LPVOID addr = mapViewOfFileEx(file_mapping, FILE_MAP_WRITE, 0, 0, size, base);
|
||||
|
||||
CloseHandle(fileMapping);
|
||||
CloseHandle(file_mapping);
|
||||
|
||||
return (char*)addr;
|
||||
}
|
||||
@ -3251,40 +3288,75 @@ char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, in
|
||||
return map_memory_to_file(base, size, fd);
|
||||
}
|
||||
|
||||
// VirtualAlloc2 / MapViewOfFile3 (Windows 1803+). Resolved in os::init_2() via lookup_kernelbase_symbol.
|
||||
os::win32::VirtualAlloc2Fn os::win32::VirtualAlloc2 = nullptr;
|
||||
|
||||
os::win32::MapViewOfFile3Fn os::win32::MapViewOfFile3 = nullptr;
|
||||
|
||||
static bool is_VirtualAlloc2_supported() {
|
||||
return os::win32::VirtualAlloc2 != nullptr;
|
||||
}
|
||||
|
||||
static bool is_MapViewOfFile3_supported() {
|
||||
return os::win32::MapViewOfFile3 != nullptr;
|
||||
}
|
||||
|
||||
// Multiple threads can race in this code but it's not possible to unmap small sections of
|
||||
// virtual space to get requested alignment, like posix-like os's.
|
||||
// Windows prevents multiple thread from remapping over each other so this loop is thread-safe.
|
||||
static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int file_desc, MemTag mem_tag) {
|
||||
static char* reserve_memory_aligned(size_t size, size_t alignment, MemTag mem_tag) {
|
||||
assert(is_aligned(alignment, os::vm_allocation_granularity()),
|
||||
"Alignment must be a multiple of allocation granularity (page size)");
|
||||
"Alignment must be a multiple of allocation granularity");
|
||||
assert(is_aligned(size, os::vm_allocation_granularity()),
|
||||
"Size must be a multiple of allocation granularity (page size)");
|
||||
"Size must be a multiple of allocation granularity");
|
||||
|
||||
size_t extra_size = size + alignment;
|
||||
assert(extra_size >= size, "overflow, size is too large to allow alignment");
|
||||
|
||||
char* aligned_base = nullptr;
|
||||
static const int max_attempts = 20;
|
||||
constexpr int max_attempts = 20;
|
||||
|
||||
for (int attempt = 0; attempt < max_attempts && aligned_base == nullptr; attempt ++) {
|
||||
char* extra_base = file_desc != -1 ? os::map_memory_to_file(extra_size, file_desc, mem_tag) :
|
||||
os::reserve_memory(extra_size, mem_tag);
|
||||
char* extra_base = os::reserve_memory(extra_size, mem_tag);
|
||||
if (extra_base == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
// Do manual alignment
|
||||
aligned_base = align_up(extra_base, alignment);
|
||||
os::release_memory(extra_base, extra_size);
|
||||
|
||||
if (file_desc != -1) {
|
||||
os::unmap_memory(extra_base, extra_size);
|
||||
} else {
|
||||
os::release_memory(extra_base, extra_size);
|
||||
// A racing thread may have taken this region instead of us, which is why we loop and retry.
|
||||
aligned_base = os::attempt_reserve_memory_at(aligned_base, size, mem_tag);
|
||||
}
|
||||
|
||||
assert(aligned_base != nullptr,
|
||||
"Did not manage to reserve after %d attempts (size %zu, alignment %zu)", max_attempts, size, alignment);
|
||||
|
||||
return aligned_base;
|
||||
}
|
||||
|
||||
// Similar to reserve_memory_aligned, other reservation/mapping requests can race with this function.
|
||||
static char* map_memory_aligned(size_t size, size_t alignment, int file_desc, MemTag mem_tag) {
|
||||
assert(is_aligned(alignment, os::vm_allocation_granularity()),
|
||||
"Alignment must be a multiple of allocation granularity");
|
||||
assert(is_aligned(size, os::vm_allocation_granularity()),
|
||||
"Size must be a multiple of allocation granularity");
|
||||
|
||||
size_t extra_size = size + alignment;
|
||||
assert(extra_size >= size, "overflow, size is too large to allow alignment");
|
||||
|
||||
char* aligned_base = nullptr;
|
||||
constexpr int max_attempts = 20;
|
||||
|
||||
for (int attempt = 0; attempt < max_attempts && aligned_base == nullptr; attempt ++) {
|
||||
char* extra_base = os::map_memory_to_file(extra_size, file_desc, mem_tag);
|
||||
if (extra_base == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
aligned_base = align_up(extra_base, alignment);
|
||||
os::unmap_memory(extra_base, extra_size);
|
||||
|
||||
// Attempt to map, into the just vacated space, the slightly smaller aligned area.
|
||||
// Which may fail, hence the loop.
|
||||
aligned_base = file_desc != -1 ? os::attempt_map_memory_to_file_at(aligned_base, size, file_desc, mem_tag) :
|
||||
os::attempt_reserve_memory_at(aligned_base, size, mem_tag);
|
||||
// A racing thread may have taken this region instead of us, which is why we loop and retry.
|
||||
aligned_base = os::attempt_map_memory_to_file_at(aligned_base, size, file_desc, mem_tag);
|
||||
}
|
||||
|
||||
assert(aligned_base != nullptr,
|
||||
@ -3293,6 +3365,84 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi
|
||||
return aligned_base;
|
||||
}
|
||||
|
||||
// MapViewOfFile3 supports alignment natively.
|
||||
static char* map_memory_aligned_va2(size_t size, size_t alignment, int file_desc, MemTag mem_tag) {
|
||||
assert(file_desc != -1, "File descriptor should not be -1");
|
||||
assert(is_aligned(alignment, os::vm_allocation_granularity()),
|
||||
"Alignment must be a multiple of allocation granularity");
|
||||
assert(is_aligned(size, os::vm_allocation_granularity()),
|
||||
"Size must be a multiple of allocation granularity");
|
||||
|
||||
MEM_ADDRESS_REQUIREMENTS requirements = {0};
|
||||
requirements.Alignment = alignment;
|
||||
|
||||
MEM_EXTENDED_PARAMETER param = {0};
|
||||
param.Type = MemExtendedParameterAddressRequirements;
|
||||
param.Pointer = &requirements;
|
||||
|
||||
char* aligned_base = nullptr;
|
||||
|
||||
// File-backed aligned mapping.
|
||||
HANDLE fh = (HANDLE)_get_osfhandle(file_desc);
|
||||
HANDLE file_mapping = CreateFileMapping(fh, nullptr, PAGE_READWRITE,(DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), nullptr);
|
||||
DWORD err = GetLastError();
|
||||
if (file_mapping != nullptr) {
|
||||
aligned_base = (char*)os::win32::MapViewOfFile3(
|
||||
file_mapping,
|
||||
GetCurrentProcess(),
|
||||
nullptr, // let the system choose an aligned address
|
||||
0, // offset
|
||||
size,
|
||||
0, // no special allocation type flags
|
||||
PAGE_READWRITE,
|
||||
¶m, 1);
|
||||
err = GetLastError();
|
||||
CloseHandle(file_mapping);
|
||||
}
|
||||
|
||||
if (aligned_base != nullptr) {
|
||||
assert(is_aligned(aligned_base, alignment), "Result must be aligned");
|
||||
MemTracker::record_virtual_memory_reserve_and_commit(aligned_base, size, CALLER_PC, mem_tag);
|
||||
return aligned_base;
|
||||
}
|
||||
log_trace(os)("Aligned allocation via MapViewOfFile3 failed, falling back to retry loop. GetLastError->%lu.", err);
|
||||
return map_memory_aligned(size, alignment, file_desc, mem_tag);
|
||||
}
|
||||
|
||||
// VirtualAlloc2 supports alignment natively.
|
||||
static char* reserve_memory_aligned_va2(size_t size, size_t alignment, MemTag mem_tag) {
|
||||
assert(is_aligned(alignment, os::vm_allocation_granularity()),
|
||||
"Alignment must be a multiple of allocation granularity");
|
||||
assert(is_aligned(size, os::vm_allocation_granularity()),
|
||||
"Size must be a multiple of allocation granularity");
|
||||
|
||||
MEM_ADDRESS_REQUIREMENTS requirements = {0};
|
||||
requirements.Alignment = alignment;
|
||||
|
||||
MEM_EXTENDED_PARAMETER param = {0};
|
||||
param.Type = MemExtendedParameterAddressRequirements;
|
||||
param.Pointer = &requirements;
|
||||
|
||||
char* aligned_base = nullptr;
|
||||
|
||||
// Anonymous aligned reservation.
|
||||
aligned_base = (char*)os::win32::VirtualAlloc2(
|
||||
GetCurrentProcess(),
|
||||
nullptr, // let the system choose an aligned address
|
||||
size,
|
||||
MEM_RESERVE,
|
||||
PAGE_READWRITE,
|
||||
¶m, 1);
|
||||
|
||||
if (aligned_base != nullptr) {
|
||||
assert(is_aligned(aligned_base, alignment), "Result must be aligned");
|
||||
MemTracker::record_virtual_memory_reserve(aligned_base, size, CALLER_PC, mem_tag);
|
||||
return aligned_base;
|
||||
}
|
||||
log_trace(os)("Aligned allocation via VirtualAlloc2 failed, falling back to retry loop. GetLastError->%lu.", GetLastError());
|
||||
return reserve_memory_aligned(size, alignment, mem_tag);
|
||||
}
|
||||
|
||||
size_t os::commit_memory_limit() {
|
||||
BOOL is_in_job_object = false;
|
||||
BOOL res = IsProcessInJob(GetCurrentProcess(), nullptr, &is_in_job_object);
|
||||
@ -3340,11 +3490,17 @@ size_t os::reserve_memory_limit() {
|
||||
|
||||
char* os::reserve_memory_aligned(size_t size, size_t alignment, MemTag mem_tag, bool exec) {
|
||||
// exec can be ignored
|
||||
return map_or_reserve_memory_aligned(size, alignment, -1/* file_desc */, mem_tag);
|
||||
if (is_VirtualAlloc2_supported()) {
|
||||
return reserve_memory_aligned_va2(size, alignment, mem_tag);
|
||||
}
|
||||
return reserve_memory_aligned(size, alignment, mem_tag);
|
||||
}
|
||||
|
||||
char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MemTag mem_tag) {
|
||||
return map_or_reserve_memory_aligned(size, alignment, fd, mem_tag);
|
||||
if (is_MapViewOfFile3_supported()) {
|
||||
return map_memory_aligned_va2(size, alignment, fd, mem_tag);
|
||||
}
|
||||
return map_memory_aligned(size, alignment, fd, mem_tag);
|
||||
}
|
||||
|
||||
char* os::pd_reserve_memory(size_t bytes, bool exec) {
|
||||
@ -4561,21 +4717,21 @@ jint os::init_2(void) {
|
||||
|
||||
// Lookup SetThreadDescription - the docs state we must use runtime-linking of
|
||||
// kernelbase.dll, so that is what we do.
|
||||
HINSTANCE _kernelbase = LoadLibrary(TEXT("kernelbase.dll"));
|
||||
if (_kernelbase != nullptr) {
|
||||
_SetThreadDescription =
|
||||
reinterpret_cast<SetThreadDescriptionFnPtr>(
|
||||
GetProcAddress(_kernelbase,
|
||||
"SetThreadDescription"));
|
||||
_SetThreadDescription = reinterpret_cast<SetThreadDescriptionFnPtr>(
|
||||
os::win32::lookup_kernelbase_symbol("SetThreadDescription"));
|
||||
#ifdef ASSERT
|
||||
_GetThreadDescription =
|
||||
reinterpret_cast<GetThreadDescriptionFnPtr>(
|
||||
GetProcAddress(_kernelbase,
|
||||
"GetThreadDescription"));
|
||||
_GetThreadDescription = reinterpret_cast<GetThreadDescriptionFnPtr>(
|
||||
os::win32::lookup_kernelbase_symbol("GetThreadDescription"));
|
||||
#endif
|
||||
}
|
||||
log_info(os, thread)("The SetThreadDescription API is%s available.", _SetThreadDescription == nullptr ? " not" : "");
|
||||
|
||||
// Prepare KernelBase APIs (VirtualAlloc2, MapViewOfFile3) if available (Windows version 1803).
|
||||
os::win32::VirtualAlloc2 = reinterpret_cast<os::win32::VirtualAlloc2Fn>(
|
||||
os::win32::lookup_kernelbase_symbol("VirtualAlloc2"));
|
||||
os::win32::MapViewOfFile3 = reinterpret_cast<os::win32::MapViewOfFile3Fn>(
|
||||
os::win32::lookup_kernelbase_symbol("MapViewOfFile3"));
|
||||
log_debug(os)("VirtualAlloc2 is%s available.", os::win32::VirtualAlloc2 == nullptr ? " not" : "");
|
||||
log_debug(os)("MapViewOfFile3 is%s available.", os::win32::MapViewOfFile3 == nullptr ? " not" : "");
|
||||
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
@ -109,6 +109,19 @@ class os::win32 {
|
||||
// load dll from Windows system directory or Windows directory
|
||||
static HINSTANCE load_Windows_dll(const char* name, char *ebuf, int ebuflen);
|
||||
|
||||
// Resolve a symbol from KernelBase.dll, returns nullptr if not found.
|
||||
static void* lookup_kernelbase_symbol(const char* name);
|
||||
|
||||
// VirtualAlloc2 (since Windows version 1803)
|
||||
// Resolved from KernelBase during os::init_2() or nullptr if unavailable.
|
||||
typedef PVOID (WINAPI *VirtualAlloc2Fn)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG);
|
||||
static VirtualAlloc2Fn VirtualAlloc2;
|
||||
|
||||
// MapViewOfFile3 (since Windows version 1803)
|
||||
// Resolved from KernelBase during os::init_2() or nullptr if unavailable.
|
||||
typedef PVOID (WINAPI *MapViewOfFile3Fn)(HANDLE, HANDLE, PVOID, ULONG64, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG);
|
||||
static MapViewOfFile3Fn MapViewOfFile3;
|
||||
|
||||
private:
|
||||
|
||||
static void initialize_performance_counter();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, Rivos Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -249,7 +249,6 @@ void RiscvHwprobe::add_features_from_query_result() {
|
||||
if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVFH)) {
|
||||
VM_Version::ext_Zvfh.enable_feature();
|
||||
}
|
||||
#ifndef PRODUCT
|
||||
if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKNED) &&
|
||||
is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKNHB) &&
|
||||
is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKB) &&
|
||||
@ -259,7 +258,6 @@ void RiscvHwprobe::add_features_from_query_result() {
|
||||
if (is_set(RISCV_HWPROBE_KEY_IMA_EXT_0, RISCV_HWPROBE_EXT_ZVKG)) {
|
||||
VM_Version::ext_Zvkg.enable_feature();
|
||||
}
|
||||
#endif
|
||||
|
||||
// ====== non-extensions ======
|
||||
//
|
||||
|
||||
@ -303,7 +303,8 @@ public:
|
||||
AllocGapNode* node = allocate_node(gap, Empty{});
|
||||
insert(gap, node);
|
||||
|
||||
log_trace(aot, alloc)("adding a gap of %zu bytes @ %p (total = %zu) in %zu blocks", gap_bytes, gap_bottom, _total_gap_bytes, size());
|
||||
log_trace(aot, alloc)("adding a gap of %zu bytes @ %p (total = %zu, used = %zu) in %zu blocks",
|
||||
gap_bytes, gap_bottom, _total_gap_bytes, _total_gap_bytes_used, size());
|
||||
return gap_bytes;
|
||||
}
|
||||
|
||||
@ -325,29 +326,25 @@ public:
|
||||
|
||||
remove(node);
|
||||
|
||||
precond(_total_gap_bytes >= num_bytes);
|
||||
_total_gap_bytes -= num_bytes;
|
||||
_total_gap_bytes_used += num_bytes;
|
||||
_total_gap_allocs++;
|
||||
DEBUG_ONLY(node = nullptr); // Don't use it anymore!
|
||||
|
||||
precond(gap_bytes >= num_bytes);
|
||||
if (gap_bytes > num_bytes) {
|
||||
gap_bytes -= num_bytes;
|
||||
gap_bottom += num_bytes;
|
||||
|
||||
AllocGap gap(gap_bytes, gap_bottom); // constructor checks alignment
|
||||
AllocGap gap(gap_bytes - num_bytes, gap_bottom + num_bytes); // constructor checks alignment
|
||||
AllocGapNode* new_node = allocate_node(gap, Empty{});
|
||||
insert(gap, new_node);
|
||||
}
|
||||
size_t unfilled_bytes = _total_gap_bytes - _total_gap_bytes_used;
|
||||
log_trace(aot, alloc)("%zu bytes @ %p in a gap of %zu bytes (used gaps %zu times, remain gap = %zu bytes in %zu blocks)",
|
||||
num_bytes, result, gap_bytes, _total_gap_allocs, _total_gap_bytes, size());
|
||||
num_bytes, result, gap_bytes, _total_gap_allocs, unfilled_bytes, size());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
size_t DumpRegion::_total_gap_bytes = 0;
|
||||
size_t DumpRegion::_total_gap_bytes_used = 0;
|
||||
size_t DumpRegion::_total_gap_bytes = 0; // All the gaps that have ever been created
|
||||
size_t DumpRegion::_total_gap_bytes_used = 0; // All the gaps that have been used
|
||||
size_t DumpRegion::_total_gap_allocs = 0;
|
||||
DumpRegion::AllocGapTree DumpRegion::_gap_tree;
|
||||
|
||||
@ -418,20 +415,21 @@ void DumpRegion::report_gaps(DumpAllocStats* stats) {
|
||||
});
|
||||
|
||||
double unfilled_percent = 0.0;
|
||||
size_t unfilled_bytes = _total_gap_bytes - _total_gap_bytes_used;
|
||||
if (_gap_tree.size() > 0) {
|
||||
unfilled_percent = percent_of(_total_gap_bytes, _total_gap_allocs);
|
||||
unfilled_percent = percent_of(unfilled_bytes, _total_gap_bytes);
|
||||
if (unfilled_percent > 5.0) {
|
||||
// We have a limited number of small objects, so some small gaps may remain
|
||||
// unfilled. If more than 5% of the gaps are unfilled, this likely indicates
|
||||
// a systematic error that should be investigated. Otherwise, do not warn to
|
||||
// avoid noise.
|
||||
log_warning(aot)("Unexpected %zu gaps (%zu bytes) for Klass alignment",
|
||||
_gap_tree.size(), _total_gap_bytes);
|
||||
log_warning(aot)("Unexpected %zu gaps (%zu bytes, %.2f%%) for Klass alignment",
|
||||
_gap_tree.size(), _total_gap_bytes, unfilled_percent);
|
||||
}
|
||||
}
|
||||
if (_total_gap_allocs > 0) {
|
||||
log_info(aot)("Allocated %zu objects of %zu bytes in gaps (remain = %zu bytes, %.2f%%)",
|
||||
_total_gap_allocs, _total_gap_bytes_used, _total_gap_bytes, unfilled_percent);
|
||||
_total_gap_allocs, _total_gap_bytes_used, unfilled_bytes, unfilled_percent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -154,6 +154,8 @@
|
||||
|
||||
#define JAVA_27_VERSION 71
|
||||
|
||||
#define JAVA_28_VERSION 72
|
||||
|
||||
void ClassFileParser::set_class_bad_constant_seen(short bad_constant) {
|
||||
assert((bad_constant == JVM_CONSTANT_Module ||
|
||||
bad_constant == JVM_CONSTANT_Package) && _major_version >= JAVA_9_VERSION,
|
||||
|
||||
@ -31,7 +31,10 @@
|
||||
#include "memory/metaspaceClosure.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/timerTrace.hpp"
|
||||
#include "utilities/concurrentHashTable.inline.hpp"
|
||||
#include "utilities/concurrentHashTableTasks.inline.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
#include "utilities/tableStatistics.hpp"
|
||||
|
||||
@ -239,10 +242,24 @@ void Dictionary::verify() {
|
||||
}
|
||||
|
||||
void Dictionary::print_table_statistics(outputStream* st, const char* table_name) {
|
||||
static TableStatistics ts;
|
||||
TableStatistics stats;
|
||||
auto sz = [&] (InstanceKlass** val) {
|
||||
return sizeof(**val);
|
||||
};
|
||||
ts = _table->statistics_get(Thread::current(), sz, ts);
|
||||
ts.print(st, table_name);
|
||||
Thread* thread = Thread::current();
|
||||
ConcurrentTable::StatisticsTask sts(_table);
|
||||
if (!sts.prepare(thread)) {
|
||||
st->print_cr("Failed to take statistics");
|
||||
return;
|
||||
}
|
||||
TraceTime timer("GetStatistics", TRACETIME_LOG(Debug, perf));
|
||||
while (sts.do_task(thread, sz)) {
|
||||
sts.pause(thread);
|
||||
if (thread->is_Java_thread()) {
|
||||
ThreadBlockInVM tbivm(JavaThread::cast(thread));
|
||||
}
|
||||
sts.cont(thread);
|
||||
}
|
||||
stats = sts.done(thread);
|
||||
stats.print(st, table_name);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -527,6 +527,10 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
|
||||
case vmIntrinsics::_intpoly_assign:
|
||||
if (!UseIntPolyIntrinsics) return true;
|
||||
break;
|
||||
case vmIntrinsics::_intpoly_mult_25519:
|
||||
case vmIntrinsics::_intpoly_square_25519:
|
||||
if (!UseIntPoly25519Intrinsics) return true;
|
||||
break;
|
||||
case vmIntrinsics::_updateBytesCRC32C:
|
||||
case vmIntrinsics::_updateDirectByteBufferCRC32C:
|
||||
if (!UseCRC32CIntrinsics) return true;
|
||||
|
||||
@ -549,6 +549,13 @@ class methodHandle;
|
||||
do_name(intPolyAssign_name, "conditionalAssign") \
|
||||
do_signature(intPolyAssign_signature, "(I[J[J)V") \
|
||||
\
|
||||
/* support for sun.security.util.math.intpoly.IntegerPolynomial25519 */ \
|
||||
do_class(sun_security_util_math_intpoly_IntegerPolynomial25519, "sun/security/util/math/intpoly/IntegerPolynomial25519") \
|
||||
do_intrinsic(_intpoly_mult_25519, sun_security_util_math_intpoly_IntegerPolynomial25519, intPolyMult_name, intPolyMult_signature, F_R) \
|
||||
do_intrinsic(_intpoly_square_25519, sun_security_util_math_intpoly_IntegerPolynomial25519, intPolySquare_name, intPolySquare_signature, F_R) \
|
||||
do_name(intPolySquare_name, "square") \
|
||||
do_signature(intPolySquare_signature, "([J[J)V") \
|
||||
\
|
||||
/* support for java.util.Base64.Encoder*/ \
|
||||
do_class(java_util_Base64_Encoder, "java/util/Base64$Encoder") \
|
||||
do_intrinsic(_base64_encodeBlock, java_util_Base64_Encoder, encodeBlock_name, encodeBlock_signature, F_R) \
|
||||
|
||||
@ -729,7 +729,7 @@ HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size) {
|
||||
result = humongous_obj_allocate(word_size);
|
||||
if (result != nullptr) {
|
||||
policy()->old_gen_alloc_tracker()->
|
||||
add_allocated_humongous_bytes_since_last_gc(humongous_byte_size);
|
||||
add_allocated_humongous_bytes(humongous_byte_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2861,7 +2861,7 @@ void G1CollectedHeap::record_obj_copy_mem_stats() {
|
||||
G1ReservePercent);
|
||||
|
||||
policy()->old_gen_alloc_tracker()->
|
||||
add_allocated_bytes_since_last_gc(total_old_allocated * HeapWordSize);
|
||||
add_allocated_non_humongous_bytes(total_old_allocated * HeapWordSize);
|
||||
|
||||
_gc_tracer_stw->report_evacuation_statistics(create_g1_evac_summary(&_survivor_evac_stats),
|
||||
create_g1_evac_summary(&_old_evac_stats));
|
||||
|
||||
131
src/hotspot/share/gc/g1/g1ConcurrentCycleTracker.cpp
Normal file
131
src/hotspot/share/gc/g1/g1ConcurrentCycleTracker.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) 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.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gc/g1/g1ConcurrentCycleTracker.hpp"
|
||||
#include "gc/g1/g1OldGenAllocationTracker.hpp"
|
||||
#include "utilities/checkedCast.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
|
||||
void G1ConcurrentCycleTracker::reset() {
|
||||
_state = CycleState::Inactive;
|
||||
_total_pause_time_s = 0.0;
|
||||
_cycle_start_time_s = 0.0;
|
||||
_cycle_end_time_s = 0.0;
|
||||
|
||||
_humongous_bytes_at_start = 0;
|
||||
_non_humongous_allocated_bytes = 0;
|
||||
_peak_extra_humongous_occupancy_bytes = 0;
|
||||
}
|
||||
|
||||
void G1ConcurrentCycleTracker::update_allocation_stats(G1AllocationIntervalStats interval_stats) {
|
||||
if (!is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_non_humongous_allocated_bytes += interval_stats._non_humongous_allocated_bytes;
|
||||
|
||||
intptr_t delta_before = checked_cast<intptr_t>(interval_stats._total_humongous_before_bytes) -
|
||||
checked_cast<intptr_t>(_humongous_bytes_at_start);
|
||||
|
||||
intptr_t delta_after = delta_before +
|
||||
checked_cast<intptr_t>(interval_stats._humongous_allocated_bytes);
|
||||
|
||||
if (delta_after > 0) {
|
||||
_peak_extra_humongous_occupancy_bytes = MAX2(_peak_extra_humongous_occupancy_bytes, checked_cast<size_t>(delta_after));
|
||||
}
|
||||
}
|
||||
|
||||
G1ConcurrentCycleTracker::G1ConcurrentCycleTracker()
|
||||
: _state(CycleState::Inactive),
|
||||
_cycle_start_time_s(0.0),
|
||||
_cycle_end_time_s(0.0),
|
||||
_total_pause_time_s(0.0),
|
||||
_humongous_bytes_at_start(0),
|
||||
_non_humongous_allocated_bytes(0),
|
||||
_peak_extra_humongous_occupancy_bytes(0)
|
||||
{ }
|
||||
|
||||
void G1ConcurrentCycleTracker::record_cycle_start(double cycle_start_time_s, size_t humongous_bytes_after_pause) {
|
||||
assert(_state == CycleState::Inactive, "Concurrent start out of order.");
|
||||
_cycle_start_time_s = cycle_start_time_s;
|
||||
_humongous_bytes_at_start = humongous_bytes_after_pause;
|
||||
_state = CycleState::Active;
|
||||
}
|
||||
|
||||
void G1ConcurrentCycleTracker::record_allocation_interval(Pause pause_type,
|
||||
bool is_periodic_gc,
|
||||
double pause_start_time_s,
|
||||
double pause_end_time_s,
|
||||
G1AllocationIntervalStats interval_stats) {
|
||||
if (is_periodic_gc || pause_type == Pause::Full || pause_type == Pause::ConcurrentStartUndo) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pause_type == Pause::ConcurrentStartFull) {
|
||||
record_cycle_start(pause_end_time_s, interval_stats._total_humongous_after_bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
update_allocation_stats(interval_stats);
|
||||
|
||||
if (pause_type == Pause::Mixed) {
|
||||
complete_cycle(pause_start_time_s);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(pause_type == Pause::Normal ||
|
||||
pause_type == Pause::PrepareMixed ||
|
||||
pause_type == Pause::Remark ||
|
||||
pause_type == Pause::Cleanup,
|
||||
"Unhandled pause type");
|
||||
|
||||
add_pause(pause_end_time_s - pause_start_time_s);
|
||||
}
|
||||
|
||||
void G1ConcurrentCycleTracker::complete_cycle(double cycle_end_time_s) {
|
||||
precond(is_active());
|
||||
|
||||
_cycle_end_time_s = cycle_end_time_s;
|
||||
_state = CycleState::Complete;
|
||||
}
|
||||
|
||||
G1ConcurrentCycleStats G1ConcurrentCycleTracker::get_and_reset_cycle_stats() {
|
||||
precond(has_completed_cycle());
|
||||
|
||||
double cycle_duration = (_cycle_end_time_s - _cycle_start_time_s - _total_pause_time_s);
|
||||
|
||||
G1ConcurrentCycleStats stats{
|
||||
cycle_duration,
|
||||
_non_humongous_allocated_bytes,
|
||||
_peak_extra_humongous_occupancy_bytes
|
||||
};
|
||||
|
||||
reset();
|
||||
return stats;
|
||||
}
|
||||
111
src/hotspot/share/gc/g1/g1ConcurrentCycleTracker.hpp
Normal file
111
src/hotspot/share/gc/g1/g1ConcurrentCycleTracker.hpp
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 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.
|
||||
*
|
||||
* 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_G1_G1CONCURRENTCYCLETRACKER_HPP
|
||||
#define SHARE_GC_G1_G1CONCURRENTCYCLETRACKER_HPP
|
||||
|
||||
#include "gc/g1/g1CollectorState.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
struct G1AllocationIntervalStats;
|
||||
|
||||
// The sampling interval for G1ConcurrentCycleTracker covers the concurrent cycle
|
||||
// from the end of the Concurrent Start GC to start of the first Mixed GC.
|
||||
struct G1ConcurrentCycleStats {
|
||||
double _cycle_duration_s;
|
||||
size_t _non_humongous_allocated_bytes;
|
||||
size_t _peak_extra_humongous_occupancy_bytes;
|
||||
|
||||
G1ConcurrentCycleStats(double cycle_duration_s,
|
||||
size_t non_humongous_allocated_bytes,
|
||||
size_t peak_extra_humongous_occupancy_bytes)
|
||||
: _cycle_duration_s(cycle_duration_s),
|
||||
_non_humongous_allocated_bytes(non_humongous_allocated_bytes),
|
||||
_peak_extra_humongous_occupancy_bytes(peak_extra_humongous_occupancy_bytes)
|
||||
{ }
|
||||
};
|
||||
|
||||
class G1ConcurrentCycleTracker {
|
||||
using Pause = G1CollectorState::Pause;
|
||||
|
||||
enum class CycleState {
|
||||
Inactive,
|
||||
Active,
|
||||
Complete,
|
||||
};
|
||||
|
||||
CycleState _state;
|
||||
double _cycle_start_time_s;
|
||||
double _cycle_end_time_s;
|
||||
double _total_pause_time_s;
|
||||
|
||||
// allocation accounting
|
||||
size_t _humongous_bytes_at_start;
|
||||
size_t _non_humongous_allocated_bytes;
|
||||
size_t _peak_extra_humongous_occupancy_bytes;
|
||||
|
||||
void reset();
|
||||
|
||||
bool is_active() const {
|
||||
return _state == CycleState::Active;
|
||||
}
|
||||
void update_allocation_stats(G1AllocationIntervalStats interval_stats);
|
||||
|
||||
void add_pause(double pause_duration_s) {
|
||||
_total_pause_time_s += pause_duration_s;
|
||||
}
|
||||
|
||||
void record_cycle_start(double cycle_start_time_s, size_t humongous_bytes_after_pause);
|
||||
|
||||
void complete_cycle(double cycle_end_time_s);
|
||||
|
||||
public:
|
||||
G1ConcurrentCycleTracker();
|
||||
|
||||
void record_allocation_interval(Pause pause_type,
|
||||
bool is_periodic_gc,
|
||||
double pause_start_time_s,
|
||||
double pause_end_time_s,
|
||||
G1AllocationIntervalStats interval_stats);
|
||||
|
||||
void abort_cycle() {
|
||||
reset();
|
||||
}
|
||||
|
||||
bool has_completed_cycle() const {
|
||||
return _state == CycleState::Complete;
|
||||
}
|
||||
|
||||
size_t non_humongous_allocated_bytes() const {
|
||||
return _non_humongous_allocated_bytes;
|
||||
}
|
||||
|
||||
size_t peak_extra_humongous_occupancy_bytes() const {
|
||||
return _peak_extra_humongous_occupancy_bytes;
|
||||
}
|
||||
|
||||
G1ConcurrentCycleStats get_and_reset_cycle_stats();
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_G1_G1CONCURRENTCYCLETRACKER_HPP
|
||||
@ -714,6 +714,11 @@ private:
|
||||
}
|
||||
|
||||
HeapWord* region_clear_limit(G1HeapRegion* r) {
|
||||
// A garbage collection might have made the region unavailable after a yield during
|
||||
// clearing. Just return bottom as the limit, causing the clearing for this region to end.
|
||||
if (G1CollectedHeap::heap()->region_at_or_null(r->hrm_index()) == nullptr) {
|
||||
return r->bottom();
|
||||
}
|
||||
// During a Concurrent Undo Mark cycle, the per region top_at_mark_start and
|
||||
// live_words data are current wrt to the _mark_bitmap. We use this information
|
||||
// to only clear ranges of the bitmap that require clearing.
|
||||
@ -743,7 +748,7 @@ private:
|
||||
}
|
||||
|
||||
HeapWord* cur = r->bottom();
|
||||
HeapWord* const end = region_clear_limit(r);
|
||||
HeapWord* end = region_clear_limit(r);
|
||||
|
||||
size_t const chunk_size_in_words = G1ClearBitMapTask::chunk_size() / HeapWordSize;
|
||||
|
||||
@ -761,8 +766,12 @@ private:
|
||||
assert(!suspendible() || _cm->is_in_reset_for_next_cycle(), "invariant");
|
||||
|
||||
// Abort iteration if necessary.
|
||||
if (has_aborted()) {
|
||||
return true;
|
||||
if (suspendible() && _cm->do_yield_check()) {
|
||||
if (_cm->has_aborted()) {
|
||||
return true;
|
||||
}
|
||||
// Re-read end. The region might have been uncommitted.
|
||||
end = region_clear_limit(r);
|
||||
}
|
||||
}
|
||||
assert(cur >= end, "Must have completed iteration over the bitmap for region %u.", r->hrm_index());
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 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.
|
||||
*
|
||||
* 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_G1_G1CONCURRENTSTARTTOMIXEDTIMETRACKER_HPP
|
||||
#define SHARE_GC_G1_G1CONCURRENTSTARTTOMIXEDTIMETRACKER_HPP
|
||||
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
// Used to track time from the end of concurrent start to the first mixed GC.
|
||||
// After calling the concurrent start/mixed gc notifications, the result can be
|
||||
// obtained in get_and_reset_last_marking_time() once, after which the tracking resets.
|
||||
// Any pauses recorded by add_pause() will be subtracted from that results.
|
||||
class G1ConcurrentStartToMixedTimeTracker {
|
||||
private:
|
||||
bool _active;
|
||||
double _concurrent_start_end_time;
|
||||
double _mixed_start_time;
|
||||
double _total_pause_time;
|
||||
|
||||
double wall_time() const {
|
||||
return _mixed_start_time - _concurrent_start_end_time;
|
||||
}
|
||||
public:
|
||||
G1ConcurrentStartToMixedTimeTracker() { reset(); }
|
||||
|
||||
// Record concurrent start pause end, starting the time tracking.
|
||||
void record_concurrent_start_end(double end_time) {
|
||||
assert(!_active, "Concurrent start out of order.");
|
||||
_concurrent_start_end_time = end_time;
|
||||
_active = true;
|
||||
}
|
||||
|
||||
// Record the first mixed gc pause start, ending the time tracking.
|
||||
void record_mixed_gc_start(double start_time) {
|
||||
if (_active) {
|
||||
_mixed_start_time = start_time;
|
||||
_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
double get_and_reset_last_marking_time() {
|
||||
assert(has_result(), "Do not have all measurements yet.");
|
||||
double result = (_mixed_start_time - _concurrent_start_end_time) - _total_pause_time;
|
||||
reset();
|
||||
return result;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
_active = false;
|
||||
_total_pause_time = 0.0;
|
||||
_concurrent_start_end_time = -1.0;
|
||||
_mixed_start_time = -1.0;
|
||||
}
|
||||
|
||||
void add_pause(double time) {
|
||||
if (_active) {
|
||||
_total_pause_time += time;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_active() const { return _active; }
|
||||
|
||||
// Returns whether we have a result that can be retrieved.
|
||||
bool has_result() const { return _mixed_start_time > 0.0 && _concurrent_start_end_time > 0.0; }
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_G1_G1CONCURRENTSTARTTOMIXEDTIMETRACKER_HPP
|
||||
@ -31,19 +31,14 @@
|
||||
double G1IHOPControl::predict(const TruncatedSeq* seq) const {
|
||||
assert(_is_adaptive, "precondition");
|
||||
assert(_predictor != nullptr, "precondition");
|
||||
|
||||
return _predictor->predict_zero_bounded(seq);
|
||||
return _predictor->predict_zero_bounded(seq);
|
||||
}
|
||||
|
||||
bool G1IHOPControl::have_enough_data_for_prediction() const {
|
||||
assert(_is_adaptive, "precondition");
|
||||
|
||||
return ((size_t)_marking_start_to_mixed_time_s.num() >= G1AdaptiveIHOPNumInitialSamples) &&
|
||||
((size_t)_old_gen_alloc_rate.num() >= G1AdaptiveIHOPNumInitialSamples);
|
||||
}
|
||||
|
||||
double G1IHOPControl::last_marking_start_to_mixed_time_s() const {
|
||||
return _marking_start_to_mixed_time_s.last();
|
||||
((size_t)_old_non_humongous_alloc_rate.num() >= G1AdaptiveIHOPNumInitialSamples);
|
||||
}
|
||||
|
||||
size_t G1IHOPControl::effective_target_occupancy() const {
|
||||
@ -66,7 +61,6 @@ size_t G1IHOPControl::effective_target_occupancy() const {
|
||||
}
|
||||
|
||||
G1IHOPControl::G1IHOPControl(double ihop_percent,
|
||||
const G1OldGenAllocationTracker* old_gen_alloc_tracker,
|
||||
bool adaptive,
|
||||
const G1Predictions* predictor,
|
||||
size_t heap_reserve_percent,
|
||||
@ -76,11 +70,10 @@ G1IHOPControl::G1IHOPControl(double ihop_percent,
|
||||
_target_occupancy(0),
|
||||
_heap_reserve_percent(heap_reserve_percent),
|
||||
_heap_waste_percent(heap_waste_percent),
|
||||
_last_allocation_time_s(0.0),
|
||||
_old_gen_alloc_tracker(old_gen_alloc_tracker),
|
||||
_predictor(predictor),
|
||||
_marking_start_to_mixed_time_s(10, 0.05),
|
||||
_old_gen_alloc_rate(10, 0.05),
|
||||
_old_non_humongous_alloc_rate(10, 0.05),
|
||||
_peak_extra_humongous_occupancy_in_mark_cycle(10, 0.05),
|
||||
_expected_young_gen_at_first_mixed_gc(0) {
|
||||
assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0,
|
||||
"IHOP percent out of range: %.3f", ihop_percent);
|
||||
@ -93,22 +86,28 @@ void G1IHOPControl::update_target_occupancy(size_t new_target_occupancy) {
|
||||
_target_occupancy = new_target_occupancy;
|
||||
}
|
||||
|
||||
void G1IHOPControl::report_statistics(G1NewTracer* new_tracer, size_t non_young_occupancy) {
|
||||
print_log(non_young_occupancy);
|
||||
send_trace_event(new_tracer, non_young_occupancy);
|
||||
void G1IHOPControl::report_statistics(G1NewTracer* new_tracer,
|
||||
size_t non_young_occupancy,
|
||||
size_t non_humongous_allocation,
|
||||
size_t peak_extra_humongous_occupancy) {
|
||||
print_log(non_young_occupancy, non_humongous_allocation, peak_extra_humongous_occupancy);
|
||||
send_trace_event(new_tracer, non_young_occupancy,
|
||||
non_humongous_allocation, peak_extra_humongous_occupancy);
|
||||
}
|
||||
|
||||
void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t expected_young_gen_size) {
|
||||
assert(allocation_time_s > 0, "Invalid allocation time: %.3f", allocation_time_s);
|
||||
_last_allocation_time_s = allocation_time_s;
|
||||
double alloc_rate = _old_gen_alloc_tracker->last_period_old_gen_growth() / allocation_time_s;
|
||||
_old_gen_alloc_rate.add(alloc_rate);
|
||||
void G1IHOPControl::record_expected_young_gen_size(size_t expected_young_gen_size) {
|
||||
_expected_young_gen_at_first_mixed_gc = expected_young_gen_size;
|
||||
}
|
||||
|
||||
void G1IHOPControl::add_marking_start_to_mixed_length(double length_s) {
|
||||
assert(length_s >= 0.0, "Invalid marking length: %.3f", length_s);
|
||||
_marking_start_to_mixed_time_s.add(length_s);
|
||||
void G1IHOPControl::record_concurrent_cycle(double marking_start_to_mixed_time_s,
|
||||
size_t non_humongous_bytes,
|
||||
size_t peak_extra_humongous_occupancy_bytes) {
|
||||
assert(marking_start_to_mixed_time_s > 0.0, "Invalid concurrent cycle duration: %.3f", marking_start_to_mixed_time_s);
|
||||
|
||||
double non_humongous_rate = non_humongous_bytes / marking_start_to_mixed_time_s;
|
||||
_marking_start_to_mixed_time_s.add(marking_start_to_mixed_time_s);
|
||||
_old_non_humongous_alloc_rate.add(non_humongous_rate);
|
||||
_peak_extra_humongous_occupancy_in_mark_cycle.add(peak_extra_humongous_occupancy_bytes);
|
||||
}
|
||||
|
||||
// Determine the old generation occupancy threshold at which to start
|
||||
@ -121,80 +120,93 @@ size_t G1IHOPControl::old_gen_threshold_for_conc_mark_start() const {
|
||||
return (size_t)(_initial_ihop_percent * _target_occupancy / 100.0);
|
||||
}
|
||||
|
||||
// During the time between marking start and the first Mixed GC,
|
||||
// additional memory will be consumed:
|
||||
// - Old gen grows due to allocations:
|
||||
// old_gen_alloc_bytes = old_gen_alloc_rate * marking_start_to_mixed_time
|
||||
// - Young gen will occupy a certain size at the first Mixed GC:
|
||||
// expected_young_gen_at_first_mixed_gc
|
||||
double marking_start_to_mixed_time = predict(&_marking_start_to_mixed_time_s);
|
||||
double old_gen_alloc_rate = predict(&_old_gen_alloc_rate);
|
||||
size_t old_gen_alloc_bytes = (size_t)(marking_start_to_mixed_time * old_gen_alloc_rate);
|
||||
|
||||
// Therefore, the total heap occupancy at the first Mixed GC is:
|
||||
// current_old_gen + old_gen_growth + expected_young_gen_at_first_mixed_gc
|
||||
// Between Concurrent Start GC and the first Mixed GC (i.e. concurrent cycle),
|
||||
// we expect extra heap occupancy from three sources:
|
||||
// - non-humongous allocations into the old-gen
|
||||
// - peak extra humongous occupancy during the cycle, relative to the humongous occupancy
|
||||
// at the end of the Concurrent Start GC.
|
||||
// - we also wish to maintain the current desired young generation until the first Mixed-gc;
|
||||
// promotions into the old gen should not shrink the young gen and degrade performance.
|
||||
//
|
||||
// To ensure this does not exceed the target_heap_occupancy, we work
|
||||
// backwards to compute the old gen occupancy at which marking must start:
|
||||
// mark_start_threshold = target_heap_occupancy -
|
||||
// (old_gen_growth + expected_young_gen_at_first_mixed_gc)
|
||||
// We therefore start marking early enough such that:
|
||||
//
|
||||
// old_gen_at_concurrent_start +
|
||||
// predicted_non_hum_old_growth +
|
||||
// predicted_peak_extra_humongous_occupancy +
|
||||
// expected_young_gen_at_first_mixed_gc
|
||||
//
|
||||
// stays below the effective target occupancy.
|
||||
double marking_start_to_mixed_time = predict(&_marking_start_to_mixed_time_s);
|
||||
double old_non_humongous_alloc_rate = predict(&_old_non_humongous_alloc_rate);
|
||||
size_t old_non_humongous_alloc_bytes = (size_t)(marking_start_to_mixed_time * old_non_humongous_alloc_rate);
|
||||
|
||||
size_t predicted_needed = old_gen_alloc_bytes + _expected_young_gen_at_first_mixed_gc;
|
||||
size_t predicted_peak_extra_humongous_occupancy =
|
||||
predict(&_peak_extra_humongous_occupancy_in_mark_cycle);
|
||||
|
||||
size_t reserve_for_young_regions = _expected_young_gen_at_first_mixed_gc;
|
||||
size_t target_heap_occupancy = effective_target_occupancy();
|
||||
|
||||
return predicted_needed < target_heap_occupancy
|
||||
? target_heap_occupancy - predicted_needed
|
||||
: 0;
|
||||
size_t needed_for_concurrent_cycle = reserve_for_young_regions +
|
||||
old_non_humongous_alloc_bytes +
|
||||
predicted_peak_extra_humongous_occupancy;
|
||||
|
||||
size_t threshold = needed_for_concurrent_cycle < target_heap_occupancy ?
|
||||
target_heap_occupancy - needed_for_concurrent_cycle : 0;
|
||||
return threshold;
|
||||
}
|
||||
|
||||
void G1IHOPControl::print_log(size_t non_young_occupancy) {
|
||||
void G1IHOPControl::print_log(size_t non_young_occupancy,
|
||||
size_t non_humongous_allocation,
|
||||
size_t peak_extra_humongous_occupancy) {
|
||||
assert(_target_occupancy > 0, "Target occupancy still not updated yet.");
|
||||
size_t old_gen_mark_start_threshold = old_gen_threshold_for_conc_mark_start();
|
||||
log_debug(gc, ihop)("Basic information (value update), old-gen threshold: %zuB (%1.2f%%), target occupancy: %zuB, old-gen occupancy: %zuB (%1.2f%%), "
|
||||
"recent old-gen allocation size: %zuB, recent allocation duration: %1.2fms, recent old-gen allocation rate: %1.2fB/s, recent marking phase length: %1.2fms",
|
||||
log_debug(gc, ihop)("Basic information (value update), old-gen threshold: %zuB (%1.2f%%), target occupancy: %zuB, old-gen occupancy: %zuB (%1.2f%%)",
|
||||
old_gen_mark_start_threshold,
|
||||
percent_of(old_gen_mark_start_threshold, _target_occupancy),
|
||||
_target_occupancy,
|
||||
non_young_occupancy,
|
||||
percent_of(non_young_occupancy, _target_occupancy),
|
||||
_old_gen_alloc_tracker->last_period_old_gen_bytes(),
|
||||
_last_allocation_time_s * 1000.0,
|
||||
_last_allocation_time_s > 0.0 ? _old_gen_alloc_tracker->last_period_old_gen_bytes() / _last_allocation_time_s : 0.0,
|
||||
last_marking_start_to_mixed_time_s() * 1000.0);
|
||||
percent_of(non_young_occupancy, _target_occupancy));
|
||||
|
||||
if (!_is_adaptive) {
|
||||
if (!_is_adaptive || !have_enough_data_for_prediction()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t effective_target = effective_target_occupancy();
|
||||
log_debug(gc, ihop)("Adaptive IHOP information (value update), prediction active: %s, old-gen threshold: %zuB (%1.2f%%), internal target occupancy: %zuB, "
|
||||
"old-gen occupancy: %zuB, additional buffer size: %zuB, predicted old-gen allocation rate: %1.2fB/s, "
|
||||
"predicted marking phase length: %1.2fms",
|
||||
BOOL_TO_STR(have_enough_data_for_prediction()),
|
||||
log_debug(gc, ihop)("Adaptive IHOP information (value update), old-gen threshold: %zuB (%1.2f%%), internal target occupancy: %zuB, "
|
||||
"old-gen occupancy: %zuB (%1.2f%%), additional buffer size: %zuB, "
|
||||
"current non-humongous allocation: %zuB, current peak extra humongous occupancy: %zuB, "
|
||||
"predicted old-gen non-humongous allocation rate: %1.2fB/s, predicted peak extra humongous occupancy: %1.2fB, "
|
||||
"predicted concurrent cycle duration: %1.2fms",
|
||||
old_gen_mark_start_threshold,
|
||||
percent_of(old_gen_mark_start_threshold, effective_target),
|
||||
effective_target,
|
||||
non_young_occupancy,
|
||||
percent_of(non_young_occupancy, effective_target),
|
||||
_expected_young_gen_at_first_mixed_gc,
|
||||
predict(&_old_gen_alloc_rate),
|
||||
non_humongous_allocation, peak_extra_humongous_occupancy,
|
||||
predict(&_old_non_humongous_alloc_rate),
|
||||
predict(&_peak_extra_humongous_occupancy_in_mark_cycle),
|
||||
predict(&_marking_start_to_mixed_time_s) * 1000.0);
|
||||
}
|
||||
|
||||
void G1IHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) {
|
||||
void G1IHOPControl::send_trace_event(G1NewTracer* tracer,
|
||||
size_t non_young_occupancy,
|
||||
size_t non_humongous_allocation,
|
||||
size_t peak_extra_humongous_occupancy) {
|
||||
assert(_target_occupancy > 0, "Target occupancy still not updated yet.");
|
||||
tracer->report_basic_ihop_statistics(old_gen_threshold_for_conc_mark_start(),
|
||||
_target_occupancy,
|
||||
non_young_occupancy,
|
||||
_old_gen_alloc_tracker->last_period_old_gen_bytes(),
|
||||
_last_allocation_time_s,
|
||||
last_marking_start_to_mixed_time_s());
|
||||
non_young_occupancy);
|
||||
|
||||
if (_is_adaptive) {
|
||||
tracer->report_adaptive_ihop_statistics(old_gen_threshold_for_conc_mark_start(),
|
||||
effective_target_occupancy(),
|
||||
non_young_occupancy,
|
||||
_expected_young_gen_at_first_mixed_gc,
|
||||
predict(&_old_gen_alloc_rate),
|
||||
non_humongous_allocation,
|
||||
peak_extra_humongous_occupancy,
|
||||
predict(&_old_non_humongous_alloc_rate),
|
||||
predict(&_peak_extra_humongous_occupancy_in_mark_cycle),
|
||||
predict(&_marking_start_to_mixed_time_s),
|
||||
have_enough_data_for_prediction());
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
#ifndef SHARE_GC_G1_G1IHOPCONTROL_HPP
|
||||
#define SHARE_GC_G1_G1IHOPCONTROL_HPP
|
||||
|
||||
#include "gc/g1/g1OldGenAllocationTracker.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "utilities/numberSeq.hpp"
|
||||
|
||||
@ -53,16 +52,15 @@ class G1IHOPControl : public CHeapObj<mtGC> {
|
||||
// Percentage of free heap that should be considered as waste.
|
||||
const size_t _heap_waste_percent;
|
||||
|
||||
// Most recent complete mutator allocation period in seconds.
|
||||
double _last_allocation_time_s;
|
||||
const G1OldGenAllocationTracker* _old_gen_alloc_tracker;
|
||||
|
||||
const G1Predictions* _predictor;
|
||||
// Wall-clock time in seconds from marking start to the first mixed GC,
|
||||
// excluding GC Pause time.
|
||||
TruncatedSeq _marking_start_to_mixed_time_s;
|
||||
// Old generation allocation rate in bytes per second.
|
||||
TruncatedSeq _old_gen_alloc_rate;
|
||||
// Track old-generation allocations during a concurrent cycle: end of the
|
||||
// Concurrent Start to the first Mixed GC.
|
||||
// These values are used only when G1UseAdaptiveIHOP is enabled.
|
||||
TruncatedSeq _old_non_humongous_alloc_rate;
|
||||
TruncatedSeq _peak_extra_humongous_occupancy_in_mark_cycle;
|
||||
|
||||
// The most recent unrestrained size of the young gen. This is used as an additional
|
||||
// factor in the calculation of the threshold, as the threshold is based on
|
||||
@ -77,19 +75,23 @@ class G1IHOPControl : public CHeapObj<mtGC> {
|
||||
double predict(const TruncatedSeq* seq) const;
|
||||
|
||||
bool have_enough_data_for_prediction() const;
|
||||
double last_marking_start_to_mixed_time_s() const;
|
||||
|
||||
// The "effective" target occupancy the algorithm wants to keep until the start
|
||||
// of Mixed GCs. This is typically lower than the target occupancy, as the
|
||||
// algorithm needs to consider restrictions by the environment.
|
||||
size_t effective_target_occupancy() const;
|
||||
|
||||
void print_log(size_t non_young_occupancy);
|
||||
void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy);
|
||||
void print_log(size_t non_young_occupancy,
|
||||
size_t non_humongous_allocation,
|
||||
size_t peak_extra_humongous_occupancy);
|
||||
|
||||
void send_trace_event(G1NewTracer* tracer,
|
||||
size_t non_young_occupancy,
|
||||
size_t non_humongous_allocation,
|
||||
size_t peak_extra_humongous_occupancy);
|
||||
|
||||
public:
|
||||
G1IHOPControl(double ihop_percent,
|
||||
const G1OldGenAllocationTracker* old_gen_alloc_tracker,
|
||||
bool adaptive,
|
||||
const G1Predictions* predictor,
|
||||
size_t heap_reserve_percent,
|
||||
@ -98,26 +100,23 @@ class G1IHOPControl : public CHeapObj<mtGC> {
|
||||
// Adjust target occupancy.
|
||||
void update_target_occupancy(size_t new_target_occupancy);
|
||||
|
||||
void update_target_after_marking_phase();
|
||||
|
||||
// Update allocation rate information and current expected young gen size for the
|
||||
// first mixed gc needed for the predictor. Allocation rate is given as the
|
||||
// separately passed in allocation increment and the time passed (mutator time)
|
||||
// for the latest allocation increment here. Allocation size is the memory needed
|
||||
// during the mutator before and the first mixed gc pause itself.
|
||||
// Updates expected young gen size for the first mixed gc needed for the predictor.
|
||||
// Contents include young gen at that point, and the memory required for evacuating
|
||||
// the collection set in that first mixed gc (including waste caused by PLAB
|
||||
// allocation etc.).
|
||||
void update_allocation_info(double allocation_time_s, size_t expected_young_gen_size);
|
||||
void record_expected_young_gen_size(size_t expected_young_gen_size);
|
||||
|
||||
// Update the time spent in the mutator beginning from the end of concurrent start to
|
||||
// the first mixed gc.
|
||||
void add_marking_start_to_mixed_length(double length_s);
|
||||
void record_concurrent_cycle(double marking_start_to_mixed_time_s,
|
||||
size_t non_humongous_bytes,
|
||||
size_t peak_extra_humongous_occupancy);
|
||||
|
||||
// Get the current non-young occupancy at which concurrent marking should start.
|
||||
size_t old_gen_threshold_for_conc_mark_start() const;
|
||||
|
||||
void report_statistics(G1NewTracer* tracer, size_t non_young_occupancy);
|
||||
void report_statistics(G1NewTracer* tracer,
|
||||
size_t non_young_occupancy,
|
||||
size_t non_humongous_allocation,
|
||||
size_t peak_extra_humongous_occupancy);
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_G1_G1IHOPCONTROL_HPP
|
||||
|
||||
@ -26,36 +26,43 @@
|
||||
#include "logging/log.hpp"
|
||||
|
||||
G1OldGenAllocationTracker::G1OldGenAllocationTracker() :
|
||||
_last_period_old_gen_bytes(0),
|
||||
_last_period_old_gen_growth(0),
|
||||
_humongous_bytes_after_last_gc(0),
|
||||
_allocated_bytes_since_last_gc(0),
|
||||
_allocated_humongous_bytes_since_last_gc(0) {
|
||||
_humongous_bytes_after_last_pause(0),
|
||||
_allocated_non_humongous_bytes_since_last_pause(0),
|
||||
_allocated_humongous_bytes_since_last_pause(0) {
|
||||
}
|
||||
|
||||
void G1OldGenAllocationTracker::reset_after_gc(size_t humongous_bytes_after_gc) {
|
||||
G1AllocationIntervalStats G1OldGenAllocationTracker::end_allocation_interval(size_t humongous_bytes_after_pause) {
|
||||
// Calculate actual increase in old, taking eager reclaim into consideration.
|
||||
size_t last_period_humongous_increase = 0;
|
||||
if (humongous_bytes_after_gc > _humongous_bytes_after_last_gc) {
|
||||
last_period_humongous_increase = humongous_bytes_after_gc - _humongous_bytes_after_last_gc;
|
||||
assert(last_period_humongous_increase <= _allocated_humongous_bytes_since_last_gc,
|
||||
size_t last_interval_humongous_increase = 0;
|
||||
if (humongous_bytes_after_pause > _humongous_bytes_after_last_pause) {
|
||||
last_interval_humongous_increase = humongous_bytes_after_pause - _humongous_bytes_after_last_pause;
|
||||
assert(last_interval_humongous_increase <= _allocated_humongous_bytes_since_last_pause,
|
||||
"Increase larger than allocated %zu <= %zu",
|
||||
last_period_humongous_increase, _allocated_humongous_bytes_since_last_gc);
|
||||
last_interval_humongous_increase, _allocated_humongous_bytes_since_last_pause);
|
||||
}
|
||||
_last_period_old_gen_growth = _allocated_bytes_since_last_gc + last_period_humongous_increase;
|
||||
|
||||
// Calculate and record needed values.
|
||||
_last_period_old_gen_bytes = _allocated_bytes_since_last_gc + _allocated_humongous_bytes_since_last_gc;
|
||||
_humongous_bytes_after_last_gc = humongous_bytes_after_gc;
|
||||
size_t last_interval_old_gen_growth = _allocated_non_humongous_bytes_since_last_pause +
|
||||
last_interval_humongous_increase;
|
||||
|
||||
log_debug(gc, alloc, stats)("Old generation allocation in the last mutator period, "
|
||||
G1AllocationIntervalStats allocation_interval_stats{
|
||||
_allocated_non_humongous_bytes_since_last_pause,
|
||||
_allocated_humongous_bytes_since_last_pause,
|
||||
_humongous_bytes_after_last_pause,
|
||||
humongous_bytes_after_pause
|
||||
};
|
||||
|
||||
_humongous_bytes_after_last_pause = humongous_bytes_after_pause;
|
||||
|
||||
log_debug(gc, alloc, stats)("Old generation allocation in the last allocation interval, "
|
||||
"old gen allocated: %zuB, humongous allocated: %zuB, "
|
||||
"old gen growth: %zuB.",
|
||||
_allocated_bytes_since_last_gc,
|
||||
_allocated_humongous_bytes_since_last_gc,
|
||||
_last_period_old_gen_growth);
|
||||
_allocated_non_humongous_bytes_since_last_pause,
|
||||
_allocated_humongous_bytes_since_last_pause,
|
||||
last_interval_old_gen_growth);
|
||||
|
||||
// Reset for next mutator period.
|
||||
_allocated_bytes_since_last_gc = 0;
|
||||
_allocated_humongous_bytes_since_last_gc = 0;
|
||||
// Reset for the next interval.
|
||||
_allocated_non_humongous_bytes_since_last_pause = 0;
|
||||
_allocated_humongous_bytes_since_last_pause = 0;
|
||||
|
||||
return allocation_interval_stats;
|
||||
}
|
||||
|
||||
@ -22,46 +22,71 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_VM_GC_G1_G1OLDGENALLOCATIONTRACKER_HPP
|
||||
#define SHARE_VM_GC_G1_G1OLDGENALLOCATIONTRACKER_HPP
|
||||
#ifndef SHARE_GC_G1_G1OLDGENALLOCATIONTRACKER_HPP
|
||||
#define SHARE_GC_G1_G1OLDGENALLOCATIONTRACKER_HPP
|
||||
|
||||
#include "gc/g1/g1HeapRegion.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
|
||||
// Allocation statistics for an allocation interval, i.e. the interval between two
|
||||
// consecutive STW pauses.
|
||||
//
|
||||
// The allocation counters record allocations made during that interval.
|
||||
//
|
||||
// _total_humongous_before_bytes is the humongous occupancy after the previous
|
||||
// pause (at the start of the allocation interval).
|
||||
//
|
||||
// _total_humongous_after_bytes is the humongous occupancy after the current
|
||||
// pause (at the end of the allocation interval).
|
||||
struct G1AllocationIntervalStats {
|
||||
size_t _non_humongous_allocated_bytes;
|
||||
size_t _humongous_allocated_bytes;
|
||||
size_t _total_humongous_before_bytes;
|
||||
size_t _total_humongous_after_bytes;
|
||||
|
||||
G1AllocationIntervalStats(size_t non_humongous_allocated_bytes,
|
||||
size_t humongous_allocated_bytes,
|
||||
size_t total_humongous_before_bytes,
|
||||
size_t total_humongous_after_bytes)
|
||||
: _non_humongous_allocated_bytes(non_humongous_allocated_bytes),
|
||||
_humongous_allocated_bytes(humongous_allocated_bytes),
|
||||
_total_humongous_before_bytes(total_humongous_before_bytes),
|
||||
_total_humongous_after_bytes(total_humongous_after_bytes)
|
||||
{ }
|
||||
|
||||
void record_humongous_allocation(size_t humongous_allocation_bytes) {
|
||||
_humongous_allocated_bytes += humongous_allocation_bytes;
|
||||
_total_humongous_after_bytes += humongous_allocation_bytes;
|
||||
}
|
||||
};
|
||||
|
||||
// Track allocation details in the old generation.
|
||||
class G1OldGenAllocationTracker : public CHeapObj<mtGC> {
|
||||
// Total number of bytes allocated in the old generation at the end
|
||||
// of the last gc.
|
||||
size_t _last_period_old_gen_bytes;
|
||||
// Total growth of the old geneneration since the last gc,
|
||||
// taking eager-reclaim into consideration.
|
||||
size_t _last_period_old_gen_growth;
|
||||
// Total size of humongous objects after the last STW pause.
|
||||
size_t _humongous_bytes_after_last_pause;
|
||||
|
||||
// Total size of humongous objects for last gc.
|
||||
size_t _humongous_bytes_after_last_gc;
|
||||
|
||||
// Non-humongous old generation allocations since the last gc.
|
||||
size_t _allocated_bytes_since_last_gc;
|
||||
// Humongous allocations during last mutator period.
|
||||
size_t _allocated_humongous_bytes_since_last_gc;
|
||||
// Non-humongous old generation allocations since the last STW pause.
|
||||
size_t _allocated_non_humongous_bytes_since_last_pause;
|
||||
// Humongous allocations during last allocation interval.
|
||||
size_t _allocated_humongous_bytes_since_last_pause;
|
||||
|
||||
public:
|
||||
G1OldGenAllocationTracker();
|
||||
|
||||
void add_allocated_bytes_since_last_gc(size_t bytes) { _allocated_bytes_since_last_gc += bytes; }
|
||||
void add_allocated_humongous_bytes_since_last_gc(size_t bytes) { _allocated_humongous_bytes_since_last_gc += bytes; }
|
||||
|
||||
// Record a humongous allocation in a collection pause. This allocation
|
||||
// is accounted to the previous mutator period.
|
||||
void record_collection_pause_humongous_allocation(size_t bytes) {
|
||||
_humongous_bytes_after_last_gc += bytes;
|
||||
void add_allocated_non_humongous_bytes(size_t bytes) {
|
||||
_allocated_non_humongous_bytes_since_last_pause += bytes;
|
||||
}
|
||||
void add_allocated_humongous_bytes(size_t bytes) {
|
||||
_allocated_humongous_bytes_since_last_pause += bytes;
|
||||
}
|
||||
|
||||
size_t last_period_old_gen_bytes() const { return _last_period_old_gen_bytes; }
|
||||
size_t last_period_old_gen_growth() const { return _last_period_old_gen_growth; };
|
||||
// Record a humongous allocation in a collection pause. This allocation
|
||||
// is accounted to the previous allocation interval.
|
||||
void record_collection_pause_humongous_allocation(size_t bytes) {
|
||||
_humongous_bytes_after_last_pause += bytes;
|
||||
}
|
||||
|
||||
// Calculates and resets stats after a collection.
|
||||
void reset_after_gc(size_t humongous_bytes_after_gc);
|
||||
// Calculate and reset allocation statistics after a pause.
|
||||
G1AllocationIntervalStats end_allocation_interval(size_t humongous_bytes_after_pause);
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_GC_G1_G1OLDGENALLOCATIONTRACKER_HPP
|
||||
#endif // SHARE_GC_G1_G1OLDGENALLOCATIONTRACKER_HPP
|
||||
|
||||
@ -55,8 +55,9 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) :
|
||||
_analytics(new G1Analytics(&_predictor)),
|
||||
_remset_tracker(),
|
||||
_mmu_tracker(new G1MMUTracker(GCPauseIntervalMillis / 1000.0, MaxGCPauseMillis / 1000.0)),
|
||||
_concurrent_cycle_tracker(),
|
||||
_old_gen_alloc_tracker(),
|
||||
_ihop_control(create_ihop_control(&_old_gen_alloc_tracker, &_predictor)),
|
||||
_ihop_control(create_ihop_control(&_predictor)),
|
||||
_policy_counters(new GCPolicyCounters("GarbageFirst", 1, 2)),
|
||||
_cur_pause_start_sec(0.0),
|
||||
_young_list_desired_length(0),
|
||||
@ -68,7 +69,6 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) :
|
||||
_young_gen_sizer(),
|
||||
_free_regions_at_end_of_collection(0),
|
||||
_pending_cards_from_gc(0),
|
||||
_concurrent_start_to_mixed(),
|
||||
_collection_set(nullptr),
|
||||
_g1h(nullptr),
|
||||
_phase_times_timer(gc_timer),
|
||||
@ -160,7 +160,7 @@ void G1Policy::record_new_heap_size(uint new_number_of_regions) {
|
||||
double reserve_regions_d = (double) new_number_of_regions * _reserve_factor;
|
||||
// We use ceiling so that if reserve_regions_d is > 0.0 (but
|
||||
// smaller than 1.0) we'll get 1.
|
||||
_reserve_regions = (uint) ceil(reserve_regions_d);
|
||||
_reserve_regions.store_relaxed((uint) ceil(reserve_regions_d));
|
||||
|
||||
_young_gen_sizer.heap_size_changed(new_number_of_regions);
|
||||
|
||||
@ -186,8 +186,22 @@ void G1Policy::update_young_length_bounds() {
|
||||
void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) {
|
||||
uint old_young_list_target_length = young_list_target_length();
|
||||
|
||||
uint new_young_list_desired_length = calculate_young_desired_length(pending_cards, card_rs_length, code_root_rs_length);
|
||||
uint new_young_list_target_length = calculate_young_target_length(new_young_list_desired_length);
|
||||
uint min_young_length_by_sizer = _young_gen_sizer.min_desired_young_length();
|
||||
uint max_young_length_by_sizer = _young_gen_sizer.max_desired_young_length();
|
||||
|
||||
if (max_young_length_by_sizer < min_young_length_by_sizer) {
|
||||
// This can happen due to races with heap_size_changed() at mutator time. Do not update the young gen
|
||||
// lengths. Will be updated on the next regular call anyway.
|
||||
assert(!SafepointSynchronize::is_at_safepoint(), "must be");
|
||||
return;
|
||||
}
|
||||
|
||||
uint new_young_list_desired_length = calculate_young_desired_length(pending_cards,
|
||||
card_rs_length,
|
||||
code_root_rs_length,
|
||||
min_young_length_by_sizer,
|
||||
max_young_length_by_sizer);
|
||||
uint new_young_list_target_length = calculate_young_target_length(new_young_list_desired_length, min_young_length_by_sizer);
|
||||
|
||||
log_trace(gc, ergo, heap)("Young list length update: pending cards %zu card_rs_length %zu old target %u desired: %u target: %u",
|
||||
pending_cards,
|
||||
@ -224,9 +238,9 @@ void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_l
|
||||
//
|
||||
uint G1Policy::calculate_young_desired_length(size_t pending_cards,
|
||||
size_t card_rs_length,
|
||||
size_t code_root_rs_length) const {
|
||||
uint min_young_length_by_sizer = _young_gen_sizer.min_desired_young_length();
|
||||
uint max_young_length_by_sizer = _young_gen_sizer.max_desired_young_length();
|
||||
size_t code_root_rs_length,
|
||||
uint min_young_length_by_sizer,
|
||||
uint max_young_length_by_sizer) const {
|
||||
|
||||
assert(min_young_length_by_sizer >= 1, "invariant");
|
||||
assert(max_young_length_by_sizer >= min_young_length_by_sizer, "invariant");
|
||||
@ -302,7 +316,7 @@ uint G1Policy::calculate_young_desired_length(size_t pending_cards,
|
||||
// Limit the desired (wished) young length by current free regions. If the request
|
||||
// can be satisfied without using up reserve regions, do so, otherwise eat into
|
||||
// the reserve, giving away at most what the heap sizer allows.
|
||||
uint G1Policy::calculate_young_target_length(uint desired_young_length) const {
|
||||
uint G1Policy::calculate_young_target_length(uint desired_young_length, uint min_young_length_by_sizer) const {
|
||||
uint allocated_young_length = _g1h->young_regions_count();
|
||||
|
||||
uint receiving_additional_eden;
|
||||
@ -319,8 +333,14 @@ uint G1Policy::calculate_young_target_length(uint desired_young_length) const {
|
||||
// do, we at most eat the sizer's minimum regions into the reserve or half the
|
||||
// reserve rounded up (if possible; this is an arbitrary value).
|
||||
|
||||
uint max_to_eat_into_reserve = MIN2(_young_gen_sizer.min_desired_young_length(),
|
||||
(_reserve_regions + 1) / 2);
|
||||
// The heap reserve needs to be snapshotted for consistent use in the following.
|
||||
// It can be concurrently modified by the mutator as it expands the heap. It can
|
||||
// only increase at that time, so this is a conservative snapshot. So at worst this
|
||||
// method will return a too small young gen length in that case.
|
||||
uint reserve_regions = _reserve_regions.load_relaxed();
|
||||
|
||||
uint max_to_eat_into_reserve = MIN2(min_young_length_by_sizer,
|
||||
(reserve_regions + 1) / 2);
|
||||
|
||||
log_trace(gc, ergo, heap)("Young target length: Common "
|
||||
"free regions at end of collection %u "
|
||||
@ -329,14 +349,14 @@ uint G1Policy::calculate_young_target_length(uint desired_young_length) const {
|
||||
"max to eat into reserve %u",
|
||||
_free_regions_at_end_of_collection,
|
||||
desired_young_length,
|
||||
_reserve_regions,
|
||||
reserve_regions,
|
||||
max_to_eat_into_reserve);
|
||||
|
||||
uint survivor_regions_count = _g1h->survivor_regions_count();
|
||||
uint desired_eden_length = desired_young_length - survivor_regions_count;
|
||||
uint allocated_eden_length = allocated_young_length - survivor_regions_count;
|
||||
|
||||
if (_free_regions_at_end_of_collection <= _reserve_regions) {
|
||||
if (_free_regions_at_end_of_collection <= reserve_regions) {
|
||||
// Fully eat (or already eating) into the reserve, hand back at most absolute_min_length regions.
|
||||
uint receiving_eden = MIN3(_free_regions_at_end_of_collection,
|
||||
desired_eden_length,
|
||||
@ -351,9 +371,9 @@ uint G1Policy::calculate_young_target_length(uint desired_young_length) const {
|
||||
log_trace(gc, ergo, heap)("Young target length: Fully eat into reserve "
|
||||
"receiving eden %u receiving additional eden %u",
|
||||
receiving_eden, receiving_additional_eden);
|
||||
} else if (_free_regions_at_end_of_collection < (desired_eden_length + _reserve_regions)) {
|
||||
} else if (_free_regions_at_end_of_collection < (desired_eden_length + reserve_regions)) {
|
||||
// Partially eat into the reserve, at most max_to_eat_into_reserve regions.
|
||||
uint free_outside_reserve = _free_regions_at_end_of_collection - _reserve_regions;
|
||||
uint free_outside_reserve = _free_regions_at_end_of_collection - reserve_regions;
|
||||
assert(free_outside_reserve < desired_eden_length,
|
||||
"must be %u %u",
|
||||
free_outside_reserve, desired_eden_length);
|
||||
@ -569,6 +589,7 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) {
|
||||
// Consider this like a collection pause for the purposes of allocation
|
||||
// since last pause.
|
||||
double end_sec = os::elapsedTime();
|
||||
double start_time_sec = cur_pause_start_sec();
|
||||
|
||||
// "Nuke" the heuristics that control the young/mixed GC
|
||||
// transitions and make sure we start with young GCs after the Full GC.
|
||||
@ -582,9 +603,6 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) {
|
||||
_survivor_surv_rate_group->reset();
|
||||
update_young_length_bounds();
|
||||
|
||||
_old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes);
|
||||
|
||||
double start_time_sec = cur_pause_start_sec();
|
||||
record_pause(Pause::Full, start_time_sec, end_sec);
|
||||
}
|
||||
|
||||
@ -934,8 +952,6 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation
|
||||
phase_times()->sum_thread_work_items(G1GCPhaseTimes::MergePSS, G1GCPhaseTimes::MergePSSToYoungGenCards));
|
||||
}
|
||||
|
||||
record_pause(collector_state()->gc_pause_type(concurrent_operation_is_full_mark), start_time_sec, end_time_sec);
|
||||
|
||||
if (collector_state()->is_in_prepare_mixed_gc()) {
|
||||
assert(!collector_state()->is_in_concurrent_start_gc(),
|
||||
"The young GC before mixed is not allowed to be concurrent start GC");
|
||||
@ -968,23 +984,27 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation
|
||||
|
||||
_free_regions_at_end_of_collection = _g1h->num_free_regions();
|
||||
|
||||
_old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes);
|
||||
Pause this_pause = collector_state()->gc_pause_type(concurrent_operation_is_full_mark);
|
||||
size_t humongous_allocation_bytes = G1CollectedHeap::is_humongous(allocation_word_size) ?
|
||||
G1CollectedHeap::allocation_used_bytes(allocation_word_size) : 0;
|
||||
|
||||
record_pause(this_pause, start_time_sec, end_time_sec, humongous_allocation_bytes);
|
||||
// Do not update dynamic IHOP due to G1 periodic collection as it is highly likely
|
||||
// that in this case we are not running in a "normal" operating mode.
|
||||
if (_g1h->gc_cause() != GCCause::_g1_periodic_collection) {
|
||||
update_young_length_bounds();
|
||||
|
||||
// Take snapshots of these values here as update_ihop_prediction
|
||||
// may complete the concurrent cycle and reset the values.
|
||||
size_t non_humongous_allocation = _concurrent_cycle_tracker.non_humongous_allocated_bytes();
|
||||
size_t peak_extra_humongous_occupancy = _concurrent_cycle_tracker.peak_extra_humongous_occupancy_bytes();
|
||||
|
||||
if (update_ihop_prediction(app_time_ms / 1000.0, is_young_only_pause)) {
|
||||
_ihop_control->report_statistics(_g1h->gc_tracer_stw(), _g1h->non_young_occupancy_after_allocation(allocation_word_size));
|
||||
_ihop_control->report_statistics(_g1h->gc_tracer_stw(),
|
||||
_g1h->non_young_occupancy_after_allocation(allocation_word_size),
|
||||
non_humongous_allocation,
|
||||
peak_extra_humongous_occupancy);
|
||||
}
|
||||
} else {
|
||||
// Any garbage collection triggered as periodic collection resets the time-to-mixed
|
||||
// measurement. Periodic collection typically means that the application is "inactive", i.e.
|
||||
// the marking threads may have received an uncharacteristic amount of cpu time
|
||||
// for completing the marking, i.e. are faster than expected.
|
||||
// This skews the predicted marking length towards smaller values which might cause
|
||||
// the mark start being too late.
|
||||
abort_time_to_mixed_tracking();
|
||||
}
|
||||
|
||||
// Note that _mmu_tracker->max_gc_time() returns the time in seconds.
|
||||
@ -1012,10 +1032,8 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation
|
||||
return next_state;
|
||||
}
|
||||
|
||||
G1IHOPControl* G1Policy::create_ihop_control(const G1OldGenAllocationTracker* old_gen_alloc_tracker,
|
||||
const G1Predictions* predictor) {
|
||||
G1IHOPControl* G1Policy::create_ihop_control(const G1Predictions* predictor) {
|
||||
return new G1IHOPControl(G1IHOP,
|
||||
old_gen_alloc_tracker,
|
||||
G1UseAdaptiveIHOP,
|
||||
predictor,
|
||||
G1ReservePercent,
|
||||
@ -1032,29 +1050,31 @@ bool G1Policy::update_ihop_prediction(double mutator_time_s,
|
||||
double const min_valid_time = 1e-6;
|
||||
|
||||
bool report = false;
|
||||
if (!this_gc_was_young_only && _concurrent_cycle_tracker.has_completed_cycle()) {
|
||||
G1ConcurrentCycleStats cycle_stats = _concurrent_cycle_tracker.get_and_reset_cycle_stats();
|
||||
|
||||
if (!this_gc_was_young_only && _concurrent_start_to_mixed.has_result()) {
|
||||
double marking_to_mixed_time = _concurrent_start_to_mixed.get_and_reset_last_marking_time();
|
||||
assert(marking_to_mixed_time > 0.0,
|
||||
"Concurrent start to mixed time must be larger than zero but is %.3f",
|
||||
marking_to_mixed_time);
|
||||
if (marking_to_mixed_time > min_valid_time) {
|
||||
_ihop_control->add_marking_start_to_mixed_length(marking_to_mixed_time);
|
||||
double concurrent_cycle_duration_s = cycle_stats._cycle_duration_s;
|
||||
assert(concurrent_cycle_duration_s > 0.0,
|
||||
"Time for Concurrent Start GC to the first Mixed GC must be larger than zero but is %.3f",
|
||||
concurrent_cycle_duration_s);
|
||||
if (concurrent_cycle_duration_s > min_valid_time) {
|
||||
_ihop_control->record_concurrent_cycle(concurrent_cycle_duration_s,
|
||||
cycle_stats._non_humongous_allocated_bytes,
|
||||
cycle_stats._peak_extra_humongous_occupancy_bytes);
|
||||
report = true;
|
||||
}
|
||||
}
|
||||
|
||||
// As an approximation for the young gc promotion rates during marking we use
|
||||
// all of them. In many applications there are only a few if any young gcs during
|
||||
// marking, which makes any prediction useless. This increases the accuracy of the
|
||||
// prediction.
|
||||
// The second clause prevents skewing the IHOP prediction with (typically) degenerate
|
||||
// back-to-back young-gen-size samples.
|
||||
if (this_gc_was_young_only && mutator_time_s > min_valid_time) {
|
||||
// IHOP control wants to know the expected young gen length if it were not
|
||||
// restrained by the heap reserve. Using the actual length would make the
|
||||
// prediction too small and the limit the young gen every time we get to the
|
||||
// predicted target occupancy.
|
||||
size_t young_gen_size = young_list_desired_length() * G1HeapRegion::GrainBytes;
|
||||
_ihop_control->update_allocation_info(mutator_time_s, young_gen_size);
|
||||
|
||||
_ihop_control->record_expected_young_gen_size(young_gen_size);
|
||||
report = true;
|
||||
}
|
||||
|
||||
@ -1291,11 +1311,11 @@ void G1Policy::decide_on_concurrent_start_pause() {
|
||||
// Force concurrent start.
|
||||
collector_state()->set_in_concurrent_start_gc();
|
||||
// We might have ended up coming here about to start a mixed phase with a collection set
|
||||
// active. The following remark might change the change the "evacuation efficiency" of
|
||||
// active. The following remark might change the "evacuation efficiency" of
|
||||
// the regions in this set, leading to failing asserts later.
|
||||
// Since the concurrent cycle will recreate the collection set anyway, simply drop it here.
|
||||
abandon_collection_set_candidates();
|
||||
abort_time_to_mixed_tracking();
|
||||
abort_concurrent_cycle_tracking();
|
||||
log_debug(gc, ergo)("Initiate concurrent cycle (%s requested concurrent cycle)",
|
||||
requester_for_mixed_abort(cause));
|
||||
} else {
|
||||
@ -1335,7 +1355,7 @@ void G1Policy::record_concurrent_mark_cleanup_end(bool has_rebuilt_remembered_se
|
||||
}
|
||||
|
||||
if (!mixed_gc_pending) {
|
||||
abort_time_to_mixed_tracking();
|
||||
abort_concurrent_cycle_tracking();
|
||||
log_debug(gc, ergo)("request young-only gcs (candidate old regions not available)");
|
||||
}
|
||||
if (mixed_gc_pending) {
|
||||
@ -1370,7 +1390,8 @@ void G1Policy::update_gc_pause_time_ratios(Pause gc_type, double start_time_sec,
|
||||
|
||||
void G1Policy::record_pause(Pause gc_type,
|
||||
double start,
|
||||
double end) {
|
||||
double end,
|
||||
size_t humongous_allocation_bytes) {
|
||||
// Manage the MMU tracker. For some reason it ignores Full GCs.
|
||||
if (gc_type != Pause::Full) {
|
||||
_mmu_tracker->add_pause(start, end);
|
||||
@ -1378,49 +1399,26 @@ void G1Policy::record_pause(Pause gc_type,
|
||||
|
||||
update_gc_pause_time_ratios(gc_type, start, end);
|
||||
|
||||
update_time_to_mixed_tracking(gc_type, start, end);
|
||||
size_t humongous_bytes = _g1h->humongous_regions_count() * G1HeapRegion::GrainBytes;
|
||||
G1AllocationIntervalStats alloc_interval_stats = _old_gen_alloc_tracker.end_allocation_interval(humongous_bytes);
|
||||
bool is_periodic_gc = _g1h->gc_cause() == GCCause::_g1_periodic_collection;
|
||||
|
||||
if (humongous_allocation_bytes > 0) {
|
||||
// Record the humongous allocation that triggered the GC and attribute it to
|
||||
// the ending allocation interval. We do this eagerly, before we know whether
|
||||
// the post GC allocation succeeds, to keep the common case simple. In the
|
||||
// rare case where this allocation fails, we over-account; this only
|
||||
// affects the stored IHOP sample if the current GC is the first Mixed GC.
|
||||
alloc_interval_stats.record_humongous_allocation(humongous_allocation_bytes);
|
||||
}
|
||||
_concurrent_cycle_tracker.record_allocation_interval(gc_type, is_periodic_gc, start, end, alloc_interval_stats);
|
||||
|
||||
double elapsed_gc_cpu_time = _analytics->gc_cpu_time_ms();
|
||||
_analytics->set_gc_cpu_time_at_pause_end_ms(elapsed_gc_cpu_time);
|
||||
}
|
||||
|
||||
void G1Policy::update_time_to_mixed_tracking(Pause gc_type,
|
||||
double start,
|
||||
double end) {
|
||||
// Manage the mutator time tracking from concurrent start to first mixed gc.
|
||||
switch (gc_type) {
|
||||
case Pause::Full:
|
||||
abort_time_to_mixed_tracking();
|
||||
break;
|
||||
case Pause::Cleanup:
|
||||
case Pause::Remark:
|
||||
case Pause::Normal:
|
||||
case Pause::PrepareMixed:
|
||||
_concurrent_start_to_mixed.add_pause(end - start);
|
||||
break;
|
||||
case Pause::ConcurrentStartFull:
|
||||
// Do not track time-to-mixed time for periodic collections as they are likely
|
||||
// to be not representative to regular operation as the mutators are idle at
|
||||
// that time. Also only track full concurrent mark cycles.
|
||||
if (_g1h->gc_cause() != GCCause::_g1_periodic_collection) {
|
||||
_concurrent_start_to_mixed.record_concurrent_start_end(end);
|
||||
}
|
||||
break;
|
||||
case Pause::ConcurrentStartUndo:
|
||||
assert(_g1h->gc_cause() == GCCause::_g1_humongous_allocation,
|
||||
"GC cause must be humongous allocation but is %d",
|
||||
_g1h->gc_cause());
|
||||
break;
|
||||
case Pause::Mixed:
|
||||
_concurrent_start_to_mixed.record_mixed_gc_start(start);
|
||||
break;
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
|
||||
void G1Policy::abort_time_to_mixed_tracking() {
|
||||
_concurrent_start_to_mixed.reset();
|
||||
void G1Policy::abort_concurrent_cycle_tracking() {
|
||||
_concurrent_cycle_tracker.abort_cycle();
|
||||
}
|
||||
|
||||
bool G1Policy::next_gc_should_be_mixed() const {
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
#define SHARE_GC_G1_G1POLICY_HPP
|
||||
|
||||
#include "gc/g1/g1CollectorState.hpp"
|
||||
#include "gc/g1/g1ConcurrentStartToMixedTimeTracker.hpp"
|
||||
#include "gc/g1/g1ConcurrentCycleTracker.hpp"
|
||||
#include "gc/g1/g1GCPhaseTimes.hpp"
|
||||
#include "gc/g1/g1HeapRegionAttr.hpp"
|
||||
#include "gc/g1/g1MMUTracker.hpp"
|
||||
@ -58,8 +58,7 @@ class STWGCTimer;
|
||||
class G1Policy: public CHeapObj<mtGC> {
|
||||
using Pause = G1CollectorState::Pause;
|
||||
|
||||
static G1IHOPControl* create_ihop_control(const G1OldGenAllocationTracker* old_gen_alloc_tracker,
|
||||
const G1Predictions* predictor);
|
||||
static G1IHOPControl* create_ihop_control(const G1Predictions* predictor);
|
||||
// Update the IHOP control with the necessary statistics. Returns true if there
|
||||
// has been a significant update to the prediction.
|
||||
bool update_ihop_prediction(double mutator_time_s,
|
||||
@ -70,8 +69,9 @@ class G1Policy: public CHeapObj<mtGC> {
|
||||
G1RemSetTrackingPolicy _remset_tracker;
|
||||
G1MMUTracker* _mmu_tracker;
|
||||
|
||||
G1ConcurrentCycleTracker _concurrent_cycle_tracker;
|
||||
// Tracking the allocation in the old generation between
|
||||
// two GCs.
|
||||
// two pauses.
|
||||
G1OldGenAllocationTracker _old_gen_alloc_tracker;
|
||||
G1IHOPControl* _ihop_control;
|
||||
|
||||
@ -91,9 +91,11 @@ class G1Policy: public CHeapObj<mtGC> {
|
||||
G1SurvRateGroup* _survivor_surv_rate_group;
|
||||
|
||||
double _reserve_factor;
|
||||
// This will be set when the heap is expanded
|
||||
// for the first time during initialization.
|
||||
uint _reserve_regions;
|
||||
// The allocation reserve in number of regions that we try to keep free.
|
||||
// G1 allocation of new regions for eden is restrained when allocating into that reserve.
|
||||
// This intentionally slows down the allocation when the heap is close to full to allow
|
||||
// concurrent marking to finish and hopefully avoid a Full GC.
|
||||
Atomic<uint> _reserve_regions;
|
||||
|
||||
G1YoungGenSizer _young_gen_sizer;
|
||||
|
||||
@ -112,8 +114,6 @@ class G1Policy: public CHeapObj<mtGC> {
|
||||
// garbage collection or the most recent refinement sweep.
|
||||
size_t _to_collection_set_cards;
|
||||
|
||||
G1ConcurrentStartToMixedTimeTracker _concurrent_start_to_mixed;
|
||||
|
||||
bool should_update_surv_rate_group_predictors();
|
||||
|
||||
double pending_cards_processing_time() const;
|
||||
@ -224,9 +224,13 @@ private:
|
||||
|
||||
// Calculate desired young length based on current situation without taking actually
|
||||
// available free regions into account.
|
||||
uint calculate_young_desired_length(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) const;
|
||||
uint calculate_young_desired_length(size_t pending_cards,
|
||||
size_t card_rs_length,
|
||||
size_t code_root_rs_length,
|
||||
uint min_young_length_by_sizer,
|
||||
uint max_young_length_by_sizer) const;
|
||||
// Limit the given desired young length to available free regions.
|
||||
uint calculate_young_target_length(uint desired_young_length) const;
|
||||
uint calculate_young_target_length(uint desired_young_length, uint min_young_length_by_sizer) const;
|
||||
|
||||
double predict_survivor_regions_evac_time() const;
|
||||
double predict_retained_regions_evac_time() const;
|
||||
@ -258,17 +262,16 @@ public:
|
||||
|
||||
private:
|
||||
void abandon_collection_set_candidates();
|
||||
// Manage time-to-mixed tracking.
|
||||
void update_time_to_mixed_tracking(Pause gc_type, double start, double end);
|
||||
// Record the given STW pause with the given start and end times (in s).
|
||||
void record_pause(Pause gc_type,
|
||||
double start,
|
||||
double end);
|
||||
double end,
|
||||
size_t humongous_allocation_bytes = 0);
|
||||
|
||||
void update_gc_pause_time_ratios(Pause gc_type, double start_sec, double end_sec);
|
||||
|
||||
// Indicate that we aborted marking before doing any mixed GCs.
|
||||
void abort_time_to_mixed_tracking();
|
||||
void abort_concurrent_cycle_tracking();
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@ -97,31 +97,31 @@ void G1NewTracer::report_evacuation_statistics(const G1EvacSummary& young_summar
|
||||
}
|
||||
|
||||
void G1NewTracer::report_basic_ihop_statistics(size_t threshold,
|
||||
size_t target_ccupancy,
|
||||
size_t non_young_occupancy,
|
||||
size_t last_allocation_size,
|
||||
double last_allocation_duration,
|
||||
double last_marking_length) {
|
||||
size_t target_occupancy,
|
||||
size_t non_young_occupancy) {
|
||||
send_basic_ihop_statistics(threshold,
|
||||
target_ccupancy,
|
||||
non_young_occupancy,
|
||||
last_allocation_size,
|
||||
last_allocation_duration,
|
||||
last_marking_length);
|
||||
target_occupancy,
|
||||
non_young_occupancy);
|
||||
}
|
||||
|
||||
void G1NewTracer::report_adaptive_ihop_statistics(size_t threshold,
|
||||
size_t internal_target_occupancy,
|
||||
size_t current_occupancy,
|
||||
size_t additional_buffer_size,
|
||||
double predicted_allocation_rate,
|
||||
size_t non_humongous_allocation,
|
||||
size_t peak_extra_humongous_occupancy,
|
||||
double predicted_old_non_hum_alloc_rate,
|
||||
size_t predicted_peak_extra_humongous_occupancy,
|
||||
double predicted_marking_length,
|
||||
bool prediction_active) {
|
||||
send_adaptive_ihop_statistics(threshold,
|
||||
internal_target_occupancy,
|
||||
current_occupancy,
|
||||
additional_buffer_size,
|
||||
predicted_allocation_rate,
|
||||
non_humongous_allocation,
|
||||
peak_extra_humongous_occupancy,
|
||||
predicted_old_non_hum_alloc_rate,
|
||||
predicted_peak_extra_humongous_occupancy,
|
||||
predicted_marking_length,
|
||||
prediction_active);
|
||||
}
|
||||
@ -206,10 +206,7 @@ void G1NewTracer::send_old_evacuation_statistics(const G1EvacSummary& summary) c
|
||||
|
||||
void G1NewTracer::send_basic_ihop_statistics(size_t threshold,
|
||||
size_t target_occupancy,
|
||||
size_t non_young_occupancy,
|
||||
size_t last_allocation_size,
|
||||
double last_allocation_duration,
|
||||
double last_marking_length) {
|
||||
size_t non_young_occupancy) {
|
||||
EventG1BasicIHOP evt;
|
||||
if (evt.should_commit()) {
|
||||
evt.set_gcId(GCId::current());
|
||||
@ -217,10 +214,6 @@ void G1NewTracer::send_basic_ihop_statistics(size_t threshold,
|
||||
evt.set_targetOccupancy(target_occupancy);
|
||||
evt.set_thresholdPercentage(target_occupancy > 0 ? ((double)threshold / target_occupancy) : 0.0);
|
||||
evt.set_currentOccupancy(non_young_occupancy);
|
||||
evt.set_recentMutatorAllocationSize(last_allocation_size);
|
||||
evt.set_recentMutatorDuration(last_allocation_duration * MILLIUNITS);
|
||||
evt.set_recentAllocationRate(last_allocation_duration != 0.0 ? last_allocation_size / last_allocation_duration : 0.0);
|
||||
evt.set_lastMarkingDuration(last_marking_length * MILLIUNITS);
|
||||
evt.commit();
|
||||
}
|
||||
}
|
||||
@ -229,7 +222,10 @@ void G1NewTracer::send_adaptive_ihop_statistics(size_t threshold,
|
||||
size_t internal_target_occupancy,
|
||||
size_t current_occupancy,
|
||||
size_t additional_buffer_size,
|
||||
double predicted_allocation_rate,
|
||||
size_t non_humongous_allocation,
|
||||
size_t peak_extra_humongous_occupancy,
|
||||
double predicted_old_non_hum_alloc_rate,
|
||||
size_t predicted_peak_extra_humongous_occupancy,
|
||||
double predicted_marking_length,
|
||||
bool prediction_active) {
|
||||
EventG1AdaptiveIHOP evt;
|
||||
@ -240,7 +236,10 @@ void G1NewTracer::send_adaptive_ihop_statistics(size_t threshold,
|
||||
evt.set_ihopTargetOccupancy(internal_target_occupancy);
|
||||
evt.set_currentOccupancy(current_occupancy);
|
||||
evt.set_additionalBufferSize(additional_buffer_size);
|
||||
evt.set_predictedAllocationRate(predicted_allocation_rate);
|
||||
evt.set_nonHumongousAllocation(non_humongous_allocation);
|
||||
evt.set_peakExtraHumongousOccupancy(peak_extra_humongous_occupancy);
|
||||
evt.set_predictedNonHumongousAllocation(predicted_old_non_hum_alloc_rate);
|
||||
evt.set_predictedPeakExtraHumongousOccupancy(predicted_peak_extra_humongous_occupancy);
|
||||
evt.set_predictedMarkingDuration(predicted_marking_length * MILLIUNITS);
|
||||
evt.set_predictionActive(prediction_active);
|
||||
evt.commit();
|
||||
|
||||
@ -52,15 +52,15 @@ public:
|
||||
|
||||
void report_basic_ihop_statistics(size_t threshold,
|
||||
size_t target_occupancy,
|
||||
size_t current_occupancy,
|
||||
size_t last_allocation_size,
|
||||
double last_allocation_duration,
|
||||
double last_marking_length);
|
||||
size_t current_occupancy);
|
||||
void report_adaptive_ihop_statistics(size_t threshold,
|
||||
size_t internal_target_occupancy,
|
||||
size_t current_occupancy,
|
||||
size_t additional_buffer_size,
|
||||
double predicted_allocation_rate,
|
||||
size_t non_humongous_allocation,
|
||||
size_t peak_extra_humongous_occupancy,
|
||||
double predicted_old_gen_non_humongous_allocation_rate,
|
||||
size_t predicted_peak_extra_humongous_occupancy,
|
||||
double predicted_marking_length,
|
||||
bool prediction_active);
|
||||
private:
|
||||
@ -73,15 +73,16 @@ private:
|
||||
|
||||
void send_basic_ihop_statistics(size_t threshold,
|
||||
size_t target_occupancy,
|
||||
size_t non_young_occupancy,
|
||||
size_t last_allocation_size,
|
||||
double last_allocation_duration,
|
||||
double last_marking_length);
|
||||
size_t non_young_occupancy);
|
||||
|
||||
void send_adaptive_ihop_statistics(size_t threshold,
|
||||
size_t internal_target_occupancy,
|
||||
size_t non_young_occupancy,
|
||||
size_t additional_buffer_size,
|
||||
double predicted_allocation_rate,
|
||||
size_t non_humongous_allocation,
|
||||
size_t peak_extra_humongous_occupancy,
|
||||
double predicted_old_gen_non_humongous_allocation_rate,
|
||||
size_t predicted_peak_extra_humongous_occupancy,
|
||||
double predicted_marking_length,
|
||||
bool prediction_active);
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -58,9 +58,9 @@ void G1UncommitRegionTask::enqueue() {
|
||||
|
||||
G1UncommitRegionTask* uncommit_task = instance();
|
||||
if (!uncommit_task->is_active()) {
|
||||
// Change state to active and schedule using UncommitInitialDelayMs.
|
||||
// Change state to active and schedule.
|
||||
uncommit_task->set_active(true);
|
||||
G1CollectedHeap::heap()->service_thread()->schedule_task(uncommit_task, UncommitInitialDelayMs);
|
||||
G1CollectedHeap::heap()->service_thread()->schedule_task(uncommit_task, G1UncommitInitialDelay);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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,8 +34,6 @@ class G1UncommitRegionTask : public G1ServiceTask {
|
||||
// This limit is small enough to ensure that the duration of each invocation
|
||||
// is short, while still making reasonable progress.
|
||||
static const uint UncommitSizeLimit = 128 * M;
|
||||
// Initial delay in milliseconds after GC before the regions are uncommitted.
|
||||
static const uint UncommitInitialDelayMs = 100;
|
||||
// The delay between two uncommit task executions.
|
||||
static const uint UncommitTaskDelayMs = 10;
|
||||
|
||||
|
||||
@ -542,7 +542,7 @@ public:
|
||||
class FreeCSetStats {
|
||||
size_t _before_used_bytes; // Usage in regions successfully evacuate
|
||||
size_t _after_used_bytes; // Usage in regions failing evacuation
|
||||
size_t _bytes_allocated_in_old_since_last_gc; // Size of young regions turned into old
|
||||
size_t _bytes_allocated_in_old_since_last_pause; // Size of young regions turned into old
|
||||
size_t _failure_used_words; // Live size in failed regions
|
||||
size_t _failure_waste_words; // Wasted size in failed regions
|
||||
uint _regions_freed; // Number of regions freed
|
||||
@ -551,7 +551,7 @@ public:
|
||||
FreeCSetStats() :
|
||||
_before_used_bytes(0),
|
||||
_after_used_bytes(0),
|
||||
_bytes_allocated_in_old_since_last_gc(0),
|
||||
_bytes_allocated_in_old_since_last_pause(0),
|
||||
_failure_used_words(0),
|
||||
_failure_waste_words(0),
|
||||
_regions_freed(0) { }
|
||||
@ -560,7 +560,7 @@ public:
|
||||
assert(other != nullptr, "invariant");
|
||||
_before_used_bytes += other->_before_used_bytes;
|
||||
_after_used_bytes += other->_after_used_bytes;
|
||||
_bytes_allocated_in_old_since_last_gc += other->_bytes_allocated_in_old_since_last_gc;
|
||||
_bytes_allocated_in_old_since_last_pause += other->_bytes_allocated_in_old_since_last_pause;
|
||||
_failure_used_words += other->_failure_used_words;
|
||||
_failure_waste_words += other->_failure_waste_words;
|
||||
_regions_freed += other->_regions_freed;
|
||||
@ -575,7 +575,7 @@ public:
|
||||
g1h->alloc_buffer_stats(G1HeapRegionAttr::Old)->add_failure_used_and_waste(_failure_used_words, _failure_waste_words);
|
||||
|
||||
G1Policy *policy = g1h->policy();
|
||||
policy->old_gen_alloc_tracker()->add_allocated_bytes_since_last_gc(_bytes_allocated_in_old_since_last_gc);
|
||||
policy->old_gen_alloc_tracker()->add_allocated_non_humongous_bytes(_bytes_allocated_in_old_since_last_pause);
|
||||
|
||||
policy->cset_regions_freed();
|
||||
}
|
||||
@ -592,7 +592,7 @@ public:
|
||||
// additional allocation: both the objects still in the region and the
|
||||
// ones already moved are accounted for elsewhere.
|
||||
if (r->is_young()) {
|
||||
_bytes_allocated_in_old_since_last_gc += G1HeapRegion::GrainBytes;
|
||||
_bytes_allocated_in_old_since_last_pause += G1HeapRegion::GrainBytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
@ -30,7 +30,7 @@
|
||||
#include "runtime/globals_extension.hpp"
|
||||
|
||||
G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults),
|
||||
_use_adaptive_sizing(true), _min_desired_young_length(0), _max_desired_young_length(0) {
|
||||
_use_adaptive_sizing(true), _min_desired_young_length(), _max_desired_young_length(0) {
|
||||
|
||||
precond(!FLAG_IS_ERGO(NewRatio));
|
||||
precond(!FLAG_IS_ERGO(NewSize));
|
||||
@ -100,16 +100,16 @@ G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults),
|
||||
}
|
||||
|
||||
if (user_specified_NewSize) {
|
||||
_min_desired_young_length = MAX2((uint)(NewSize / G1HeapRegion::GrainBytes), 1U);
|
||||
_min_desired_young_length.store_relaxed(MAX2((uint)(NewSize / G1HeapRegion::GrainBytes), 1U));
|
||||
}
|
||||
|
||||
if (user_specified_MaxNewSize) {
|
||||
_max_desired_young_length = MAX2((uint)(MaxNewSize / G1HeapRegion::GrainBytes), 1U);
|
||||
_max_desired_young_length.store_relaxed(MAX2((uint)(MaxNewSize / G1HeapRegion::GrainBytes), 1U));
|
||||
}
|
||||
|
||||
if (user_specified_NewSize && user_specified_MaxNewSize) {
|
||||
_sizer_kind = SizerMaxAndNewSize;
|
||||
_use_adaptive_sizing = _min_desired_young_length != _max_desired_young_length;
|
||||
_use_adaptive_sizing = min_desired_young_length() != max_desired_young_length();
|
||||
} else if (user_specified_NewSize) {
|
||||
_sizer_kind = SizerNewSizeOnly;
|
||||
} else {
|
||||
@ -159,20 +159,22 @@ void G1YoungGenSizer::recalculate_min_max_young_length(uint number_of_heap_regio
|
||||
}
|
||||
|
||||
void G1YoungGenSizer::adjust_max_new_size(uint number_of_heap_regions) {
|
||||
|
||||
// We need to pass the desired values because recalculation may not update these
|
||||
// values in some cases.
|
||||
uint temp = _min_desired_young_length;
|
||||
uint result = _max_desired_young_length;
|
||||
recalculate_min_max_young_length(number_of_heap_regions, &temp, &result);
|
||||
uint unused_new_min = min_desired_young_length();
|
||||
uint new_max = max_desired_young_length();
|
||||
recalculate_min_max_young_length(number_of_heap_regions, &unused_new_min, &new_max);
|
||||
|
||||
size_t max_young_size = result * G1HeapRegion::GrainBytes;
|
||||
size_t max_young_size = new_max * G1HeapRegion::GrainBytes;
|
||||
if (max_young_size != MaxNewSize) {
|
||||
FLAG_SET_ERGO(MaxNewSize, max_young_size);
|
||||
}
|
||||
}
|
||||
|
||||
void G1YoungGenSizer::heap_size_changed(uint new_number_of_heap_regions) {
|
||||
recalculate_min_max_young_length(new_number_of_heap_regions, &_min_desired_young_length,
|
||||
&_max_desired_young_length);
|
||||
uint min = min_desired_young_length();
|
||||
uint max = max_desired_young_length();
|
||||
recalculate_min_max_young_length(new_number_of_heap_regions, &min, &max);
|
||||
_min_desired_young_length.store_relaxed(min);
|
||||
_max_desired_young_length.store_relaxed(max);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2022, 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
|
||||
@ -25,6 +25,7 @@
|
||||
#ifndef SHARE_GC_G1_G1YOUNGGENSIZER_HPP
|
||||
#define SHARE_GC_G1_G1YOUNGGENSIZER_HPP
|
||||
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
// There are three command line options related to the young gen size:
|
||||
@ -78,8 +79,8 @@ private:
|
||||
// true otherwise.
|
||||
bool _use_adaptive_sizing;
|
||||
|
||||
uint _min_desired_young_length;
|
||||
uint _max_desired_young_length;
|
||||
Atomic<uint> _min_desired_young_length;
|
||||
Atomic<uint> _max_desired_young_length;
|
||||
|
||||
uint calculate_default_min_length(uint new_number_of_heap_regions);
|
||||
uint calculate_default_max_length(uint new_number_of_heap_regions);
|
||||
@ -96,10 +97,10 @@ public:
|
||||
|
||||
virtual void heap_size_changed(uint new_number_of_heap_regions);
|
||||
uint min_desired_young_length() const {
|
||||
return _min_desired_young_length;
|
||||
return _min_desired_young_length.load_relaxed();
|
||||
}
|
||||
uint max_desired_young_length() const {
|
||||
return _max_desired_young_length;
|
||||
return _max_desired_young_length.load_relaxed();
|
||||
}
|
||||
|
||||
bool use_adaptive_young_list_length() const {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 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
|
||||
@ -184,6 +184,10 @@
|
||||
"shrink attempt.") \
|
||||
range(0, 100) \
|
||||
\
|
||||
develop(uint, G1UncommitInitialDelay, 100, \
|
||||
"Delay in milliseconds until regions just made eligible for " \
|
||||
"uncommit are actually uncommitted.") \
|
||||
\
|
||||
product(uint, G1CPUUsageDeviationPercent, 25, DIAGNOSTIC, \
|
||||
"The acceptable deviation (in percent) from the target GC CPU " \
|
||||
"usage (based on GCTimeRatio). Creates a tolerance range " \
|
||||
|
||||
@ -1268,8 +1268,7 @@ void PSParallelCompact::marking_phase(ParallelOldTracer *gc_tracer) {
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
void PSParallelCompact::adjust_in_space_helper(SpaceId id, Atomic<uint>* claim_counter, Func&& on_stripe) {
|
||||
void PSParallelCompact::adjust_in_space_helper(SpaceId id, Atomic<uint>* claim_counter) {
|
||||
MutableSpace* sp = PSParallelCompact::space(id);
|
||||
HeapWord* const bottom = sp->bottom();
|
||||
HeapWord* const top = sp->top();
|
||||
@ -1288,53 +1287,46 @@ void PSParallelCompact::adjust_in_space_helper(SpaceId id, Atomic<uint>* claim_c
|
||||
break;
|
||||
}
|
||||
HeapWord* stripe_end = MIN2(cur_stripe + stripe_size, top);
|
||||
on_stripe(cur_stripe, stripe_end);
|
||||
adjust_in_stripe(cur_stripe, stripe_end);
|
||||
}
|
||||
}
|
||||
|
||||
size_t PSParallelCompact::adjust_in_obj_with_limit(HeapWord* obj_start, HeapWord* left, HeapWord* right) {
|
||||
precond(mark_bitmap()->is_marked(obj_start));
|
||||
oop obj = cast_to_oop(obj_start);
|
||||
return obj->oop_iterate_size(&pc_adjust_pointer_closure, MemRegion(left, right));
|
||||
}
|
||||
|
||||
void PSParallelCompact::adjust_in_stripe(HeapWord* stripe_start, HeapWord* stripe_end) {
|
||||
precond(_summary_data.is_region_aligned(stripe_start));
|
||||
|
||||
RegionData* cur_region = _summary_data.addr_to_region_ptr(stripe_start);
|
||||
HeapWord* obj_start;
|
||||
if (cur_region->partial_obj_size() != 0) {
|
||||
obj_start = cur_region->partial_obj_addr();
|
||||
obj_start += adjust_in_obj_with_limit(obj_start, stripe_start, stripe_end);
|
||||
} else {
|
||||
obj_start = stripe_start;
|
||||
}
|
||||
|
||||
while (obj_start < stripe_end) {
|
||||
obj_start = mark_bitmap()->find_obj_beg(obj_start, stripe_end);
|
||||
if (obj_start >= stripe_end) {
|
||||
break;
|
||||
}
|
||||
obj_start += adjust_in_obj_with_limit(obj_start, stripe_start, stripe_end);
|
||||
}
|
||||
}
|
||||
|
||||
void PSParallelCompact::adjust_in_old_space(Atomic<uint>* claim_counter) {
|
||||
// Regions in old-space shouldn't be split.
|
||||
assert(!_space_info[old_space_id].split_info().is_valid(), "inv");
|
||||
precond(!_space_info[old_space_id].split_info().is_valid());
|
||||
|
||||
auto scan_obj_with_limit = [&] (HeapWord* obj_start, HeapWord* left, HeapWord* right) {
|
||||
assert(mark_bitmap()->is_marked(obj_start), "inv");
|
||||
oop obj = cast_to_oop(obj_start);
|
||||
return obj->oop_iterate_size(&pc_adjust_pointer_closure, MemRegion(left, right));
|
||||
};
|
||||
|
||||
adjust_in_space_helper(old_space_id, claim_counter, [&] (HeapWord* stripe_start, HeapWord* stripe_end) {
|
||||
assert(_summary_data.is_region_aligned(stripe_start), "inv");
|
||||
RegionData* cur_region = _summary_data.addr_to_region_ptr(stripe_start);
|
||||
HeapWord* obj_start;
|
||||
if (cur_region->partial_obj_size() != 0) {
|
||||
obj_start = cur_region->partial_obj_addr();
|
||||
obj_start += scan_obj_with_limit(obj_start, stripe_start, stripe_end);
|
||||
} else {
|
||||
obj_start = stripe_start;
|
||||
}
|
||||
|
||||
while (obj_start < stripe_end) {
|
||||
obj_start = mark_bitmap()->find_obj_beg(obj_start, stripe_end);
|
||||
if (obj_start >= stripe_end) {
|
||||
break;
|
||||
}
|
||||
obj_start += scan_obj_with_limit(obj_start, stripe_start, stripe_end);
|
||||
}
|
||||
});
|
||||
adjust_in_space_helper(old_space_id, claim_counter);
|
||||
}
|
||||
|
||||
void PSParallelCompact::adjust_in_young_space(SpaceId id, Atomic<uint>* claim_counter) {
|
||||
adjust_in_space_helper(id, claim_counter, [](HeapWord* stripe_start, HeapWord* stripe_end) {
|
||||
HeapWord* obj_start = stripe_start;
|
||||
while (obj_start < stripe_end) {
|
||||
obj_start = mark_bitmap()->find_obj_beg(obj_start, stripe_end);
|
||||
if (obj_start >= stripe_end) {
|
||||
break;
|
||||
}
|
||||
oop obj = cast_to_oop(obj_start);
|
||||
obj_start += obj->oop_iterate_size(&pc_adjust_pointer_closure);
|
||||
}
|
||||
});
|
||||
adjust_in_space_helper(id, claim_counter);
|
||||
}
|
||||
|
||||
void PSParallelCompact::adjust_pointers_in_spaces(uint worker_id, Atomic<uint>* claim_counters) {
|
||||
|
||||
@ -760,8 +760,11 @@ public:
|
||||
// should_do_max_compaction controls whether all spaces for dead objs should be reclaimed.
|
||||
static bool invoke(bool clear_all_soft_refs, bool should_do_max_compaction);
|
||||
|
||||
template<typename Func>
|
||||
static void adjust_in_space_helper(SpaceId id, Atomic<uint>* claim_counter, Func&& on_stripe);
|
||||
static void adjust_in_space_helper(SpaceId id, Atomic<uint>* claim_counter);
|
||||
|
||||
static size_t adjust_in_obj_with_limit(HeapWord* obj_start, HeapWord* left, HeapWord* right);
|
||||
|
||||
static void adjust_in_stripe(HeapWord* stripe_start, HeapWord* stripe_end);
|
||||
|
||||
static void adjust_in_old_space(Atomic<uint>* claim_counter);
|
||||
|
||||
|
||||
@ -49,7 +49,14 @@ void CardTableRS::scan_old_to_young_refs(TenuredGeneration* tg, HeapWord* saved_
|
||||
void CardTableRS::maintain_old_to_young_invariant(TenuredGeneration* old_gen,
|
||||
bool is_young_gen_empty) {
|
||||
if (is_young_gen_empty) {
|
||||
clear_MemRegion(old_gen->prev_used_region());
|
||||
MemRegion prev_used_mr = old_gen->prev_used_region();
|
||||
if (!prev_used_mr.is_empty()) {
|
||||
clear_MemRegion(prev_used_mr);
|
||||
}
|
||||
{
|
||||
MemRegion old_gen_committed{old_gen->space()->bottom(), old_gen->space()->end()};
|
||||
verify_region(old_gen_committed, clean_card_val(), true);
|
||||
}
|
||||
} else {
|
||||
MemRegion used_mr = old_gen->used_region();
|
||||
MemRegion prev_used_mr = old_gen->prev_used_region();
|
||||
@ -59,7 +66,9 @@ void CardTableRS::maintain_old_to_young_invariant(TenuredGeneration* old_gen,
|
||||
}
|
||||
// No idea which card contains old-to-young pointer, so dirtying cards for
|
||||
// the entire used part of old-gen conservatively.
|
||||
dirty_MemRegion(used_mr);
|
||||
if (!used_mr.is_empty()) {
|
||||
dirty_MemRegion(used_mr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -194,6 +194,7 @@ void CardTable::resize_covered_region(MemRegion new_region) {
|
||||
// Note that these versions are precise! The scanning code has to handle the
|
||||
// fact that the write barrier may be either precise or imprecise.
|
||||
void CardTable::dirty_MemRegion(MemRegion mr) {
|
||||
assert(!mr.is_empty(), "precondition");
|
||||
assert(align_down(mr.start(), HeapWordSize) == mr.start(), "Unaligned start");
|
||||
assert(align_up (mr.end(), HeapWordSize) == mr.end(), "Unaligned end" );
|
||||
assert(_covered[0].contains(mr) || _covered[1].contains(mr), "precondition");
|
||||
@ -203,6 +204,7 @@ void CardTable::dirty_MemRegion(MemRegion mr) {
|
||||
}
|
||||
|
||||
void CardTable::clear_MemRegion(MemRegion mr) {
|
||||
assert(!mr.is_empty(), "precondition");
|
||||
// Be conservative: only clean cards entirely contained within the
|
||||
// region.
|
||||
CardValue* cur;
|
||||
|
||||
@ -130,7 +130,10 @@ oop_arraycopy_in_heap(arrayOop src_obj, size_t src_offset_in_bytes, T* src_raw,
|
||||
// pointer delta is scaled to number of elements (length field in
|
||||
// objArrayOop) which we assume is 32 bit.
|
||||
assert(pd == (size_t)(int)pd, "length field overflow");
|
||||
bs->write_ref_array((HeapWord*)dst_raw, pd);
|
||||
if (pd > 0) {
|
||||
// Copied at least one element; call the barrier.
|
||||
bs->write_ref_array((HeapWord*)dst_raw, pd);
|
||||
}
|
||||
return OopCopyResult::failed_check_class_cast;
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,10 +199,9 @@
|
||||
range(1, (INT_MAX - 1)) \
|
||||
\
|
||||
product(size_t, ReferencesPerThread, 1000, EXPERIMENTAL, \
|
||||
"Ergonomically start one thread for this amount of " \
|
||||
"references for reference processing if " \
|
||||
"ParallelRefProcEnabled is true. Specify 0 to disable and " \
|
||||
"use all threads.") \
|
||||
"Ergonomically start one thread for this amount of references " \
|
||||
"for reference processing for parallel stop-the-world garbage " \
|
||||
"collectors. Specify 0 to force use of all available threads.") \
|
||||
\
|
||||
product(uint, InitiatingHeapOccupancyPercent, 45, \
|
||||
"The percent occupancy (IHOP) of the current old generation " \
|
||||
|
||||
@ -27,7 +27,6 @@
|
||||
#include "gc/shared/barrierSet.hpp"
|
||||
#include "gc/shenandoah/c2/shenandoahBarrierSetC2.hpp"
|
||||
#include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
|
||||
#include "gc/shenandoah/shenandoahForwarding.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.hpp"
|
||||
#include "gc/shenandoah/shenandoahRuntime.hpp"
|
||||
#include "gc/shenandoah/shenandoahThreadLocalData.hpp"
|
||||
@ -712,10 +711,9 @@ void ShenandoahBarrierSetC2::print_barrier_data(outputStream* os, uint8_t data)
|
||||
fatal("Unknown bit!");
|
||||
}
|
||||
|
||||
os->print_cr(" GC configuration: %sLRB %sSATB %sCAS %sClone %sCard",
|
||||
os->print_cr(" GC configuration: %sLRB %sSATB %sClone %sCard",
|
||||
(ShenandoahLoadRefBarrier ? "+" : "-"),
|
||||
(ShenandoahSATBBarrier ? "+" : "-"),
|
||||
(ShenandoahCASBarrier ? "+" : "-"),
|
||||
(ShenandoahCloneBarrier ? "+" : "-"),
|
||||
(ShenandoahCardBarrier ? "+" : "-")
|
||||
);
|
||||
@ -912,16 +910,16 @@ void ShenandoahBarrierStubC2::load_post(MacroAssembler* masm, const MachNode* no
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahBarrierStubC2::store_pre(MacroAssembler* masm, const MachNode* node, Register obj, Address addr, Register tmp1, Register tmp2, bool narrow) {
|
||||
void ShenandoahBarrierStubC2::store_pre(MacroAssembler* masm, const MachNode* node, Address addr, Register tmp1, Register tmp2, Register tmp3, bool narrow) {
|
||||
// Store pre-barrier: SATB, keep-alive the current memory value.
|
||||
if (needs_slow_barrier(node)) {
|
||||
assert(!needs_load_ref_barrier(node), "Should not be required for stores");
|
||||
ShenandoahBarrierStubC2* const stub = create(node, obj, addr, tmp1, tmp2, narrow, /* do_load = */ true);
|
||||
ShenandoahBarrierStubC2* const stub = create(node, tmp1, addr, tmp2, tmp3, narrow, /* do_load = */ true);
|
||||
stub->enter_if_gc_state(*masm, ShenandoahHeap::MARKING, tmp1);
|
||||
}
|
||||
}
|
||||
|
||||
void ShenandoahBarrierStubC2::load_store_pre(MacroAssembler* masm, const MachNode* node, Register obj, Address addr, Register tmp1, Register tmp2, bool narrow) {
|
||||
void ShenandoahBarrierStubC2::load_store_pre(MacroAssembler* masm, const MachNode* node, Address addr, Register tmp1, Register tmp2, Register tmp3, bool narrow) {
|
||||
// Load/Store pre-barrier:
|
||||
// a. Avoids false positives from CAS encountering to-space memory values.
|
||||
// b. Satisfies the need for LRB for the CAE result.
|
||||
@ -930,7 +928,7 @@ void ShenandoahBarrierStubC2::load_store_pre(MacroAssembler* masm, const MachNod
|
||||
// (a) and (b) are covered because load barrier does memory location fixup.
|
||||
// (c) is covered by KA on the current memory value.
|
||||
if (needs_slow_barrier(node)) {
|
||||
ShenandoahBarrierStubC2* const stub = create(node, obj, addr, tmp1, tmp2, narrow, /* do_load = */ true);
|
||||
ShenandoahBarrierStubC2* const stub = create(node, tmp1, addr, tmp2, tmp3, narrow, /* do_load = */ true);
|
||||
char check = 0;
|
||||
check |= needs_keep_alive_barrier(node) ? ShenandoahHeap::MARKING : 0;
|
||||
check |= needs_load_ref_barrier(node) ? ShenandoahHeap::HAS_FORWARDED : 0;
|
||||
|
||||
@ -228,10 +228,10 @@ public:
|
||||
return needs_load_ref_barrier(node) || needs_keep_alive_barrier(node);
|
||||
}
|
||||
|
||||
static void load_post(MacroAssembler* masm, const MachNode* node, Register obj, Address addr, Register tmp1, Register tmp2, bool narrow);
|
||||
static void store_pre(MacroAssembler* masm, const MachNode* node, Register obj, Address addr, Register tmp1, Register tmp2, bool narrow);
|
||||
static void store_post(MacroAssembler* masm, const MachNode* node, Address addr, Register tmp1, Register tmp2);
|
||||
static void load_store_pre(MacroAssembler* masm, const MachNode* node, Register obj, Address addr, Register tmp1, Register tmp2, bool narrow);
|
||||
static void load_post(MacroAssembler* masm, const MachNode* node, Register obj, Address addr, Register tmp1, Register tmp2, bool narrow);
|
||||
static void store_pre(MacroAssembler* masm, const MachNode* node, Address addr, Register tmp1, Register tmp2, Register tmp3, bool narrow);
|
||||
static void store_post(MacroAssembler* masm, const MachNode* node, Address addr, Register tmp1, Register tmp2);
|
||||
static void load_store_pre(MacroAssembler* masm, const MachNode* node, Address addr, Register tmp1, Register tmp2, Register tmp3, bool narrow);
|
||||
static void load_store_post(MacroAssembler* masm, const MachNode* node, Address addr, Register tmp1, Register tmp2);
|
||||
|
||||
void emit_code(MacroAssembler& masm);
|
||||
|
||||
@ -358,7 +358,7 @@ size_t ShenandoahGenerationalHeuristics::select_aged_regions(ShenandoahInPlacePr
|
||||
|
||||
// Having chosen the collection set, adjust the budgets for generational mode based on its composition. Note
|
||||
// that young_generation->available() now knows about recently discovered immediate garbage.
|
||||
void ShenandoahGenerationalHeuristics::adjust_evacuation_budgets(ShenandoahHeap* const heap,
|
||||
void ShenandoahGenerationalHeuristics::adjust_evacuation_budgets(ShenandoahGenerationalHeap* const heap,
|
||||
ShenandoahCollectionSet* const collection_set) {
|
||||
shenandoah_assert_generational();
|
||||
// We may find that old_evacuation_reserve and/or loaned_for_young_evacuation are not fully consumed, in which case we may
|
||||
@ -481,6 +481,16 @@ void ShenandoahGenerationalHeuristics::adjust_evacuation_budgets(ShenandoahHeap*
|
||||
|
||||
if (add_regions_to_young > 0) {
|
||||
assert(excess_old >= add_regions_to_young * region_size_bytes, "Cannot xfer more than excess old");
|
||||
if (heap->age_census()->is_always_tenure()) {
|
||||
// Cap excess_old at one min-PLAB per worker so this much stays in old's promotion reserve
|
||||
// instead of being transferred to young.
|
||||
const size_t min_plab_total = heap->plab_min_size() * HeapWordSize * heap->workers()->max_workers();
|
||||
if (excess_old > min_plab_total) {
|
||||
excess_old = min_plab_total;
|
||||
// Avoid underflowing excess_old when we subtract below.
|
||||
add_regions_to_young = 0;
|
||||
}
|
||||
}
|
||||
excess_old -= add_regions_to_young * region_size_bytes;
|
||||
log_debug(gc, ergo)("Before start of evacuation, total_promotion reserve is young_advance_promoted_reserve: %zu "
|
||||
"plus excess: old: %zu", young_advance_promoted_reserve_used, excess_old);
|
||||
|
||||
@ -92,7 +92,7 @@ private:
|
||||
|
||||
// Adjust evacuation budgets after choosing collection set. On entry, the instance variable _regions_to_xfer
|
||||
// represents regions to be transferred to old based on decisions made in top_off_collection_set()
|
||||
void adjust_evacuation_budgets(ShenandoahHeap* const heap,
|
||||
void adjust_evacuation_budgets(ShenandoahGenerationalHeap* const heap,
|
||||
ShenandoahCollectionSet* const collection_set);
|
||||
|
||||
protected:
|
||||
|
||||
@ -179,6 +179,12 @@ void ShenandoahGlobalHeuristics::choose_global_collection_set(ShenandoahCollecti
|
||||
size_t free_target = (capacity * ShenandoahMinFreeThreshold) / 100 + original_young_evac_reserve;
|
||||
size_t min_garbage = (free_target > actual_free) ? (free_target - actual_free) : 0;
|
||||
|
||||
// Admit every region with any garbage so every live object gets a chance to be promoted.
|
||||
if (heap->age_census()->is_always_tenure()) {
|
||||
ignore_threshold = 0;
|
||||
min_garbage = SIZE_MAX;
|
||||
}
|
||||
|
||||
ShenandoahGlobalCSetBudget budget(region_size_bytes,
|
||||
shared_reserve_regions * region_size_bytes,
|
||||
garbage_threshold, ignore_threshold, min_garbage,
|
||||
|
||||
@ -57,7 +57,6 @@ void ShenandoahGenerationalMode::initialize_flags() const {
|
||||
// Final configuration checks
|
||||
SHENANDOAH_CHECK_FLAG_SET(ShenandoahLoadRefBarrier);
|
||||
SHENANDOAH_CHECK_FLAG_SET(ShenandoahSATBBarrier);
|
||||
SHENANDOAH_CHECK_FLAG_SET(ShenandoahCASBarrier);
|
||||
SHENANDOAH_CHECK_FLAG_SET(ShenandoahCloneBarrier);
|
||||
SHENANDOAH_CHECK_FLAG_SET(ShenandoahCardBarrier);
|
||||
}
|
||||
|
||||
@ -48,7 +48,6 @@ void ShenandoahPassiveMode::initialize_flags() const {
|
||||
// Disable known barriers by default.
|
||||
SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahLoadRefBarrier);
|
||||
SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahSATBBarrier);
|
||||
SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCASBarrier);
|
||||
SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCloneBarrier);
|
||||
SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCardBarrier);
|
||||
}
|
||||
|
||||
@ -40,7 +40,6 @@ void ShenandoahSATBMode::initialize_flags() const {
|
||||
// Final configuration checks
|
||||
SHENANDOAH_CHECK_FLAG_SET(ShenandoahLoadRefBarrier);
|
||||
SHENANDOAH_CHECK_FLAG_SET(ShenandoahSATBBarrier);
|
||||
SHENANDOAH_CHECK_FLAG_SET(ShenandoahCASBarrier);
|
||||
SHENANDOAH_CHECK_FLAG_SET(ShenandoahCloneBarrier);
|
||||
SHENANDOAH_CHECK_FLAG_UNSET(ShenandoahCardBarrier);
|
||||
}
|
||||
|
||||
@ -25,6 +25,8 @@
|
||||
#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHAFFILIATION_HPP
|
||||
#define SHARE_GC_SHENANDOAH_SHENANDOAHAFFILIATION_HPP
|
||||
|
||||
#include "utilities/debug.hpp"
|
||||
|
||||
enum ShenandoahAffiliation {
|
||||
FREE,
|
||||
YOUNG_GENERATION,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user