mirror of
https://github.com/darlinghq/darling-openjdk.git
synced 2024-11-26 22:00:25 +00:00
Merge
This commit is contained in:
commit
d77f96b0b2
@ -1,19 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="pandoc">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Building the JDK</title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
<link rel="stylesheet" href="../make/data/docs-resources/resources/jdk-default.css">
|
||||
<style type="text/css">
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
</style>
|
||||
<link rel="stylesheet" href="../make/data/docs-resources/resources/jdk-default.css" />
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
<style type="text/css">pre, code, tt { color: #1d6ae5; }</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Building the JDK</h1>
|
||||
</header>
|
||||
<nav id="TOC">
|
||||
@ -640,11 +645,13 @@ x86_64-linux-gnu-to-ppc64le-linux-gnu</code></pre>
|
||||
<p>Note that alsa is needed even if you only want to build a headless JDK.</p>
|
||||
<ul>
|
||||
<li><p>Go to <a href="https://www.debian.org/distrib/packages">Debian Package Search</a> and search for the <code>libasound2</code> and <code>libasound2-dev</code> packages for your <em>target</em> system. Download them to /tmp.</p></li>
|
||||
<li><p>Install the libraries into the cross-compilation toolchain. For instance:</p>
|
||||
<li>Install the libraries into the cross-compilation toolchain. For instance:</li>
|
||||
</ul>
|
||||
<pre><code>cd /tools/gcc-linaro-arm-linux-gnueabihf-raspbian-2012.09-20120921_linux/arm-linux-gnueabihf/libc
|
||||
dpkg-deb -x /tmp/libasound2_1.0.25-4_armhf.deb .
|
||||
dpkg-deb -x /tmp/libasound2-dev_1.0.25-4_armhf.deb .</code></pre></li>
|
||||
<li><p>If alsa is not properly detected by <code>configure</code>, you can point it out by <code>--with-alsa</code>.</p></li>
|
||||
dpkg-deb -x /tmp/libasound2-dev_1.0.25-4_armhf.deb .</code></pre>
|
||||
<ul>
|
||||
<li>If alsa is not properly detected by <code>configure</code>, you can point it out by <code>--with-alsa</code>.</li>
|
||||
</ul>
|
||||
<h4 id="x11-1">X11</h4>
|
||||
<p>You will need X11 libraries suitable for your <em>target</em> system. For most cases, using Debian's pre-built libraries work fine.</p>
|
||||
@ -690,17 +697,21 @@ cp: cannot stat `arm-linux-gnueabihf/libXt.so': No such file or directory</c
|
||||
<p>Fortunately, you can create sysroots for foreign architectures with tools provided by your OS. On Debian/Ubuntu systems, one could use <code>qemu-deboostrap</code> to create the <em>target</em> system chroot, which would have the native libraries and headers specific to that <em>target</em> system. After that, we can use the cross-compiler on the <em>build</em> system, pointing into chroot to get the build dependencies right. This allows building for foreign architectures with native compilation speed.</p>
|
||||
<p>For example, cross-compiling to AArch64 from x86_64 could be done like this:</p>
|
||||
<ul>
|
||||
<li><p>Install cross-compiler on the <em>build</em> system:</p>
|
||||
<pre><code>apt install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu</code></pre></li>
|
||||
<li><p>Create chroot on the <em>build</em> system, configuring it for <em>target</em> system:</p>
|
||||
<li>Install cross-compiler on the <em>build</em> system:</li>
|
||||
</ul>
|
||||
<pre><code>apt install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu</code></pre>
|
||||
<ul>
|
||||
<li>Create chroot on the <em>build</em> system, configuring it for <em>target</em> system:</li>
|
||||
</ul>
|
||||
<pre><code>sudo qemu-debootstrap --arch=arm64 --verbose \
|
||||
--include=fakeroot,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng12-dev \
|
||||
--resolve-deps jessie /chroots/arm64 http://httpredir.debian.org/debian/</code></pre></li>
|
||||
<li><p>Configure and build with newly created chroot as sysroot/toolchain-path:</p>
|
||||
--include=fakeroot,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng12-dev \
|
||||
--resolve-deps jessie /chroots/arm64 http://httpredir.debian.org/debian/</code></pre>
|
||||
<ul>
|
||||
<li>Configure and build with newly created chroot as sysroot/toolchain-path:</li>
|
||||
</ul>
|
||||
<pre><code>CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ sh ./configure --openjdk-target=aarch64-linux-gnu --with-sysroot=/chroots/arm64/ --with-toolchain-path=/chroots/arm64/
|
||||
make images
|
||||
ls build/linux-aarch64-normal-server-release/</code></pre></li>
|
||||
</ul>
|
||||
ls build/linux-aarch64-normal-server-release/</code></pre>
|
||||
<p>The build does not create new files in that chroot, so it can be reused for multiple builds without additional cleanup.</p>
|
||||
<p>Architectures that are known to successfully cross-compile like this are:</p>
|
||||
<table>
|
||||
@ -860,12 +871,7 @@ cannot create ... Permission denied
|
||||
spawn failed</code></pre>
|
||||
<p>This can be a sign of a Cygwin problem. See the information about solving problems in the <a href="#cygwin">Cygwin</a> section. Rebooting the computer might help temporarily.</p>
|
||||
<h3 id="getting-help">Getting Help</h3>
|
||||
<p>If none of the suggestions in this document helps you, or if you find what you believe is a bug in the build system, please contact the Build Group by sending a mail to <script type="text/javascript">
|
||||
<!--
|
||||
h='openjdk.java.net';a='@';n='build-dev';e=n+a+h;
|
||||
document.write('<a h'+'ref'+'="ma'+'ilto'+':'+e+'" clas'+'s="em' + 'ail">'+e+'<\/'+'a'+'>');
|
||||
// -->
|
||||
</script><noscript>build-dev at openjdk dot java dot net</noscript>. Please include the relevant parts of the configure and/or build log.</p>
|
||||
<p>If none of the suggestions in this document helps you, or if you find what you believe is a bug in the build system, please contact the Build Group by sending a mail to <a href="mailto:build-dev@openjdk.java.net">build-dev@openjdk.java.net</a>. Please include the relevant parts of the configure and/or build log.</p>
|
||||
<p>If you need general help or advice about developing for the JDK, you can also contact the Adoption Group. See the section on <a href="#contributing-to-openjdk">Contributing to OpenJDK</a> for more information.</p>
|
||||
<h2 id="hints-and-suggestions-for-advanced-users">Hints and Suggestions for Advanced Users</h2>
|
||||
<h3 id="setting-up-a-repository-for-pushing-changes-defpath">Setting Up a Repository for Pushing Changes (defpath)</h3>
|
||||
|
@ -918,7 +918,7 @@ targets are given, a native toolchain for the current platform will be
|
||||
created. Currently, at least the following targets are known to work:
|
||||
|
||||
Supported devkit targets
|
||||
------------------------
|
||||
-------------------------
|
||||
x86_64-linux-gnu
|
||||
aarch64-linux-gnu
|
||||
arm-linux-gnueabihf
|
||||
@ -1129,13 +1129,13 @@ without additional cleanup.
|
||||
|
||||
Architectures that are known to successfully cross-compile like this are:
|
||||
|
||||
Target `CC` `CXX` `--arch=...` `--openjdk-target=...`
|
||||
------------ ------------------------- --------------------------- ------------ ----------------------
|
||||
x86 default default i386 i386-linux-gnu
|
||||
armhf gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf armhf arm-linux-gnueabihf
|
||||
aarch64 gcc-aarch64-linux-gnu g++-aarch64-linux-gnu arm64 aarch64-linux-gnu
|
||||
ppc64el gcc-powerpc64le-linux-gnu g++-powerpc64le-linux-gnu ppc64el powerpc64le-linux-gnu
|
||||
s390x gcc-s390x-linux-gnu g++-s390x-linux-gnu s390x s390x-linux-gnu
|
||||
Target `CC` `CXX` `--arch=...` `--openjdk-target=...`
|
||||
------------ ------------------------- --------------------------- ------------- -----------------------
|
||||
x86 default default i386 i386-linux-gnu
|
||||
armhf gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf armhf arm-linux-gnueabihf
|
||||
aarch64 gcc-aarch64-linux-gnu g++-aarch64-linux-gnu arm64 aarch64-linux-gnu
|
||||
ppc64el gcc-powerpc64le-linux-gnu g++-powerpc64le-linux-gnu ppc64el powerpc64le-linux-gnu
|
||||
s390x gcc-s390x-linux-gnu g++-s390x-linux-gnu s390x s390x-linux-gnu
|
||||
|
||||
Additional architectures might be supported by Debian/Ubuntu Ports.
|
||||
|
||||
|
@ -1,19 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="pandoc">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<meta charset="utf-8" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||
<title>Testing the JDK</title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
<link rel="stylesheet" href="../make/data/docs-resources/resources/jdk-default.css">
|
||||
<style type="text/css">
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
span.underline{text-decoration: underline;}
|
||||
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||
</style>
|
||||
<link rel="stylesheet" href="../make/data/docs-resources/resources/jdk-default.css" />
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
<style type="text/css">pre, code, tt { color: #1d6ae5; }</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<header id="title-block-header">
|
||||
<h1 class="title">Testing the JDK</h1>
|
||||
</header>
|
||||
<nav id="TOC">
|
||||
|
@ -321,7 +321,7 @@ jdk.jshell_COPY += .jsh .properties
|
||||
|
||||
################################################################################
|
||||
|
||||
jdk.internal.le_COPY += .properties
|
||||
jdk.internal.le_COPY += .properties .caps .txt
|
||||
|
||||
################################################################################
|
||||
|
||||
|
@ -91,15 +91,17 @@ TARGETS += $(COMPILE_DEPEND) $(DEPEND_SERVICE_PROVIDER)
|
||||
# To be able to call the javascript filter when generating man pages using
|
||||
# pandoc, we need to create this executable wrapper script.
|
||||
ifneq ($(PANDOC), )
|
||||
# PANDOC_FILTER is duplicated for export in ToolsJdk.gmk.
|
||||
PANDOC_FILTER := $(BUILDTOOLS_OUTPUTDIR)/manpages/pandoc-manpage-filter
|
||||
PANDOC_FILTER_SETUP := $(BUILDTOOLS_OUTPUTDIR)/manpages/_pandoc_filter_setup.marker
|
||||
# PANDOC_TROFF_MANPAGE_FILTER is duplicated for export in ToolsJdk.gmk.
|
||||
PANDOC_TROFF_MANPAGE_FILTER := \
|
||||
$(BUILDTOOLS_OUTPUTDIR)/manpages/pandoc-troff-manpage-filter
|
||||
PANDOC_TROFF_MANPAGE_FILTER_SETUP := \
|
||||
$(BUILDTOOLS_OUTPUTDIR)/manpages/_pandoc_troff_manpage_filter_setup.marker
|
||||
|
||||
# Create a usable instance of the wrapper script that calls the pandoc filter
|
||||
# (which is written in javascript).
|
||||
$(eval $(call SetupTextFileProcessing, CREATE_PANDOC_FILTER, \
|
||||
SOURCE_FILES := $(TOPDIR)/make/scripts/pandoc-manpage-filter.sh.template, \
|
||||
OUTPUT_FILE := $(PANDOC_FILTER), \
|
||||
$(eval $(call SetupTextFileProcessing, CREATE_PANDOC_TROFF_MANPAGE_FILTER, \
|
||||
SOURCE_FILES := $(TOPDIR)/make/scripts/pandoc-troff-manpage-filter.sh.template, \
|
||||
OUTPUT_FILE := $(PANDOC_TROFF_MANPAGE_FILTER), \
|
||||
REPLACEMENTS := \
|
||||
@@BOOT_JDK@@ => $(BOOT_JDK) ; \
|
||||
@@TOPDIR@@ => $(TOPDIR) ; \
|
||||
@ -107,11 +109,35 @@ ifneq ($(PANDOC), )
|
||||
))
|
||||
|
||||
# Created script must be made executable
|
||||
$(PANDOC_FILTER_SETUP): $(CREATE_PANDOC_FILTER)
|
||||
$(CHMOD) a+rx $(PANDOC_FILTER)
|
||||
$(PANDOC_TROFF_MANPAGE_FILTER_SETUP): $(CREATE_PANDOC_TROFF_MANPAGE_FILTER)
|
||||
$(CHMOD) a+rx $(PANDOC_TROFF_MANPAGE_FILTER)
|
||||
$(TOUCH) $@
|
||||
|
||||
TARGETS += $(PANDOC_FILTER_SETUP)
|
||||
TARGETS += $(PANDOC_TROFF_MANPAGE_FILTER_SETUP)
|
||||
|
||||
# PANDOC_HTML_MANPAGE_FILTER is duplicated for export in ToolsJdk.gmk.
|
||||
PANDOC_HTML_MANPAGE_FILTER := \
|
||||
$(BUILDTOOLS_OUTPUTDIR)/manpages/pandoc-html-manpage-filter
|
||||
PANDOC_HTML_MANPAGE_FILTER_SETUP := \
|
||||
$(BUILDTOOLS_OUTPUTDIR)/manpages/_pandoc_html_manpage_filter_setup.marker
|
||||
|
||||
# Create a usable instance of the wrapper script that calls the pandoc filter
|
||||
# (which is written in javascript).
|
||||
$(eval $(call SetupTextFileProcessing, CREATE_PANDOC_HTML_MANPAGE_FILTER, \
|
||||
SOURCE_FILES := $(TOPDIR)/make/scripts/pandoc-html-manpage-filter.sh.template, \
|
||||
OUTPUT_FILE := $(PANDOC_HTML_MANPAGE_FILTER), \
|
||||
REPLACEMENTS := \
|
||||
@@BOOT_JDK@@ => $(BOOT_JDK) ; \
|
||||
@@TOPDIR@@ => $(TOPDIR) ; \
|
||||
@@JJS_FLAGS@@ => $(addprefix -J, $(JAVA_FLAGS_SMALL)), \
|
||||
))
|
||||
|
||||
# Created script must be made executable
|
||||
$(PANDOC_HTML_MANPAGE_FILTER_SETUP): $(CREATE_PANDOC_HTML_MANPAGE_FILTER)
|
||||
$(CHMOD) a+rx $(PANDOC_HTML_MANPAGE_FILTER)
|
||||
$(TOUCH) $@
|
||||
|
||||
TARGETS += $(PANDOC_HTML_MANPAGE_FILTER_SETUP)
|
||||
endif
|
||||
|
||||
all: $(TARGETS)
|
||||
|
@ -27,10 +27,10 @@ default: all
|
||||
include $(SPEC)
|
||||
include MakeBase.gmk
|
||||
include Modules.gmk
|
||||
include ModuleTools.gmk
|
||||
include ProcessMarkdown.gmk
|
||||
include ToolsJdk.gmk
|
||||
include ZipArchive.gmk
|
||||
include $(TOPDIR)/make/ToolsJdk.gmk
|
||||
include $(TOPDIR)/make/ModuleTools.gmk
|
||||
|
||||
# This is needed to properly setup DOCS_MODULES.
|
||||
$(eval $(call ReadImportMetaData))
|
||||
@ -168,14 +168,6 @@ JAVADOC_TOP := \
|
||||
font-family: DejaVu Sans, Arial, Helvetica, sans-serif; \
|
||||
font-weight: normal;">$(DRAFT_TEXT)</div>
|
||||
|
||||
JDK_INDEX_CONTENT := \
|
||||
<!DOCTYPE html> \
|
||||
<html lang="en"> \
|
||||
<head> \
|
||||
<meta http-equiv="refresh" content="0;url=api/index.html"> \
|
||||
</head> \
|
||||
</html>
|
||||
|
||||
################################################################################
|
||||
# JDK javadoc titles/text snippets
|
||||
|
||||
@ -485,14 +477,7 @@ $(eval $(call SetupApiDocsGeneration, REFERENCE_API, \
|
||||
|
||||
################################################################################
|
||||
|
||||
JDK_INDEX_HTML := $(DOCS_OUTPUTDIR)/index.html
|
||||
|
||||
$(JDK_INDEX_HTML):
|
||||
$(ECHO) '$(JDK_INDEX_CONTENT)' > $@
|
||||
|
||||
JDK_INDEX_TARGETS += $(JDK_INDEX_HTML)
|
||||
|
||||
# Copy the global resources
|
||||
# Copy the global resources, including the top-level redirect index.html
|
||||
GLOBAL_SPECS_RESOURCES_DIR := $(TOPDIR)/make/data/docs-resources/
|
||||
$(eval $(call SetupCopyFiles, COPY_GLOBAL_RESOURCES, \
|
||||
SRC := $(GLOBAL_SPECS_RESOURCES_DIR), \
|
||||
@ -532,9 +517,9 @@ $(foreach m, $(ALL_MODULES), \
|
||||
) \
|
||||
)
|
||||
|
||||
ifeq ($(ENABLE_FULL_DOCS), true)
|
||||
ifneq ($(PANDOC), )
|
||||
# For all markdown files in $module/share/specs directories, convert them to
|
||||
# html.
|
||||
# html, if we have pandoc (otherwise we'll just skip this).
|
||||
|
||||
GLOBAL_SPECS_DEFAULT_CSS_FILE := $(DOCS_OUTPUTDIR)/resources/jdk-default.css
|
||||
|
||||
@ -556,6 +541,15 @@ ifeq ($(ENABLE_FULL_DOCS), true)
|
||||
|
||||
# For all markdown files in $module/share/man directories, convert them to
|
||||
# html.
|
||||
|
||||
# Create dynamic man pages from markdown using pandoc. We need
|
||||
# PANDOC_HTML_MANPAGE_FILTER, a wrapper around
|
||||
# PANDOC_HTML_MANPAGE_FILTER_JAVASCRIPT. This is created by buildtools-jdk.
|
||||
|
||||
# We should also depend on the source javascript filter
|
||||
PANDOC_HTML_MANPAGE_FILTER_JAVASCRIPT := \
|
||||
$(TOPDIR)/make/scripts/pandoc-html-manpage-filter.js
|
||||
|
||||
$(foreach m, $(ALL_MODULES), \
|
||||
$(eval MAN_$m := $(call FindModuleManDirs, $m)) \
|
||||
$(foreach d, $(MAN_$m), \
|
||||
@ -565,8 +559,11 @@ ifeq ($(ENABLE_FULL_DOCS), true)
|
||||
SRC := $d, \
|
||||
FILES := $(filter %.md, $(call CacheFind, $d)), \
|
||||
DEST := $(DOCS_OUTPUTDIR)/specs/man, \
|
||||
FILTER := $(PANDOC_HTML_MANPAGE_FILTER), \
|
||||
CSS := $(GLOBAL_SPECS_DEFAULT_CSS_FILE), \
|
||||
REPLACEMENTS := @@VERSION_SHORT@@ => $(VERSION_SHORT), \
|
||||
EXTRA_DEPS := $(PANDOC_HTML_MANPAGE_FILTER) \
|
||||
$(PANDOC_HTML_MANPAGE_FILTER_JAVASCRIPT), \
|
||||
)) \
|
||||
$(eval JDK_SPECS_TARGETS += $($($m_$d_NAME))) \
|
||||
) \
|
||||
|
@ -36,7 +36,7 @@ JRE_TARGETS :=
|
||||
# Hook to include the corresponding custom file, if present.
|
||||
$(eval $(call IncludeCustomExtension, Images-pre.gmk))
|
||||
|
||||
############################################################################
|
||||
################################################################################
|
||||
|
||||
# All modules for the current target platform.
|
||||
ALL_MODULES := $(call FindAllModules)
|
||||
@ -57,7 +57,7 @@ BASE_RELEASE_FILE := $(JDK_OUTPUTDIR)/release
|
||||
JMODS := $(wildcard $(IMAGES_OUTPUTDIR)/jmods/*.jmod)
|
||||
|
||||
# Use this file inside the image as target for make rule
|
||||
JIMAGE_TARGET_FILE := bin/java$(EXE_SUFFIX)
|
||||
JIMAGE_TARGET_FILE := release
|
||||
|
||||
JLINK_ORDER_RESOURCES := **module-info.class
|
||||
JLINK_JLI_CLASSES :=
|
||||
|
@ -862,8 +862,6 @@ else
|
||||
docs-jdk-specs: $(JVM_DOCS_TARGETS) jdk.jdi-gensrc \
|
||||
docs-jdk-index
|
||||
|
||||
docs-jdk-index: exploded-image buildtools-modules
|
||||
|
||||
docs-zip: docs-jdk
|
||||
|
||||
# Tests
|
||||
|
@ -118,7 +118,8 @@ TOOL_PUBLICSUFFIXLIST = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_clas
|
||||
|
||||
# Executable javascript filter for man page generation using pandoc.
|
||||
|
||||
PANDOC_FILTER := $(BUILDTOOLS_OUTPUTDIR)/manpages/pandoc-manpage-filter
|
||||
PANDOC_TROFF_MANPAGE_FILTER := $(BUILDTOOLS_OUTPUTDIR)/manpages/pandoc-troff-manpage-filter
|
||||
PANDOC_HTML_MANPAGE_FILTER := $(BUILDTOOLS_OUTPUTDIR)/manpages/pandoc-html-manpage-filter
|
||||
|
||||
##########################################################################################
|
||||
|
||||
|
@ -80,7 +80,7 @@ define ProcessMarkdown
|
||||
$$(call LogInfo, Converting $2 to $$($1_FORMAT))
|
||||
$$(call MakeDir, $$(SUPPORT_OUTPUTDIR)/markdown $$(dir $$($1_$2_PANDOC_OUTPUT)))
|
||||
$$(call ExecuteWithLog, $$(SUPPORT_OUTPUTDIR)/markdown/$$($1_$2_MARKER), \
|
||||
$$(PANDOC) $$($1_OPTIONS) -f markdown -t $$($1_FORMAT) --standalone \
|
||||
$$(PANDOC) $$($1_OPTIONS) -f markdown-smart -t $$($1_FORMAT) --standalone \
|
||||
$$($1_$2_CSS_OPTION) $$($1_$2_OPTIONS) '$$($1_$2_PANDOC_INPUT)' \
|
||||
-o '$$($1_$2_PANDOC_OUTPUT)')
|
||||
ifneq ($$(findstring $$(LOG_LEVEL), debug trace),)
|
||||
|
@ -754,6 +754,21 @@ class CharacterData00 extends CharacterData {
|
||||
return retval;
|
||||
}
|
||||
|
||||
boolean isDigit(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.DECIMAL_DIGIT_NUMBER;
|
||||
}
|
||||
|
||||
boolean isLowerCase(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.LOWERCASE_LETTER;
|
||||
}
|
||||
|
||||
boolean isUpperCase(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.UPPERCASE_LETTER;
|
||||
}
|
||||
|
||||
boolean isWhitespace(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return ((props & $$maskIdentifierInfo) == $$valueJavaWhitespace);
|
||||
|
@ -460,6 +460,21 @@ class CharacterData01 extends CharacterData {
|
||||
return retval;
|
||||
}
|
||||
|
||||
boolean isDigit(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.DECIMAL_DIGIT_NUMBER;
|
||||
}
|
||||
|
||||
boolean isLowerCase(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.LOWERCASE_LETTER;
|
||||
}
|
||||
|
||||
boolean isUpperCase(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.UPPERCASE_LETTER;
|
||||
}
|
||||
|
||||
boolean isWhitespace(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return ((props & $$maskIdentifierInfo) == $$valueJavaWhitespace);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2018, 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
|
||||
@ -217,6 +217,21 @@ class CharacterData02 extends CharacterData {
|
||||
return retval;
|
||||
}
|
||||
|
||||
boolean isDigit(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.DECIMAL_DIGIT_NUMBER;
|
||||
}
|
||||
|
||||
boolean isLowerCase(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.LOWERCASE_LETTER;
|
||||
}
|
||||
|
||||
boolean isUpperCase(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.UPPERCASE_LETTER;
|
||||
}
|
||||
|
||||
boolean isWhitespace(int ch) {
|
||||
return (getProperties(ch) & $$maskIdentifierInfo) == $$valueJavaWhitespace;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2018, 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
|
||||
@ -217,6 +217,21 @@ class CharacterData0E extends CharacterData {
|
||||
return retval;
|
||||
}
|
||||
|
||||
boolean isDigit(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.DECIMAL_DIGIT_NUMBER;
|
||||
}
|
||||
|
||||
boolean isLowerCase(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.LOWERCASE_LETTER;
|
||||
}
|
||||
|
||||
boolean isUpperCase(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.UPPERCASE_LETTER;
|
||||
}
|
||||
|
||||
boolean isWhitespace(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return ((props & $$maskIdentifierInfo) == $$valueJavaWhitespace);
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package java.lang;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
|
||||
/** The CharacterData class encapsulates the large tables found in
|
||||
Java.lang.Character. */
|
||||
|
||||
@ -78,6 +80,23 @@ class CharacterDataLatin1 extends CharacterData {
|
||||
return props;
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
boolean isDigit(int ch) {
|
||||
return '0' <= ch && ch <= '9';
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
boolean isLowerCase(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.LOWERCASE_LETTER;
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
boolean isUpperCase(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return (props & $$maskType) == Character.UPPERCASE_LETTER;
|
||||
}
|
||||
|
||||
boolean isOtherLowercase(int ch) {
|
||||
int props = getPropertiesEx(ch);
|
||||
return (props & $$maskOtherLowercase) != 0;
|
||||
@ -214,6 +233,7 @@ class CharacterDataLatin1 extends CharacterData {
|
||||
return retval;
|
||||
}
|
||||
|
||||
@HotSpotIntrinsicCandidate
|
||||
boolean isWhitespace(int ch) {
|
||||
int props = getProperties(ch);
|
||||
return ((props & $$maskIdentifierInfo) == $$valueJavaWhitespace);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2018, 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
|
||||
@ -41,47 +41,59 @@ class CharacterDataPrivateUse extends CharacterData {
|
||||
}
|
||||
|
||||
boolean isJavaIdentifierStart(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isJavaIdentifierPart(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isUnicodeIdentifierStart(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isUnicodeIdentifierPart(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isIdentifierIgnorable(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
int toLowerCase(int ch) {
|
||||
return ch;
|
||||
return ch;
|
||||
}
|
||||
|
||||
int toUpperCase(int ch) {
|
||||
return ch;
|
||||
return ch;
|
||||
}
|
||||
|
||||
int toTitleCase(int ch) {
|
||||
return ch;
|
||||
return ch;
|
||||
}
|
||||
|
||||
int digit(int ch, int radix) {
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int getNumericValue(int ch) {
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean isDigit(int ch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isLowerCase(int ch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isUpperCase(int ch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isWhitespace(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
byte getDirectionality(int ch) {
|
||||
@ -91,7 +103,7 @@ class CharacterDataPrivateUse extends CharacterData {
|
||||
}
|
||||
|
||||
boolean isMirrored(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
static final CharacterData instance = new CharacterDataPrivateUse();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2018, 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
|
||||
@ -35,59 +35,71 @@ class CharacterDataUndefined extends CharacterData {
|
||||
}
|
||||
|
||||
int getType(int ch) {
|
||||
return Character.UNASSIGNED;
|
||||
return Character.UNASSIGNED;
|
||||
}
|
||||
|
||||
boolean isJavaIdentifierStart(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isJavaIdentifierPart(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isUnicodeIdentifierStart(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isUnicodeIdentifierPart(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isIdentifierIgnorable(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
int toLowerCase(int ch) {
|
||||
return ch;
|
||||
return ch;
|
||||
}
|
||||
|
||||
int toUpperCase(int ch) {
|
||||
return ch;
|
||||
return ch;
|
||||
}
|
||||
|
||||
int toTitleCase(int ch) {
|
||||
return ch;
|
||||
return ch;
|
||||
}
|
||||
|
||||
int digit(int ch, int radix) {
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int getNumericValue(int ch) {
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean isDigit(int ch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isLowerCase(int ch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isUpperCase(int ch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isWhitespace(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
byte getDirectionality(int ch) {
|
||||
return Character.DIRECTIONALITY_UNDEFINED;
|
||||
return Character.DIRECTIONALITY_UNDEFINED;
|
||||
}
|
||||
|
||||
boolean isMirrored(int ch) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
static final CharacterData instance = new CharacterDataUndefined();
|
||||
|
@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<!--
|
||||
Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
|
||||
Copyright (c) 2018, 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
|
||||
@ -22,34 +23,11 @@
|
||||
or visit www.oracle.com if you need additional information or have any
|
||||
questions.
|
||||
-->
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<html>
|
||||
<body bgcolor="white">
|
||||
|
||||
Provides classes and interface for RMI distributed
|
||||
garbage-collection (DGC). When the RMI server returns an object to
|
||||
its client (caller of the remote method), it tracks the remote
|
||||
object's usage in the client. When there are no more references to the
|
||||
remote object on the client, or if the reference's ``lease'' expires and
|
||||
not renewed, the server garbage-collects the remote object.
|
||||
|
||||
<!--
|
||||
<h2>Package Specification</h2>
|
||||
|
||||
##### FILL IN ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT #####
|
||||
<ul>
|
||||
<li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a>
|
||||
</ul>
|
||||
|
||||
<h2>Related Documentation</h2>
|
||||
|
||||
For overviews, tutorials, examples, guides, and tool documentation, please see:
|
||||
<ul>
|
||||
<li><a href="">##### REFER TO NON-SPEC DOCUMENTATION HERE #####</a>
|
||||
</ul>
|
||||
-->
|
||||
|
||||
@since 1.1
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;url=api/index.html">
|
||||
<title>Java API Documentation redirect</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2018, 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
|
||||
@ -36,11 +36,13 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
@ -552,6 +554,21 @@ public class ModuleSummary {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String providesEntry(Provides p) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("provides %s<br>\n", p.service()));
|
||||
List<String> pvs = new ArrayList<>(p.providers());
|
||||
pvs.sort(Comparator.naturalOrder());
|
||||
for (int i = 0; i < pvs.size(); i++) { // My kingdom for Stream::zip ...
|
||||
String fmt = ((i == 0)
|
||||
? " with %s"
|
||||
: ",<br> %s");
|
||||
sb.append(String.format(fmt, pvs.get(i)));
|
||||
}
|
||||
sb.append("\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String servicesColumn() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format(" <td class=\"%s\">", CODE));
|
||||
@ -560,8 +577,7 @@ public class ModuleSummary {
|
||||
.forEach(s -> sb.append("uses ").append(s).append("<br>").append("\n"));
|
||||
ms.descriptor().provides().stream()
|
||||
.sorted(Comparator.comparing(Provides::service))
|
||||
.map(p -> String.format("provides %s<br> with %s",
|
||||
p.service(), p.providers()))
|
||||
.map(this::providesEntry)
|
||||
.forEach(p -> sb.append(p).append("<br>").append("\n"));
|
||||
sb.append("</td>");
|
||||
return sb.toString();
|
||||
|
@ -207,11 +207,12 @@ ifeq ($(OPENJDK_TARGET_OS_TYPE), unix)
|
||||
$(info Warning: pandoc not found. Not generating man pages)
|
||||
else
|
||||
# Create dynamic man pages from markdown using pandoc. We need
|
||||
# PANDOC_FILTER, a wrapper around PANDOC_FILTER_JAVASCRIPT. This is
|
||||
# created by buildtools-jdk.
|
||||
# PANDOC_TROFF_MANPAGE_FILTER, a wrapper around
|
||||
# PANDOC_TROFF_MANPAGE_FILTER_JAVASCRIPT. This is created by buildtools-jdk.
|
||||
|
||||
# We should also depend on the source javascript filter
|
||||
PANDOC_FILTER_JAVASCRIPT := $(TOPDIR)/make/scripts/pandoc-manpage-filter.js
|
||||
PANDOC_TROFF_MANPAGE_FILTER_JAVASCRIPT := \
|
||||
$(TOPDIR)/make/scripts/pandoc-troff-manpage-filter.js
|
||||
|
||||
# The norm in man pages is to display code literals as bold, but pandoc
|
||||
# "correctly" converts these constructs (encoded in markdown using `...`
|
||||
@ -234,10 +235,11 @@ ifeq ($(OPENJDK_TARGET_OS_TYPE), unix)
|
||||
DEST := $(SUPPORT_OUTPUTDIR)/modules_man/$(MODULE)/man1, \
|
||||
FILES := $(MAN_FILES_MD), \
|
||||
FORMAT := man, \
|
||||
FILTER := $(PANDOC_FILTER), \
|
||||
FILTER := $(PANDOC_TROFF_MANPAGE_FILTER), \
|
||||
POST_PROCESS := $(MAN_POST_PROCESS), \
|
||||
REPLACEMENTS := @@VERSION_SHORT@@ => $(VERSION_SHORT), \
|
||||
EXTRA_DEPS := $(PANDOC_FILTER) $(PANDOC_FILTER_JAVASCRIPT), \
|
||||
EXTRA_DEPS := $(PANDOC_TROFF_MANPAGE_FILTER) \
|
||||
$(PANDOC_TROFF_MANPAGE_FILTER_JAVASCRIPT), \
|
||||
))
|
||||
|
||||
TARGETS += $(BUILD_MAN_PAGES)
|
||||
|
125
make/scripts/pandoc-html-manpage-filter.js
Normal file
125
make/scripts/pandoc-html-manpage-filter.js
Normal file
@ -0,0 +1,125 @@
|
||||
//
|
||||
// Copyright (c) 2018, 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.
|
||||
//
|
||||
|
||||
//
|
||||
// Traverse a tree of pandoc format objects, calling callback on each
|
||||
// element, and replacing it if callback returns a new object.
|
||||
//
|
||||
// Inspired by the walk method in
|
||||
// https://github.com/jgm/pandocfilters/blob/master/pandocfilters.py
|
||||
//
|
||||
function traverse(obj, callback) {
|
||||
if (Array.isArray(obj)) {
|
||||
var processed_array = [];
|
||||
obj.forEach(function(elem) {
|
||||
if (elem === Object(elem) && elem.t) {
|
||||
var replacement = callback(elem.t, elem.c || []);
|
||||
if (!replacement) {
|
||||
// no replacement object returned, use original
|
||||
processed_array.push(traverse(elem, callback));
|
||||
} else if (Array.isArray(replacement)) {
|
||||
// array of objects returned, splice all elements into array
|
||||
replacement.forEach(function(repl_elem) {
|
||||
processed_array.push(traverse(repl_elem, callback));
|
||||
})
|
||||
} else {
|
||||
// replacement object given, traverse it
|
||||
processed_array.push(traverse(replacement, callback));
|
||||
}
|
||||
} else {
|
||||
processed_array.push(traverse(elem, callback));
|
||||
}
|
||||
})
|
||||
return processed_array;
|
||||
} else if (obj === Object(obj)) {
|
||||
if (obj.t) {
|
||||
var replacement = callback(obj.t, obj.c || []);
|
||||
if (replacement) {
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
var processed_obj = {};
|
||||
Object.keys(obj).forEach(function(key) {
|
||||
processed_obj[key] = traverse(obj[key], callback);
|
||||
})
|
||||
return processed_obj;
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Helper constructors to create pandoc format objects
|
||||
//
|
||||
function Space() {
|
||||
return { 't': 'Space' };
|
||||
}
|
||||
|
||||
function Str(value) {
|
||||
return { 't': 'Str', 'c': value };
|
||||
}
|
||||
|
||||
function MetaInlines(value) {
|
||||
return { 't': 'MetaInlines', 'c': value };
|
||||
}
|
||||
|
||||
function change_title(type, value) {
|
||||
if (type === 'MetaInlines') {
|
||||
if (value[0].t === 'Str') {
|
||||
var match = value[0].c.match(/^([A-Z]+)\([0-9]+\)$/);
|
||||
if (match) {
|
||||
return MetaInlines([
|
||||
Str("The"), Space(),
|
||||
Str(match[1].toLowerCase()),
|
||||
Space(), Str("Command")
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Main function
|
||||
//
|
||||
function main() {
|
||||
var input = "";
|
||||
while (line = readLine()) {
|
||||
input = input.concat(line);
|
||||
}
|
||||
|
||||
var json = JSON.parse(input);
|
||||
|
||||
var meta = json.meta;
|
||||
if (meta) {
|
||||
meta.date = undefined;
|
||||
var title = meta.title;
|
||||
if (meta.title) {
|
||||
meta.title = traverse(meta.title, change_title);
|
||||
}
|
||||
}
|
||||
|
||||
print(JSON.stringify(json));
|
||||
}
|
||||
|
||||
// ... and execute it
|
||||
main();
|
@ -25,4 +25,4 @@
|
||||
# Simple wrapper script to call Nashorn with the javascript pandoc filter
|
||||
|
||||
@@BOOT_JDK@@/bin/jjs @@JJS_FLAGS@@ -scripting \
|
||||
"@@TOPDIR@@/make/scripts/pandoc-manpage-filter.js" 2> /dev/null
|
||||
"@@TOPDIR@@/make/scripts/pandoc-html-manpage-filter.js" 2> /dev/null
|
28
make/scripts/pandoc-troff-manpage-filter.sh.template
Normal file
28
make/scripts/pandoc-troff-manpage-filter.sh.template
Normal file
@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2018, 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.
|
||||
#
|
||||
|
||||
# Simple wrapper script to call Nashorn with the javascript pandoc filter
|
||||
|
||||
@@BOOT_JDK@@/bin/jjs @@JJS_FLAGS@@ -scripting \
|
||||
"@@TOPDIR@@/make/scripts/pandoc-troff-manpage-filter.js" 2> /dev/null
|
@ -725,8 +725,11 @@ class StubGenerator: public StubCodeGenerator {
|
||||
stub_name = "forward_copy_longs";
|
||||
else
|
||||
stub_name = "backward_copy_longs";
|
||||
StubCodeMark mark(this, "StubRoutines", stub_name);
|
||||
|
||||
__ align(CodeEntryAlignment);
|
||||
|
||||
StubCodeMark mark(this, "StubRoutines", stub_name);
|
||||
|
||||
__ bind(start);
|
||||
|
||||
Label unaligned_copy_long;
|
||||
@ -1976,9 +1979,10 @@ class StubGenerator: public StubCodeGenerator {
|
||||
const Register dst_pos = c_rarg3; // destination position
|
||||
const Register length = c_rarg4;
|
||||
|
||||
__ align(CodeEntryAlignment);
|
||||
|
||||
StubCodeMark mark(this, "StubRoutines", name);
|
||||
|
||||
__ align(CodeEntryAlignment);
|
||||
address start = __ pc();
|
||||
|
||||
__ enter(); // required for proper stackwalking of RuntimeStub frame
|
||||
@ -3653,7 +3657,6 @@ class StubGenerator: public StubCodeGenerator {
|
||||
}
|
||||
|
||||
address generate_has_negatives(address &has_negatives_long) {
|
||||
StubCodeMark mark(this, "StubRoutines", "has_negatives");
|
||||
const u1 large_loop_size = 64;
|
||||
const uint64_t UPPER_BIT_MASK=0x8080808080808080;
|
||||
int dcache_line = VM_Version::dcache_line_size();
|
||||
@ -3661,6 +3664,9 @@ class StubGenerator: public StubCodeGenerator {
|
||||
Register ary1 = r1, len = r2, result = r0;
|
||||
|
||||
__ align(CodeEntryAlignment);
|
||||
|
||||
StubCodeMark mark(this, "StubRoutines", "has_negatives");
|
||||
|
||||
address entry = __ pc();
|
||||
|
||||
__ enter();
|
||||
@ -3900,7 +3906,6 @@ class StubGenerator: public StubCodeGenerator {
|
||||
// cnt1 = r10 - amount of elements left to check, reduced by wordSize
|
||||
// r3-r5 are reserved temporary registers
|
||||
address generate_large_array_equals() {
|
||||
StubCodeMark mark(this, "StubRoutines", "large_array_equals");
|
||||
Register a1 = r1, a2 = r2, result = r0, cnt1 = r10, tmp1 = rscratch1,
|
||||
tmp2 = rscratch2, tmp3 = r3, tmp4 = r4, tmp5 = r5, tmp6 = r11,
|
||||
tmp7 = r12, tmp8 = r13;
|
||||
@ -3915,6 +3920,9 @@ class StubGenerator: public StubCodeGenerator {
|
||||
tmp5, tmp6, tmp7, tmp8);
|
||||
|
||||
__ align(CodeEntryAlignment);
|
||||
|
||||
StubCodeMark mark(this, "StubRoutines", "large_array_equals");
|
||||
|
||||
address entry = __ pc();
|
||||
__ enter();
|
||||
__ sub(cnt1, cnt1, wordSize); // first 8 bytes were loaded outside of stub
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2017 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2012, 2018 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
|
||||
@ -299,6 +299,8 @@ class Assembler : public AbstractAssembler {
|
||||
CMPI_OPCODE = (11u << OPCODE_SHIFT),
|
||||
CMPL_OPCODE = (31u << OPCODE_SHIFT | 32u << 1),
|
||||
CMPLI_OPCODE = (10u << OPCODE_SHIFT),
|
||||
CMPRB_OPCODE = (31u << OPCODE_SHIFT | 192u << 1),
|
||||
CMPEQB_OPCODE = (31u << OPCODE_SHIFT | 224u << 1),
|
||||
|
||||
ISEL_OPCODE = (31u << OPCODE_SHIFT | 15u << 1),
|
||||
|
||||
@ -336,6 +338,7 @@ class Assembler : public AbstractAssembler {
|
||||
MTCRF_OPCODE = (31u << OPCODE_SHIFT | 144u << 1),
|
||||
MFCR_OPCODE = (31u << OPCODE_SHIFT | 19u << 1),
|
||||
MCRF_OPCODE = (19u << OPCODE_SHIFT | 0u << 1),
|
||||
SETB_OPCODE = (31u << OPCODE_SHIFT | 128u << 1),
|
||||
|
||||
// condition register logic instructions
|
||||
CRAND_OPCODE = (19u << OPCODE_SHIFT | 257u << 1),
|
||||
@ -1076,7 +1079,7 @@ class Assembler : public AbstractAssembler {
|
||||
static int frs( int x) { return opp_u_field(x, 10, 6); }
|
||||
static int frt( int x) { return opp_u_field(x, 10, 6); }
|
||||
static int fxm( int x) { return opp_u_field(x, 19, 12); }
|
||||
static int l10( int x) { return opp_u_field(x, 10, 10); }
|
||||
static int l10( int x) { assert(x == 0 || x == 1, "must be 0 or 1"); return opp_u_field(x, 10, 10); }
|
||||
static int l14( int x) { return opp_u_field(x, 15, 14); }
|
||||
static int l15( int x) { return opp_u_field(x, 15, 15); }
|
||||
static int l910( int x) { return opp_u_field(x, 10, 9); }
|
||||
@ -1443,6 +1446,10 @@ class Assembler : public AbstractAssembler {
|
||||
inline void cmplw( ConditionRegister crx, Register a, Register b);
|
||||
inline void cmpld( ConditionRegister crx, Register a, Register b);
|
||||
|
||||
// >= Power9
|
||||
inline void cmprb( ConditionRegister bf, int l, Register a, Register b);
|
||||
inline void cmpeqb(ConditionRegister bf, Register a, Register b);
|
||||
|
||||
inline void isel( Register d, Register a, Register b, int bc);
|
||||
// Convenient version which takes: Condition register, Condition code and invert flag. Omit b to keep old value.
|
||||
inline void isel( Register d, ConditionRegister cr, Condition cc, bool inv, Register a, Register b = noreg);
|
||||
@ -1642,6 +1649,8 @@ class Assembler : public AbstractAssembler {
|
||||
inline void mfcr( Register d);
|
||||
inline void mcrf( ConditionRegister crd, ConditionRegister cra);
|
||||
inline void mtcr( Register s);
|
||||
// >= Power9
|
||||
inline void setb( Register d, ConditionRegister cra);
|
||||
|
||||
// Special purpose registers
|
||||
// Exception Register
|
||||
|
@ -171,6 +171,8 @@ inline void Assembler::cmpi( ConditionRegister f, int l, Register a, int si16)
|
||||
inline void Assembler::cmp( ConditionRegister f, int l, Register a, Register b) { emit_int32( CMP_OPCODE | bf(f) | l10(l) | ra(a) | rb(b)); }
|
||||
inline void Assembler::cmpli( ConditionRegister f, int l, Register a, int ui16) { emit_int32( CMPLI_OPCODE | bf(f) | l10(l) | ra(a) | uimm(ui16,16)); }
|
||||
inline void Assembler::cmpl( ConditionRegister f, int l, Register a, Register b) { emit_int32( CMPL_OPCODE | bf(f) | l10(l) | ra(a) | rb(b)); }
|
||||
inline void Assembler::cmprb( ConditionRegister f, int l, Register a, Register b) { emit_int32( CMPRB_OPCODE | bf(f) | l10(l) | ra(a) | rb(b)); }
|
||||
inline void Assembler::cmpeqb(ConditionRegister f, Register a, Register b) { emit_int32( CMPEQB_OPCODE| bf(f) | ra(a) | rb(b)); }
|
||||
|
||||
// extended mnemonics of Compare Instructions
|
||||
inline void Assembler::cmpwi( ConditionRegister crx, Register a, int si16) { Assembler::cmpi( crx, 0, a, si16); }
|
||||
@ -371,6 +373,8 @@ inline void Assembler::mfcr( Register d ) { emit_int32(MFCR_OPCODE | rt
|
||||
inline void Assembler::mcrf( ConditionRegister crd, ConditionRegister cra)
|
||||
{ emit_int32(MCRF_OPCODE | bf(crd) | bfa(cra)); }
|
||||
inline void Assembler::mtcr( Register s) { Assembler::mtcrf(0xff, s); }
|
||||
inline void Assembler::setb(Register d, ConditionRegister cra)
|
||||
{ emit_int32(SETB_OPCODE | rt(d) | bfa(cra)); }
|
||||
|
||||
// Special purpose registers
|
||||
// Exception Register
|
||||
|
@ -2273,7 +2273,7 @@ void MacroAssembler::tlab_allocate(
|
||||
) {
|
||||
// make sure arguments make sense
|
||||
assert_different_registers(obj, var_size_in_bytes, t1);
|
||||
assert(0 <= con_size_in_bytes && is_simm13(con_size_in_bytes), "illegal object size");
|
||||
assert(0 <= con_size_in_bytes && is_simm16(con_size_in_bytes), "illegal object size");
|
||||
assert((con_size_in_bytes & MinObjAlignmentInBytesMask) == 0, "object size is not multiple of alignment");
|
||||
|
||||
const Register new_top = t1;
|
||||
|
@ -2257,6 +2257,11 @@ const bool Matcher::match_rule_supported(int opcode) {
|
||||
return SuperwordUseVSX;
|
||||
case Op_PopCountVI:
|
||||
return (SuperwordUseVSX && UsePopCountInstruction);
|
||||
case Op_Digit:
|
||||
case Op_LowerCase:
|
||||
case Op_UpperCase:
|
||||
case Op_Whitespace:
|
||||
return UseCharacterCompareIntrinsics;
|
||||
}
|
||||
|
||||
return true; // Per default match rules are supported.
|
||||
@ -12400,6 +12405,132 @@ instruct cmpD3_reg_reg_ExEx(iRegIdst dst, regD src1, regD src2) %{
|
||||
%}
|
||||
%}
|
||||
|
||||
// Compare char
|
||||
instruct cmprb_Digit_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsReg crx) %{
|
||||
match(Set dst (Digit src1));
|
||||
effect(TEMP src2, TEMP crx);
|
||||
ins_cost(3 * DEFAULT_COST);
|
||||
|
||||
format %{ "LI $src2, 0x3930\n\t"
|
||||
"CMPRB $crx, 0, $src1, $src2\n\t"
|
||||
"SETB $dst, $crx" %}
|
||||
size(12);
|
||||
ins_encode %{
|
||||
// 0x30: 0, 0x39: 9
|
||||
__ li($src2$$Register, 0x3930);
|
||||
// compare src1 with ranges 0x30 to 0x39
|
||||
__ cmprb($crx$$CondRegister, 0, $src1$$Register, $src2$$Register);
|
||||
__ setb($dst$$Register, $crx$$CondRegister);
|
||||
%}
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct cmprb_LowerCase_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsReg crx) %{
|
||||
match(Set dst (LowerCase src1));
|
||||
effect(TEMP src2, TEMP crx);
|
||||
ins_cost(12 * DEFAULT_COST);
|
||||
|
||||
format %{ "LI $src2, 0x7A61\n\t"
|
||||
"CMPRB $crx, 0, $src1, $src2\n\t"
|
||||
"BGT $crx, done\n\t"
|
||||
"LIS $src2, (signed short)0xF6DF\n\t"
|
||||
"ORI $src2, $src2, 0xFFF8\n\t"
|
||||
"CMPRB $crx, 1, $src1, $src2\n\t"
|
||||
"BGT $crx, done\n\t"
|
||||
"LIS $src2, (signed short)0xAAB5\n\t"
|
||||
"ORI $src2, $src2, 0xBABA\n\t"
|
||||
"INSRDI $src2, $src2, 32, 0\n\t"
|
||||
"CMPEQB $crx, 1, $src1, $src2\n"
|
||||
"done:\n\t"
|
||||
"SETB $dst, $crx" %}
|
||||
|
||||
size(48);
|
||||
ins_encode %{
|
||||
Label done;
|
||||
// 0x61: a, 0x7A: z
|
||||
__ li($src2$$Register, 0x7A61);
|
||||
// compare src1 with ranges 0x61 to 0x7A
|
||||
__ cmprb($crx$$CondRegister, 0, $src1$$Register, $src2$$Register);
|
||||
__ bgt($crx$$CondRegister, done);
|
||||
|
||||
// 0xDF: sharp s, 0xFF: y with diaeresis, 0xF7 is not the lower case
|
||||
__ lis($src2$$Register, (signed short)0xF6DF);
|
||||
__ ori($src2$$Register, $src2$$Register, 0xFFF8);
|
||||
// compare src1 with ranges 0xDF to 0xF6 and 0xF8 to 0xFF
|
||||
__ cmprb($crx$$CondRegister, 1, $src1$$Register, $src2$$Register);
|
||||
__ bgt($crx$$CondRegister, done);
|
||||
|
||||
// 0xAA: feminine ordinal indicator
|
||||
// 0xB5: micro sign
|
||||
// 0xBA: masculine ordinal indicator
|
||||
__ lis($src2$$Register, (signed short)0xAAB5);
|
||||
__ ori($src2$$Register, $src2$$Register, 0xBABA);
|
||||
__ insrdi($src2$$Register, $src2$$Register, 32, 0);
|
||||
// compare src1 with 0xAA, 0xB5, and 0xBA
|
||||
__ cmpeqb($crx$$CondRegister, $src1$$Register, $src2$$Register);
|
||||
|
||||
__ bind(done);
|
||||
__ setb($dst$$Register, $crx$$CondRegister);
|
||||
%}
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct cmprb_UpperCase_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsReg crx) %{
|
||||
match(Set dst (UpperCase src1));
|
||||
effect(TEMP src2, TEMP crx);
|
||||
ins_cost(7 * DEFAULT_COST);
|
||||
|
||||
format %{ "LI $src2, 0x5A41\n\t"
|
||||
"CMPRB $crx, 0, $src1, $src2\n\t"
|
||||
"BGT $crx, done\n\t"
|
||||
"LIS $src2, (signed short)0xD6C0\n\t"
|
||||
"ORI $src2, $src2, 0xDED8\n\t"
|
||||
"CMPRB $crx, 1, $src1, $src2\n"
|
||||
"done:\n\t"
|
||||
"SETB $dst, $crx" %}
|
||||
|
||||
size(28);
|
||||
ins_encode %{
|
||||
Label done;
|
||||
// 0x41: A, 0x5A: Z
|
||||
__ li($src2$$Register, 0x5A41);
|
||||
// compare src1 with a range 0x41 to 0x5A
|
||||
__ cmprb($crx$$CondRegister, 0, $src1$$Register, $src2$$Register);
|
||||
__ bgt($crx$$CondRegister, done);
|
||||
|
||||
// 0xC0: a with grave, 0xDE: thorn, 0xD7 is not the upper case
|
||||
__ lis($src2$$Register, (signed short)0xD6C0);
|
||||
__ ori($src2$$Register, $src2$$Register, 0xDED8);
|
||||
// compare src1 with ranges 0xC0 to 0xD6 and 0xD8 to 0xDE
|
||||
__ cmprb($crx$$CondRegister, 1, $src1$$Register, $src2$$Register);
|
||||
|
||||
__ bind(done);
|
||||
__ setb($dst$$Register, $crx$$CondRegister);
|
||||
%}
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
instruct cmprb_Whitespace_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2, flagsReg crx) %{
|
||||
match(Set dst (Whitespace src1));
|
||||
effect(TEMP src2, TEMP crx);
|
||||
ins_cost(4 * DEFAULT_COST);
|
||||
|
||||
format %{ "LI $src2, 0x0D09\n\t"
|
||||
"ADDIS $src2, 0x201C\n\t"
|
||||
"CMPRB $crx, 1, $src1, $src2\n\t"
|
||||
"SETB $dst, $crx" %}
|
||||
size(16);
|
||||
ins_encode %{
|
||||
// 0x09 to 0x0D, 0x1C to 0x20
|
||||
__ li($src2$$Register, 0x0D09);
|
||||
__ addis($src2$$Register, $src2$$Register, 0x0201C);
|
||||
// compare src with ranges 0x09 to 0x0D and 0x1C to 0x20
|
||||
__ cmprb($crx$$CondRegister, 1, $src1$$Register, $src2$$Register);
|
||||
__ setb($dst$$Register, $crx$$CondRegister);
|
||||
%}
|
||||
ins_pipe(pipe_class_default);
|
||||
%}
|
||||
|
||||
//----------Branches---------------------------------------------------------
|
||||
// Jump
|
||||
|
||||
|
@ -134,11 +134,18 @@ void VM_Version::initialize() {
|
||||
if (FLAG_IS_DEFAULT(UseCountTrailingZerosInstructionsPPC64)) {
|
||||
FLAG_SET_ERGO(bool, UseCountTrailingZerosInstructionsPPC64, true);
|
||||
}
|
||||
if (FLAG_IS_DEFAULT(UseCharacterCompareIntrinsics)) {
|
||||
FLAG_SET_ERGO(bool, UseCharacterCompareIntrinsics, true);
|
||||
}
|
||||
} else {
|
||||
if (UseCountTrailingZerosInstructionsPPC64) {
|
||||
warning("UseCountTrailingZerosInstructionsPPC64 specified, but needs at least Power9.");
|
||||
FLAG_SET_DEFAULT(UseCountTrailingZerosInstructionsPPC64, false);
|
||||
}
|
||||
if (UseCharacterCompareIntrinsics) {
|
||||
warning("UseCharacterCompareIntrinsics specified, but needs at least Power9.");
|
||||
FLAG_SET_DEFAULT(UseCharacterCompareIntrinsics, false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -3966,6 +3966,34 @@ void Assembler::vpmovzxwd(XMMRegister dst, XMMRegister src, int vector_len) {
|
||||
emit_int8((unsigned char)(0xC0 | encode));
|
||||
}
|
||||
|
||||
void Assembler::pmaddwd(XMMRegister dst, XMMRegister src) {
|
||||
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
|
||||
InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true);
|
||||
int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes);
|
||||
emit_int8((unsigned char)0xF5);
|
||||
emit_int8((unsigned char)(0xC0 | encode));
|
||||
}
|
||||
|
||||
void Assembler::vpmaddwd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) {
|
||||
assert(vector_len == AVX_128bit ? VM_Version::supports_avx() :
|
||||
(vector_len == AVX_256bit ? VM_Version::supports_avx2() :
|
||||
(vector_len == AVX_512bit ? VM_Version::supports_evex() : 0)), "");
|
||||
InstructionAttr attributes(vector_len, /* rex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true);
|
||||
int encode = simd_prefix_and_encode(dst, nds, src, VEX_SIMD_66, VEX_OPCODE_0F, &attributes);
|
||||
emit_int8((unsigned char)0xF5);
|
||||
emit_int8((unsigned char)(0xC0 | encode));
|
||||
}
|
||||
|
||||
void Assembler::evpdpwssd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) {
|
||||
assert(VM_Version::supports_evex(), "");
|
||||
assert(VM_Version::supports_vnni(), "must support vnni");
|
||||
InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ true);
|
||||
attributes.set_is_evex_instruction();
|
||||
int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
|
||||
emit_int8(0x52);
|
||||
emit_int8((unsigned char)(0xC0 | encode));
|
||||
}
|
||||
|
||||
// generic
|
||||
void Assembler::pop(Register dst) {
|
||||
int encode = prefix_and_encode(dst->encoding());
|
||||
@ -4178,6 +4206,17 @@ void Assembler::psrldq(XMMRegister dst, int shift) {
|
||||
emit_int8(shift);
|
||||
}
|
||||
|
||||
void Assembler::vpsrldq(XMMRegister dst, XMMRegister src, int shift, int vector_len) {
|
||||
assert(vector_len == AVX_128bit ? VM_Version::supports_avx() :
|
||||
vector_len == AVX_256bit ? VM_Version::supports_avx2() :
|
||||
vector_len == AVX_512bit ? VM_Version::supports_avx512bw() : 0, "");
|
||||
InstructionAttr attributes(vector_len, /*vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true);
|
||||
int encode = vex_prefix_and_encode(xmm3->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes);
|
||||
emit_int8(0x73);
|
||||
emit_int8((unsigned char)(0xC0 | encode));
|
||||
emit_int8(shift & 0xFF);
|
||||
}
|
||||
|
||||
void Assembler::pslldq(XMMRegister dst, int shift) {
|
||||
// Shift left 128 bit value in dst XMMRegister by shift number of bytes.
|
||||
NOT_LP64(assert(VM_Version::supports_sse2(), ""));
|
||||
@ -4189,6 +4228,17 @@ void Assembler::pslldq(XMMRegister dst, int shift) {
|
||||
emit_int8(shift);
|
||||
}
|
||||
|
||||
void Assembler::vpslldq(XMMRegister dst, XMMRegister src, int shift, int vector_len) {
|
||||
assert(vector_len == AVX_128bit ? VM_Version::supports_avx() :
|
||||
vector_len == AVX_256bit ? VM_Version::supports_avx2() :
|
||||
vector_len == AVX_512bit ? VM_Version::supports_avx512bw() : 0, "");
|
||||
InstructionAttr attributes(vector_len, /*vex_w */ false, /* legacy_mode */ _legacy_mode_bw, /* no_mask_reg */ true, /* uses_vl */ true);
|
||||
int encode = vex_prefix_and_encode(xmm7->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F, &attributes);
|
||||
emit_int8(0x73);
|
||||
emit_int8((unsigned char)(0xC0 | encode));
|
||||
emit_int8(shift & 0xFF);
|
||||
}
|
||||
|
||||
void Assembler::ptest(XMMRegister dst, Address src) {
|
||||
assert(VM_Version::supports_sse4_1(), "");
|
||||
assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes");
|
||||
@ -4200,7 +4250,7 @@ void Assembler::ptest(XMMRegister dst, Address src) {
|
||||
}
|
||||
|
||||
void Assembler::ptest(XMMRegister dst, XMMRegister src) {
|
||||
assert(VM_Version::supports_sse4_1(), "");
|
||||
assert(VM_Version::supports_sse4_1() || VM_Version::supports_avx(), "");
|
||||
InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
|
||||
int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
|
||||
emit_int8(0x17);
|
||||
|
@ -1668,6 +1668,12 @@ private:
|
||||
|
||||
void evpmovdb(Address dst, XMMRegister src, int vector_len);
|
||||
|
||||
// Multiply add
|
||||
void pmaddwd(XMMRegister dst, XMMRegister src);
|
||||
void vpmaddwd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
|
||||
// Multiply add accumulate
|
||||
void evpdpwssd(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
|
||||
|
||||
#ifndef _LP64 // no 32bit push/pop on amd64
|
||||
void popl(Address dst);
|
||||
#endif
|
||||
@ -2055,6 +2061,7 @@ private:
|
||||
void vpsllw(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len);
|
||||
void vpslld(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len);
|
||||
void vpsllq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len);
|
||||
void vpslldq(XMMRegister dst, XMMRegister src, int shift, int vector_len);
|
||||
|
||||
// Logical shift right packed integers
|
||||
void psrlw(XMMRegister dst, int shift);
|
||||
@ -2069,6 +2076,7 @@ private:
|
||||
void vpsrlw(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len);
|
||||
void vpsrld(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len);
|
||||
void vpsrlq(XMMRegister dst, XMMRegister src, XMMRegister shift, int vector_len);
|
||||
void vpsrldq(XMMRegister dst, XMMRegister src, int shift, int vector_len);
|
||||
void evpsrlvw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
|
||||
void evpsllvw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
|
||||
|
||||
|
@ -943,12 +943,17 @@ class MacroAssembler: public Assembler {
|
||||
int iter);
|
||||
|
||||
void addm(int disp, Register r1, Register r2);
|
||||
|
||||
void gfmul(XMMRegister tmp0, XMMRegister t);
|
||||
void schoolbookAAD(int i, Register subkeyH, XMMRegister data, XMMRegister tmp0,
|
||||
XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3);
|
||||
void generateHtbl_one_block(Register htbl);
|
||||
void generateHtbl_eight_blocks(Register htbl);
|
||||
public:
|
||||
void sha256_AVX2(XMMRegister msg, XMMRegister state0, XMMRegister state1, XMMRegister msgtmp0,
|
||||
XMMRegister msgtmp1, XMMRegister msgtmp2, XMMRegister msgtmp3, XMMRegister msgtmp4,
|
||||
Register buf, Register state, Register ofs, Register limit, Register rsp,
|
||||
bool multi_block, XMMRegister shuf_mask);
|
||||
void avx_ghash(Register state, Register htbl, Register data, Register blocks);
|
||||
#endif
|
||||
|
||||
#ifdef _LP64
|
||||
@ -1498,6 +1503,15 @@ public:
|
||||
// 0x11 - multiply upper 64 bits [64:127]
|
||||
Assembler::vpclmulqdq(dst, nds, src, 0x11);
|
||||
}
|
||||
void vpclmullqhqdq(XMMRegister dst, XMMRegister nds, XMMRegister src) {
|
||||
// 0x10 - multiply nds[0:63] and src[64:127]
|
||||
Assembler::vpclmulqdq(dst, nds, src, 0x10);
|
||||
}
|
||||
void vpclmulhqlqdq(XMMRegister dst, XMMRegister nds, XMMRegister src) {
|
||||
//0x01 - multiply nds[64:127] and src[0:63]
|
||||
Assembler::vpclmulqdq(dst, nds, src, 0x01);
|
||||
}
|
||||
|
||||
void evpclmulldq(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) {
|
||||
// 0x00 - multiply lower 64 bits [0:63]
|
||||
Assembler::evpclmulqdq(dst, nds, src, 0x00, vector_len);
|
||||
|
322
src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp
Normal file
322
src/hotspot/cpu/x86/macroAssembler_x86_aes.cpp
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Intel Corporation.
|
||||
*
|
||||
* 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 "precompiled.hpp"
|
||||
#include "asm/assembler.hpp"
|
||||
#include "asm/assembler.inline.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "macroAssembler_x86.hpp"
|
||||
|
||||
// Multiply 128 x 128 bits, using 4 pclmulqdq operations
|
||||
void MacroAssembler::schoolbookAAD(int i, Register htbl, XMMRegister data,
|
||||
XMMRegister tmp0, XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3) {
|
||||
movdqu(xmm15, Address(htbl, i * 16));
|
||||
vpclmulhqlqdq(tmp3, data, xmm15); // 0x01
|
||||
vpxor(tmp2, tmp2, tmp3, Assembler::AVX_128bit);
|
||||
vpclmulldq(tmp3, data, xmm15); // 0x00
|
||||
vpxor(tmp0, tmp0, tmp3, Assembler::AVX_128bit);
|
||||
vpclmulhdq(tmp3, data, xmm15); // 0x11
|
||||
vpxor(tmp1, tmp1, tmp3, Assembler::AVX_128bit);
|
||||
vpclmullqhqdq(tmp3, data, xmm15); // 0x10
|
||||
vpxor(tmp2, tmp2, tmp3, Assembler::AVX_128bit);
|
||||
}
|
||||
|
||||
// Multiply two 128 bit numbers resulting in a 256 bit value
|
||||
// Result of the multiplication followed by reduction stored in state
|
||||
void MacroAssembler::gfmul(XMMRegister tmp0, XMMRegister state) {
|
||||
const XMMRegister tmp1 = xmm4;
|
||||
const XMMRegister tmp2 = xmm5;
|
||||
const XMMRegister tmp3 = xmm6;
|
||||
const XMMRegister tmp4 = xmm7;
|
||||
|
||||
vpclmulldq(tmp1, state, tmp0); //0x00 (a0 * b0)
|
||||
vpclmulhdq(tmp4, state, tmp0);//0x11 (a1 * b1)
|
||||
vpclmullqhqdq(tmp2, state, tmp0);//0x10 (a1 * b0)
|
||||
vpclmulhqlqdq(tmp3, state, tmp0); //0x01 (a0 * b1)
|
||||
|
||||
vpxor(tmp2, tmp2, tmp3, Assembler::AVX_128bit); // (a0 * b1) + (a1 * b0)
|
||||
|
||||
vpslldq(tmp3, tmp2, 8, Assembler::AVX_128bit);
|
||||
vpsrldq(tmp2, tmp2, 8, Assembler::AVX_128bit);
|
||||
vpxor(tmp1, tmp1, tmp3, Assembler::AVX_128bit); // tmp1 and tmp4 hold the result
|
||||
vpxor(tmp4, tmp4, tmp2, Assembler::AVX_128bit); // of carryless multiplication
|
||||
// Follows the reduction technique mentioned in
|
||||
// Shift-XOR reduction described in Gueron-Kounavis May 2010
|
||||
// First phase of reduction
|
||||
//
|
||||
vpslld(xmm8, tmp1, 31, Assembler::AVX_128bit); // packed right shift shifting << 31
|
||||
vpslld(xmm9, tmp1, 30, Assembler::AVX_128bit); // packed right shift shifting << 30
|
||||
vpslld(xmm10, tmp1, 25, Assembler::AVX_128bit);// packed right shift shifting << 25
|
||||
// xor the shifted versions
|
||||
vpxor(xmm8, xmm8, xmm9, Assembler::AVX_128bit);
|
||||
vpxor(xmm8, xmm8, xmm10, Assembler::AVX_128bit);
|
||||
vpslldq(xmm9, xmm8, 12, Assembler::AVX_128bit);
|
||||
vpsrldq(xmm8, xmm8, 4, Assembler::AVX_128bit);
|
||||
vpxor(tmp1, tmp1, xmm9, Assembler::AVX_128bit);// first phase of the reduction complete
|
||||
//
|
||||
// Second phase of the reduction
|
||||
//
|
||||
vpsrld(xmm9, tmp1, 1, Assembler::AVX_128bit);// packed left shifting >> 1
|
||||
vpsrld(xmm10, tmp1, 2, Assembler::AVX_128bit);// packed left shifting >> 2
|
||||
vpsrld(xmm11, tmp1, 7, Assembler::AVX_128bit);// packed left shifting >> 7
|
||||
vpxor(xmm9, xmm9, xmm10, Assembler::AVX_128bit);// xor the shifted versions
|
||||
vpxor(xmm9, xmm9, xmm11, Assembler::AVX_128bit);
|
||||
vpxor(xmm9, xmm9, xmm8, Assembler::AVX_128bit);
|
||||
vpxor(tmp1, tmp1, xmm9, Assembler::AVX_128bit);
|
||||
vpxor(state, tmp4, tmp1, Assembler::AVX_128bit);// the result is in state
|
||||
ret(0);
|
||||
}
|
||||
|
||||
// This method takes the subkey after expansion as input and generates 1 * 16 power of subkey H.
|
||||
// The power of H is used in reduction process for one block ghash
|
||||
void MacroAssembler::generateHtbl_one_block(Register htbl) {
|
||||
const XMMRegister t = xmm13;
|
||||
|
||||
// load the original subkey hash
|
||||
movdqu(t, Address(htbl, 0));
|
||||
// shuffle using long swap mask
|
||||
movdqu(xmm10, ExternalAddress(StubRoutines::x86::ghash_long_swap_mask_addr()));
|
||||
vpshufb(t, t, xmm10, Assembler::AVX_128bit);
|
||||
|
||||
// Compute H' = GFMUL(H, 2)
|
||||
vpsrld(xmm3, t, 7, Assembler::AVX_128bit);
|
||||
movdqu(xmm4, ExternalAddress(StubRoutines::x86::ghash_shufflemask_addr()));
|
||||
vpshufb(xmm3, xmm3, xmm4, Assembler::AVX_128bit);
|
||||
movl(rax, 0xff00);
|
||||
movdl(xmm4, rax);
|
||||
vpshufb(xmm4, xmm4, xmm3, Assembler::AVX_128bit);
|
||||
movdqu(xmm5, ExternalAddress(StubRoutines::x86::ghash_polynomial_addr()));
|
||||
vpand(xmm5, xmm5, xmm4, Assembler::AVX_128bit);
|
||||
vpsrld(xmm3, t, 31, Assembler::AVX_128bit);
|
||||
vpslld(xmm4, t, 1, Assembler::AVX_128bit);
|
||||
vpslldq(xmm3, xmm3, 4, Assembler::AVX_128bit);
|
||||
vpxor(t, xmm4, xmm3, Assembler::AVX_128bit);// t holds p(x) <<1 or H * 2
|
||||
|
||||
//Adding p(x)<<1 to xmm5 which holds the reduction polynomial
|
||||
vpxor(t, t, xmm5, Assembler::AVX_128bit);
|
||||
movdqu(Address(htbl, 1 * 16), t); // H * 2
|
||||
|
||||
ret(0);
|
||||
}
|
||||
|
||||
// This method takes the subkey after expansion as input and generates the remaining powers of subkey H.
|
||||
// The power of H is used in reduction process for eight block ghash
|
||||
void MacroAssembler::generateHtbl_eight_blocks(Register htbl) {
|
||||
const XMMRegister t = xmm13;
|
||||
const XMMRegister tmp0 = xmm1;
|
||||
Label GFMUL;
|
||||
|
||||
movdqu(t, Address(htbl, 1 * 16));
|
||||
movdqu(tmp0, t);
|
||||
|
||||
// tmp0 and t hold H. Now we compute powers of H by using GFMUL(H, H)
|
||||
call(GFMUL, relocInfo::none);
|
||||
movdqu(Address(htbl, 2 * 16), t); //H ^ 2 * 2
|
||||
call(GFMUL, relocInfo::none);
|
||||
movdqu(Address(htbl, 3 * 16), t); //H ^ 3 * 2
|
||||
call(GFMUL, relocInfo::none);
|
||||
movdqu(Address(htbl, 4 * 16), t); //H ^ 4 * 2
|
||||
call(GFMUL, relocInfo::none);
|
||||
movdqu(Address(htbl, 5 * 16), t); //H ^ 5 * 2
|
||||
call(GFMUL, relocInfo::none);
|
||||
movdqu(Address(htbl, 6 * 16), t); //H ^ 6 * 2
|
||||
call(GFMUL, relocInfo::none);
|
||||
movdqu(Address(htbl, 7 * 16), t); //H ^ 7 * 2
|
||||
call(GFMUL, relocInfo::none);
|
||||
movdqu(Address(htbl, 8 * 16), t); //H ^ 8 * 2
|
||||
ret(0);
|
||||
|
||||
bind(GFMUL);
|
||||
gfmul(tmp0, t);
|
||||
}
|
||||
|
||||
// Multiblock and single block GHASH computation using Shift XOR reduction technique
|
||||
void MacroAssembler::avx_ghash(Register input_state, Register htbl,
|
||||
Register input_data, Register blocks) {
|
||||
|
||||
// temporary variables to hold input data and input state
|
||||
const XMMRegister data = xmm1;
|
||||
const XMMRegister state = xmm0;
|
||||
// temporary variables to hold intermediate results
|
||||
const XMMRegister tmp0 = xmm3;
|
||||
const XMMRegister tmp1 = xmm4;
|
||||
const XMMRegister tmp2 = xmm5;
|
||||
const XMMRegister tmp3 = xmm6;
|
||||
// temporary variables to hold byte and long swap masks
|
||||
const XMMRegister bswap_mask = xmm2;
|
||||
const XMMRegister lswap_mask = xmm14;
|
||||
|
||||
Label GENERATE_HTBL_1_BLK, GENERATE_HTBL_8_BLKS, BEGIN_PROCESS, GFMUL, BLOCK8_REDUCTION,
|
||||
ONE_BLK_INIT, PROCESS_1_BLOCK, PROCESS_8_BLOCKS, SAVE_STATE, EXIT_GHASH;
|
||||
|
||||
testptr(blocks, blocks);
|
||||
jcc(Assembler::zero, EXIT_GHASH);
|
||||
|
||||
// Check if Hashtable (1*16) has been already generated
|
||||
// For anything less than 8 blocks, we generate only the first power of H.
|
||||
movdqu(tmp2, Address(htbl, 1 * 16));
|
||||
ptest(tmp2, tmp2);
|
||||
jcc(Assembler::notZero, BEGIN_PROCESS);
|
||||
call(GENERATE_HTBL_1_BLK, relocInfo::none);
|
||||
|
||||
// Shuffle the input state
|
||||
bind(BEGIN_PROCESS);
|
||||
movdqu(lswap_mask, ExternalAddress(StubRoutines::x86::ghash_long_swap_mask_addr()));
|
||||
movdqu(state, Address(input_state, 0));
|
||||
vpshufb(state, state, lswap_mask, Assembler::AVX_128bit);
|
||||
|
||||
cmpl(blocks, 8);
|
||||
jcc(Assembler::below, ONE_BLK_INIT);
|
||||
// If we have 8 blocks or more data, then generate remaining powers of H
|
||||
movdqu(tmp2, Address(htbl, 8 * 16));
|
||||
ptest(tmp2, tmp2);
|
||||
jcc(Assembler::notZero, PROCESS_8_BLOCKS);
|
||||
call(GENERATE_HTBL_8_BLKS, relocInfo::none);
|
||||
|
||||
//Do 8 multiplies followed by a reduction processing 8 blocks of data at a time
|
||||
//Each block = 16 bytes.
|
||||
bind(PROCESS_8_BLOCKS);
|
||||
subl(blocks, 8);
|
||||
movdqu(bswap_mask, ExternalAddress(StubRoutines::x86::ghash_byte_swap_mask_addr()));
|
||||
movdqu(data, Address(input_data, 16 * 7));
|
||||
vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
|
||||
//Loading 1*16 as calculated powers of H required starts at that location.
|
||||
movdqu(xmm15, Address(htbl, 1 * 16));
|
||||
//Perform carryless multiplication of (H*2, data block #7)
|
||||
vpclmulhqlqdq(tmp2, data, xmm15);//a0 * b1
|
||||
vpclmulldq(tmp0, data, xmm15);//a0 * b0
|
||||
vpclmulhdq(tmp1, data, xmm15);//a1 * b1
|
||||
vpclmullqhqdq(tmp3, data, xmm15);//a1* b0
|
||||
vpxor(tmp2, tmp2, tmp3, Assembler::AVX_128bit);// (a0 * b1) + (a1 * b0)
|
||||
|
||||
movdqu(data, Address(input_data, 16 * 6));
|
||||
vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
|
||||
// Perform carryless multiplication of (H^2 * 2, data block #6)
|
||||
schoolbookAAD(2, htbl, data, tmp0, tmp1, tmp2, tmp3);
|
||||
|
||||
movdqu(data, Address(input_data, 16 * 5));
|
||||
vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
|
||||
// Perform carryless multiplication of (H^3 * 2, data block #5)
|
||||
schoolbookAAD(3, htbl, data, tmp0, tmp1, tmp2, tmp3);
|
||||
movdqu(data, Address(input_data, 16 * 4));
|
||||
vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
|
||||
// Perform carryless multiplication of (H^4 * 2, data block #4)
|
||||
schoolbookAAD(4, htbl, data, tmp0, tmp1, tmp2, tmp3);
|
||||
movdqu(data, Address(input_data, 16 * 3));
|
||||
vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
|
||||
// Perform carryless multiplication of (H^5 * 2, data block #3)
|
||||
schoolbookAAD(5, htbl, data, tmp0, tmp1, tmp2, tmp3);
|
||||
movdqu(data, Address(input_data, 16 * 2));
|
||||
vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
|
||||
// Perform carryless multiplication of (H^6 * 2, data block #2)
|
||||
schoolbookAAD(6, htbl, data, tmp0, tmp1, tmp2, tmp3);
|
||||
movdqu(data, Address(input_data, 16 * 1));
|
||||
vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
|
||||
// Perform carryless multiplication of (H^7 * 2, data block #1)
|
||||
schoolbookAAD(7, htbl, data, tmp0, tmp1, tmp2, tmp3);
|
||||
movdqu(data, Address(input_data, 16 * 0));
|
||||
// xor data block#0 with input state before perfoming carry-less multiplication
|
||||
vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
|
||||
vpxor(data, data, state, Assembler::AVX_128bit);
|
||||
// Perform carryless multiplication of (H^8 * 2, data block #0)
|
||||
schoolbookAAD(8, htbl, data, tmp0, tmp1, tmp2, tmp3);
|
||||
vpslldq(tmp3, tmp2, 8, Assembler::AVX_128bit);
|
||||
vpsrldq(tmp2, tmp2, 8, Assembler::AVX_128bit);
|
||||
vpxor(tmp0, tmp0, tmp3, Assembler::AVX_128bit);// tmp0, tmp1 contains aggregated results of
|
||||
vpxor(tmp1, tmp1, tmp2, Assembler::AVX_128bit);// the multiplication operation
|
||||
|
||||
// we have the 2 128-bit partially accumulated multiplication results in tmp0:tmp1
|
||||
// with higher 128-bit in tmp1 and lower 128-bit in corresponding tmp0
|
||||
// Follows the reduction technique mentioned in
|
||||
// Shift-XOR reduction described in Gueron-Kounavis May 2010
|
||||
bind(BLOCK8_REDUCTION);
|
||||
// First Phase of the reduction
|
||||
vpslld(xmm8, tmp0, 31, Assembler::AVX_128bit); // packed right shifting << 31
|
||||
vpslld(xmm9, tmp0, 30, Assembler::AVX_128bit); // packed right shifting << 30
|
||||
vpslld(xmm10, tmp0, 25, Assembler::AVX_128bit); // packed right shifting << 25
|
||||
// xor the shifted versions
|
||||
vpxor(xmm8, xmm8, xmm10, Assembler::AVX_128bit);
|
||||
vpxor(xmm8, xmm8, xmm9, Assembler::AVX_128bit);
|
||||
|
||||
vpslldq(xmm9, xmm8, 12, Assembler::AVX_128bit);
|
||||
vpsrldq(xmm8, xmm8, 4, Assembler::AVX_128bit);
|
||||
|
||||
vpxor(tmp0, tmp0, xmm9, Assembler::AVX_128bit); // first phase of reduction is complete
|
||||
// second phase of the reduction
|
||||
vpsrld(xmm9, tmp0, 1, Assembler::AVX_128bit); // packed left shifting >> 1
|
||||
vpsrld(xmm10, tmp0, 2, Assembler::AVX_128bit); // packed left shifting >> 2
|
||||
vpsrld(tmp2, tmp0, 7, Assembler::AVX_128bit); // packed left shifting >> 7
|
||||
// xor the shifted versions
|
||||
vpxor(xmm9, xmm9, xmm10, Assembler::AVX_128bit);
|
||||
vpxor(xmm9, xmm9, tmp2, Assembler::AVX_128bit);
|
||||
vpxor(xmm9, xmm9, xmm8, Assembler::AVX_128bit);
|
||||
vpxor(tmp0, xmm9, tmp0, Assembler::AVX_128bit);
|
||||
// Final result is in state
|
||||
vpxor(state, tmp0, tmp1, Assembler::AVX_128bit);
|
||||
|
||||
lea(input_data, Address(input_data, 16 * 8));
|
||||
cmpl(blocks, 8);
|
||||
jcc(Assembler::below, ONE_BLK_INIT);
|
||||
jmp(PROCESS_8_BLOCKS);
|
||||
|
||||
// Since this is one block operation we will only use H * 2 i.e. the first power of H
|
||||
bind(ONE_BLK_INIT);
|
||||
movdqu(tmp0, Address(htbl, 1 * 16));
|
||||
movdqu(bswap_mask, ExternalAddress(StubRoutines::x86::ghash_byte_swap_mask_addr()));
|
||||
|
||||
//Do one (128 bit x 128 bit) carry-less multiplication at a time followed by a reduction.
|
||||
bind(PROCESS_1_BLOCK);
|
||||
cmpl(blocks, 0);
|
||||
jcc(Assembler::equal, SAVE_STATE);
|
||||
subl(blocks, 1);
|
||||
movdqu(data, Address(input_data, 0));
|
||||
vpshufb(data, data, bswap_mask, Assembler::AVX_128bit);
|
||||
vpxor(state, state, data, Assembler::AVX_128bit);
|
||||
// gfmul(H*2, state)
|
||||
call(GFMUL, relocInfo::none);
|
||||
addptr(input_data, 16);
|
||||
jmp(PROCESS_1_BLOCK);
|
||||
|
||||
bind(SAVE_STATE);
|
||||
vpshufb(state, state, lswap_mask, Assembler::AVX_128bit);
|
||||
movdqu(Address(input_state, 0), state);
|
||||
jmp(EXIT_GHASH);
|
||||
|
||||
bind(GFMUL);
|
||||
gfmul(tmp0, state);
|
||||
|
||||
bind(GENERATE_HTBL_1_BLK);
|
||||
generateHtbl_one_block(htbl);
|
||||
|
||||
bind(GENERATE_HTBL_8_BLKS);
|
||||
generateHtbl_eight_blocks(htbl);
|
||||
|
||||
bind(EXIT_GHASH);
|
||||
// zero out xmm registers used for Htbl storage
|
||||
vpxor(xmm0, xmm0, xmm0, Assembler::AVX_128bit);
|
||||
vpxor(xmm1, xmm1, xmm1, Assembler::AVX_128bit);
|
||||
vpxor(xmm3, xmm3, xmm3, Assembler::AVX_128bit);
|
||||
vpxor(xmm15, xmm15, xmm15, Assembler::AVX_128bit);
|
||||
}
|
@ -4388,6 +4388,45 @@ address generate_cipherBlockChaining_decryptVectorAESCrypt() {
|
||||
return start;
|
||||
}
|
||||
|
||||
// Polynomial x^128+x^127+x^126+x^121+1
|
||||
address ghash_polynomial_addr() {
|
||||
__ align(CodeEntryAlignment);
|
||||
StubCodeMark mark(this, "StubRoutines", "_ghash_poly_addr");
|
||||
address start = __ pc();
|
||||
__ emit_data64(0x0000000000000001, relocInfo::none);
|
||||
__ emit_data64(0xc200000000000000, relocInfo::none);
|
||||
return start;
|
||||
}
|
||||
|
||||
address ghash_shufflemask_addr() {
|
||||
__ align(CodeEntryAlignment);
|
||||
StubCodeMark mark(this, "StubRoutines", "_ghash_shuffmask_addr");
|
||||
address start = __ pc();
|
||||
__ emit_data64(0x0f0f0f0f0f0f0f0f, relocInfo::none);
|
||||
__ emit_data64(0x0f0f0f0f0f0f0f0f, relocInfo::none);
|
||||
return start;
|
||||
}
|
||||
|
||||
// Ghash single and multi block operations using AVX instructions
|
||||
address generate_avx_ghash_processBlocks() {
|
||||
__ align(CodeEntryAlignment);
|
||||
|
||||
StubCodeMark mark(this, "StubRoutines", "ghash_processBlocks");
|
||||
address start = __ pc();
|
||||
|
||||
// arguments
|
||||
const Register state = c_rarg0;
|
||||
const Register htbl = c_rarg1;
|
||||
const Register data = c_rarg2;
|
||||
const Register blocks = c_rarg3;
|
||||
__ enter();
|
||||
// Save state before entering routine
|
||||
__ avx_ghash(state, htbl, data, blocks);
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
return start;
|
||||
}
|
||||
|
||||
// byte swap x86 long
|
||||
address generate_ghash_long_swap_mask() {
|
||||
__ align(CodeEntryAlignment);
|
||||
@ -5886,9 +5925,15 @@ address generate_cipherBlockChaining_decryptVectorAESCrypt() {
|
||||
|
||||
// Generate GHASH intrinsics code
|
||||
if (UseGHASHIntrinsics) {
|
||||
StubRoutines::x86::_ghash_long_swap_mask_addr = generate_ghash_long_swap_mask();
|
||||
StubRoutines::x86::_ghash_byte_swap_mask_addr = generate_ghash_byte_swap_mask();
|
||||
StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks();
|
||||
StubRoutines::x86::_ghash_long_swap_mask_addr = generate_ghash_long_swap_mask();
|
||||
StubRoutines::x86::_ghash_byte_swap_mask_addr = generate_ghash_byte_swap_mask();
|
||||
if (VM_Version::supports_avx()) {
|
||||
StubRoutines::x86::_ghash_shuffmask_addr = ghash_shufflemask_addr();
|
||||
StubRoutines::x86::_ghash_poly_addr = ghash_polynomial_addr();
|
||||
StubRoutines::_ghash_processBlocks = generate_avx_ghash_processBlocks();
|
||||
} else {
|
||||
StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
if (UseBASE64Intrinsics) {
|
||||
|
@ -38,6 +38,8 @@ address StubRoutines::x86::_key_shuffle_mask_addr = NULL;
|
||||
address StubRoutines::x86::_counter_shuffle_mask_addr = NULL;
|
||||
address StubRoutines::x86::_ghash_long_swap_mask_addr = NULL;
|
||||
address StubRoutines::x86::_ghash_byte_swap_mask_addr = NULL;
|
||||
address StubRoutines::x86::_ghash_poly_addr = NULL;
|
||||
address StubRoutines::x86::_ghash_shuffmask_addr = NULL;
|
||||
address StubRoutines::x86::_upper_word_mask_addr = NULL;
|
||||
address StubRoutines::x86::_shuffle_byte_flip_mask_addr = NULL;
|
||||
address StubRoutines::x86::_k256_adr = NULL;
|
||||
|
@ -128,6 +128,8 @@ class x86 {
|
||||
// swap mask for ghash
|
||||
static address _ghash_long_swap_mask_addr;
|
||||
static address _ghash_byte_swap_mask_addr;
|
||||
static address _ghash_poly_addr;
|
||||
static address _ghash_shuffmask_addr;
|
||||
|
||||
// upper word mask for sha1
|
||||
static address _upper_word_mask_addr;
|
||||
@ -205,6 +207,8 @@ class x86 {
|
||||
static address crc_by128_masks_addr() { return (address)_crc_by128_masks; }
|
||||
static address ghash_long_swap_mask_addr() { return _ghash_long_swap_mask_addr; }
|
||||
static address ghash_byte_swap_mask_addr() { return _ghash_byte_swap_mask_addr; }
|
||||
static address ghash_shufflemask_addr() { return _ghash_shuffmask_addr; }
|
||||
static address ghash_polynomial_addr() { return _ghash_poly_addr; }
|
||||
static address upper_word_mask_addr() { return _upper_word_mask_addr; }
|
||||
static address shuffle_byte_flip_mask_addr() { return _shuffle_byte_flip_mask_addr; }
|
||||
static address k256_addr() { return _k256_adr; }
|
||||
|
@ -1289,7 +1289,7 @@ void VM_Version::get_processor_features() {
|
||||
if (FLAG_IS_DEFAULT(UseXMMForArrayCopy)) {
|
||||
UseXMMForArrayCopy = true; // use SSE2 movq on new Intel cpus
|
||||
}
|
||||
if (supports_sse4_2() && supports_ht()) { // Newest Intel cpus
|
||||
if ((supports_sse4_2() && supports_ht()) || supports_avx()) { // Newest Intel cpus
|
||||
if (FLAG_IS_DEFAULT(UseUnalignedLoadStores)) {
|
||||
UseUnalignedLoadStores = true; // use movdqu on newest Intel cpus
|
||||
}
|
||||
|
@ -336,6 +336,7 @@ protected:
|
||||
#define CPU_AVX512_VPOPCNTDQ ((uint64_t)UCONST64(0x2000000000)) // Vector popcount
|
||||
#define CPU_VPCLMULQDQ ((uint64_t)UCONST64(0x4000000000)) //Vector carryless multiplication
|
||||
#define CPU_VAES ((uint64_t)UCONST64(0x8000000000)) // Vector AES instructions
|
||||
#define CPU_VNNI ((uint64_t)UCONST64(0x16000000000)) // Vector Neural Network Instructions
|
||||
|
||||
enum Extended_Family {
|
||||
// AMD
|
||||
@ -548,6 +549,8 @@ protected:
|
||||
result |= CPU_VPCLMULQDQ;
|
||||
if (_cpuid_info.sef_cpuid7_ecx.bits.vaes != 0)
|
||||
result |= CPU_VAES;
|
||||
if (_cpuid_info.sef_cpuid7_ecx.bits.avx512_vnni != 0)
|
||||
result |= CPU_VNNI;
|
||||
}
|
||||
}
|
||||
if(_cpuid_info.sef_cpuid7_ebx.bits.bmi1 != 0)
|
||||
@ -828,6 +831,7 @@ public:
|
||||
static bool supports_vpopcntdq() { return (_features & CPU_AVX512_VPOPCNTDQ) != 0; }
|
||||
static bool supports_vpclmulqdq() { return (_features & CPU_VPCLMULQDQ) != 0; }
|
||||
static bool supports_vaes() { return (_features & CPU_VAES) != 0; }
|
||||
static bool supports_vnni() { return (_features & CPU_VNNI) != 0; }
|
||||
|
||||
// Intel features
|
||||
static bool is_intel_family_core() { return is_intel() &&
|
||||
|
@ -1446,6 +1446,10 @@ const bool Matcher::match_rule_supported(int opcode) {
|
||||
if (VM_Version::supports_on_spin_wait() == false)
|
||||
ret_value = false;
|
||||
break;
|
||||
case Op_MulAddVS2VI:
|
||||
if (UseSSE < 2)
|
||||
ret_value = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_value; // Per default match rules are supported.
|
||||
@ -9855,6 +9859,118 @@ instruct vfma16F_mem(vecZ a, memory b, vecZ c) %{
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
// --------------------------------- Vector Multiply Add --------------------------------------
|
||||
|
||||
instruct smuladd4S2I_reg(vecD dst, vecD src1) %{
|
||||
predicate(UseSSE >= 2 && UseAVX == 0 && n->as_Vector()->length() == 2);
|
||||
match(Set dst (MulAddVS2VI dst src1));
|
||||
format %{ "pmaddwd $dst,$dst,$src1\t! muladd packed4Sto2I" %}
|
||||
ins_encode %{
|
||||
__ pmaddwd($dst$$XMMRegister, $src1$$XMMRegister);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct vmuladd4S2I_reg(vecD dst, vecD src1, vecD src2) %{
|
||||
predicate(UseAVX > 0 && n->as_Vector()->length() == 2);
|
||||
match(Set dst (MulAddVS2VI src1 src2));
|
||||
format %{ "vpmaddwd $dst,$src1,$src2\t! muladd packed4Sto2I" %}
|
||||
ins_encode %{
|
||||
int vector_len = 0;
|
||||
__ vpmaddwd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct smuladd8S4I_reg(vecX dst, vecX src1) %{
|
||||
predicate(UseSSE >= 2 && UseAVX == 0 && n->as_Vector()->length() == 4);
|
||||
match(Set dst (MulAddVS2VI dst src1));
|
||||
format %{ "pmaddwd $dst,$dst,$src1\t! muladd packed8Sto4I" %}
|
||||
ins_encode %{
|
||||
__ pmaddwd($dst$$XMMRegister, $src1$$XMMRegister);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct vmuladd8S4I_reg(vecX dst, vecX src1, vecX src2) %{
|
||||
predicate(UseAVX > 0 && n->as_Vector()->length() == 4);
|
||||
match(Set dst (MulAddVS2VI src1 src2));
|
||||
format %{ "vpmaddwd $dst,$src1,$src2\t! muladd packed8Sto4I" %}
|
||||
ins_encode %{
|
||||
int vector_len = 0;
|
||||
__ vpmaddwd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct vmuladd16S8I_reg(vecY dst, vecY src1, vecY src2) %{
|
||||
predicate(UseAVX > 1 && n->as_Vector()->length() == 8);
|
||||
match(Set dst (MulAddVS2VI src1 src2));
|
||||
format %{ "vpmaddwd $dst,$src1,$src2\t! muladd packed16Sto8I" %}
|
||||
ins_encode %{
|
||||
int vector_len = 1;
|
||||
__ vpmaddwd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct vmuladd32S16I_reg(vecZ dst, vecZ src1, vecZ src2) %{
|
||||
predicate(UseAVX > 2 && n->as_Vector()->length() == 16);
|
||||
match(Set dst (MulAddVS2VI src1 src2));
|
||||
format %{ "vpmaddwd $dst,$src1,$src2\t! muladd packed32Sto16I" %}
|
||||
ins_encode %{
|
||||
int vector_len = 2;
|
||||
__ vpmaddwd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
// --------------------------------- Vector Multiply Add Add ----------------------------------
|
||||
|
||||
instruct vmuladdadd4S2I_reg(vecD dst, vecD src1, vecD src2) %{
|
||||
predicate(VM_Version::supports_vnni() && UseAVX > 2 && n->as_Vector()->length() == 2);
|
||||
match(Set dst (AddVI (MulAddVS2VI src1 src2) dst));
|
||||
format %{ "evpdpwssd $dst,$src1,$src2\t! muladdadd packed4Sto2I" %}
|
||||
ins_encode %{
|
||||
int vector_len = 0;
|
||||
__ evpdpwssd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct vmuladdadd8S4I_reg(vecX dst, vecX src1, vecX src2) %{
|
||||
predicate(VM_Version::supports_vnni() && UseAVX > 2 && n->as_Vector()->length() == 4);
|
||||
match(Set dst (AddVI (MulAddVS2VI src1 src2) dst));
|
||||
format %{ "evpdpwssd $dst,$src1,$src2\t! muladdadd packed8Sto4I" %}
|
||||
ins_encode %{
|
||||
int vector_len = 0;
|
||||
__ evpdpwssd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct vmuladdadd16S8I_reg(vecY dst, vecY src1, vecY src2) %{
|
||||
predicate(VM_Version::supports_vnni() && UseAVX > 2 && n->as_Vector()->length() == 8);
|
||||
match(Set dst (AddVI (MulAddVS2VI src1 src2) dst));
|
||||
format %{ "evpdpwssd $dst,$src1,$src2\t! muladdadd packed16Sto8I" %}
|
||||
ins_encode %{
|
||||
int vector_len = 1;
|
||||
__ evpdpwssd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
instruct vmuladdadd32S16I_reg(vecZ dst, vecZ src1, vecZ src2) %{
|
||||
predicate(VM_Version::supports_vnni() && UseAVX > 2 && n->as_Vector()->length() == 16);
|
||||
match(Set dst (AddVI (MulAddVS2VI src1 src2) dst));
|
||||
format %{ "evpdpwssd $dst,$src1,$src2\t! muladdadd packed32Sto16I" %}
|
||||
ins_encode %{
|
||||
int vector_len = 2;
|
||||
__ evpdpwssd($dst$$XMMRegister, $src1$$XMMRegister, $src2$$XMMRegister, vector_len);
|
||||
%}
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
// --------------------------------- PopCount --------------------------------------
|
||||
|
||||
instruct vpopcount2I(vecD dst, vecD src) %{
|
||||
|
@ -7755,6 +7755,16 @@ instruct mulI(rRegI dst, memory src, eFlagsReg cr) %{
|
||||
ins_pipe( ialu_reg_mem_alu0 );
|
||||
%}
|
||||
|
||||
instruct mulAddS2I_rReg(rRegI dst, rRegI src1, rRegI src2, rRegI src3, eFlagsReg cr)
|
||||
%{
|
||||
match(Set dst (MulAddS2I (Binary dst src1) (Binary src2 src3)));
|
||||
effect(KILL cr, KILL src2);
|
||||
|
||||
expand %{ mulI_rReg(dst, src1, cr);
|
||||
mulI_rReg(src2, src3, cr);
|
||||
addI_rReg(dst, src2, cr); %}
|
||||
%}
|
||||
|
||||
// Multiply Register Int to Long
|
||||
instruct mulI2L(eADXRegL dst, eAXRegI src, nadxRegI src1, eFlagsReg flags) %{
|
||||
// Basic Idea: long = (long)int * (long)int
|
||||
|
@ -8175,6 +8175,16 @@ instruct mulI_mem_imm(rRegI dst, memory src, immI imm, rFlagsReg cr)
|
||||
ins_pipe(ialu_reg_mem_alu0);
|
||||
%}
|
||||
|
||||
instruct mulAddS2I_rReg(rRegI dst, rRegI src1, rRegI src2, rRegI src3, rFlagsReg cr)
|
||||
%{
|
||||
match(Set dst (MulAddS2I (Binary dst src1) (Binary src2 src3)));
|
||||
effect(KILL cr, KILL src2);
|
||||
|
||||
expand %{ mulI_rReg(dst, src1, cr);
|
||||
mulI_rReg(src2, src3, cr);
|
||||
addI_rReg(dst, src2, cr); %}
|
||||
%}
|
||||
|
||||
instruct mulL_rReg(rRegL dst, rRegL src, rFlagsReg cr)
|
||||
%{
|
||||
match(Set dst (MulL dst src));
|
||||
|
@ -67,6 +67,11 @@
|
||||
" of quotas (if set), when true. Otherwise, use the CPU" \
|
||||
" shares value, provided it is less than quota.") \
|
||||
\
|
||||
diagnostic(bool, DumpPrivateMappingsInCore, true, \
|
||||
"If true, sets bit 2 of /proc/PID/coredump_filter, thus " \
|
||||
"resulting in file-backed private mappings of the process to "\
|
||||
"be dumped into the corefile, if UseSharedSpaces is true.") \
|
||||
\
|
||||
diagnostic(bool, UseCpuAllocPath, false, \
|
||||
"Use CPU_ALLOC code path in os::active_processor_count ")
|
||||
|
||||
|
@ -128,8 +128,12 @@
|
||||
// for timer info max values which include all bits
|
||||
#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF)
|
||||
|
||||
#define LARGEPAGES_BIT (1 << 6)
|
||||
#define DAX_SHARED_BIT (1 << 8)
|
||||
enum CoredumpFilterBit {
|
||||
FILE_BACKED_PVT_BIT = 1 << 2,
|
||||
LARGEPAGES_BIT = 1 << 6,
|
||||
DAX_SHARED_BIT = 1 << 8
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// global variables
|
||||
julong os::Linux::_physical_memory = 0;
|
||||
@ -1350,6 +1354,9 @@ void os::shutdown() {
|
||||
void os::abort(bool dump_core, void* siginfo, const void* context) {
|
||||
os::shutdown();
|
||||
if (dump_core) {
|
||||
if (UseSharedSpaces && DumpPrivateMappingsInCore) {
|
||||
ClassLoader::close_jrt_image();
|
||||
}
|
||||
#ifndef PRODUCT
|
||||
fdStream out(defaultStream::output_fd());
|
||||
out.print_raw("Current thread is ");
|
||||
@ -3401,10 +3408,9 @@ bool os::Linux::hugetlbfs_sanity_check(bool warn, size_t page_size) {
|
||||
// - (bit 7) dax private memory
|
||||
// - (bit 8) dax shared memory
|
||||
//
|
||||
static void set_coredump_filter(bool largepages, bool dax_shared) {
|
||||
static void set_coredump_filter(CoredumpFilterBit bit) {
|
||||
FILE *f;
|
||||
long cdm;
|
||||
bool filter_changed = false;
|
||||
|
||||
if ((f = fopen("/proc/self/coredump_filter", "r+")) == NULL) {
|
||||
return;
|
||||
@ -3415,17 +3421,11 @@ static void set_coredump_filter(bool largepages, bool dax_shared) {
|
||||
return;
|
||||
}
|
||||
|
||||
long saved_cdm = cdm;
|
||||
rewind(f);
|
||||
cdm |= bit;
|
||||
|
||||
if (largepages && (cdm & LARGEPAGES_BIT) == 0) {
|
||||
cdm |= LARGEPAGES_BIT;
|
||||
filter_changed = true;
|
||||
}
|
||||
if (dax_shared && (cdm & DAX_SHARED_BIT) == 0) {
|
||||
cdm |= DAX_SHARED_BIT;
|
||||
filter_changed = true;
|
||||
}
|
||||
if (filter_changed) {
|
||||
if (cdm != saved_cdm) {
|
||||
fprintf(f, "%#lx", cdm);
|
||||
}
|
||||
|
||||
@ -3564,7 +3564,7 @@ void os::large_page_init() {
|
||||
size_t large_page_size = Linux::setup_large_page_size();
|
||||
UseLargePages = Linux::setup_large_page_type(large_page_size);
|
||||
|
||||
set_coredump_filter(true /*largepages*/, false /*dax_shared*/);
|
||||
set_coredump_filter(LARGEPAGES_BIT);
|
||||
}
|
||||
|
||||
#ifndef SHM_HUGETLB
|
||||
@ -5072,8 +5072,13 @@ jint os::init_2(void) {
|
||||
prio_init();
|
||||
|
||||
if (!FLAG_IS_DEFAULT(AllocateHeapAt)) {
|
||||
set_coredump_filter(false /*largepages*/, true /*dax_shared*/);
|
||||
set_coredump_filter(DAX_SHARED_BIT);
|
||||
}
|
||||
|
||||
if (UseSharedSpaces && DumpPrivateMappingsInCore) {
|
||||
set_coredump_filter(FILE_BACKED_PVT_BIT);
|
||||
}
|
||||
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
|
@ -74,15 +74,17 @@
|
||||
// * 63-47 Fixed (17-bits, always zero)
|
||||
//
|
||||
|
||||
const size_t ZPlatformPageSizeSmallShift = 21; // 2M
|
||||
const size_t ZPlatformPageSizeSmallShift = 21; // 2M
|
||||
|
||||
const size_t ZPlatformAddressOffsetBits = 42; // 4TB
|
||||
const size_t ZPlatformAddressOffsetBits = 42; // 4TB
|
||||
|
||||
const uintptr_t ZPlatformAddressMetadataShift = ZPlatformAddressOffsetBits;
|
||||
const uintptr_t ZPlatformAddressMetadataShift = ZPlatformAddressOffsetBits;
|
||||
|
||||
const uintptr_t ZPlatformAddressSpaceStart = (uintptr_t)1 << ZPlatformAddressOffsetBits;
|
||||
const uintptr_t ZPlatformAddressSpaceSize = ((uintptr_t)1 << ZPlatformAddressOffsetBits) * 4;
|
||||
const uintptr_t ZPlatformAddressSpaceStart = (uintptr_t)1 << ZPlatformAddressOffsetBits;
|
||||
const uintptr_t ZPlatformAddressSpaceSize = ((uintptr_t)1 << ZPlatformAddressOffsetBits) * 4;
|
||||
|
||||
const size_t ZPlatformCacheLineSize = 64;
|
||||
const size_t ZPlatformNMethodDisarmedOffset = 4;
|
||||
|
||||
const size_t ZPlatformCacheLineSize = 64;
|
||||
|
||||
#endif // OS_CPU_LINUX_X86_ZGLOBALS_LINUX_X86_HPP
|
||||
|
@ -4181,6 +4181,7 @@ bool MatchRule::is_vector() const {
|
||||
"AddReductionVF", "AddReductionVD",
|
||||
"MulReductionVI", "MulReductionVL",
|
||||
"MulReductionVF", "MulReductionVD",
|
||||
"MulAddVS2VI",
|
||||
"LShiftCntV","RShiftCntV",
|
||||
"LShiftVB","LShiftVS","LShiftVI","LShiftVL",
|
||||
"RShiftVB","RShiftVS","RShiftVI","RShiftVL",
|
||||
|
@ -5703,7 +5703,8 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
|
||||
ik->major_version() != JAVA_MIN_SUPPORTED_VERSION &&
|
||||
log_is_enabled(Info, class, preview)) {
|
||||
ResourceMark rm;
|
||||
log_info(class, preview)("Loading preview feature type %s", ik->external_name());
|
||||
log_info(class, preview)("Loading class %s that depends on preview features (class file version %d.65535)",
|
||||
ik->external_name(), ik->major_version());
|
||||
}
|
||||
|
||||
if (log_is_enabled(Debug, class, resolve)) {
|
||||
|
@ -363,6 +363,13 @@ void ClassPathZipEntry::contents_do(void f(const char* name, void* context), voi
|
||||
}
|
||||
}
|
||||
|
||||
void ClassPathImageEntry::close_jimage() {
|
||||
if (_jimage != NULL) {
|
||||
(*JImageClose)(_jimage);
|
||||
_jimage = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ClassPathImageEntry::ClassPathImageEntry(JImageFile* jimage, const char* name) :
|
||||
ClassPathEntry(),
|
||||
_jimage(jimage) {
|
||||
@ -614,6 +621,12 @@ void ClassLoader::update_module_path_entry_list(const char *path, TRAPS) {
|
||||
void ClassLoader::setup_module_search_path(const char* path, TRAPS) {
|
||||
update_module_path_entry_list(path, THREAD);
|
||||
}
|
||||
|
||||
void ClassLoader::close_jrt_image() {
|
||||
assert(ClassLoader::has_jrt_entry(), "Not applicable for exploded builds");
|
||||
_jrt_entry->close_jimage();
|
||||
}
|
||||
|
||||
#endif // INCLUDE_CDS
|
||||
|
||||
// Construct the array of module/path pairs as specified to --patch-module
|
||||
|
@ -55,6 +55,7 @@ public:
|
||||
virtual bool is_jar_file() const = 0;
|
||||
virtual const char* name() const = 0;
|
||||
virtual JImageFile* jimage() const = 0;
|
||||
virtual void close_jimage() = 0;
|
||||
// Constructor
|
||||
ClassPathEntry() : _next(NULL) {}
|
||||
// Attempt to locate file_name through this class path entry.
|
||||
@ -70,6 +71,7 @@ class ClassPathDirEntry: public ClassPathEntry {
|
||||
bool is_jar_file() const { return false; }
|
||||
const char* name() const { return _dir; }
|
||||
JImageFile* jimage() const { return NULL; }
|
||||
void close_jimage() {}
|
||||
ClassPathDirEntry(const char* dir);
|
||||
virtual ~ClassPathDirEntry() {}
|
||||
ClassFileStream* open_stream(const char* name, TRAPS);
|
||||
@ -98,6 +100,7 @@ class ClassPathZipEntry: public ClassPathEntry {
|
||||
bool is_jar_file() const { return true; }
|
||||
const char* name() const { return _zip_name; }
|
||||
JImageFile* jimage() const { return NULL; }
|
||||
void close_jimage() {}
|
||||
ClassPathZipEntry(jzfile* zip, const char* zip_name, bool is_boot_append);
|
||||
virtual ~ClassPathZipEntry();
|
||||
u1* open_entry(const char* name, jint* filesize, bool nul_terminate, TRAPS);
|
||||
@ -117,6 +120,7 @@ public:
|
||||
bool is_open() const { return _jimage != NULL; }
|
||||
const char* name() const { return _name == NULL ? "" : _name; }
|
||||
JImageFile* jimage() const { return _jimage; }
|
||||
void close_jimage();
|
||||
ClassPathImageEntry(JImageFile* jimage, const char* name);
|
||||
virtual ~ClassPathImageEntry();
|
||||
ClassFileStream* open_stream(const char* name, TRAPS);
|
||||
@ -333,6 +337,7 @@ class ClassLoader: AllStatic {
|
||||
// Modular java runtime image is present vs. a build with exploded modules
|
||||
static bool has_jrt_entry() { return (_jrt_entry != NULL); }
|
||||
static ClassPathEntry* get_jrt_entry() { return _jrt_entry; }
|
||||
static void close_jrt_image();
|
||||
|
||||
// Add a module's exploded directory to the boot loader's exploded module build list
|
||||
static void add_to_exploded_build_list(Symbol* module_name, TRAPS);
|
||||
|
@ -116,14 +116,17 @@ public:
|
||||
return SymbolTableHash::BaseConfig::allocate_node(size, value);
|
||||
}
|
||||
static void free_node(void* memory, Symbol* const& value) {
|
||||
// We get here either because #1 some threads lost a race
|
||||
// to insert a newly created Symbol, or #2 we are freeing
|
||||
// a symbol during normal cleanup deletion.
|
||||
// If #1, then the symbol can be a permanent (refcount==PERM_REFCOUNT),
|
||||
// or regular newly created one but with refcount==0 (see SymbolTableCreateEntry)
|
||||
// If #2, then the symbol must have refcount==0
|
||||
assert((value->refcount() == PERM_REFCOUNT) || (value->refcount() == 0),
|
||||
// We get here because #1 some threads lost a race to insert a newly created Symbol
|
||||
// or #2 we're cleaning up unused symbol.
|
||||
// If #1, then the symbol can be either permanent (refcount==PERM_REFCOUNT),
|
||||
// or regular newly created one (refcount==1)
|
||||
// If #2, then the symbol is dead (refcount==0)
|
||||
assert((value->refcount() == PERM_REFCOUNT) || (value->refcount() == 1) || (value->refcount() == 0),
|
||||
"refcount %d", value->refcount());
|
||||
if (value->refcount() == 1) {
|
||||
value->decrement_refcount();
|
||||
assert(value->refcount() == 0, "expected dead symbol");
|
||||
}
|
||||
SymbolTable::delete_symbol(value);
|
||||
SymbolTableHash::BaseConfig::free_node(memory, value);
|
||||
SymbolTable::item_removed();
|
||||
@ -162,6 +165,12 @@ void SymbolTable::delete_symbol(Symbol* sym) {
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolTable::update_needs_rehash(bool rehash) {
|
||||
if (rehash) {
|
||||
_needs_rehashing = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolTable::item_added() {
|
||||
Atomic::inc(&(SymbolTable::the_table()->_items_count));
|
||||
}
|
||||
@ -398,9 +407,7 @@ Symbol* SymbolTable::do_lookup(const char* name, int len, uintx hash) {
|
||||
SymbolTableGet stg;
|
||||
bool rehash_warning = false;
|
||||
_local_table->get(thread, lookup, stg, &rehash_warning);
|
||||
if (rehash_warning) {
|
||||
_needs_rehashing = true;
|
||||
}
|
||||
update_needs_rehash(rehash_warning);
|
||||
Symbol* sym = stg.get_res_sym();
|
||||
assert((sym == NULL) || sym->refcount() != 0, "found dead symbol");
|
||||
return sym;
|
||||
@ -462,71 +469,26 @@ void SymbolTable::add(ClassLoaderData* loader_data, const constantPoolHandle& cp
|
||||
}
|
||||
}
|
||||
|
||||
class SymbolTableCreateEntry : public StackObj {
|
||||
private:
|
||||
Thread* _thread;
|
||||
const char* _name;
|
||||
int _len;
|
||||
bool _heap;
|
||||
Symbol* _return;
|
||||
Symbol* _created;
|
||||
|
||||
void assert_for_name(Symbol* sym, const char* where) const {
|
||||
#ifdef ASSERT
|
||||
assert(sym->utf8_length() == _len, "%s [%d,%d]", where, sym->utf8_length(), _len);
|
||||
for (int i = 0; i < _len; i++) {
|
||||
assert(sym->char_at(i) == _name[i],
|
||||
"%s [%d,%d,%d]", where, i, sym->char_at(i), _name[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
SymbolTableCreateEntry(Thread* thread, const char* name, int len, bool heap)
|
||||
: _thread(thread), _name(name) , _len(len), _heap(heap), _return(NULL) , _created(NULL) {
|
||||
assert(_name != NULL, "expected valid name");
|
||||
}
|
||||
Symbol* operator()() {
|
||||
_created = SymbolTable::the_table()->allocate_symbol(_name, _len, _heap, _thread);
|
||||
assert(_created != NULL, "expected created symbol");
|
||||
assert_for_name(_created, "operator()()");
|
||||
assert(_created->equals(_name, _len),
|
||||
"symbol must be properly initialized [%p,%d,%d]", _name, _len, (int)_heap);
|
||||
return _created;
|
||||
}
|
||||
void operator()(bool inserted, Symbol** value) {
|
||||
assert(value != NULL, "expected valid value");
|
||||
assert(*value != NULL, "value should point to a symbol");
|
||||
if (!inserted && (_created != NULL)) {
|
||||
// We created our symbol, but someone else inserted
|
||||
// theirs first, so ours will be destroyed.
|
||||
// Since symbols are created with refcount of 1,
|
||||
// we must decrement it here to 0 to delete,
|
||||
// unless it's a permanent one.
|
||||
if (_created->refcount() != PERM_REFCOUNT) {
|
||||
assert(_created->refcount() == 1, "expected newly created symbol");
|
||||
_created->decrement_refcount();
|
||||
assert(_created->refcount() == 0, "expected dead symbol");
|
||||
}
|
||||
}
|
||||
_return = *value;
|
||||
assert_for_name(_return, "operator()");
|
||||
}
|
||||
Symbol* get_new_sym() const {
|
||||
assert_for_name(_return, "get_new_sym");
|
||||
return _return;
|
||||
}
|
||||
};
|
||||
|
||||
Symbol* SymbolTable::do_add_if_needed(const char* name, int len, uintx hash, bool heap, TRAPS) {
|
||||
SymbolTableLookup lookup(THREAD, name, len, hash);
|
||||
SymbolTableCreateEntry stce(THREAD, name, len, heap);
|
||||
bool rehash_warning = false;
|
||||
SymbolTableGet stg;
|
||||
bool clean_hint = false;
|
||||
_local_table->get_insert_lazy(THREAD, lookup, stce, stce, &rehash_warning, &clean_hint);
|
||||
if (rehash_warning) {
|
||||
_needs_rehashing = true;
|
||||
}
|
||||
bool rehash_warning = false;
|
||||
Symbol* sym = NULL;
|
||||
|
||||
do {
|
||||
if (_local_table->get(THREAD, lookup, stg, &rehash_warning)) {
|
||||
sym = stg.get_res_sym();
|
||||
break;
|
||||
}
|
||||
sym = SymbolTable::the_table()->allocate_symbol(name, len, heap, THREAD);
|
||||
if (_local_table->insert(THREAD, lookup, sym, &rehash_warning, &clean_hint)) {
|
||||
break;
|
||||
}
|
||||
} while(true);
|
||||
|
||||
update_needs_rehash(rehash_warning);
|
||||
|
||||
if (clean_hint) {
|
||||
// we just found out that there is a dead item,
|
||||
// which we were unable to clean right now,
|
||||
@ -536,8 +498,8 @@ Symbol* SymbolTable::do_add_if_needed(const char* name, int len, uintx hash, boo
|
||||
mark_item_clean_count();
|
||||
check_concurrent_work();
|
||||
}
|
||||
Symbol* sym = stce.get_new_sym();
|
||||
assert(sym->refcount() != 0, "zero is invalid");
|
||||
|
||||
assert((sym == NULL) || sym->refcount() != 0, "found dead symbol");
|
||||
return sym;
|
||||
}
|
||||
|
||||
|
@ -169,6 +169,7 @@ private:
|
||||
|
||||
void try_rehash_table();
|
||||
bool do_rehash();
|
||||
inline void update_needs_rehash(bool rehash);
|
||||
|
||||
public:
|
||||
// The symbol table
|
||||
|
@ -379,6 +379,10 @@ bool vmIntrinsics::preserves_state(vmIntrinsics::ID id) {
|
||||
case vmIntrinsics::_vectorizedMismatch:
|
||||
case vmIntrinsics::_fmaD:
|
||||
case vmIntrinsics::_fmaF:
|
||||
case vmIntrinsics::_isDigit:
|
||||
case vmIntrinsics::_isLowerCase:
|
||||
case vmIntrinsics::_isUpperCase:
|
||||
case vmIntrinsics::_isWhitespace:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -828,6 +832,12 @@ bool vmIntrinsics::is_disabled_by_flags(vmIntrinsics::ID id) {
|
||||
case vmIntrinsics::_subtractExactL:
|
||||
if (!UseMathExactIntrinsics || !InlineMathNatives) return true;
|
||||
break;
|
||||
case vmIntrinsics::_isDigit:
|
||||
case vmIntrinsics::_isLowerCase:
|
||||
case vmIntrinsics::_isUpperCase:
|
||||
case vmIntrinsics::_isWhitespace:
|
||||
if (!UseCharacterCompareIntrinsics) return true;
|
||||
break;
|
||||
#endif // COMPILER2
|
||||
default:
|
||||
return false;
|
||||
|
@ -71,6 +71,7 @@
|
||||
template(java_lang_Boolean, "java/lang/Boolean") \
|
||||
template(java_lang_Character, "java/lang/Character") \
|
||||
template(java_lang_Character_CharacterCache, "java/lang/Character$CharacterCache") \
|
||||
template(java_lang_CharacterDataLatin1, "java/lang/CharacterDataLatin1") \
|
||||
template(java_lang_Float, "java/lang/Float") \
|
||||
template(java_lang_Double, "java/lang/Double") \
|
||||
template(java_lang_Byte, "java/lang/Byte") \
|
||||
@ -933,6 +934,15 @@
|
||||
do_intrinsic(_equalsL, java_lang_StringLatin1,equals_name, equalsB_signature, F_S) \
|
||||
do_intrinsic(_equalsU, java_lang_StringUTF16, equals_name, equalsB_signature, F_S) \
|
||||
\
|
||||
do_intrinsic(_isDigit, java_lang_CharacterDataLatin1, isDigit_name, int_bool_signature, F_R) \
|
||||
do_name( isDigit_name, "isDigit") \
|
||||
do_intrinsic(_isLowerCase, java_lang_CharacterDataLatin1, isLowerCase_name, int_bool_signature, F_R) \
|
||||
do_name( isLowerCase_name, "isLowerCase") \
|
||||
do_intrinsic(_isUpperCase, java_lang_CharacterDataLatin1, isUpperCase_name, int_bool_signature, F_R) \
|
||||
do_name( isUpperCase_name, "isUpperCase") \
|
||||
do_intrinsic(_isWhitespace, java_lang_CharacterDataLatin1, isWhitespace_name, int_bool_signature, F_R) \
|
||||
do_name( isWhitespace_name, "isWhitespace") \
|
||||
\
|
||||
do_intrinsic(_Preconditions_checkIndex, jdk_internal_util_Preconditions, checkIndex_name, Preconditions_checkIndex_signature, F_S) \
|
||||
do_signature(Preconditions_checkIndex_signature, "(IILjava/util/function/BiFunction;)I") \
|
||||
\
|
||||
|
@ -51,6 +51,7 @@ VMReg VtableStub::_receiver_location = VMRegImpl::Bad();
|
||||
|
||||
|
||||
void* VtableStub::operator new(size_t size, int code_size) throw() {
|
||||
assert_lock_strong(VtableStubs_lock);
|
||||
assert(size == sizeof(VtableStub), "mismatched size");
|
||||
// compute real VtableStub size (rounded to nearest word)
|
||||
const int real_size = align_up(code_size + (int)sizeof(VtableStub), wordSize);
|
||||
@ -208,31 +209,35 @@ void VtableStubs::bookkeeping(MacroAssembler* masm, outputStream* out, VtableStu
|
||||
address VtableStubs::find_stub(bool is_vtable_stub, int vtable_index) {
|
||||
assert(vtable_index >= 0, "must be positive");
|
||||
|
||||
VtableStub* s = ShareVtableStubs ? lookup(is_vtable_stub, vtable_index) : NULL;
|
||||
if (s == NULL) {
|
||||
if (is_vtable_stub) {
|
||||
s = create_vtable_stub(vtable_index);
|
||||
} else {
|
||||
s = create_itable_stub(vtable_index);
|
||||
}
|
||||
|
||||
// Creation of vtable or itable can fail if there is not enough free space in the code cache.
|
||||
VtableStub* s;
|
||||
{
|
||||
MutexLockerEx ml(VtableStubs_lock, Mutex::_no_safepoint_check_flag);
|
||||
s = ShareVtableStubs ? lookup(is_vtable_stub, vtable_index) : NULL;
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (is_vtable_stub) {
|
||||
s = create_vtable_stub(vtable_index);
|
||||
} else {
|
||||
s = create_itable_stub(vtable_index);
|
||||
}
|
||||
|
||||
enter(is_vtable_stub, vtable_index, s);
|
||||
if (PrintAdapterHandlers) {
|
||||
tty->print_cr("Decoding VtableStub %s[%d]@" INTX_FORMAT,
|
||||
is_vtable_stub? "vtbl": "itbl", vtable_index, p2i(VtableStub::receiver_location()));
|
||||
Disassembler::decode(s->code_begin(), s->code_end());
|
||||
}
|
||||
// Notify JVMTI about this stub. The event will be recorded by the enclosing
|
||||
// JvmtiDynamicCodeEventCollector and posted when this thread has released
|
||||
// all locks.
|
||||
if (JvmtiExport::should_post_dynamic_code_generated()) {
|
||||
JvmtiExport::post_dynamic_code_generated_while_holding_locks(is_vtable_stub? "vtable stub": "itable stub",
|
||||
s->code_begin(), s->code_end());
|
||||
// Creation of vtable or itable can fail if there is not enough free space in the code cache.
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enter(is_vtable_stub, vtable_index, s);
|
||||
if (PrintAdapterHandlers) {
|
||||
tty->print_cr("Decoding VtableStub %s[%d]@" INTX_FORMAT,
|
||||
is_vtable_stub? "vtbl": "itbl", vtable_index, p2i(VtableStub::receiver_location()));
|
||||
Disassembler::decode(s->code_begin(), s->code_end());
|
||||
}
|
||||
// Notify JVMTI about this stub. The event will be recorded by the enclosing
|
||||
// JvmtiDynamicCodeEventCollector and posted when this thread has released
|
||||
// all locks.
|
||||
if (JvmtiExport::should_post_dynamic_code_generated()) {
|
||||
JvmtiExport::post_dynamic_code_generated_while_holding_locks(is_vtable_stub? "vtable stub": "itable stub",
|
||||
s->code_begin(), s->code_end());
|
||||
}
|
||||
}
|
||||
}
|
||||
return s->entry_point();
|
||||
@ -247,7 +252,7 @@ inline uint VtableStubs::hash(bool is_vtable_stub, int vtable_index){
|
||||
|
||||
|
||||
VtableStub* VtableStubs::lookup(bool is_vtable_stub, int vtable_index) {
|
||||
MutexLockerEx ml(VtableStubs_lock, Mutex::_no_safepoint_check_flag);
|
||||
assert_lock_strong(VtableStubs_lock);
|
||||
unsigned hash = VtableStubs::hash(is_vtable_stub, vtable_index);
|
||||
VtableStub* s = _table[hash];
|
||||
while( s && !s->matches(is_vtable_stub, vtable_index)) s = s->next();
|
||||
@ -256,7 +261,7 @@ VtableStub* VtableStubs::lookup(bool is_vtable_stub, int vtable_index) {
|
||||
|
||||
|
||||
void VtableStubs::enter(bool is_vtable_stub, int vtable_index, VtableStub* s) {
|
||||
MutexLockerEx ml(VtableStubs_lock, Mutex::_no_safepoint_check_flag);
|
||||
assert_lock_strong(VtableStubs_lock);
|
||||
assert(s->matches(is_vtable_stub, vtable_index), "bad vtable stub");
|
||||
unsigned int h = VtableStubs::hash(is_vtable_stub, vtable_index);
|
||||
// enter s at the beginning of the corresponding list
|
||||
|
@ -70,19 +70,24 @@ CMSHeap::CMSHeap(GenCollectorPolicy *policy) :
|
||||
Generation::ParNew,
|
||||
Generation::ConcurrentMarkSweep,
|
||||
"ParNew:CMS"),
|
||||
_workers(NULL),
|
||||
_eden_pool(NULL),
|
||||
_survivor_pool(NULL),
|
||||
_old_pool(NULL) {
|
||||
_workers = new WorkGang("GC Thread", ParallelGCThreads,
|
||||
/* are_GC_task_threads */true,
|
||||
/* are_ConcurrentGC_threads */false);
|
||||
_workers->initialize_workers();
|
||||
}
|
||||
|
||||
jint CMSHeap::initialize() {
|
||||
jint status = GenCollectedHeap::initialize();
|
||||
if (status != JNI_OK) return status;
|
||||
|
||||
_workers = new WorkGang("GC Thread", ParallelGCThreads,
|
||||
/* are_GC_task_threads */true,
|
||||
/* are_ConcurrentGC_threads */false);
|
||||
if (_workers == NULL) {
|
||||
return JNI_ENOMEM;
|
||||
}
|
||||
_workers->initialize_workers();
|
||||
|
||||
// If we are running CMS, create the collector responsible
|
||||
// for collecting the CMS generations.
|
||||
if (!create_cms_collector()) {
|
||||
|
@ -1531,10 +1531,6 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* collector_policy) :
|
||||
_is_subject_to_discovery_cm(this),
|
||||
_in_cset_fast_test() {
|
||||
|
||||
_workers = new WorkGang("GC Thread", ParallelGCThreads,
|
||||
true /* are_GC_task_threads */,
|
||||
false /* are_ConcurrentGC_threads */);
|
||||
_workers->initialize_workers();
|
||||
_verifier = new G1HeapVerifier(this);
|
||||
|
||||
_allocator = new G1Allocator(this);
|
||||
@ -1767,6 +1763,14 @@ jint G1CollectedHeap::initialize() {
|
||||
_humongous_reclaim_candidates.initialize(start, end, granularity);
|
||||
}
|
||||
|
||||
_workers = new WorkGang("GC Thread", ParallelGCThreads,
|
||||
true /* are_GC_task_threads */,
|
||||
false /* are_ConcurrentGC_threads */);
|
||||
if (_workers == NULL) {
|
||||
return JNI_ENOMEM;
|
||||
}
|
||||
_workers->initialize_workers();
|
||||
|
||||
// Create the G1ConcurrentMark data structure and thread.
|
||||
// (Must do this late, so that "max_regions" is defined.)
|
||||
_cm = new G1ConcurrentMark(this, prev_bitmap_storage, next_bitmap_storage);
|
||||
|
@ -1026,6 +1026,9 @@ public:
|
||||
// The number of regions that are completely free.
|
||||
uint num_free_regions() const { return _hrm.num_free_regions(); }
|
||||
|
||||
// The number of regions that can be allocated into.
|
||||
uint num_free_or_available_regions() const { return num_free_regions() + _hrm.available(); }
|
||||
|
||||
MemoryUsage get_auxiliary_data_memory_usage() const {
|
||||
return _hrm.get_auxiliary_data_memory_usage();
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ void G1HeapTransition::print() {
|
||||
Data after(_g1_heap);
|
||||
|
||||
size_t eden_capacity_length_after_gc = _g1_heap->g1_policy()->young_list_target_length() - after._survivor_length;
|
||||
size_t survivor_capacity_length_after_gc = _g1_heap->g1_policy()->max_survivor_regions();
|
||||
size_t survivor_capacity_length_before_gc = _g1_heap->g1_policy()->max_survivor_regions();
|
||||
|
||||
DetailedUsage usage;
|
||||
if (log_is_enabled(Trace, gc, heap)) {
|
||||
@ -112,7 +112,7 @@ void G1HeapTransition::print() {
|
||||
log_trace(gc, heap)(" Used: 0K, Waste: 0K");
|
||||
|
||||
log_info(gc, heap)("Survivor regions: " SIZE_FORMAT "->" SIZE_FORMAT "(" SIZE_FORMAT ")",
|
||||
_before._survivor_length, after._survivor_length, survivor_capacity_length_after_gc);
|
||||
_before._survivor_length, after._survivor_length, survivor_capacity_length_before_gc);
|
||||
log_trace(gc, heap)(" Used: " SIZE_FORMAT "K, Waste: " SIZE_FORMAT "K",
|
||||
usage._survivor_used / K, ((after._survivor_length * HeapRegion::GrainBytes) - usage._survivor_used) / K);
|
||||
|
||||
|
@ -470,6 +470,10 @@ void G1Policy::record_collection_pause_start(double start_time_sec) {
|
||||
// every time we calculate / recalculate the target young length.
|
||||
update_survivors_policy();
|
||||
|
||||
assert(max_survivor_regions() + _g1h->num_used_regions() <= _g1h->max_regions(),
|
||||
"Maximum survivor regions %u plus used regions %u exceeds max regions %u",
|
||||
max_survivor_regions(), _g1h->num_used_regions(), _g1h->max_regions());
|
||||
|
||||
assert(_g1h->used() == _g1h->recalculate_used(),
|
||||
"sanity, used: " SIZE_FORMAT " recalculate_used: " SIZE_FORMAT,
|
||||
_g1h->used(), _g1h->recalculate_used());
|
||||
@ -899,8 +903,8 @@ bool G1Policy::adaptive_young_list_length() const {
|
||||
return _young_gen_sizer.adaptive_young_list_length();
|
||||
}
|
||||
|
||||
size_t G1Policy::desired_survivor_size() const {
|
||||
size_t const survivor_capacity = HeapRegion::GrainWords * _max_survivor_regions;
|
||||
size_t G1Policy::desired_survivor_size(uint max_regions) const {
|
||||
size_t const survivor_capacity = HeapRegion::GrainWords * max_regions;
|
||||
return (size_t)((((double)survivor_capacity) * TargetSurvivorRatio) / 100);
|
||||
}
|
||||
|
||||
@ -927,15 +931,22 @@ void G1Policy::update_max_gc_locker_expansion() {
|
||||
void G1Policy::update_survivors_policy() {
|
||||
double max_survivor_regions_d =
|
||||
(double) _young_list_target_length / (double) SurvivorRatio;
|
||||
// We use ceiling so that if max_survivor_regions_d is > 0.0 (but
|
||||
// smaller than 1.0) we'll get 1.
|
||||
_max_survivor_regions = (uint) ceil(max_survivor_regions_d);
|
||||
|
||||
_tenuring_threshold = _survivors_age_table.compute_tenuring_threshold(desired_survivor_size());
|
||||
// Calculate desired survivor size based on desired max survivor regions (unconstrained
|
||||
// by remaining heap). Otherwise we may cause undesired promotions as we are
|
||||
// already getting close to end of the heap, impacting performance even more.
|
||||
uint const desired_max_survivor_regions = ceil(max_survivor_regions_d);
|
||||
size_t const survivor_size = desired_survivor_size(desired_max_survivor_regions);
|
||||
|
||||
_tenuring_threshold = _survivors_age_table.compute_tenuring_threshold(survivor_size);
|
||||
if (UsePerfData) {
|
||||
_policy_counters->tenuring_threshold()->set_value(_tenuring_threshold);
|
||||
_policy_counters->desired_survivor_size()->set_value(desired_survivor_size() * oopSize);
|
||||
_policy_counters->desired_survivor_size()->set_value(survivor_size * oopSize);
|
||||
}
|
||||
// The real maximum survivor size is bounded by the number of regions that can
|
||||
// be allocated into.
|
||||
_max_survivor_regions = MIN2(desired_max_survivor_regions,
|
||||
_g1h->num_free_or_available_regions());
|
||||
}
|
||||
|
||||
bool G1Policy::force_initial_mark_if_outside_cycle(GCCause::Cause gc_cause) {
|
||||
|
@ -399,7 +399,7 @@ private:
|
||||
|
||||
AgeTable _survivors_age_table;
|
||||
|
||||
size_t desired_survivor_size() const;
|
||||
size_t desired_survivor_size(uint max_regions) const;
|
||||
public:
|
||||
// Fraction used when predicting how many optional regions to include in
|
||||
// the CSet. This fraction of the available time is used for optional regions,
|
||||
|
@ -26,47 +26,24 @@
|
||||
#include "gc/shared/barrierSet.hpp"
|
||||
#include "gc/shared/barrierSetAssembler.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
|
||||
BarrierSet* BarrierSet::_barrier_set = NULL;
|
||||
|
||||
class SetBarrierSetNonJavaThread : public ThreadClosure {
|
||||
BarrierSet* _barrier_set;
|
||||
size_t _count;
|
||||
|
||||
public:
|
||||
SetBarrierSetNonJavaThread(BarrierSet* barrier_set) :
|
||||
_barrier_set(barrier_set), _count(0) {}
|
||||
|
||||
virtual void do_thread(Thread* thread) {
|
||||
_barrier_set->on_thread_create(thread);
|
||||
++_count;
|
||||
}
|
||||
|
||||
size_t count() const { return _count; }
|
||||
};
|
||||
|
||||
void BarrierSet::set_barrier_set(BarrierSet* barrier_set) {
|
||||
assert(_barrier_set == NULL, "Already initialized");
|
||||
_barrier_set = barrier_set;
|
||||
|
||||
// Some threads are created before the barrier set, so the call to
|
||||
// BarrierSet::on_thread_create had to be deferred for them. Now that
|
||||
// we have the barrier set, do those deferred calls.
|
||||
|
||||
// First do any non-JavaThreads.
|
||||
SetBarrierSetNonJavaThread njt_closure(_barrier_set);
|
||||
Threads::non_java_threads_do(&njt_closure);
|
||||
|
||||
// Do the current (main) thread. Ensure it's the one and only
|
||||
// JavaThread so far. Also verify that it isn't yet on the thread
|
||||
// Notify barrier set of the current (main) thread. Normally the
|
||||
// Thread constructor deals with this, but the main thread is
|
||||
// created before we get here. Verify it isn't yet on the thread
|
||||
// list, else we'd also need to call BarrierSet::on_thread_attach.
|
||||
// This is the only thread that can exist at this point; the Thread
|
||||
// constructor objects to other threads being created before the
|
||||
// barrier set is available.
|
||||
assert(Thread::current()->is_Java_thread(),
|
||||
"Expected main thread to be a JavaThread");
|
||||
assert((njt_closure.count() + 1) == Threads::threads_before_barrier_set(),
|
||||
"Unexpected JavaThreads before barrier set initialization: "
|
||||
"Non-JavaThreads: " SIZE_FORMAT ", all: " SIZE_FORMAT,
|
||||
njt_closure.count(), Threads::threads_before_barrier_set());
|
||||
assert(!JavaThread::current()->on_thread_list(),
|
||||
"Main thread already on thread list.");
|
||||
_barrier_set->on_thread_create(Thread::current());
|
||||
|
@ -80,10 +80,6 @@ void ZArguments::initialize() {
|
||||
FLAG_SET_DEFAULT(UseCompressedOops, false);
|
||||
FLAG_SET_DEFAULT(UseCompressedClassPointers, false);
|
||||
|
||||
// ClassUnloading not (yet) supported
|
||||
FLAG_SET_DEFAULT(ClassUnloading, false);
|
||||
FLAG_SET_DEFAULT(ClassUnloadingWithConcurrentMark, false);
|
||||
|
||||
// Verification before startup and after exit not (yet) supported
|
||||
FLAG_SET_DEFAULT(VerifyDuringStartup, false);
|
||||
FLAG_SET_DEFAULT(VerifyBeforeExit, false);
|
||||
|
@ -81,6 +81,7 @@ public:
|
||||
static void load_barrier_on_oop_fields(oop o);
|
||||
static oop load_barrier_on_weak_oop_field_preloaded(volatile oop* p, oop o);
|
||||
static oop load_barrier_on_phantom_oop_field_preloaded(volatile oop* p, oop o);
|
||||
static void load_barrier_on_root_oop_field(oop* p);
|
||||
|
||||
// Weak load barrier
|
||||
static oop weak_load_barrier_on_oop_field(volatile oop* p);
|
||||
@ -99,6 +100,7 @@ public:
|
||||
// Keep alive barrier
|
||||
static void keep_alive_barrier_on_weak_oop_field(volatile oop* p);
|
||||
static void keep_alive_barrier_on_phantom_oop_field(volatile oop* p);
|
||||
static void keep_alive_barrier_on_phantom_root_oop_field(oop* p);
|
||||
|
||||
// Mark barrier
|
||||
static void mark_barrier_on_oop_field(volatile oop* p, bool finalizable);
|
||||
|
@ -111,11 +111,12 @@ inline void ZBarrier::root_barrier(oop* p, oop o) {
|
||||
const uintptr_t good_addr = slow_path(addr);
|
||||
|
||||
// Non-atomic healing helps speed up root scanning. This is safe to do
|
||||
// since we are always healing roots in a safepoint, which means we are
|
||||
// never racing with mutators modifying roots while we are healing them.
|
||||
// It's also safe in case multiple GC threads try to heal the same root,
|
||||
// since they would always heal the root in the same way and it does not
|
||||
// matter in which order it happens.
|
||||
// since we are always healing roots in a safepoint, or under a lock,
|
||||
// which ensures we are never racing with mutators modifying roots while
|
||||
// we are healing them. It's also safe in case multiple GC threads try
|
||||
// to heal the same root if it is aligned, since they would always heal
|
||||
// the root in the same way and it does not matter in which order it
|
||||
// happens. For misaligned oops, there needs to be mutual exclusion.
|
||||
*p = ZOop::to_oop(good_addr);
|
||||
}
|
||||
|
||||
@ -188,6 +189,11 @@ inline oop ZBarrier::load_barrier_on_phantom_oop_field_preloaded(volatile oop* p
|
||||
return load_barrier_on_oop_field_preloaded(p, o);
|
||||
}
|
||||
|
||||
inline void ZBarrier::load_barrier_on_root_oop_field(oop* p) {
|
||||
const oop o = *p;
|
||||
root_barrier<is_good_or_null_fast_path, load_barrier_on_oop_slow_path>(p, o);
|
||||
}
|
||||
|
||||
//
|
||||
// Weak load barrier
|
||||
//
|
||||
@ -269,6 +275,13 @@ inline void ZBarrier::keep_alive_barrier_on_phantom_oop_field(volatile oop* p) {
|
||||
barrier<is_good_or_null_fast_path, keep_alive_barrier_on_phantom_oop_slow_path>(p, o);
|
||||
}
|
||||
|
||||
inline void ZBarrier::keep_alive_barrier_on_phantom_root_oop_field(oop* p) {
|
||||
// This operation is only valid when resurrection is blocked.
|
||||
assert(ZResurrection::is_blocked(), "Invalid phase");
|
||||
const oop o = *p;
|
||||
root_barrier<is_good_or_null_fast_path, keep_alive_barrier_on_phantom_oop_slow_path>(p, o);
|
||||
}
|
||||
|
||||
//
|
||||
// Mark barrier
|
||||
//
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "precompiled.hpp"
|
||||
#include "gc/z/zBarrierSet.hpp"
|
||||
#include "gc/z/zBarrierSetAssembler.hpp"
|
||||
#include "gc/z/zBarrierSetNMethod.hpp"
|
||||
#include "gc/z/zGlobals.hpp"
|
||||
#include "gc/z/zHeap.inline.hpp"
|
||||
#include "gc/z/zThreadLocalData.hpp"
|
||||
@ -39,11 +40,20 @@
|
||||
class ZBarrierSetC1;
|
||||
class ZBarrierSetC2;
|
||||
|
||||
static BarrierSetNMethod* make_barrier_set_nmethod() {
|
||||
// NMethod barriers are only used when class unloading is enabled
|
||||
if (!ClassUnloading) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new ZBarrierSetNMethod();
|
||||
}
|
||||
|
||||
ZBarrierSet::ZBarrierSet() :
|
||||
BarrierSet(make_barrier_set_assembler<ZBarrierSetAssembler>(),
|
||||
make_barrier_set_c1<ZBarrierSetC1>(),
|
||||
make_barrier_set_c2<ZBarrierSetC2>(),
|
||||
NULL /* barrier_set_nmethod */,
|
||||
make_barrier_set_nmethod(),
|
||||
BarrierSet::FakeRtti(BarrierSet::ZBarrierSet)) {}
|
||||
|
||||
ZBarrierSetAssembler* ZBarrierSet::assembler() {
|
||||
|
73
src/hotspot/share/gc/z/zBarrierSetNMethod.cpp
Normal file
73
src/hotspot/share/gc/z/zBarrierSetNMethod.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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 "precompiled.hpp"
|
||||
#include "code/nmethod.hpp"
|
||||
#include "gc/z/zBarrierSetNMethod.hpp"
|
||||
#include "gc/z/zGlobals.hpp"
|
||||
#include "gc/z/zLock.inline.hpp"
|
||||
#include "gc/z/zOopClosures.hpp"
|
||||
#include "gc/z/zNMethodTable.hpp"
|
||||
#include "gc/z/zThreadLocalData.hpp"
|
||||
#include "logging/log.hpp"
|
||||
|
||||
bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) {
|
||||
ZLocker<ZReentrantLock> locker(ZNMethodTable::lock_for_nmethod(nm));
|
||||
log_trace(nmethod, barrier)("Entered critical zone for %p", nm);
|
||||
|
||||
if (!is_armed(nm)) {
|
||||
// Some other thread got here first and healed the oops
|
||||
// and disarmed the nmethod.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nm->is_unloading()) {
|
||||
// We can end up calling nmethods that are unloading
|
||||
// since we clear compiled ICs lazily. Returning false
|
||||
// will re-resovle the call and update the compiled IC.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Heal oops and disarm
|
||||
ZNMethodOopClosure cl;
|
||||
nm->oops_do(&cl);
|
||||
nm->fix_oop_relocations();
|
||||
|
||||
OrderAccess::release();
|
||||
|
||||
disarm(nm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ZBarrierSetNMethod::disarmed_value() const {
|
||||
// We override the default BarrierSetNMethod::disarmed_value() since
|
||||
// this can be called by GC threads, which doesn't keep an up to date
|
||||
// address_bad_mask.
|
||||
const uintptr_t disarmed_addr = ((uintptr_t)&ZAddressBadMask) + ZNMethodDisarmedOffset;
|
||||
return *((int*)disarmed_addr);
|
||||
}
|
||||
|
||||
ByteSize ZBarrierSetNMethod::thread_disarmed_offset() const {
|
||||
return ZThreadLocalData::nmethod_disarmed_offset();
|
||||
}
|
41
src/hotspot/share/gc/z/zBarrierSetNMethod.hpp
Normal file
41
src/hotspot/share/gc/z/zBarrierSetNMethod.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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_Z_ZBARRIERSETNMETHOD_HPP
|
||||
#define SHARE_GC_Z_ZBARRIERSETNMETHOD_HPP
|
||||
|
||||
#include "gc/shared/barrierSetNMethod.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
|
||||
class nmethod;
|
||||
|
||||
class ZBarrierSetNMethod : public BarrierSetNMethod {
|
||||
protected:
|
||||
virtual int disarmed_value() const;
|
||||
virtual bool nmethod_entry_barrier(nmethod* nm);
|
||||
|
||||
public:
|
||||
virtual ByteSize thread_disarmed_offset() const;
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_Z_ZBARRIERSETNMETHOD_HPP
|
@ -259,12 +259,10 @@ bool ZCollectedHeap::block_is_obj(const HeapWord* addr) const {
|
||||
}
|
||||
|
||||
void ZCollectedHeap::register_nmethod(nmethod* nm) {
|
||||
assert_locked_or_safepoint(CodeCache_lock);
|
||||
ZNMethodTable::register_nmethod(nm);
|
||||
}
|
||||
|
||||
void ZCollectedHeap::unregister_nmethod(nmethod* nm) {
|
||||
assert_locked_or_safepoint(CodeCache_lock);
|
||||
ZNMethodTable::unregister_nmethod(nm);
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,9 @@ const uintptr_t ZAddressSpaceStart = ZPlatformAddressSpaceStart;
|
||||
const uintptr_t ZAddressSpaceSize = ZPlatformAddressSpaceSize;
|
||||
const uintptr_t ZAddressSpaceEnd = ZAddressSpaceStart + ZAddressSpaceSize;
|
||||
|
||||
// NMethod entry barrier
|
||||
const size_t ZNMethodDisarmedOffset = ZPlatformNMethodDisarmedOffset;
|
||||
|
||||
// Cache line size
|
||||
const size_t ZCacheLineSize = ZPlatformCacheLineSize;
|
||||
|
||||
|
@ -69,6 +69,7 @@ ZHeap::ZHeap() :
|
||||
_weak_roots_processor(&_workers),
|
||||
_relocate(&_workers),
|
||||
_relocation_set(),
|
||||
_unload(&_workers),
|
||||
_serviceability(heap_min_size(), heap_max_size()) {
|
||||
// Install global heap instance
|
||||
assert(_heap == NULL, "Already initialized");
|
||||
@ -353,9 +354,6 @@ bool ZHeap::mark_end() {
|
||||
// Enter mark completed phase
|
||||
ZGlobalPhase = ZPhaseMarkCompleted;
|
||||
|
||||
// Resize metaspace
|
||||
MetaspaceGC::compute_new_size();
|
||||
|
||||
// Update statistics
|
||||
ZStatSample(ZSamplerHeapUsedAfterMark, used());
|
||||
ZStatHeap::set_at_mark_end(capacity(), allocated(), used());
|
||||
@ -366,6 +364,9 @@ bool ZHeap::mark_end() {
|
||||
// Process weak roots
|
||||
_weak_roots_processor.process_weak_roots();
|
||||
|
||||
// Prepare to unload unused classes and code
|
||||
_unload.prepare();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -380,6 +381,9 @@ void ZHeap::process_non_strong_references() {
|
||||
// Process concurrent weak roots
|
||||
_weak_roots_processor.process_concurrent_weak_roots();
|
||||
|
||||
// Unload unused classes and code
|
||||
_unload.unload();
|
||||
|
||||
// Unblock resurrection of weak/phantom references
|
||||
ZResurrection::unblock();
|
||||
|
||||
@ -463,8 +467,8 @@ void ZHeap::reset_relocation_set() {
|
||||
void ZHeap::relocate_start() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint");
|
||||
|
||||
// Update statistics
|
||||
ZStatSample(ZSamplerHeapUsedBeforeRelocation, used());
|
||||
// Finish unloading of classes and code
|
||||
_unload.finish();
|
||||
|
||||
// Flip address view
|
||||
ZAddressMasks::flip_to_remapped();
|
||||
@ -474,6 +478,7 @@ void ZHeap::relocate_start() {
|
||||
ZGlobalPhase = ZPhaseRelocate;
|
||||
|
||||
// Update statistics
|
||||
ZStatSample(ZSamplerHeapUsedBeforeRelocation, used());
|
||||
ZStatHeap::set_at_relocate_start(capacity(), allocated(), used());
|
||||
|
||||
// Remap/Relocate roots
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "gc/z/zRootsIterator.hpp"
|
||||
#include "gc/z/zWeakRootsProcessor.hpp"
|
||||
#include "gc/z/zServiceability.hpp"
|
||||
#include "gc/z/zUnload.hpp"
|
||||
#include "gc/z/zWorkers.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
|
||||
@ -59,6 +60,7 @@ private:
|
||||
ZWeakRootsProcessor _weak_roots_processor;
|
||||
ZRelocate _relocate;
|
||||
ZRelocationSet _relocation_set;
|
||||
ZUnload _unload;
|
||||
ZServiceability _serviceability;
|
||||
|
||||
size_t heap_min_size() const;
|
||||
|
@ -287,6 +287,14 @@ void ZMark::follow_partial_array(ZMarkStackEntry entry, bool finalizable) {
|
||||
}
|
||||
|
||||
void ZMark::follow_array_object(objArrayOop obj, bool finalizable) {
|
||||
if (finalizable) {
|
||||
ZMarkBarrierOopClosure<true /* finalizable */> cl;
|
||||
cl.do_klass(obj->klass());
|
||||
} else {
|
||||
ZMarkBarrierOopClosure<false /* finalizable */> cl;
|
||||
cl.do_klass(obj->klass());
|
||||
}
|
||||
|
||||
const uintptr_t addr = (uintptr_t)obj->base();
|
||||
const size_t size = (size_t)obj->length() * oopSize;
|
||||
|
||||
|
@ -23,45 +23,62 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "code/relocInfo.hpp"
|
||||
#include "code/nativeInst.hpp"
|
||||
#include "code/nmethod.hpp"
|
||||
#include "code/icBuffer.hpp"
|
||||
#include "gc/shared/barrierSet.hpp"
|
||||
#include "gc/shared/barrierSetNMethod.hpp"
|
||||
#include "gc/z/zArray.inline.hpp"
|
||||
#include "gc/z/zGlobals.hpp"
|
||||
#include "gc/z/zHash.inline.hpp"
|
||||
#include "gc/z/zLock.inline.hpp"
|
||||
#include "gc/z/zNMethodTable.hpp"
|
||||
#include "gc/z/zOopClosures.inline.hpp"
|
||||
#include "gc/z/zTask.hpp"
|
||||
#include "gc/z/zWorkers.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/orderAccess.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
|
||||
class ZNMethodWithImmediateOops {
|
||||
class ZNMethodDataImmediateOops {
|
||||
private:
|
||||
nmethod* const _nm;
|
||||
const size_t _nimmediate_oops;
|
||||
const size_t _nimmediate_oops;
|
||||
|
||||
static size_t header_size();
|
||||
|
||||
ZNMethodWithImmediateOops(nmethod* nm, const GrowableArray<oop*>& immediate_oops);
|
||||
ZNMethodDataImmediateOops(const GrowableArray<oop*>& immediate_oops);
|
||||
|
||||
public:
|
||||
static ZNMethodWithImmediateOops* create(nmethod* nm, const GrowableArray<oop*>& immediate_oops);
|
||||
static void destroy(ZNMethodWithImmediateOops* nmi);
|
||||
static ZNMethodDataImmediateOops* create(const GrowableArray<oop*>& immediate_oops);
|
||||
static void destroy(ZNMethodDataImmediateOops* data_immediate_oops);
|
||||
|
||||
nmethod* method() const;
|
||||
size_t immediate_oops_count() const;
|
||||
oop** immediate_oops_begin() const;
|
||||
oop** immediate_oops_end() const;
|
||||
};
|
||||
|
||||
size_t ZNMethodWithImmediateOops::header_size() {
|
||||
const size_t size = sizeof(ZNMethodWithImmediateOops);
|
||||
size_t ZNMethodDataImmediateOops::header_size() {
|
||||
const size_t size = sizeof(ZNMethodDataImmediateOops);
|
||||
assert(is_aligned(size, sizeof(oop*)), "Header misaligned");
|
||||
return size;
|
||||
}
|
||||
|
||||
ZNMethodWithImmediateOops::ZNMethodWithImmediateOops(nmethod* nm, const GrowableArray<oop*>& immediate_oops) :
|
||||
_nm(nm),
|
||||
ZNMethodDataImmediateOops* ZNMethodDataImmediateOops::create(const GrowableArray<oop*>& immediate_oops) {
|
||||
// Allocate memory for the ZNMethodDataImmediateOops object
|
||||
// plus the immediate oop* array that follows right after.
|
||||
const size_t size = ZNMethodDataImmediateOops::header_size() + (sizeof(oop*) * immediate_oops.length());
|
||||
void* const data_immediate_oops = NEW_C_HEAP_ARRAY(uint8_t, size, mtGC);
|
||||
return ::new (data_immediate_oops) ZNMethodDataImmediateOops(immediate_oops);
|
||||
}
|
||||
|
||||
void ZNMethodDataImmediateOops::destroy(ZNMethodDataImmediateOops* data_immediate_oops) {
|
||||
ZNMethodTable::safe_delete(data_immediate_oops);
|
||||
}
|
||||
|
||||
ZNMethodDataImmediateOops::ZNMethodDataImmediateOops(const GrowableArray<oop*>& immediate_oops) :
|
||||
_nimmediate_oops(immediate_oops.length()) {
|
||||
// Save all immediate oops
|
||||
for (size_t i = 0; i < _nimmediate_oops; i++) {
|
||||
@ -69,41 +86,97 @@ ZNMethodWithImmediateOops::ZNMethodWithImmediateOops(nmethod* nm, const Growable
|
||||
}
|
||||
}
|
||||
|
||||
ZNMethodWithImmediateOops* ZNMethodWithImmediateOops::create(nmethod* nm, const GrowableArray<oop*>& immediate_oops) {
|
||||
// Allocate memory for the ZNMethodWithImmediateOops object
|
||||
// plus the immediate oop* array that follows right after.
|
||||
const size_t size = header_size() + (sizeof(oop*) * immediate_oops.length());
|
||||
void* const method_with_immediate_oops = NEW_C_HEAP_ARRAY(uint8_t, size, mtGC);
|
||||
return ::new (method_with_immediate_oops) ZNMethodWithImmediateOops(nm, immediate_oops);
|
||||
}
|
||||
|
||||
void ZNMethodWithImmediateOops::destroy(ZNMethodWithImmediateOops* nmi) {
|
||||
FREE_C_HEAP_ARRAY(uint8_t, nmi);
|
||||
}
|
||||
|
||||
nmethod* ZNMethodWithImmediateOops::method() const {
|
||||
return _nm;
|
||||
}
|
||||
|
||||
size_t ZNMethodWithImmediateOops::immediate_oops_count() const {
|
||||
size_t ZNMethodDataImmediateOops::immediate_oops_count() const {
|
||||
return _nimmediate_oops;
|
||||
}
|
||||
|
||||
oop** ZNMethodWithImmediateOops::immediate_oops_begin() const {
|
||||
oop** ZNMethodDataImmediateOops::immediate_oops_begin() const {
|
||||
// The immediate oop* array starts immediately after this object
|
||||
return (oop**)((uintptr_t)this + header_size());
|
||||
}
|
||||
|
||||
oop** ZNMethodWithImmediateOops::immediate_oops_end() const {
|
||||
oop** ZNMethodDataImmediateOops::immediate_oops_end() const {
|
||||
return immediate_oops_begin() + immediate_oops_count();
|
||||
}
|
||||
|
||||
class ZNMethodData {
|
||||
private:
|
||||
ZReentrantLock _lock;
|
||||
ZNMethodDataImmediateOops* volatile _immediate_oops;
|
||||
|
||||
ZNMethodData(nmethod* nm);
|
||||
|
||||
public:
|
||||
static ZNMethodData* create(nmethod* nm);
|
||||
static void destroy(ZNMethodData* data);
|
||||
|
||||
ZReentrantLock* lock();
|
||||
|
||||
ZNMethodDataImmediateOops* immediate_oops() const;
|
||||
ZNMethodDataImmediateOops* swap_immediate_oops(const GrowableArray<oop*>& immediate_oops);
|
||||
};
|
||||
|
||||
ZNMethodData* ZNMethodData::create(nmethod* nm) {
|
||||
void* const method = NEW_C_HEAP_ARRAY(uint8_t, sizeof(ZNMethodData), mtGC);
|
||||
return ::new (method) ZNMethodData(nm);
|
||||
}
|
||||
|
||||
void ZNMethodData::destroy(ZNMethodData* data) {
|
||||
ZNMethodDataImmediateOops::destroy(data->immediate_oops());
|
||||
ZNMethodTable::safe_delete(data);
|
||||
}
|
||||
|
||||
ZNMethodData::ZNMethodData(nmethod* nm) :
|
||||
_lock(),
|
||||
_immediate_oops(NULL) {}
|
||||
|
||||
ZReentrantLock* ZNMethodData::lock() {
|
||||
return &_lock;
|
||||
}
|
||||
|
||||
ZNMethodDataImmediateOops* ZNMethodData::immediate_oops() const {
|
||||
return OrderAccess::load_acquire(&_immediate_oops);
|
||||
}
|
||||
|
||||
ZNMethodDataImmediateOops* ZNMethodData::swap_immediate_oops(const GrowableArray<oop*>& immediate_oops) {
|
||||
ZNMethodDataImmediateOops* const data_immediate_oops =
|
||||
immediate_oops.is_empty() ? NULL : ZNMethodDataImmediateOops::create(immediate_oops);
|
||||
return Atomic::xchg(data_immediate_oops, &_immediate_oops);
|
||||
}
|
||||
|
||||
static ZNMethodData* gc_data(const nmethod* nm) {
|
||||
return nm->gc_data<ZNMethodData>();
|
||||
}
|
||||
|
||||
static void set_gc_data(nmethod* nm, ZNMethodData* data) {
|
||||
return nm->set_gc_data<ZNMethodData>(data);
|
||||
}
|
||||
|
||||
ZNMethodTableEntry* ZNMethodTable::_table = NULL;
|
||||
size_t ZNMethodTable::_size = 0;
|
||||
ZLock ZNMethodTable::_iter_lock;
|
||||
ZNMethodTableEntry* ZNMethodTable::_iter_table = NULL;
|
||||
size_t ZNMethodTable::_iter_table_size = 0;
|
||||
ZArray<void*> ZNMethodTable::_iter_deferred_deletes;
|
||||
size_t ZNMethodTable::_nregistered = 0;
|
||||
size_t ZNMethodTable::_nunregistered = 0;
|
||||
volatile size_t ZNMethodTable::_claimed = 0;
|
||||
|
||||
void ZNMethodTable::safe_delete(void* data) {
|
||||
if (data == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZLocker<ZLock> locker(&_iter_lock);
|
||||
if (_iter_table != NULL) {
|
||||
// Iteration in progress, defer delete
|
||||
_iter_deferred_deletes.add(data);
|
||||
} else {
|
||||
// Iteration not in progress, delete now
|
||||
FREE_C_HEAP_ARRAY(uint8_t, data);
|
||||
}
|
||||
}
|
||||
|
||||
ZNMethodTableEntry ZNMethodTable::create_entry(nmethod* nm) {
|
||||
GrowableArray<oop*> immediate_oops;
|
||||
bool non_immediate_oops = false;
|
||||
@ -132,29 +205,27 @@ ZNMethodTableEntry ZNMethodTable::create_entry(nmethod* nm) {
|
||||
}
|
||||
}
|
||||
|
||||
// oops_count() returns the number of oops in the oop table plus one
|
||||
if (immediate_oops.is_empty() && nm->oops_count() == 1) {
|
||||
// No oops found, return empty entry
|
||||
return ZNMethodTableEntry();
|
||||
// Attach GC data to nmethod
|
||||
ZNMethodData* data = gc_data(nm);
|
||||
if (data == NULL) {
|
||||
data = ZNMethodData::create(nm);
|
||||
set_gc_data(nm, data);
|
||||
}
|
||||
|
||||
if (immediate_oops.is_empty()) {
|
||||
// No immediate oops found, return entry without immediate oops
|
||||
return ZNMethodTableEntry(nm, non_immediate_oops);
|
||||
}
|
||||
// Attach immediate oops in GC data
|
||||
ZNMethodDataImmediateOops* const old_data_immediate_oops = data->swap_immediate_oops(immediate_oops);
|
||||
ZNMethodDataImmediateOops::destroy(old_data_immediate_oops);
|
||||
|
||||
// Return entry with immediate oops
|
||||
return ZNMethodTableEntry(ZNMethodWithImmediateOops::create(nm, immediate_oops), non_immediate_oops);
|
||||
// Create entry
|
||||
return ZNMethodTableEntry(nm, non_immediate_oops, !immediate_oops.is_empty());
|
||||
}
|
||||
|
||||
void ZNMethodTable::destroy_entry(ZNMethodTableEntry entry) {
|
||||
if (entry.immediate_oops()) {
|
||||
ZNMethodWithImmediateOops::destroy(entry.method_with_immediate_oops());
|
||||
ZReentrantLock* ZNMethodTable::lock_for_nmethod(nmethod* nm) {
|
||||
ZNMethodData* const data = gc_data(nm);
|
||||
if (data == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
nmethod* ZNMethodTable::method(ZNMethodTableEntry entry) {
|
||||
return entry.immediate_oops() ? entry.method_with_immediate_oops()->method() : entry.method();
|
||||
return data->lock();
|
||||
}
|
||||
|
||||
size_t ZNMethodTable::first_index(const nmethod* nm, size_t size) {
|
||||
@ -171,7 +242,7 @@ size_t ZNMethodTable::next_index(size_t prev_index, size_t size) {
|
||||
}
|
||||
|
||||
bool ZNMethodTable::register_entry(ZNMethodTableEntry* table, size_t size, ZNMethodTableEntry entry) {
|
||||
const nmethod* const nm = method(entry);
|
||||
const nmethod* const nm = entry.method();
|
||||
size_t index = first_index(nm, size);
|
||||
|
||||
for (;;) {
|
||||
@ -183,9 +254,8 @@ bool ZNMethodTable::register_entry(ZNMethodTableEntry* table, size_t size, ZNMet
|
||||
return true;
|
||||
}
|
||||
|
||||
if (table_entry.registered() && method(table_entry) == nm) {
|
||||
if (table_entry.registered() && table_entry.method() == nm) {
|
||||
// Replace existing entry
|
||||
destroy_entry(table_entry);
|
||||
table[index] = entry;
|
||||
return false;
|
||||
}
|
||||
@ -194,7 +264,7 @@ bool ZNMethodTable::register_entry(ZNMethodTableEntry* table, size_t size, ZNMet
|
||||
}
|
||||
}
|
||||
|
||||
bool ZNMethodTable::unregister_entry(ZNMethodTableEntry* table, size_t size, const nmethod* nm) {
|
||||
bool ZNMethodTable::unregister_entry(ZNMethodTableEntry* table, size_t size, nmethod* nm) {
|
||||
if (size == 0) {
|
||||
// Table is empty
|
||||
return false;
|
||||
@ -210,10 +280,13 @@ bool ZNMethodTable::unregister_entry(ZNMethodTableEntry* table, size_t size, con
|
||||
return false;
|
||||
}
|
||||
|
||||
if (table_entry.registered() && method(table_entry) == nm) {
|
||||
if (table_entry.registered() && table_entry.method() == nm) {
|
||||
// Remove entry
|
||||
destroy_entry(table_entry);
|
||||
table[index] = ZNMethodTableEntry(true /* unregistered */);
|
||||
|
||||
// Destroy GC data
|
||||
ZNMethodData::destroy(gc_data(nm));
|
||||
set_gc_data(nm, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -222,6 +295,7 @@ bool ZNMethodTable::unregister_entry(ZNMethodTableEntry* table, size_t size, con
|
||||
}
|
||||
|
||||
void ZNMethodTable::rebuild(size_t new_size) {
|
||||
ZLocker<ZLock> locker(&_iter_lock);
|
||||
assert(is_power_of_2(new_size), "Invalid size");
|
||||
|
||||
log_debug(gc, nmethod)("Rebuilding NMethod Table: "
|
||||
@ -243,8 +317,10 @@ void ZNMethodTable::rebuild(size_t new_size) {
|
||||
}
|
||||
}
|
||||
|
||||
// Delete old table
|
||||
delete [] _table;
|
||||
if (_iter_table != _table) {
|
||||
// Delete old table
|
||||
delete [] _table;
|
||||
}
|
||||
|
||||
// Install new table
|
||||
_table = new_table;
|
||||
@ -294,8 +370,8 @@ void ZNMethodTable::log_register(const nmethod* nm, ZNMethodTableEntry entry) {
|
||||
p2i(nm),
|
||||
nm->compiler_name(),
|
||||
nm->oops_count() - 1,
|
||||
entry.immediate_oops() ? entry.method_with_immediate_oops()->immediate_oops_count() : 0,
|
||||
BOOL_TO_STR(entry.non_immediate_oops()));
|
||||
entry.immediate_oops() ? gc_data(nm)->immediate_oops()->immediate_oops_count() : 0,
|
||||
entry.non_immediate_oops() ? "Yes" : "No");
|
||||
|
||||
LogTarget(Trace, gc, nmethod, oops) log_oops;
|
||||
if (!log_oops.is_enabled()) {
|
||||
@ -312,12 +388,14 @@ void ZNMethodTable::log_register(const nmethod* nm, ZNMethodTableEntry entry) {
|
||||
|
||||
if (entry.immediate_oops()) {
|
||||
// Print nmethod immediate oops
|
||||
const ZNMethodWithImmediateOops* const nmi = entry.method_with_immediate_oops();
|
||||
oop** const begin = nmi->immediate_oops_begin();
|
||||
oop** const end = nmi->immediate_oops_end();
|
||||
for (oop** p = begin; p < end; p++) {
|
||||
log_oops.print(" ImmediateOop[" SIZE_FORMAT "] " PTR_FORMAT " @ " PTR_FORMAT " (%s)",
|
||||
(p - begin), p2i(**p), p2i(*p), (**p)->klass()->external_name());
|
||||
const ZNMethodDataImmediateOops* const nmi = gc_data(nm)->immediate_oops();
|
||||
if (nmi != NULL) {
|
||||
oop** const begin = nmi->immediate_oops_begin();
|
||||
oop** const end = nmi->immediate_oops_end();
|
||||
for (oop** p = begin; p < end; p++) {
|
||||
log_oops.print(" ImmediateOop[" SIZE_FORMAT "] " PTR_FORMAT " @ " PTR_FORMAT " (%s)",
|
||||
(p - begin), p2i(**p), p2i(*p), (**p)->klass()->external_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -343,21 +421,17 @@ size_t ZNMethodTable::unregistered_nmethods() {
|
||||
}
|
||||
|
||||
void ZNMethodTable::register_nmethod(nmethod* nm) {
|
||||
assert(CodeCache_lock->owned_by_self(), "Lock must be held");
|
||||
ResourceMark rm;
|
||||
|
||||
// Grow/Shrink/Prune table if needed
|
||||
rebuild_if_needed();
|
||||
|
||||
// Create entry
|
||||
const ZNMethodTableEntry entry = create_entry(nm);
|
||||
|
||||
log_register(nm, entry);
|
||||
|
||||
if (!entry.registered()) {
|
||||
// Method doesn't have any oops, ignore it
|
||||
return;
|
||||
}
|
||||
|
||||
// Grow/Shrink/Prune table if needed
|
||||
rebuild_if_needed();
|
||||
|
||||
// Insert new entry
|
||||
if (register_entry(_table, _size, entry)) {
|
||||
// New entry registered. When register_entry() instead returns
|
||||
@ -365,11 +439,31 @@ void ZNMethodTable::register_nmethod(nmethod* nm) {
|
||||
// to increase number of registered entries in that case.
|
||||
_nregistered++;
|
||||
}
|
||||
|
||||
// Disarm nmethod entry barrier
|
||||
disarm_nmethod(nm);
|
||||
}
|
||||
|
||||
void ZNMethodTable::sweeper_wait_for_iteration() {
|
||||
// The sweeper must wait for any ongoing iteration to complete
|
||||
// before it can unregister an nmethod.
|
||||
if (!Thread::current()->is_Code_cache_sweeper_thread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(CodeCache_lock->owned_by_self(), "Lock must be held");
|
||||
|
||||
while (_iter_table != NULL) {
|
||||
MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
os::naked_short_sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
void ZNMethodTable::unregister_nmethod(nmethod* nm) {
|
||||
ResourceMark rm;
|
||||
|
||||
sweeper_wait_for_iteration();
|
||||
|
||||
log_unregister(nm);
|
||||
|
||||
// Remove entry
|
||||
@ -383,20 +477,45 @@ void ZNMethodTable::unregister_nmethod(nmethod* nm) {
|
||||
}
|
||||
}
|
||||
|
||||
void ZNMethodTable::gc_prologue() {
|
||||
_claimed = 0;
|
||||
void ZNMethodTable::disarm_nmethod(nmethod* nm) {
|
||||
BarrierSetNMethod* const bs = BarrierSet::barrier_set()->barrier_set_nmethod();
|
||||
if (bs != NULL) {
|
||||
bs->disarm(nm);
|
||||
}
|
||||
}
|
||||
|
||||
void ZNMethodTable::gc_epilogue() {
|
||||
assert(_claimed >= _size, "Failed to claim all table entries");
|
||||
void ZNMethodTable::nmethod_entries_do_begin() {
|
||||
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
ZLocker<ZLock> locker(&_iter_lock);
|
||||
|
||||
// Prepare iteration
|
||||
_iter_table = _table;
|
||||
_iter_table_size = _size;
|
||||
_claimed = 0;
|
||||
assert(_iter_deferred_deletes.is_empty(), "Should be emtpy");
|
||||
}
|
||||
|
||||
void ZNMethodTable::nmethod_entries_do_end() {
|
||||
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
ZLocker<ZLock> locker(&_iter_lock);
|
||||
|
||||
// Finish iteration
|
||||
if (_iter_table != _table) {
|
||||
delete [] _iter_table;
|
||||
}
|
||||
_iter_table = NULL;
|
||||
assert(_claimed >= _iter_table_size, "Failed to claim all table entries");
|
||||
|
||||
// Process deferred deletes
|
||||
ZArrayIterator<void*> iter(&_iter_deferred_deletes);
|
||||
for (void* data; iter.next(&data);) {
|
||||
FREE_C_HEAP_ARRAY(uint8_t, data);
|
||||
}
|
||||
_iter_deferred_deletes.clear();
|
||||
}
|
||||
|
||||
void ZNMethodTable::entry_oops_do(ZNMethodTableEntry entry, OopClosure* cl) {
|
||||
nmethod* const nm = method(entry);
|
||||
if (!nm->is_alive()) {
|
||||
// No need to visit oops
|
||||
return;
|
||||
}
|
||||
nmethod* const nm = entry.method();
|
||||
|
||||
// Process oops table
|
||||
oop* const begin = nm->oops_begin();
|
||||
@ -407,29 +526,52 @@ void ZNMethodTable::entry_oops_do(ZNMethodTableEntry entry, OopClosure* cl) {
|
||||
}
|
||||
}
|
||||
|
||||
// Process immediate oops
|
||||
if (entry.immediate_oops()) {
|
||||
// Process immediate oops
|
||||
const ZNMethodWithImmediateOops* const nmi = entry.method_with_immediate_oops();
|
||||
oop** const begin = nmi->immediate_oops_begin();
|
||||
oop** const end = nmi->immediate_oops_end();
|
||||
for (oop** p = begin; p < end; p++) {
|
||||
cl->do_oop(*p);
|
||||
const ZNMethodDataImmediateOops* const nmi = gc_data(nm)->immediate_oops();
|
||||
if (nmi != NULL) {
|
||||
oop** const begin = nmi->immediate_oops_begin();
|
||||
oop** const end = nmi->immediate_oops_end();
|
||||
for (oop** p = begin; p < end; p++) {
|
||||
if (**p != Universe::non_oop_word()) {
|
||||
cl->do_oop(*p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process non-immediate oops
|
||||
if (entry.non_immediate_oops()) {
|
||||
// Process non-immediate oops
|
||||
nmethod* const nm = entry.method();
|
||||
nm->fix_oop_relocations();
|
||||
}
|
||||
}
|
||||
|
||||
class ZNMethodTableEntryToOopsDo : public ZNMethodTableEntryClosure {
|
||||
private:
|
||||
OopClosure* _cl;
|
||||
|
||||
public:
|
||||
ZNMethodTableEntryToOopsDo(OopClosure* cl) :
|
||||
_cl(cl) {}
|
||||
|
||||
void do_nmethod_entry(ZNMethodTableEntry entry) {
|
||||
ZNMethodTable::entry_oops_do(entry, _cl);
|
||||
}
|
||||
};
|
||||
|
||||
void ZNMethodTable::oops_do(OopClosure* cl) {
|
||||
ZNMethodTableEntryToOopsDo entry_cl(cl);
|
||||
nmethod_entries_do(&entry_cl);
|
||||
}
|
||||
|
||||
void ZNMethodTable::nmethod_entries_do(ZNMethodTableEntryClosure* cl) {
|
||||
for (;;) {
|
||||
// Claim table partition. Each partition is currently sized to span
|
||||
// two cache lines. This number is just a guess, but seems to work well.
|
||||
const size_t partition_size = (ZCacheLineSize * 2) / sizeof(ZNMethodTableEntry);
|
||||
const size_t partition_start = MIN2(Atomic::add(partition_size, &_claimed) - partition_size, _size);
|
||||
const size_t partition_end = MIN2(partition_start + partition_size, _size);
|
||||
const size_t partition_start = MIN2(Atomic::add(partition_size, &_claimed) - partition_size, _iter_table_size);
|
||||
const size_t partition_end = MIN2(partition_start + partition_size, _iter_table_size);
|
||||
if (partition_start == partition_end) {
|
||||
// End of table
|
||||
break;
|
||||
@ -437,10 +579,141 @@ void ZNMethodTable::oops_do(OopClosure* cl) {
|
||||
|
||||
// Process table partition
|
||||
for (size_t i = partition_start; i < partition_end; i++) {
|
||||
const ZNMethodTableEntry entry = _table[i];
|
||||
const ZNMethodTableEntry entry = _iter_table[i];
|
||||
if (entry.registered()) {
|
||||
entry_oops_do(entry, cl);
|
||||
cl->do_nmethod_entry(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ZNMethodTableUnlinkClosure : public ZNMethodTableEntryClosure {
|
||||
private:
|
||||
bool _unloading_occurred;
|
||||
volatile bool _failed;
|
||||
|
||||
void set_failed() {
|
||||
Atomic::store(true, &_failed);
|
||||
}
|
||||
|
||||
public:
|
||||
ZNMethodTableUnlinkClosure(bool unloading_occurred) :
|
||||
_unloading_occurred(unloading_occurred),
|
||||
_failed(false) {}
|
||||
|
||||
virtual void do_nmethod_entry(ZNMethodTableEntry entry) {
|
||||
if (failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nmethod* const nm = entry.method();
|
||||
if (!nm->is_alive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nm->is_unloading()) {
|
||||
// Unlinking of the dependencies must happen before the
|
||||
// handshake separating unlink and purge.
|
||||
nm->flush_dependencies(false /* delete_immediately */);
|
||||
return;
|
||||
}
|
||||
|
||||
ZLocker<ZReentrantLock> locker(ZNMethodTable::lock_for_nmethod(nm));
|
||||
|
||||
// Heal oops and disarm
|
||||
ZNMethodOopClosure cl;
|
||||
ZNMethodTable::entry_oops_do(entry, &cl);
|
||||
ZNMethodTable::disarm_nmethod(nm);
|
||||
|
||||
// Clear compiled ICs and exception caches
|
||||
if (!nm->unload_nmethod_caches(_unloading_occurred)) {
|
||||
set_failed();
|
||||
}
|
||||
}
|
||||
|
||||
bool failed() const {
|
||||
return Atomic::load(&_failed);
|
||||
}
|
||||
};
|
||||
|
||||
class ZNMethodTableUnlinkTask : public ZTask {
|
||||
private:
|
||||
ZNMethodTableUnlinkClosure _cl;
|
||||
ICRefillVerifier* _verifier;
|
||||
|
||||
public:
|
||||
ZNMethodTableUnlinkTask(bool unloading_occurred, ICRefillVerifier* verifier) :
|
||||
ZTask("ZNMethodTableUnlinkTask"),
|
||||
_cl(unloading_occurred),
|
||||
_verifier(verifier) {
|
||||
ZNMethodTable::nmethod_entries_do_begin();
|
||||
}
|
||||
|
||||
~ZNMethodTableUnlinkTask() {
|
||||
ZNMethodTable::nmethod_entries_do_end();
|
||||
}
|
||||
|
||||
virtual void work() {
|
||||
ICRefillVerifierMark mark(_verifier);
|
||||
ZNMethodTable::nmethod_entries_do(&_cl);
|
||||
}
|
||||
|
||||
bool success() const {
|
||||
return !_cl.failed();
|
||||
}
|
||||
};
|
||||
|
||||
void ZNMethodTable::unlink(ZWorkers* workers, bool unloading_occurred) {
|
||||
for (;;) {
|
||||
ICRefillVerifier verifier;
|
||||
|
||||
{
|
||||
ZNMethodTableUnlinkTask task(unloading_occurred, &verifier);
|
||||
workers->run_concurrent(&task);
|
||||
if (task.success()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleaning failed because we ran out of transitional IC stubs,
|
||||
// so we have to refill and try again. Refilling requires taking
|
||||
// a safepoint, so we temporarily leave the suspendible thread set.
|
||||
SuspendibleThreadSetLeaver sts;
|
||||
InlineCacheBuffer::refill_ic_stubs();
|
||||
}
|
||||
}
|
||||
|
||||
class ZNMethodTablePurgeClosure : public ZNMethodTableEntryClosure {
|
||||
public:
|
||||
virtual void do_nmethod_entry(ZNMethodTableEntry entry) {
|
||||
nmethod* const nm = entry.method();
|
||||
if (nm->is_alive() && nm->is_unloading()) {
|
||||
nm->make_unloaded();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ZNMethodTablePurgeTask : public ZTask {
|
||||
private:
|
||||
ZNMethodTablePurgeClosure _cl;
|
||||
|
||||
public:
|
||||
ZNMethodTablePurgeTask() :
|
||||
ZTask("ZNMethodTablePurgeTask"),
|
||||
_cl() {
|
||||
ZNMethodTable::nmethod_entries_do_begin();
|
||||
}
|
||||
|
||||
~ZNMethodTablePurgeTask() {
|
||||
ZNMethodTable::nmethod_entries_do_end();
|
||||
}
|
||||
|
||||
virtual void work() {
|
||||
ZNMethodTable::nmethod_entries_do(&_cl);
|
||||
}
|
||||
};
|
||||
|
||||
void ZNMethodTable::purge(ZWorkers* workers) {
|
||||
ZNMethodTablePurgeTask task;
|
||||
workers->run_concurrent(&task);
|
||||
}
|
||||
|
@ -24,28 +24,40 @@
|
||||
#ifndef SHARE_GC_Z_ZNMETHODTABLE_HPP
|
||||
#define SHARE_GC_Z_ZNMETHODTABLE_HPP
|
||||
|
||||
#include "gc/z/zArray.hpp"
|
||||
#include "gc/z/zGlobals.hpp"
|
||||
#include "gc/z/zLock.hpp"
|
||||
#include "gc/z/zNMethodTableEntry.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
|
||||
class ZWorkers;
|
||||
|
||||
class ZNMethodTableEntryClosure {
|
||||
public:
|
||||
virtual void do_nmethod_entry(ZNMethodTableEntry entry) = 0;
|
||||
};
|
||||
|
||||
class ZNMethodTable : public AllStatic {
|
||||
private:
|
||||
static ZNMethodTableEntry* _table;
|
||||
static size_t _size;
|
||||
static ZLock _iter_lock;
|
||||
static ZNMethodTableEntry* _iter_table;
|
||||
static size_t _iter_table_size;
|
||||
static ZArray<void*> _iter_deferred_deletes;
|
||||
static size_t _nregistered;
|
||||
static size_t _nunregistered;
|
||||
static volatile size_t _claimed ATTRIBUTE_ALIGNED(ZCacheLineSize);
|
||||
|
||||
static ZNMethodTableEntry create_entry(nmethod* nm);
|
||||
static void destroy_entry(ZNMethodTableEntry entry);
|
||||
|
||||
static nmethod* method(ZNMethodTableEntry entry);
|
||||
|
||||
static size_t first_index(const nmethod* nm, size_t size);
|
||||
static size_t next_index(size_t prev_index, size_t size);
|
||||
|
||||
static void sweeper_wait_for_iteration();
|
||||
|
||||
static bool register_entry(ZNMethodTableEntry* table, size_t size, ZNMethodTableEntry entry);
|
||||
static bool unregister_entry(ZNMethodTableEntry* table, size_t size, const nmethod* nm);
|
||||
static bool unregister_entry(ZNMethodTableEntry* table, size_t size, nmethod* nm);
|
||||
|
||||
static void rebuild(size_t new_size);
|
||||
static void rebuild_if_needed();
|
||||
@ -53,19 +65,28 @@ private:
|
||||
static void log_register(const nmethod* nm, ZNMethodTableEntry entry);
|
||||
static void log_unregister(const nmethod* nm);
|
||||
|
||||
static void entry_oops_do(ZNMethodTableEntry entry, OopClosure* cl);
|
||||
|
||||
public:
|
||||
static void safe_delete(void* data);
|
||||
|
||||
static size_t registered_nmethods();
|
||||
static size_t unregistered_nmethods();
|
||||
|
||||
static void register_nmethod(nmethod* nm);
|
||||
static void unregister_nmethod(nmethod* nm);
|
||||
static void disarm_nmethod(nmethod* nm);
|
||||
|
||||
static void gc_prologue();
|
||||
static void gc_epilogue();
|
||||
static ZReentrantLock* lock_for_nmethod(nmethod* nm);
|
||||
|
||||
static void oops_do(OopClosure* cl);
|
||||
|
||||
static void entry_oops_do(ZNMethodTableEntry entry, OopClosure* cl);
|
||||
|
||||
static void nmethod_entries_do_begin();
|
||||
static void nmethod_entries_do_end();
|
||||
static void nmethod_entries_do(ZNMethodTableEntryClosure* cl);
|
||||
|
||||
static void unlink(ZWorkers* workers, bool unloading_occurred);
|
||||
static void purge(ZWorkers* workers);
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_Z_ZNMETHODTABLE_HPP
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -43,38 +43,30 @@
|
||||
// | |
|
||||
// | 0-0 Registered Flag (1-bits) *
|
||||
// |
|
||||
// * 63-3 NMethod/ZNMethodWithImmediateOops Address (61-bits)
|
||||
// * 63-3 NMethod Address (61-bits)
|
||||
//
|
||||
|
||||
class nmethod;
|
||||
class ZNMethodWithImmediateOops;
|
||||
|
||||
class ZNMethodTableEntry : public CHeapObj<mtGC> {
|
||||
private:
|
||||
typedef ZBitField<uint64_t, bool, 0, 1> field_registered;
|
||||
typedef ZBitField<uint64_t, bool, 1, 1> field_unregistered;
|
||||
typedef ZBitField<uint64_t, bool, 1, 1> field_immediate_oops;
|
||||
typedef ZBitField<uint64_t, bool, 2, 1> field_non_immediate_oops;
|
||||
typedef ZBitField<uint64_t, nmethod*, 3, 61, 3> field_method;
|
||||
typedef ZBitField<uint64_t, ZNMethodWithImmediateOops*, 3, 61, 3> field_method_with_immediate_oops;
|
||||
typedef ZBitField<uint64_t, bool, 0, 1> field_registered;
|
||||
typedef ZBitField<uint64_t, bool, 1, 1> field_unregistered;
|
||||
typedef ZBitField<uint64_t, bool, 1, 1> field_immediate_oops;
|
||||
typedef ZBitField<uint64_t, bool, 2, 1> field_non_immediate_oops;
|
||||
typedef ZBitField<uint64_t, nmethod*, 3, 61, 3> field_method;
|
||||
|
||||
uint64_t _entry;
|
||||
|
||||
public:
|
||||
ZNMethodTableEntry(bool unregistered = false) :
|
||||
explicit ZNMethodTableEntry(bool unregistered = false) :
|
||||
_entry(field_unregistered::encode(unregistered) |
|
||||
field_registered::encode(false)) {}
|
||||
|
||||
ZNMethodTableEntry(nmethod* method, bool non_immediate_oops) :
|
||||
ZNMethodTableEntry(nmethod* method, bool non_immediate_oops, bool immediate_oops) :
|
||||
_entry(field_method::encode(method) |
|
||||
field_non_immediate_oops::encode(non_immediate_oops) |
|
||||
field_immediate_oops::encode(false) |
|
||||
field_registered::encode(true)) {}
|
||||
|
||||
ZNMethodTableEntry(ZNMethodWithImmediateOops* method_with_immediate_oops, bool non_immediate_oops) :
|
||||
_entry(field_method_with_immediate_oops::encode(method_with_immediate_oops) |
|
||||
field_non_immediate_oops::encode(non_immediate_oops) |
|
||||
field_immediate_oops::encode(true) |
|
||||
field_immediate_oops::encode(immediate_oops) |
|
||||
field_registered::encode(true)) {}
|
||||
|
||||
bool registered() const {
|
||||
@ -96,10 +88,6 @@ public:
|
||||
nmethod* method() const {
|
||||
return field_method::decode(_entry);
|
||||
}
|
||||
|
||||
ZNMethodWithImmediateOops* method_with_immediate_oops() const {
|
||||
return field_method_with_immediate_oops::decode(_entry);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_Z_ZNMETHODTABLEENTRY_HPP
|
||||
|
@ -39,14 +39,23 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
class ZNMethodOopClosure : public OopClosure {
|
||||
public:
|
||||
virtual void do_oop(oop* p);
|
||||
virtual void do_oop(narrowOop* p);
|
||||
};
|
||||
|
||||
template <bool finalizable>
|
||||
class ZMarkBarrierOopClosure : public BasicOopIterateClosure {
|
||||
class ZMarkBarrierOopClosure : public MetadataVisitingOopIterateClosure {
|
||||
public:
|
||||
ZMarkBarrierOopClosure();
|
||||
|
||||
virtual void do_oop(oop* p);
|
||||
virtual void do_oop(narrowOop* p);
|
||||
|
||||
virtual void do_klass(Klass* k);
|
||||
virtual void do_cld(ClassLoaderData* cld);
|
||||
|
||||
#ifdef ASSERT
|
||||
virtual bool should_verify_oops() {
|
||||
return false;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#ifndef SHARE_GC_Z_ZOOPCLOSURES_INLINE_HPP
|
||||
#define SHARE_GC_Z_ZOOPCLOSURES_INLINE_HPP
|
||||
|
||||
#include "classfile/classLoaderData.hpp"
|
||||
#include "gc/z/zBarrier.inline.hpp"
|
||||
#include "gc/z/zHeap.inline.hpp"
|
||||
#include "gc/z/zOop.inline.hpp"
|
||||
@ -40,9 +41,21 @@ inline void ZLoadBarrierOopClosure::do_oop(narrowOop* p) {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
inline void ZNMethodOopClosure::do_oop(oop* p) {
|
||||
if (ZResurrection::is_blocked()) {
|
||||
ZBarrier::keep_alive_barrier_on_phantom_root_oop_field(p);
|
||||
} else {
|
||||
ZBarrier::load_barrier_on_root_oop_field(p);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ZNMethodOopClosure::do_oop(narrowOop* p) {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
template <bool finalizable>
|
||||
inline ZMarkBarrierOopClosure<finalizable>::ZMarkBarrierOopClosure() :
|
||||
BasicOopIterateClosure(finalizable ? NULL : ZHeap::heap()->reference_discoverer()) {}
|
||||
MetadataVisitingOopIterateClosure(finalizable ? NULL : ZHeap::heap()->reference_discoverer()) {}
|
||||
|
||||
template <bool finalizable>
|
||||
inline void ZMarkBarrierOopClosure<finalizable>::do_oop(oop* p) {
|
||||
@ -54,6 +67,18 @@ inline void ZMarkBarrierOopClosure<finalizable>::do_oop(narrowOop* p) {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
template <bool finalizable>
|
||||
inline void ZMarkBarrierOopClosure<finalizable>::do_klass(Klass* k) {
|
||||
ClassLoaderData* const cld = k->class_loader_data();
|
||||
ZMarkBarrierOopClosure<finalizable>::do_cld(cld);
|
||||
}
|
||||
|
||||
template <bool finalizable>
|
||||
inline void ZMarkBarrierOopClosure<finalizable>::do_cld(ClassLoaderData* cld) {
|
||||
const int claim = finalizable ? ClassLoaderData::_claim_finalizable : ClassLoaderData::_claim_strong;
|
||||
cld->oops_do(this, claim);
|
||||
}
|
||||
|
||||
inline bool ZPhantomIsAliveObjectClosure::do_object_b(oop o) {
|
||||
return ZBarrier::is_alive_barrier_on_phantom_oop(o);
|
||||
}
|
||||
|
@ -27,8 +27,11 @@
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "code/codeCache.hpp"
|
||||
#include "compiler/oopMap.hpp"
|
||||
#include "gc/shared/barrierSet.hpp"
|
||||
#include "gc/shared/barrierSetNMethod.hpp"
|
||||
#include "gc/shared/oopStorageParState.inline.hpp"
|
||||
#include "gc/shared/suspendibleThreadSet.hpp"
|
||||
#include "gc/z/zBarrierSetNMethod.hpp"
|
||||
#include "gc/z/zGlobals.hpp"
|
||||
#include "gc/z/zNMethodTable.hpp"
|
||||
#include "gc/z/zOopClosures.inline.hpp"
|
||||
@ -132,6 +135,30 @@ void ZParallelWeakOopsDo<T, F>::weak_oops_do(BoolObjectClosure* is_alive, ZRoots
|
||||
}
|
||||
}
|
||||
|
||||
class ZCodeBlobClosure : public CodeBlobToOopClosure {
|
||||
private:
|
||||
BarrierSetNMethod* _bs;
|
||||
|
||||
public:
|
||||
ZCodeBlobClosure(OopClosure* cl) :
|
||||
CodeBlobToOopClosure(cl, true /* fix_relocations */),
|
||||
_bs(BarrierSet::barrier_set()->barrier_set_nmethod()) {}
|
||||
|
||||
virtual void do_code_blob(CodeBlob* cb) {
|
||||
nmethod* const nm = cb->as_nmethod_or_null();
|
||||
if (nm == NULL || nm->test_set_oops_do_mark()) {
|
||||
return;
|
||||
}
|
||||
CodeBlobToOopClosure::do_code_blob(cb);
|
||||
_bs->disarm(nm);
|
||||
}
|
||||
};
|
||||
|
||||
void ZRootsIteratorClosure::do_thread(Thread* thread) {
|
||||
ZCodeBlobClosure code_cl(this);
|
||||
thread->oops_do(this, ClassUnloading ? &code_cl : NULL);
|
||||
}
|
||||
|
||||
ZRootsIterator::ZRootsIterator() :
|
||||
_universe(this),
|
||||
_object_synchronizer(this),
|
||||
@ -145,16 +172,23 @@ ZRootsIterator::ZRootsIterator() :
|
||||
ZStatTimer timer(ZSubPhasePauseRootsSetup);
|
||||
Threads::change_thread_claim_parity();
|
||||
COMPILER2_PRESENT(DerivedPointerTable::clear());
|
||||
CodeCache::gc_prologue();
|
||||
ZNMethodTable::gc_prologue();
|
||||
if (ClassUnloading) {
|
||||
nmethod::oops_do_marking_prologue();
|
||||
} else {
|
||||
ZNMethodTable::nmethod_entries_do_begin();
|
||||
}
|
||||
}
|
||||
|
||||
ZRootsIterator::~ZRootsIterator() {
|
||||
ZStatTimer timer(ZSubPhasePauseRootsTeardown);
|
||||
ResourceMark rm;
|
||||
ZNMethodTable::gc_epilogue();
|
||||
CodeCache::gc_epilogue();
|
||||
if (ClassUnloading) {
|
||||
nmethod::oops_do_marking_epilogue();
|
||||
} else {
|
||||
ZNMethodTable::nmethod_entries_do_end();
|
||||
}
|
||||
JvmtiExport::gc_epilogue();
|
||||
|
||||
COMPILER2_PRESENT(DerivedPointerTable::update_pointers());
|
||||
Threads::assert_all_threads_claimed();
|
||||
}
|
||||
@ -209,7 +243,9 @@ void ZRootsIterator::oops_do(ZRootsIteratorClosure* cl, bool visit_jvmti_weak_ex
|
||||
_jvmti_export.oops_do(cl);
|
||||
_system_dictionary.oops_do(cl);
|
||||
_threads.oops_do(cl);
|
||||
_code_cache.oops_do(cl);
|
||||
if (!ClassUnloading) {
|
||||
_code_cache.oops_do(cl);
|
||||
}
|
||||
if (visit_jvmti_weak_export) {
|
||||
_jvmti_weak_export.oops_do(cl);
|
||||
}
|
||||
@ -242,8 +278,13 @@ void ZConcurrentRootsIterator::do_jni_handles(ZRootsIteratorClosure* cl) {
|
||||
|
||||
void ZConcurrentRootsIterator::do_class_loader_data_graph(ZRootsIteratorClosure* cl) {
|
||||
ZStatTimer timer(ZSubPhaseConcurrentRootsClassLoaderDataGraph);
|
||||
CLDToOopClosure cld_cl(cl, _marking ? ClassLoaderData::_claim_strong : ClassLoaderData::_claim_none);
|
||||
ClassLoaderDataGraph::cld_do(&cld_cl);
|
||||
if (_marking) {
|
||||
CLDToOopClosure cld_cl(cl, ClassLoaderData::_claim_strong);
|
||||
ClassLoaderDataGraph::always_strong_cld_do(&cld_cl);
|
||||
} else {
|
||||
CLDToOopClosure cld_cl(cl, ClassLoaderData::_claim_none);
|
||||
ClassLoaderDataGraph::cld_do(&cld_cl);
|
||||
}
|
||||
}
|
||||
|
||||
void ZConcurrentRootsIterator::oops_do(ZRootsIteratorClosure* cl) {
|
||||
|
@ -33,9 +33,7 @@
|
||||
|
||||
class ZRootsIteratorClosure : public OopClosure, public ThreadClosure {
|
||||
public:
|
||||
virtual void do_thread(Thread* thread) {
|
||||
thread->oops_do(this, NULL);
|
||||
}
|
||||
virtual void do_thread(Thread* thread);
|
||||
};
|
||||
|
||||
typedef OopStorage::ParState<true /* concurrent */, false /* is_const */> ZOopStorageIterator;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#define SHARE_GC_Z_ZTHREADLOCALDATA_HPP
|
||||
|
||||
#include "gc/z/zMarkStack.hpp"
|
||||
#include "gc/z/zGlobals.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/sizes.hpp"
|
||||
@ -62,6 +63,10 @@ public:
|
||||
static ByteSize address_bad_mask_offset() {
|
||||
return Thread::gc_data_offset() + byte_offset_of(ZThreadLocalData, _address_bad_mask);
|
||||
}
|
||||
|
||||
static ByteSize nmethod_disarmed_offset() {
|
||||
return address_bad_mask_offset() + in_ByteSize(ZNMethodDisarmedOffset);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_Z_ZTHREADLOCALDATA_HPP
|
||||
|
194
src/hotspot/share/gc/z/zUnload.cpp
Normal file
194
src/hotspot/share/gc/z/zUnload.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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 "precompiled.hpp"
|
||||
#include "classfile/classLoaderDataGraph.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "code/codeBehaviours.hpp"
|
||||
#include "code/codeCache.hpp"
|
||||
#include "code/dependencyContext.hpp"
|
||||
#include "gc/shared/gcBehaviours.hpp"
|
||||
#include "gc/shared/suspendibleThreadSet.hpp"
|
||||
#include "gc/z/zLock.inline.hpp"
|
||||
#include "gc/z/zNMethodTable.hpp"
|
||||
#include "gc/z/zOopClosures.hpp"
|
||||
#include "gc/z/zStat.hpp"
|
||||
#include "gc/z/zUnload.hpp"
|
||||
#include "oops/access.inline.hpp"
|
||||
|
||||
static const ZStatSubPhase ZSubPhaseConcurrentClassesUnload("Concurrent Classes Unload");
|
||||
|
||||
class ZIsUnloadingOopClosure : public OopClosure {
|
||||
private:
|
||||
ZPhantomIsAliveObjectClosure _is_alive;
|
||||
bool _is_unloading;
|
||||
|
||||
public:
|
||||
ZIsUnloadingOopClosure() :
|
||||
_is_alive(),
|
||||
_is_unloading(false) {}
|
||||
|
||||
virtual void do_oop(oop* p) {
|
||||
const oop o = RawAccess<>::oop_load(p);
|
||||
if (o != NULL && !_is_alive.do_object_b(o)) {
|
||||
_is_unloading = true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void do_oop(narrowOop* p) {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
bool is_unloading() const {
|
||||
return _is_unloading;
|
||||
}
|
||||
};
|
||||
|
||||
class ZIsUnloadingBehaviour : public IsUnloadingBehaviour {
|
||||
private:
|
||||
bool is_unloading(nmethod* nm) const {
|
||||
ZIsUnloadingOopClosure cl;
|
||||
nm->oops_do(&cl, true /* allow_zombie */);
|
||||
return cl.is_unloading();
|
||||
}
|
||||
|
||||
public:
|
||||
virtual bool is_unloading(CompiledMethod* method) const {
|
||||
nmethod* const nm = method->as_nmethod();
|
||||
ZReentrantLock* const lock = ZNMethodTable::lock_for_nmethod(nm);
|
||||
if (lock == NULL) {
|
||||
return is_unloading(nm);
|
||||
} else {
|
||||
ZLocker<ZReentrantLock> locker(lock);
|
||||
return is_unloading(nm);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ZCompiledICProtectionBehaviour : public CompiledICProtectionBehaviour {
|
||||
public:
|
||||
virtual bool lock(CompiledMethod* method) {
|
||||
nmethod* const nm = method->as_nmethod();
|
||||
ZReentrantLock* const lock = ZNMethodTable::lock_for_nmethod(nm);
|
||||
if (lock != NULL) {
|
||||
lock->lock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void unlock(CompiledMethod* method) {
|
||||
nmethod* const nm = method->as_nmethod();
|
||||
ZReentrantLock* const lock = ZNMethodTable::lock_for_nmethod(nm);
|
||||
if (lock != NULL) {
|
||||
lock->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool is_safe(CompiledMethod* method) {
|
||||
if (SafepointSynchronize::is_at_safepoint()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nmethod* const nm = method->as_nmethod();
|
||||
ZReentrantLock* const lock = ZNMethodTable::lock_for_nmethod(nm);
|
||||
return lock == NULL || lock->is_owned();
|
||||
}
|
||||
};
|
||||
|
||||
ZUnload::ZUnload(ZWorkers* workers) :
|
||||
_workers(workers) {
|
||||
|
||||
if (!ClassUnloading) {
|
||||
return;
|
||||
}
|
||||
|
||||
static ZIsUnloadingBehaviour is_unloading_behaviour;
|
||||
IsUnloadingBehaviour::set_current(&is_unloading_behaviour);
|
||||
|
||||
static ZCompiledICProtectionBehaviour ic_protection_behaviour;
|
||||
CompiledICProtectionBehaviour::set_current(&ic_protection_behaviour);
|
||||
}
|
||||
|
||||
void ZUnload::prepare() {
|
||||
if (!ClassUnloading) {
|
||||
return;
|
||||
}
|
||||
|
||||
CodeCache::increment_unloading_cycle();
|
||||
DependencyContext::cleaning_start();
|
||||
}
|
||||
|
||||
void ZUnload::unlink() {
|
||||
SuspendibleThreadSetJoiner sts;
|
||||
bool unloading_occurred;
|
||||
|
||||
{
|
||||
MutexLockerEx ml(ClassLoaderDataGraph_lock);
|
||||
unloading_occurred = SystemDictionary::do_unloading(ZStatPhase::timer());
|
||||
}
|
||||
|
||||
Klass::clean_weak_klass_links(unloading_occurred);
|
||||
|
||||
ZNMethodTable::unlink(_workers, unloading_occurred);
|
||||
|
||||
DependencyContext::cleaning_end();
|
||||
}
|
||||
|
||||
void ZUnload::purge() {
|
||||
{
|
||||
SuspendibleThreadSetJoiner sts;
|
||||
ZNMethodTable::purge(_workers);
|
||||
}
|
||||
|
||||
ClassLoaderDataGraph::purge();
|
||||
CodeCache::purge_exception_caches();
|
||||
}
|
||||
|
||||
class ZUnloadRendezvousClosure : public ThreadClosure {
|
||||
public:
|
||||
void do_thread(Thread* thread) {}
|
||||
};
|
||||
|
||||
void ZUnload::unload() {
|
||||
if (!ClassUnloading) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZStatTimer timer(ZSubPhaseConcurrentClassesUnload);
|
||||
|
||||
// Unlink stale metadata and nmethods
|
||||
unlink();
|
||||
|
||||
// Make sure stale metadata and nmethods are no longer observable
|
||||
ZUnloadRendezvousClosure cl;
|
||||
Handshake::execute(&cl);
|
||||
|
||||
// Purge stale metadata and nmethods that were unlinked
|
||||
purge();
|
||||
}
|
||||
|
||||
void ZUnload::finish() {
|
||||
// Resize and verify metaspace
|
||||
MetaspaceGC::compute_new_size();
|
||||
MetaspaceUtils::verify_metrics();
|
||||
}
|
44
src/hotspot/share/gc/z/zUnload.hpp
Normal file
44
src/hotspot/share/gc/z/zUnload.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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_Z_ZUNLOAD_HPP
|
||||
#define SHARE_GC_Z_ZUNLOAD_HPP
|
||||
|
||||
class ZWorkers;
|
||||
|
||||
class ZUnload {
|
||||
private:
|
||||
ZWorkers* const _workers;
|
||||
|
||||
void unlink();
|
||||
void purge();
|
||||
|
||||
public:
|
||||
ZUnload(ZWorkers* workers);
|
||||
|
||||
void prepare();
|
||||
void unload();
|
||||
void finish();
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_Z_ZUNLOAD_HPP
|
@ -153,9 +153,9 @@ void InvocationCounter::reinitialize(bool delay_overflow) {
|
||||
// don't need the shift by number_of_noncount_bits, but we do need to adjust
|
||||
// the factor by which we scale the threshold.
|
||||
if (ProfileInterpreter) {
|
||||
InterpreterBackwardBranchLimit = (CompileThreshold * (OnStackReplacePercentage - InterpreterProfilePercentage)) / 100;
|
||||
InterpreterBackwardBranchLimit = (int)((int64_t)CompileThreshold * (OnStackReplacePercentage - InterpreterProfilePercentage) / 100);
|
||||
} else {
|
||||
InterpreterBackwardBranchLimit = ((CompileThreshold * OnStackReplacePercentage) / 100) << number_of_noncount_bits;
|
||||
InterpreterBackwardBranchLimit = (int)(((int64_t)CompileThreshold * OnStackReplacePercentage / 100) << number_of_noncount_bits);
|
||||
}
|
||||
|
||||
assert(0 <= InterpreterBackwardBranchLimit,
|
||||
|
@ -40,7 +40,8 @@
|
||||
|
||||
<Event name="ThreadPark" category="Java Application" label="Java Thread Park" thread="true" stackTrace="true">
|
||||
<Field type="Class" name="parkedClass" label="Class Parked On" />
|
||||
<Field type="long" contentType="millis" name="timeout" label="Park Timeout" />
|
||||
<Field type="long" contentType="nanos" name="timeout" label="Park Timeout" />
|
||||
<Field type="long" contentType="epochmillis" name="until" label="Park Until" />
|
||||
<Field type="ulong" contentType="address" name="address" label="Address of Object Parked" relation="JavaMonitorAddress" />
|
||||
</Event>
|
||||
|
||||
@ -1140,5 +1141,6 @@
|
||||
<XmlContentType name="address" annotationType="jdk.jfr.MemoryAddress" />
|
||||
<XmlContentType name="percentage" annotationType="jdk.jfr.Percentage" />
|
||||
<XmlContentType name="millis" annotationType="jdk.jfr.Timespan" annotationValue="MILLISECONDS" />
|
||||
|
||||
<XmlContentType name="nanos" annotationType="jdk.jfr.Timespan" annotationValue="NANOSECONDS" />
|
||||
|
||||
</Metadata>
|
||||
|
@ -103,9 +103,9 @@ class MethodCounters : public Metadata {
|
||||
// If interpreter profiling is enabled, the backward branch limit
|
||||
// is compared against the method data counter rather than an invocation
|
||||
// counter, therefore no shifting of bits is required.
|
||||
_interpreter_backward_branch_limit = (compile_threshold * (OnStackReplacePercentage - InterpreterProfilePercentage)) / 100;
|
||||
_interpreter_backward_branch_limit = (int)((int64_t)compile_threshold * (OnStackReplacePercentage - InterpreterProfilePercentage) / 100);
|
||||
} else {
|
||||
_interpreter_backward_branch_limit = ((compile_threshold * OnStackReplacePercentage) / 100) << InvocationCounter::count_shift;
|
||||
_interpreter_backward_branch_limit = (int)(((int64_t)compile_threshold * OnStackReplacePercentage / 100) << InvocationCounter::count_shift);
|
||||
}
|
||||
_interpreter_profile_limit = ((compile_threshold * InterpreterProfilePercentage) / 100) << InvocationCounter::count_shift;
|
||||
_invoke_mask = right_n_bits(CompilerConfig::scaled_freq_log(Tier0InvokeNotifyFreqLog, scale)) << InvocationCounter::count_shift;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2018, 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
|
||||
@ -710,6 +710,9 @@
|
||||
diagnostic(bool, UseMathExactIntrinsics, true, \
|
||||
"Enables intrinsification of various java.lang.Math functions") \
|
||||
\
|
||||
diagnostic(bool, UseCharacterCompareIntrinsics, false, \
|
||||
"Enables intrinsification of java.lang.Character functions") \
|
||||
\
|
||||
diagnostic(bool, UseMultiplyToLenIntrinsic, false, \
|
||||
"Enables intrinsification of BigInteger.multiplyToLen()") \
|
||||
\
|
||||
|
@ -428,6 +428,18 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt
|
||||
case vmIntrinsics::_fmaF:
|
||||
if (!UseFMA || !Matcher::match_rule_supported(Op_FmaF)) return false;
|
||||
break;
|
||||
case vmIntrinsics::_isDigit:
|
||||
if (!Matcher::match_rule_supported(Op_Digit)) return false;
|
||||
break;
|
||||
case vmIntrinsics::_isLowerCase:
|
||||
if (!Matcher::match_rule_supported(Op_LowerCase)) return false;
|
||||
break;
|
||||
case vmIntrinsics::_isUpperCase:
|
||||
if (!Matcher::match_rule_supported(Op_UpperCase)) return false;
|
||||
break;
|
||||
case vmIntrinsics::_isWhitespace:
|
||||
if (!Matcher::match_rule_supported(Op_Whitespace)) return false;
|
||||
break;
|
||||
case vmIntrinsics::_hashCode:
|
||||
case vmIntrinsics::_identityHashCode:
|
||||
case vmIntrinsics::_getClass:
|
||||
|
@ -201,6 +201,7 @@ macro(Loop)
|
||||
macro(LoopLimit)
|
||||
macro(Mach)
|
||||
macro(MachProj)
|
||||
macro(MulAddS2I)
|
||||
macro(MaxI)
|
||||
macro(MemBarAcquire)
|
||||
macro(LoadFence)
|
||||
@ -341,6 +342,7 @@ macro(MulVF)
|
||||
macro(MulReductionVF)
|
||||
macro(MulVD)
|
||||
macro(MulReductionVD)
|
||||
macro(MulAddVS2VI)
|
||||
macro(FmaVD)
|
||||
macro(FmaVF)
|
||||
macro(DivVF)
|
||||
@ -394,3 +396,7 @@ macro(ExtractI)
|
||||
macro(ExtractL)
|
||||
macro(ExtractF)
|
||||
macro(ExtractD)
|
||||
macro(Digit)
|
||||
macro(LowerCase)
|
||||
macro(UpperCase)
|
||||
macro(Whitespace)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2018, 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
|
||||
@ -180,4 +180,40 @@ class EncodeISOArrayNode: public Node {
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
};
|
||||
|
||||
//-------------------------------DigitNode----------------------------------------
|
||||
class DigitNode : public Node {
|
||||
public:
|
||||
DigitNode(Node* control, Node *in1) : Node(control, in1) {}
|
||||
virtual int Opcode() const;
|
||||
const Type* bottom_type() const { return TypeInt::BOOL; }
|
||||
virtual uint ideal_reg() const { return Op_RegI; }
|
||||
};
|
||||
|
||||
//------------------------------LowerCaseNode------------------------------------
|
||||
class LowerCaseNode : public Node {
|
||||
public:
|
||||
LowerCaseNode(Node* control, Node *in1) : Node(control, in1) {}
|
||||
virtual int Opcode() const;
|
||||
const Type* bottom_type() const { return TypeInt::BOOL; }
|
||||
virtual uint ideal_reg() const { return Op_RegI; }
|
||||
};
|
||||
|
||||
//------------------------------UpperCaseNode------------------------------------
|
||||
class UpperCaseNode : public Node {
|
||||
public:
|
||||
UpperCaseNode(Node* control, Node *in1) : Node(control, in1) {}
|
||||
virtual int Opcode() const;
|
||||
const Type* bottom_type() const { return TypeInt::BOOL; }
|
||||
virtual uint ideal_reg() const { return Op_RegI; }
|
||||
};
|
||||
|
||||
//------------------------------WhitespaceCode-----------------------------------
|
||||
class WhitespaceNode : public Node {
|
||||
public:
|
||||
WhitespaceNode(Node* control, Node *in1) : Node(control, in1) {}
|
||||
virtual int Opcode() const;
|
||||
const Type* bottom_type() const { return TypeInt::BOOL; }
|
||||
virtual uint ideal_reg() const { return Op_RegI; }
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_OPTO_INTRINSICNODE_HPP
|
||||
|
@ -324,6 +324,7 @@ class LibraryCallKit : public GraphKit {
|
||||
bool inline_montgomerySquare();
|
||||
bool inline_vectorizedMismatch();
|
||||
bool inline_fma(vmIntrinsics::ID id);
|
||||
bool inline_character_compare(vmIntrinsics::ID id);
|
||||
|
||||
bool inline_profileBoolean();
|
||||
bool inline_isCompileConstant();
|
||||
@ -867,6 +868,12 @@ bool LibraryCallKit::try_to_inline(int predicate) {
|
||||
case vmIntrinsics::_fmaF:
|
||||
return inline_fma(intrinsic_id());
|
||||
|
||||
case vmIntrinsics::_isDigit:
|
||||
case vmIntrinsics::_isLowerCase:
|
||||
case vmIntrinsics::_isUpperCase:
|
||||
case vmIntrinsics::_isWhitespace:
|
||||
return inline_character_compare(intrinsic_id());
|
||||
|
||||
default:
|
||||
// If you get here, it may be that someone has added a new intrinsic
|
||||
// to the list in vmSymbols.hpp without implementing it here.
|
||||
@ -6555,6 +6562,32 @@ bool LibraryCallKit::inline_fma(vmIntrinsics::ID id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibraryCallKit::inline_character_compare(vmIntrinsics::ID id) {
|
||||
// argument(0) is receiver
|
||||
Node* codePoint = argument(1);
|
||||
Node* n = NULL;
|
||||
|
||||
switch (id) {
|
||||
case vmIntrinsics::_isDigit :
|
||||
n = new DigitNode(control(), codePoint);
|
||||
break;
|
||||
case vmIntrinsics::_isLowerCase :
|
||||
n = new LowerCaseNode(control(), codePoint);
|
||||
break;
|
||||
case vmIntrinsics::_isUpperCase :
|
||||
n = new UpperCaseNode(control(), codePoint);
|
||||
break;
|
||||
case vmIntrinsics::_isWhitespace :
|
||||
n = new WhitespaceNode(control(), codePoint);
|
||||
break;
|
||||
default:
|
||||
fatal_unexpected_iid(id);
|
||||
}
|
||||
|
||||
set_result(_gvn.transform(n));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibraryCallKit::inline_profileBoolean() {
|
||||
Node* counts = argument(1);
|
||||
const TypeAryPtr* ary = NULL;
|
||||
|
@ -1249,6 +1249,9 @@ public:
|
||||
// important (common) to do address expressions.
|
||||
Node *remix_address_expressions( Node *n );
|
||||
|
||||
// Convert add to muladd to generate MuladdS2I under certain criteria
|
||||
Node * convert_add_to_muladd(Node * n);
|
||||
|
||||
// Attempt to use a conditional move instead of a phi/branch
|
||||
Node *conditional_move( Node *n );
|
||||
|
||||
|
@ -493,6 +493,54 @@ Node *PhaseIdealLoop::remix_address_expressions( Node *n ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Optimize ((in1[2*i] * in2[2*i]) + (in1[2*i+1] * in2[2*i+1]))
|
||||
Node *PhaseIdealLoop::convert_add_to_muladd(Node* n) {
|
||||
assert(n->Opcode() == Op_AddI, "sanity");
|
||||
Node * nn = NULL;
|
||||
Node * in1 = n->in(1);
|
||||
Node * in2 = n->in(2);
|
||||
if (in1->Opcode() == Op_MulI && in2->Opcode() == Op_MulI) {
|
||||
IdealLoopTree* loop_n = get_loop(get_ctrl(n));
|
||||
if (loop_n->_head->as_Loop()->is_valid_counted_loop() &&
|
||||
Matcher::match_rule_supported(Op_MulAddS2I) &&
|
||||
Matcher::match_rule_supported(Op_MulAddVS2VI)) {
|
||||
Node* mul_in1 = in1->in(1);
|
||||
Node* mul_in2 = in1->in(2);
|
||||
Node* mul_in3 = in2->in(1);
|
||||
Node* mul_in4 = in2->in(2);
|
||||
if (mul_in1->Opcode() == Op_LoadS &&
|
||||
mul_in2->Opcode() == Op_LoadS &&
|
||||
mul_in3->Opcode() == Op_LoadS &&
|
||||
mul_in4->Opcode() == Op_LoadS) {
|
||||
IdealLoopTree* loop1 = get_loop(get_ctrl(mul_in1));
|
||||
IdealLoopTree* loop2 = get_loop(get_ctrl(mul_in2));
|
||||
IdealLoopTree* loop3 = get_loop(get_ctrl(mul_in3));
|
||||
IdealLoopTree* loop4 = get_loop(get_ctrl(mul_in4));
|
||||
IdealLoopTree* loop5 = get_loop(get_ctrl(in1));
|
||||
IdealLoopTree* loop6 = get_loop(get_ctrl(in2));
|
||||
// All nodes should be in the same counted loop.
|
||||
if (loop_n == loop1 && loop_n == loop2 && loop_n == loop3 &&
|
||||
loop_n == loop4 && loop_n == loop5 && loop_n == loop6) {
|
||||
Node* adr1 = mul_in1->in(MemNode::Address);
|
||||
Node* adr2 = mul_in2->in(MemNode::Address);
|
||||
Node* adr3 = mul_in3->in(MemNode::Address);
|
||||
Node* adr4 = mul_in4->in(MemNode::Address);
|
||||
if (adr1->is_AddP() && adr2->is_AddP() && adr3->is_AddP() && adr4->is_AddP()) {
|
||||
if ((adr1->in(AddPNode::Base) == adr3->in(AddPNode::Base)) &&
|
||||
(adr2->in(AddPNode::Base) == adr4->in(AddPNode::Base))) {
|
||||
nn = new MulAddS2INode(mul_in1, mul_in2, mul_in3, mul_in4);
|
||||
register_new_node(nn, get_ctrl(n));
|
||||
_igvn.replace_node(n, nn);
|
||||
return nn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nn;
|
||||
}
|
||||
|
||||
//------------------------------conditional_move-------------------------------
|
||||
// Attempt to replace a Phi with a conditional move. We have some pretty
|
||||
// strict profitability requirements. All Phis at the merge point must
|
||||
@ -927,6 +975,11 @@ Node *PhaseIdealLoop::split_if_with_blocks_pre( Node *n ) {
|
||||
Node *m = remix_address_expressions( n );
|
||||
if( m ) return m;
|
||||
|
||||
if (n_op == Op_AddI) {
|
||||
Node *nn = convert_add_to_muladd( n );
|
||||
if ( nn ) return nn;
|
||||
}
|
||||
|
||||
if (n->is_ConstraintCast()) {
|
||||
Node* dom_cast = n->as_ConstraintCast()->dominating_cast(&_igvn, this);
|
||||
// ConstraintCastNode::dominating_cast() uses node control input to determine domination.
|
||||
|
@ -2352,6 +2352,15 @@ void Matcher::find_shared_post_visit(Node* n, uint opcode) {
|
||||
n->del_req(3);
|
||||
break;
|
||||
}
|
||||
case Op_MulAddS2I: {
|
||||
Node* pair1 = new BinaryNode(n->in(1), n->in(2));
|
||||
Node* pair2 = new BinaryNode(n->in(3), n->in(4));
|
||||
n->set_req(1, pair1);
|
||||
n->set_req(2, pair2);
|
||||
n->del_req(4);
|
||||
n->del_req(3);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -285,4 +285,15 @@ public:
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
};
|
||||
|
||||
//------------------------------MulAddS2INode----------------------------------
|
||||
// Multiply shorts into integers and add them.
|
||||
// Semantics: I_OUT = S1 * S2 + S3 * S4
|
||||
class MulAddS2INode : public Node {
|
||||
public:
|
||||
MulAddS2INode(Node* in1, Node *in2, Node *in3, Node* in4) : Node(0, in1, in2, in3, in4) {}
|
||||
virtual int Opcode() const;
|
||||
const Type *bottom_type() const { return TypeInt::INT; }
|
||||
virtual uint ideal_reg() const { return Op_RegI; }
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_OPTO_MULNODE_HPP
|
||||
|
@ -471,15 +471,15 @@ void Parse::profile_taken_branch(int target_bci, bool force_update) {
|
||||
if (osr_site) {
|
||||
ciProfileData* data = md->bci_to_data(cur_bci);
|
||||
assert(data != NULL && data->is_JumpData(), "need JumpData for taken branch");
|
||||
int limit = (CompileThreshold
|
||||
* (OnStackReplacePercentage - InterpreterProfilePercentage)) / 100;
|
||||
int limit = (int)((int64_t)CompileThreshold
|
||||
* (OnStackReplacePercentage - InterpreterProfilePercentage) / 100);
|
||||
test_for_osr_md_counter_at(md, data, JumpData::taken_offset(), limit);
|
||||
}
|
||||
} else {
|
||||
// With method data update off, use the invocation counter to trigger an
|
||||
// OSR compilation, as done in the interpreter.
|
||||
if (osr_site) {
|
||||
int limit = (CompileThreshold * OnStackReplacePercentage) / 100;
|
||||
int limit = (int)((int64_t)CompileThreshold * OnStackReplacePercentage / 100);
|
||||
increment_and_test_invocation_counter(limit);
|
||||
}
|
||||
}
|
||||
|
@ -645,6 +645,10 @@ void SuperWord::find_adjacent_refs() {
|
||||
// with a different alignment were created before.
|
||||
for (uint i = 0; i < align_to_refs.size(); i++) {
|
||||
MemNode* mr = align_to_refs.at(i)->as_Mem();
|
||||
if (mr == mem_ref) {
|
||||
// Skip when we are looking at same memory operation.
|
||||
continue;
|
||||
}
|
||||
if (same_velt_type(mr, mem_ref) &&
|
||||
memory_alignment(mr, iv_adjustment) != 0)
|
||||
create_pack = false;
|
||||
@ -846,6 +850,27 @@ MemNode* SuperWord::find_align_to_ref(Node_List &memops) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//------------------span_works_for_memory_size-----------------------------
|
||||
static bool span_works_for_memory_size(MemNode* mem, int span, int mem_size, int offset) {
|
||||
bool span_matches_memory = false;
|
||||
if ((mem_size == type2aelembytes(T_BYTE) || mem_size == type2aelembytes(T_SHORT))
|
||||
&& ABS(span) == type2aelembytes(T_INT)) {
|
||||
// There is a mismatch on span size compared to memory.
|
||||
for (DUIterator_Fast jmax, j = mem->fast_outs(jmax); j < jmax; j++) {
|
||||
Node* use = mem->fast_out(j);
|
||||
if (!VectorNode::is_type_transition_to_int(use)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// If all uses transition to integer, it means that we can successfully align even on mismatch.
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
span_matches_memory = ABS(span) == mem_size;
|
||||
}
|
||||
return span_matches_memory && (ABS(offset) % mem_size) == 0;
|
||||
}
|
||||
|
||||
//------------------------------ref_is_alignable---------------------------
|
||||
// Can the preloop align the reference to position zero in the vector?
|
||||
bool SuperWord::ref_is_alignable(SWPointer& p) {
|
||||
@ -862,7 +887,7 @@ bool SuperWord::ref_is_alignable(SWPointer& p) {
|
||||
int offset = p.offset_in_bytes();
|
||||
// Stride one accesses are alignable if offset is aligned to memory operation size.
|
||||
// Offset can be unaligned when UseUnalignedAccesses is used.
|
||||
if (ABS(span) == mem_size && (ABS(offset) % mem_size) == 0) {
|
||||
if (span_works_for_memory_size(p.mem(), span, mem_size, offset)) {
|
||||
return true;
|
||||
}
|
||||
// If the initial offset from start of the object is computable,
|
||||
@ -915,6 +940,28 @@ bool SuperWord::ref_is_alignable(SWPointer& p) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//---------------------------get_vw_bytes_special------------------------
|
||||
int SuperWord::get_vw_bytes_special(MemNode* s) {
|
||||
// Get the vector width in bytes.
|
||||
int vw = vector_width_in_bytes(s);
|
||||
|
||||
// Check for special case where there is an MulAddS2I usage where short vectors are going to need combined.
|
||||
BasicType btype = velt_basic_type(s);
|
||||
if (type2aelembytes(btype) == 2) {
|
||||
bool should_combine_adjacent = true;
|
||||
for (DUIterator_Fast imax, i = s->fast_outs(imax); i < imax; i++) {
|
||||
Node* user = s->fast_out(i);
|
||||
if (!VectorNode::is_muladds2i(user)) {
|
||||
should_combine_adjacent = false;
|
||||
}
|
||||
}
|
||||
if (should_combine_adjacent) {
|
||||
vw = MIN2(Matcher::max_vector_size(btype)*type2aelembytes(btype), vw * 2);
|
||||
}
|
||||
}
|
||||
|
||||
return vw;
|
||||
}
|
||||
|
||||
//---------------------------get_iv_adjustment---------------------------
|
||||
// Calculate loop's iv adjustment for this memory ops.
|
||||
@ -923,7 +970,7 @@ int SuperWord::get_iv_adjustment(MemNode* mem_ref) {
|
||||
int offset = align_to_ref_p.offset_in_bytes();
|
||||
int scale = align_to_ref_p.scale_in_bytes();
|
||||
int elt_size = align_to_ref_p.memory_size();
|
||||
int vw = vector_width_in_bytes(mem_ref);
|
||||
int vw = get_vw_bytes_special(mem_ref);
|
||||
assert(vw > 1, "sanity");
|
||||
int iv_adjustment;
|
||||
if (scale != 0) {
|
||||
@ -2303,6 +2350,12 @@ void SuperWord::output() {
|
||||
const TypePtr* atyp = n->adr_type();
|
||||
vn = StoreVectorNode::make(opc, ctl, mem, adr, atyp, val, vlen);
|
||||
vlen_in_bytes = vn->as_StoreVector()->memory_size();
|
||||
} else if (VectorNode::is_muladds2i(n)) {
|
||||
assert(n->req() == 5u, "MulAddS2I should have 4 operands.");
|
||||
Node* in1 = vector_opd(p, 1);
|
||||
Node* in2 = vector_opd(p, 2);
|
||||
vn = VectorNode::make(opc, in1, in2, vlen, velt_basic_type(n));
|
||||
vlen_in_bytes = vn->as_Vector()->length_in_bytes();
|
||||
} else if (n->req() == 3 && !is_cmov_pack(p)) {
|
||||
// Promote operands to vector
|
||||
Node* in1 = NULL;
|
||||
@ -2615,6 +2668,16 @@ Node* SuperWord::vector_opd(Node_List* p, int opd_idx) {
|
||||
}
|
||||
assert(opd_bt == in->bottom_type()->basic_type(), "all same type");
|
||||
pk->add_opd(in);
|
||||
if (VectorNode::is_muladds2i(pi)) {
|
||||
Node* in2 = pi->in(opd_idx + 2);
|
||||
assert(my_pack(in2) == NULL, "Should already have been unpacked");
|
||||
if (my_pack(in2) != NULL) {
|
||||
NOT_PRODUCT(if (is_trace_loop_reverse() || TraceLoopOpts) { tty->print_cr("Should already have been unpacked"); })
|
||||
return NULL;
|
||||
}
|
||||
assert(opd_bt == in2->bottom_type()->basic_type(), "all same type");
|
||||
pk->add_opd(in2);
|
||||
}
|
||||
}
|
||||
_igvn.register_new_node_with_optimizer(pk);
|
||||
_phase->set_ctrl(pk, _phase->get_ctrl(opd));
|
||||
@ -2692,6 +2755,21 @@ bool SuperWord::is_vector_use(Node* use, int u_idx) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (VectorNode::is_muladds2i(use)) {
|
||||
// MulAddS2I takes shorts and produces ints - hence the special checks
|
||||
// on alignment and size.
|
||||
if (u_pk->size() * 2 != d_pk->size()) {
|
||||
return false;
|
||||
}
|
||||
for (uint i = 0; i < MIN2(d_pk->size(), u_pk->size()); i++) {
|
||||
Node* ui = u_pk->at(i);
|
||||
Node* di = d_pk->at(i);
|
||||
if (alignment(ui) != alignment(di) * 2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (u_pk->size() != d_pk->size())
|
||||
return false;
|
||||
for (uint i = 0; i < u_pk->size(); i++) {
|
||||
@ -3017,7 +3095,7 @@ int SuperWord::memory_alignment(MemNode* s, int iv_adjust) {
|
||||
NOT_PRODUCT(if(is_trace_alignment()) tty->print("SWPointer::memory_alignment: SWPointer p invalid, return bottom_align");)
|
||||
return bottom_align;
|
||||
}
|
||||
int vw = vector_width_in_bytes(s);
|
||||
int vw = get_vw_bytes_special(s);
|
||||
if (vw < 2) {
|
||||
NOT_PRODUCT(if(is_trace_alignment()) tty->print_cr("SWPointer::memory_alignment: vector_width_in_bytes < 2, return bottom_align");)
|
||||
return bottom_align; // No vectors for this type
|
||||
|
@ -347,6 +347,7 @@ class SuperWord : public ResourceObj {
|
||||
BasicType bt = velt_basic_type(n);
|
||||
return vector_width(n)*type2aelembytes(bt);
|
||||
}
|
||||
int get_vw_bytes_special(MemNode* s);
|
||||
MemNode* align_to_ref() { return _align_to_ref; }
|
||||
void set_align_to_ref(MemNode* m) { _align_to_ref = m; }
|
||||
|
||||
|
@ -196,6 +196,8 @@ int VectorNode::opcode(int sopc, BasicType bt) {
|
||||
case Op_StoreF:
|
||||
case Op_StoreD:
|
||||
return Op_StoreVector;
|
||||
case Op_MulAddS2I:
|
||||
return Op_MulAddVS2VI;
|
||||
|
||||
default:
|
||||
return 0; // Unimplemented
|
||||
@ -214,6 +216,25 @@ bool VectorNode::implemented(int opc, uint vlen, BasicType bt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VectorNode::is_type_transition_short_to_int(Node* n) {
|
||||
switch (n->Opcode()) {
|
||||
case Op_MulAddS2I:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VectorNode::is_type_transition_to_int(Node* n) {
|
||||
return is_type_transition_short_to_int(n);
|
||||
}
|
||||
|
||||
bool VectorNode::is_muladds2i(Node* n) {
|
||||
if (n->Opcode() == Op_MulAddS2I) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VectorNode::is_shift(Node* n) {
|
||||
switch (n->Opcode()) {
|
||||
case Op_LShiftI:
|
||||
@ -277,6 +298,7 @@ void VectorNode::vector_operands(Node* n, uint* start, uint* end) {
|
||||
case Op_AndI: case Op_AndL:
|
||||
case Op_OrI: case Op_OrL:
|
||||
case Op_XorI: case Op_XorL:
|
||||
case Op_MulAddS2I:
|
||||
*start = 1;
|
||||
*end = 3; // 2 vector operands
|
||||
break;
|
||||
@ -354,6 +376,8 @@ VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType b
|
||||
case Op_AndV: return new AndVNode(n1, n2, vt);
|
||||
case Op_OrV: return new OrVNode (n1, n2, vt);
|
||||
case Op_XorV: return new XorVNode(n1, n2, vt);
|
||||
|
||||
case Op_MulAddVS2VI: return new MulAddVS2VINode(n1, n2, vt);
|
||||
default:
|
||||
fatal("Missed vector creation for '%s'", NodeClassNames[vopc]);
|
||||
return NULL;
|
||||
|
@ -67,6 +67,9 @@ class VectorNode : public TypeNode {
|
||||
static int opcode(int opc, BasicType bt);
|
||||
static bool implemented(int opc, uint vlen, BasicType bt);
|
||||
static bool is_shift(Node* n);
|
||||
static bool is_type_transition_short_to_int(Node* n);
|
||||
static bool is_type_transition_to_int(Node* n);
|
||||
static bool is_muladds2i(Node* n);
|
||||
static bool is_invariant_vector(Node* n);
|
||||
// [Start, end) half-open range defining which operands are vectors
|
||||
static void vector_operands(Node* n, uint* start, uint* end);
|
||||
@ -261,6 +264,14 @@ public:
|
||||
virtual int Opcode() const;
|
||||
};
|
||||
|
||||
//------------------------------MulAddVS2VINode--------------------------------
|
||||
// Vector multiply shorts to int and add adjacent ints.
|
||||
class MulAddVS2VINode : public VectorNode {
|
||||
public:
|
||||
MulAddVS2VINode(Node* in1, Node* in2, const TypeVect* vt) : VectorNode(in1, in2, vt) {}
|
||||
virtual int Opcode() const;
|
||||
};
|
||||
|
||||
//------------------------------FmaVDNode--------------------------------------
|
||||
// Vector multiply double
|
||||
class FmaVDNode : public VectorNode {
|
||||
|
@ -926,11 +926,12 @@ UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetLong(JNIEnv *env, jobject unsafe, job
|
||||
}
|
||||
} UNSAFE_END
|
||||
|
||||
static void post_thread_park_event(EventThreadPark* event, const oop obj, jlong timeout) {
|
||||
static void post_thread_park_event(EventThreadPark* event, const oop obj, jlong timeout_nanos, jlong until_epoch_millis) {
|
||||
assert(event != NULL, "invariant");
|
||||
assert(event->should_commit(), "invariant");
|
||||
event->set_parkedClass((obj != NULL) ? obj->klass() : NULL);
|
||||
event->set_timeout(timeout);
|
||||
event->set_timeout(timeout_nanos);
|
||||
event->set_until(until_epoch_millis);
|
||||
event->set_address((obj != NULL) ? (u8)cast_from_oop<uintptr_t>(obj) : 0);
|
||||
event->commit();
|
||||
}
|
||||
@ -942,7 +943,16 @@ UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute,
|
||||
JavaThreadParkedState jtps(thread, time != 0);
|
||||
thread->parker()->park(isAbsolute != 0, time);
|
||||
if (event.should_commit()) {
|
||||
post_thread_park_event(&event, thread->current_park_blocker(), time);
|
||||
const oop obj = thread->current_park_blocker();
|
||||
if (time == 0) {
|
||||
post_thread_park_event(&event, obj, min_jlong, min_jlong);
|
||||
} else {
|
||||
if (isAbsolute != 0) {
|
||||
post_thread_park_event(&event, obj, min_jlong, time);
|
||||
} else {
|
||||
post_thread_park_event(&event, obj, time, min_jlong);
|
||||
}
|
||||
}
|
||||
}
|
||||
HOTSPOT_THREAD_PARK_END((uintptr_t) thread->parker());
|
||||
} UNSAFE_END
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user