# HG changeset patch # User alanb # Date 1558606057 -3600 # Node ID 276d3e54026898e6920eae9c20c3b44c946f43c7 # Parent f0a1d9760c5e86101284ebdbc976922ff6ee467a# Parent 4645b6d57f548bbbf61b2f7ee164923886e1e589 Merge diff -r f0a1d9760c5e -r 276d3e540268 .hgtags --- a/.hgtags Fri May 17 13:21:44 2019 +0100 +++ b/.hgtags Thu May 23 11:07:37 2019 +0100 @@ -559,3 +559,4 @@ a43d6467317d8f1e160f67aadec37919c9d64443 jdk-13+19 6ccc7cd7931e34129f6b7e04988fc9a63958dde0 jdk-13+20 f2f11d7f7f4e7128f8aba6ffa576cfa76fbf7d1a jdk-13+21 +181986c5476468bc2dd4532af49599003ee8af37 jdk-13+22 diff -r f0a1d9760c5e -r 276d3e540268 doc/building.html --- a/doc/building.html Fri May 17 13:21:44 2019 +0100 +++ b/doc/building.html Thu May 23 11:07:37 2019 +0100 @@ -297,6 +297,10 @@ +

All compilers are expected to be able to compile to the C99 language standard, +as some C99 features are used in the source code. Microsoft Visual Studio +doesn't fully support C99 so in practice shared code is limited to using C99 +features that it does support.

gcc

The minimum accepted version of gcc is 4.8. Older versions will generate a warning by configure and are unlikely to work.

The JDK is currently known to be able to compile with at least version 7.4 of gcc.

diff -r f0a1d9760c5e -r 276d3e540268 doc/building.md --- a/doc/building.md Fri May 17 13:21:44 2019 +0100 +++ b/doc/building.md Thu May 23 11:07:37 2019 +0100 @@ -328,6 +328,11 @@ Solaris Oracle Solaris Studio 12.6 (with compiler version 5.15) Windows Microsoft Visual Studio 2017 update 15.9.6 +All compilers are expected to be able to compile to the C99 language standard, +as some C99 features are used in the source code. Microsoft Visual Studio +doesn't fully support C99 so in practice shared code is limited to using C99 +features that it does support. + ### gcc The minimum accepted version of gcc is 4.8. Older versions will generate a warning diff -r f0a1d9760c5e -r 276d3e540268 make/Docs.gmk --- a/make/Docs.gmk Fri May 17 13:21:44 2019 +0100 +++ b/make/Docs.gmk Thu May 23 11:07:37 2019 +0100 @@ -85,13 +85,14 @@ -tag param \ -tag return \ -tag throws \ + -taglet build.tools.taglet.JSpec\$$JLS \ + -taglet build.tools.taglet.JSpec\$$JVMS \ -taglet build.tools.taglet.ModuleGraph \ + -taglet build.tools.taglet.ToolGuide \ -tag since \ -tag serialData \ -tag factory \ -tag see \ - -tag 'jvms:a:See The Java™ Virtual Machine Specification:' \ - -tag 'jls:a:See The Java™ Language Specification:' \ -taglet build.tools.taglet.ExtLink \ -taglet build.tools.taglet.Incubating \ -tagletpath $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ @@ -275,7 +276,8 @@ $1_INDIRECT_EXPORTS := $$(call FindTransitiveIndirectDepsForModules, $$($1_MODULES)) $1_ALL_MODULES := $$(sort $$($1_MODULES) $$($1_INDIRECT_EXPORTS)) - $1_JAVA_ARGS := -Dextlink.spec.version=$$(VERSION_SPECIFICATION) + $1_JAVA_ARGS := -Dextlink.spec.version=$$(VERSION_SPECIFICATION) \ + -Djspec.version=$$(VERSION_SPECIFICATION) ifeq ($$(ENABLE_FULL_DOCS), true) # Tell the ModuleGraph taglet to generate html links to soon-to-be-created diff -r f0a1d9760c5e -r 276d3e540268 make/autoconf/flags-cflags.m4 --- a/make/autoconf/flags-cflags.m4 Fri May 17 13:21:44 2019 +0100 +++ b/make/autoconf/flags-cflags.m4 Thu May 23 11:07:37 2019 +0100 @@ -300,6 +300,20 @@ C_O_FLAG_DEBUG="-O0" C_O_FLAG_DEBUG_JVM="-O0" C_O_FLAG_NONE="-O0" + # -D_FORTIFY_SOURCE=2 hardening option needs optimization (at least -O1) enabled + # set for lower O-levels -U_FORTIFY_SOURCE to overwrite previous settings + if test "x$OPENJDK_TARGET_OS" = xlinux -a "x$DEBUG_LEVEL" = "xfastdebug"; then + ENABLE_FORTIFY_CFLAGS="-D_FORTIFY_SOURCE=2" + DISABLE_FORTIFY_CFLAGS="-U_FORTIFY_SOURCE" + C_O_FLAG_HIGHEST_JVM="${C_O_FLAG_HIGHEST_JVM} ${ENABLE_FORTIFY_CFLAGS}" + C_O_FLAG_HIGHEST="${C_O_FLAG_HIGHEST} ${ENABLE_FORTIFY_CFLAGS}" + C_O_FLAG_HI="${C_O_FLAG_HI} ${ENABLE_FORTIFY_CFLAGS}" + C_O_FLAG_NORM="${C_O_FLAG_NORM} ${ENABLE_FORTIFY_CFLAGS}" + C_O_FLAG_SIZE="${C_O_FLAG_SIZE} ${DISABLE_FORTIFY_CFLAGS}" + C_O_FLAG_DEBUG="${C_O_FLAG_DEBUG} ${DISABLE_FORTIFY_CFLAGS}" + C_O_FLAG_DEBUG_JVM="${C_O_FLAG_DEBUG_JVM} ${DISABLE_FORTIFY_CFLAGS}" + C_O_FLAG_NONE="${C_O_FLAG_NONE} ${DISABLE_FORTIFY_CFLAGS}" + fi elif test "x$TOOLCHAIN_TYPE" = xclang; then if test "x$OPENJDK_TARGET_OS" = xmacosx; then # On MacOSX we optimize for size, something @@ -550,7 +564,7 @@ TOOLCHAIN_CFLAGS="-errshort=tags" TOOLCHAIN_CFLAGS_JDK="-mt $TOOLCHAIN_FLAGS" - TOOLCHAIN_CFLAGS_JDK_CONLY="-xCC -Xa -W0,-noglobal $TOOLCHAIN_CFLAGS" # C only + TOOLCHAIN_CFLAGS_JDK_CONLY="-W0,-noglobal $TOOLCHAIN_CFLAGS" # C only TOOLCHAIN_CFLAGS_JDK_CXXONLY="-features=no%except -norunpath -xnolib" # CXX only TOOLCHAIN_CFLAGS_JVM="-template=no%extdef -features=no%split_init \ -library=stlport4 -mt -features=no%except $TOOLCHAIN_FLAGS" @@ -571,6 +585,30 @@ TOOLCHAIN_CFLAGS_JDK="-nologo -MD -Zc:wchar_t-" fi + # CFLAGS C language level for JDK sources (hotspot only uses C++) + # Ideally we would have a common level across all toolchains so that all sources + # are sure to conform to the same standard. Unfortunately neither our sources nor + # our toolchains are in a condition to support that. But what we loosely aim for is + # C99 level. + if test "x$TOOLCHAIN_TYPE" = xgcc || test "x$TOOLCHAIN_TYPE" = xclang || test "x$TOOLCHAIN_TYPE" = xxlc; then + # This raises the language level for older 4.8 gcc, while lowering it for later + # versions. clang and xlclang support the same flag. + LANGSTD_CFLAGS="-std=c99" + elif test "x$TOOLCHAIN_TYPE" = xsolstudio; then + # We can't turn on -std=c99 without breaking compilation of the splashscreen/png + # utilities. But we can enable c99 as below (previously achieved by using -Xa). + # It is the no_lib that makes the difference. + LANGSTD_CFLAGS="-xc99=all,no_lib" + elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then + # MSVC doesn't support C99/C11 explicitly, unless you compile as C++: + # LANGSTD_CFLAGS="/TP" + # but that requires numerous changes to the sources files. So we are limited + # to C89/C90 plus whatever extensions Visual Studio has decided to implement. + # This is the lowest bar for shared code. + LANGSTD_CFLAGS="" + fi + TOOLCHAIN_CFLAGS_JDK_CONLY="$LANGSTD_CFLAGS $TOOLCHAIN_CFLAGS_JDK_CONLY" + # CFLAGS WARNINGS STUFF # Set JVM_CFLAGS warning handling if test "x$TOOLCHAIN_TYPE" = xgcc; then diff -r f0a1d9760c5e -r 276d3e540268 make/hotspot/lib/JvmFeatures.gmk --- a/make/hotspot/lib/JvmFeatures.gmk Fri May 17 13:21:44 2019 +0100 +++ b/make/hotspot/lib/JvmFeatures.gmk Thu May 23 11:07:37 2019 +0100 @@ -111,6 +111,7 @@ JVM_EXCLUDE_FILES += \ classListParser.cpp \ classLoaderExt.cpp \ + dynamicArchive.cpp \ filemap.cpp \ heapShared.cpp \ metaspaceShared.cpp \ diff -r f0a1d9760c5e -r 276d3e540268 make/jdk/src/classes/build/tools/taglet/JSpec.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/jdk/src/classes/build/tools/taglet/JSpec.java Thu May 23 11:07:37 2019 +0100 @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package build.tools.taglet; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.lang.model.element.Element; + +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.LiteralTree; +import com.sun.source.doctree.UnknownBlockTagTree; +import com.sun.source.util.SimpleDocTreeVisitor; +import jdk.javadoc.doclet.Taglet; + +import static com.sun.source.doctree.DocTree.Kind.*; + +/** + * A base class for block tags to insert a link to an external copy of JLS or JVMS. + * The tags can be used as follows: + * + *
+ * @jls section-number description
+ * 
+ * + * For example: + * + *
+ * @jls 3.4 Line Terminators
+ * 
+ * + * will produce the following HTML for a docs build configured for Java SE 12. + * + *
{@code
+ * 
See Java Language Specification: + *
3.4 Line terminators + * }
+ * + * The version of the spec must be set in the jspec.version system property. + */ +public class JSpec implements Taglet { + static final String SPEC_VERSION; + + static { + SPEC_VERSION = System.getProperty("jspec.version"); + if (SPEC_VERSION == null) { + throw new RuntimeException("jspec.version property not set"); + } + } + + public static class JLS extends JSpec { + public JLS() { + super("jls", + "Java Language Specification", + "https://docs.oracle.com/javase/specs/jls/se" + SPEC_VERSION + "/html", + "jls"); + } + } + + public static class JVMS extends JSpec { + public JVMS() { + super("jvms", + "Java Virtual Machine Specification", + "https://docs.oracle.com/javase/specs/jvms/se" + SPEC_VERSION + "/html", + "jvms"); + } + } + + private String tagName; + private String specTitle; + private String baseURL; + private String idPrefix; + + protected JSpec(String tagName, String specTitle, String baseURL, String idPrefix) { + this.tagName = tagName; + this.specTitle = specTitle; + this.baseURL = baseURL; + this.idPrefix = idPrefix; + } + + + static final Pattern TAG_PATTERN = Pattern.compile("(?s)(.+ )?(?[1-9][0-9]*)(?
[0-9.]*)( .*)?$"); + + + /** + * Returns the set of locations in which the tag may be used. + */ + @Override + public Set getAllowedLocations() { + return EnumSet.allOf(jdk.javadoc.doclet.Taglet.Location.class); + } + + @Override + public boolean isInlineTag() { + return false; + } + + @Override + public String getName() { + return tagName; + } + + @Override + public String toString(List tags, Element elem) { + + if (tags.isEmpty()) + return ""; + + StringBuilder sb = new StringBuilder(); + sb.append("
See " + specTitle + ":
\n") + .append("
\n"); + + for (DocTree tag : tags) { + + if (tag.getKind() != UNKNOWN_BLOCK_TAG) { + continue; + } + + UnknownBlockTagTree blockTag = (UnknownBlockTagTree)tag; + String tagText = blockTag.getContent().toString().trim(); + Matcher m = TAG_PATTERN.matcher(tagText); + if (m.find()) { + String chapter = m.group("chapter"); + String section = m.group("section"); + + String url = String.format("%1$s/%2$s-%3$s.html#jls-%3$s%4$s", + baseURL, idPrefix, chapter, section); + + + sb.append("") + .append(expand(blockTag)) + .append("
"); + } + + } + + sb.append("
"); + + return sb.toString(); + } + + private String expand(UnknownBlockTagTree tree) { + StringBuilder sb = new StringBuilder(); + return (new SimpleDocTreeVisitor() { + public StringBuilder defaultAction(DocTree tree, StringBuilder sb) { + return sb.append(tree.toString()); + } + + public StringBuilder visitLiteral(LiteralTree tree, StringBuilder sb) { + if (tree.getKind() == CODE) { + sb.append(""); + } + sb.append(escape(tree.getBody().toString())); + if (tree.getKind() == CODE) { + sb.append(""); + } + return sb; + } + + private String escape(String s) { + return s.replace("&", "&").replace("<", "<").replace(">", ">"); + } + }).visit(tree.getContent(), new StringBuilder()).toString(); + } +} diff -r f0a1d9760c5e -r 276d3e540268 make/jdk/src/classes/build/tools/taglet/ToolGuide.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/make/jdk/src/classes/build/tools/taglet/ToolGuide.java Thu May 23 11:07:37 2019 +0100 @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package build.tools.taglet; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.lang.model.element.Element; +import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.PackageElement; + +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.UnknownBlockTagTree; +import jdk.javadoc.doclet.Taglet; + +import static com.sun.source.doctree.DocTree.Kind.*; +import static jdk.javadoc.doclet.Taglet.Location.*; + +/** + * A block tag to insert a link to tool guide in a nearby directory. + * The tag can be used as follows: + *
    + *
  • @toolGuide tool-name label + *
+ * + * If the label is omitted, it defaults to the tool name. + * + * For example + *

+ * @toolGuide javac + *

+ * will produce the following html, depending on the file containing + * the tag. + *

+ * {@code + *

Tool Guides: + *
javac + * } + */ +public class ToolGuide implements Taglet { + + static final String TAG_NAME = "toolGuide"; + + static final String BASE_URL = "../specs/man"; + + static final Pattern TAG_PATTERN = Pattern.compile("(?s)(?[A-Za-z0-9]+)\\s*(?
Tool Guides:
\n") + .append("
"); + + boolean needComma = false; + for (DocTree tag : tags) { + + if (tag.getKind() != UNKNOWN_BLOCK_TAG) { + continue; + } + + UnknownBlockTagTree blockTag = (UnknownBlockTagTree)tag; + String tagText = blockTag.getContent().toString().trim(); + Matcher m = TAG_PATTERN.matcher(tagText); + if (m.matches()) { + String name = m.group("name"); + String label = m.group("label"); + if (label.isEmpty()) { + label = name; + } + + String url = String.format("%s/%s/%s.html", + docRoot(elem), BASE_URL, name); + + if (needComma) { + sb.append(",\n"); + } else { + needComma = true; + } + + sb.append("") + .append(label) + .append(""); + } + } + + sb.append("
\n"); + + return sb.toString(); + } + + private String docRoot(Element elem) { + switch (elem.getKind()) { + case MODULE: + return ".."; + + case PACKAGE: + PackageElement pe = (PackageElement)elem; + String pkgPart = pe.getQualifiedName() + .toString() + .replace('.', '/') + .replaceAll("[^/]+", ".."); + return pe.getEnclosingElement() != null + ? "../" + pkgPart + : pkgPart; + + default: + throw new IllegalArgumentException(elem.getKind().toString()); + } + } +} diff -r f0a1d9760c5e -r 276d3e540268 make/lib/Awt2dLibraries.gmk --- a/make/lib/Awt2dLibraries.gmk Fri May 17 13:21:44 2019 +0100 +++ b/make/lib/Awt2dLibraries.gmk Thu May 23 11:07:37 2019 +0100 @@ -378,7 +378,6 @@ OPTIMIZATION := HIGHEST, \ CFLAGS := $(CFLAGS_JDKLIB) \ $(LCMS_CFLAGS), \ - CFLAGS_solaris := -xc99=no_lib, \ CFLAGS_windows := -DCMS_IS_WINDOWS_, \ EXTRA_HEADER_DIRS := \ common/awt/debug \ diff -r f0a1d9760c5e -r 276d3e540268 make/test/JtregNativeJdk.gmk --- a/make/test/JtregNativeJdk.gmk Fri May 17 13:21:44 2019 +0100 +++ b/make/test/JtregNativeJdk.gmk Thu May 23 11:07:37 2019 +0100 @@ -62,9 +62,11 @@ WIN_LIB_JLI := $(SUPPORT_OUTPUTDIR)/native/java.base/libjli/jli.lib BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeJliLaunchTest := $(WIN_LIB_JLI) BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeCallerAccessTest := jvm.lib + BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib else BUILD_JDK_JTREG_LIBRARIES_LIBS_libstringPlatformChars := -ljava BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava + BUILD_JDK_JTREG_EXCLUDE += exerevokeall.c ifeq ($(call isTargetOs, linux), true) BUILD_JDK_JTREG_LIBRARIES_LIBS_libInheritedChannel := -ljava else ifeq ($(call isTargetOs, solaris), true) diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/aarch64/assembler_aarch64.hpp --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp Thu May 23 11:07:37 2019 +0100 @@ -629,6 +629,14 @@ enum { instruction_size = 4 }; + //---< calculate length of instruction >--- + // We just use the values set above. + // instruction must start at passed address + static unsigned int instr_len(unsigned char *instr) { return instruction_size; } + + //---< longest instructions >--- + static unsigned int instr_maxlen() { return instruction_size; } + Address adjust(Register base, int offset, bool preIncrement) { if (preIncrement) return Address(Pre(base, offset)); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp --- a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp Thu May 23 11:07:37 2019 +0100 @@ -76,7 +76,7 @@ define_pd_global(intx, NonProfiledCodeHeapSize, 21*M); define_pd_global(intx, ProfiledCodeHeapSize, 22*M); define_pd_global(intx, NonNMethodCodeHeapSize, 5*M ); -define_pd_global(uintx, CodeCacheMinBlockLength, 4); +define_pd_global(uintx, CodeCacheMinBlockLength, 6); define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); // Heap related flags diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/aarch64/disassembler_aarch64.hpp --- a/src/hotspot/cpu/aarch64/disassembler_aarch64.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/aarch64/disassembler_aarch64.hpp Thu May 23 11:07:37 2019 +0100 @@ -34,4 +34,24 @@ return ""; } + // Returns address of n-th instruction preceding addr, + // NULL if no preceding instruction can be found. + // On ARM(aarch64), we assume a constant instruction length. + // It might be beneficial to check "is_readable" as we do on ppc and s390. + static address find_prev_instr(address addr, int n_instr) { + return addr - Assembler::instruction_size*n_instr; + } + + // special-case instruction decoding. + // There may be cases where the binutils disassembler doesn't do + // the perfect job. In those cases, decode_instruction0 may kick in + // and do it right. + // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)" + static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL) { + return here; + } + + // platform-specific instruction annotations (like value of loaded constants) + static void annotate(address pc, outputStream* st) { }; + #endif // CPU_AARCH64_DISASSEMBLER_AARCH64_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp --- a/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/aarch64/gc/shared/barrierSetAssembler_aarch64.hpp Thu May 23 11:07:37 2019 +0100 @@ -37,7 +37,7 @@ public: virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, - Register addr, Register count, RegSet saved_regs) {} + Register src, Register dst, Register count, RegSet saved_regs) {} virtual void arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, Register start, Register end, Register tmp, RegSet saved_regs) {} virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/aarch64/gc/shared/modRefBarrierSetAssembler_aarch64.cpp --- a/src/hotspot/cpu/aarch64/gc/shared/modRefBarrierSetAssembler_aarch64.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/aarch64/gc/shared/modRefBarrierSetAssembler_aarch64.cpp Thu May 23 11:07:37 2019 +0100 @@ -29,10 +29,10 @@ #define __ masm-> void ModRefBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, - Register addr, Register count, RegSet saved_regs) { + Register src, Register dst, Register count, RegSet saved_regs) { if (is_oop) { - gen_write_ref_array_pre_barrier(masm, decorators, addr, count, saved_regs); + gen_write_ref_array_pre_barrier(masm, decorators, dst, count, saved_regs); } } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/aarch64/gc/shared/modRefBarrierSetAssembler_aarch64.hpp --- a/src/hotspot/cpu/aarch64/gc/shared/modRefBarrierSetAssembler_aarch64.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/aarch64/gc/shared/modRefBarrierSetAssembler_aarch64.hpp Thu May 23 11:07:37 2019 +0100 @@ -44,7 +44,7 @@ public: virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, - Register addr, Register count, RegSet saved_regs); + Register src, Register dst, Register count, RegSet saved_regs); virtual void arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, Register start, Register count, Register tmp, RegSet saved_regs); virtual void store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp Thu May 23 11:07:37 2019 +0100 @@ -44,7 +44,7 @@ address ShenandoahBarrierSetAssembler::_shenandoah_lrb = NULL; void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, - Register addr, Register count, RegSet saved_regs) { + Register src, Register dst, Register count, RegSet saved_regs) { if (is_oop) { bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; if (ShenandoahSATBBarrier && !dest_uninitialized && !ShenandoahHeap::heap()->heuristics()->can_do_traversal_gc()) { @@ -61,17 +61,17 @@ __ push(saved_regs, sp); if (count == c_rarg0) { - if (addr == c_rarg1) { + if (dst == c_rarg1) { // exactly backwards!! __ mov(rscratch1, c_rarg0); __ mov(c_rarg0, c_rarg1); __ mov(c_rarg1, rscratch1); } else { __ mov(c_rarg1, count); - __ mov(c_rarg0, addr); + __ mov(c_rarg0, dst); } } else { - __ mov(c_rarg0, addr); + __ mov(c_rarg0, dst); __ mov(c_rarg1, count); } if (UseCompressedOops) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp Thu May 23 11:07:37 2019 +0100 @@ -73,7 +73,7 @@ #endif virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, - Register addr, Register count, RegSet saved_regs); + Register src, Register dst, Register count, RegSet saved_regs); virtual void arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, Register start, Register count, Register tmp, RegSet saved_regs); virtual void load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp Thu May 23 11:07:37 2019 +0100 @@ -1364,7 +1364,7 @@ } BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler(); - bs->arraycopy_prologue(_masm, decorators, is_oop, d, count, saved_reg); + bs->arraycopy_prologue(_masm, decorators, is_oop, s, d, count, saved_reg); if (is_oop) { // save regs before copy_memory @@ -1436,7 +1436,7 @@ } BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler(); - bs->arraycopy_prologue(_masm, decorators, is_oop, d, count, saved_regs); + bs->arraycopy_prologue(_masm, decorators, is_oop, s, d, count, saved_regs); if (is_oop) { // save regs before copy_memory @@ -1796,7 +1796,7 @@ } BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler(); - bs->arraycopy_prologue(_masm, decorators, is_oop, to, count, wb_pre_saved_regs); + bs->arraycopy_prologue(_masm, decorators, is_oop, from, to, count, wb_pre_saved_regs); // save the original count __ mov(count_save, count); @@ -4035,14 +4035,14 @@ : "compare_long_string_different_encoding UL"); address entry = __ pc(); Label SMALL_LOOP, TAIL, TAIL_LOAD_16, LOAD_LAST, DIFF1, DIFF2, - DONE, CALCULATE_DIFFERENCE, LARGE_LOOP_PREFETCH, SMALL_LOOP_ENTER, + DONE, CALCULATE_DIFFERENCE, LARGE_LOOP_PREFETCH, NO_PREFETCH, LARGE_LOOP_PREFETCH_REPEAT1, LARGE_LOOP_PREFETCH_REPEAT2; Register result = r0, str1 = r1, cnt1 = r2, str2 = r3, cnt2 = r4, tmp1 = r10, tmp2 = r11, tmp3 = r12, tmp4 = r14; FloatRegister vtmpZ = v0, vtmp = v1, vtmp3 = v2; RegSet spilled_regs = RegSet::of(tmp3, tmp4); - int prefetchLoopExitCondition = MAX(32, SoftwarePrefetchHintDistance/2); + int prefetchLoopExitCondition = MAX(64, SoftwarePrefetchHintDistance/2); __ eor(vtmpZ, __ T16B, vtmpZ, vtmpZ); // cnt2 == amount of characters left to compare @@ -4069,7 +4069,7 @@ if (SoftwarePrefetchHintDistance >= 0) { __ subs(rscratch2, cnt2, prefetchLoopExitCondition); - __ br(__ LT, SMALL_LOOP); + __ br(__ LT, NO_PREFETCH); __ bind(LARGE_LOOP_PREFETCH); __ prfm(Address(tmp2, SoftwarePrefetchHintDistance)); __ mov(tmp4, 2); @@ -4089,51 +4089,20 @@ __ br(__ GE, LARGE_LOOP_PREFETCH); } __ cbz(cnt2, LOAD_LAST); // no characters left except last load + __ bind(NO_PREFETCH); __ subs(cnt2, cnt2, 16); __ br(__ LT, TAIL); - __ b(SMALL_LOOP_ENTER); __ bind(SMALL_LOOP); // smaller loop __ subs(cnt2, cnt2, 16); - __ bind(SMALL_LOOP_ENTER); compare_string_16_x_LU(tmpL, tmpU, DIFF1, DIFF2); __ br(__ GE, SMALL_LOOP); - __ cbz(cnt2, LOAD_LAST); - __ bind(TAIL); // 1..15 characters left - __ subs(zr, cnt2, -8); - __ br(__ GT, TAIL_LOAD_16); - __ ldrd(vtmp, Address(tmp2)); - __ zip1(vtmp3, __ T8B, vtmp, vtmpZ); - - __ ldr(tmpU, Address(__ post(cnt1, 8))); - __ fmovd(tmpL, vtmp3); - __ eor(rscratch2, tmp3, tmpL); - __ cbnz(rscratch2, DIFF2); - __ umov(tmpL, vtmp3, __ D, 1); - __ eor(rscratch2, tmpU, tmpL); - __ cbnz(rscratch2, DIFF1); - __ b(LOAD_LAST); - __ bind(TAIL_LOAD_16); - __ ldrq(vtmp, Address(tmp2)); - __ ldr(tmpU, Address(__ post(cnt1, 8))); - __ zip1(vtmp3, __ T16B, vtmp, vtmpZ); - __ zip2(vtmp, __ T16B, vtmp, vtmpZ); - __ fmovd(tmpL, vtmp3); - __ eor(rscratch2, tmp3, tmpL); - __ cbnz(rscratch2, DIFF2); - - __ ldr(tmp3, Address(__ post(cnt1, 8))); - __ umov(tmpL, vtmp3, __ D, 1); - __ eor(rscratch2, tmpU, tmpL); - __ cbnz(rscratch2, DIFF1); - - __ ldr(tmpU, Address(__ post(cnt1, 8))); - __ fmovd(tmpL, vtmp); - __ eor(rscratch2, tmp3, tmpL); - __ cbnz(rscratch2, DIFF2); - - __ umov(tmpL, vtmp, __ D, 1); - __ eor(rscratch2, tmpU, tmpL); - __ cbnz(rscratch2, DIFF1); + __ cmn(cnt2, (u1)16); + __ br(__ EQ, LOAD_LAST); + __ bind(TAIL); // 1..15 characters left until last load (last 4 characters) + __ add(cnt1, cnt1, cnt2, __ LSL, 1); // Address of 8 bytes before last 4 characters in UTF-16 string + __ add(tmp2, tmp2, cnt2); // Address of 16 bytes before last 4 characters in Latin1 string + __ ldr(tmp3, Address(cnt1, -8)); + compare_string_16_x_LU(tmpL, tmpU, DIFF1, DIFF2); // last 16 characters before last load __ b(LOAD_LAST); __ bind(DIFF2); __ mov(tmpU, tmp3); @@ -4141,10 +4110,12 @@ __ pop(spilled_regs, sp); __ b(CALCULATE_DIFFERENCE); __ bind(LOAD_LAST); + // Last 4 UTF-16 characters are already pre-loaded into tmp3 by compare_string_16_x_LU. + // No need to load it again + __ mov(tmpU, tmp3); __ pop(spilled_regs, sp); __ ldrs(vtmp, Address(strL)); - __ ldr(tmpU, Address(strU)); __ zip1(vtmp, __ T8B, vtmp, vtmpZ); __ fmovd(tmpL, vtmp); @@ -4206,10 +4177,10 @@ compare_string_16_bytes_same(DIFF, DIFF2); __ br(__ GT, LARGE_LOOP_PREFETCH); __ cbz(cnt2, LAST_CHECK_AND_LENGTH_DIFF); // no more chars left? - // less than 16 bytes left? - __ subs(cnt2, cnt2, isLL ? 16 : 8); - __ br(__ LT, TAIL); } + // less than 16 bytes left? + __ subs(cnt2, cnt2, isLL ? 16 : 8); + __ br(__ LT, TAIL); __ bind(SMALL_LOOP); compare_string_16_bytes_same(DIFF, DIFF2); __ subs(cnt2, cnt2, isLL ? 16 : 8); @@ -4338,6 +4309,7 @@ __ ldr(ch1, Address(str1)); // Read whole register from str2. It is safe, because length >=8 here __ ldr(ch2, Address(str2)); + __ sub(cnt2, cnt2, cnt1); __ andr(first, ch1, str1_isL ? 0xFF : 0xFFFF); if (str1_isL != str2_isL) { __ eor(v0, __ T16B, v0, v0); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/arm/assembler_arm_32.hpp --- a/src/hotspot/cpu/arm/assembler_arm_32.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/arm/assembler_arm_32.hpp Thu May 23 11:07:37 2019 +0100 @@ -199,6 +199,14 @@ static const int LogInstructionSize = 2; static const int InstructionSize = 1 << LogInstructionSize; + //---< calculate length of instruction >--- + // We just use the values set above. + // instruction must start at passed address + static unsigned int instr_len(unsigned char *instr) { return InstructionSize; } + + //---< longest instructions >--- + static unsigned int instr_maxlen() { return InstructionSize; } + static inline AsmCondition inverse(AsmCondition cond) { assert ((cond != al) && (cond != nv), "AL and NV conditions cannot be inversed"); return (AsmCondition)((int)cond ^ 1); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/arm/c2_globals_arm.hpp --- a/src/hotspot/cpu/arm/c2_globals_arm.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/arm/c2_globals_arm.hpp Thu May 23 11:07:37 2019 +0100 @@ -97,7 +97,7 @@ // Ergonomics related flags define_pd_global(uint64_t, MaxRAM, 4ULL*G); #endif -define_pd_global(uintx, CodeCacheMinBlockLength, 4); +define_pd_global(uintx, CodeCacheMinBlockLength, 6); define_pd_global(size_t, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/arm/disassembler_arm.hpp --- a/src/hotspot/cpu/arm/disassembler_arm.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/arm/disassembler_arm.hpp Thu May 23 11:07:37 2019 +0100 @@ -33,4 +33,24 @@ return ""; } + // Returns address of n-th instruction preceding addr, + // NULL if no preceding instruction can be found. + // On ARM, we assume a constant instruction length. + // It might be beneficial to check "is_readable" as we do on ppc and s390. + static address find_prev_instr(address addr, int n_instr) { + return addr - Assembler::InstructionSize*n_instr; + } + + // special-case instruction decoding. + // There may be cases where the binutils disassembler doesn't do + // the perfect job. In those cases, decode_instruction0 may kick in + // and do it right. + // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)" + static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL) { + return here; + } + + // platform-specific instruction annotations (like value of loaded constants) + static void annotate(address pc, outputStream* st) { }; + #endif // CPU_ARM_DISASSEMBLER_ARM_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/ppc/assembler_ppc.hpp --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp Thu May 23 11:07:37 2019 +0100 @@ -929,11 +929,13 @@ enum Predict { pt = 1, pn = 0 }; // pt = predict taken - // Instruction must start at passed address. - static int instr_len(unsigned char *instr) { return BytesPerInstWord; } + //---< calculate length of instruction >--- + // With PPC64 being a RISC architecture, this always is BytesPerInstWord + // instruction must start at passed address + static unsigned int instr_len(unsigned char *instr) { return BytesPerInstWord; } - // longest instructions - static int instr_maxlen() { return BytesPerInstWord; } + //---< longest instructions >--- + static unsigned int instr_maxlen() { return BytesPerInstWord; } // Test if x is within signed immediate range for nbits. static bool is_simm(int x, unsigned int nbits) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/ppc/c2_globals_ppc.hpp --- a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp Thu May 23 11:07:37 2019 +0100 @@ -1,6 +1,6 @@ /* * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2018 SAP SE. All rights reserved. + * Copyright (c) 2012, 2019 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 @@ -90,7 +90,7 @@ // Ergonomics related flags define_pd_global(uint64_t, MaxRAM, 128ULL*G); -define_pd_global(uintx, CodeCacheMinBlockLength, 4); +define_pd_global(uintx, CodeCacheMinBlockLength, 6); define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, true); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/ppc/c2_init_ppc.cpp --- a/src/hotspot/cpu/ppc/c2_init_ppc.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/ppc/c2_init_ppc.cpp Thu May 23 11:07:37 2019 +0100 @@ -36,18 +36,18 @@ // Power7 and later. if (PowerArchitecturePPC64 > 6) { if (FLAG_IS_DEFAULT(UsePopCountInstruction)) { - FLAG_SET_ERGO(bool, UsePopCountInstruction, true); + FLAG_SET_ERGO(UsePopCountInstruction, true); } } if (PowerArchitecturePPC64 == 6) { if (FLAG_IS_DEFAULT(InsertEndGroupPPC64)) { - FLAG_SET_ERGO(bool, InsertEndGroupPPC64, true); + FLAG_SET_ERGO(InsertEndGroupPPC64, true); } } if (!VM_Version::has_isel() && FLAG_IS_DEFAULT(ConditionalMoveLimit)) { - FLAG_SET_ERGO(intx, ConditionalMoveLimit, 0); + FLAG_SET_ERGO(ConditionalMoveLimit, 0); } if (OptimizeFill) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/ppc/disassembler_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/cpu/ppc/disassembler_ppc.cpp Thu May 23 11:07:37 2019 +0100 @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "asm/macroAssembler.inline.hpp" +#include "code/codeCache.hpp" +#include "compiler/disassembler.hpp" +#include "depChecker_ppc.hpp" +#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp" +#include "gc/cms/parOopClosures.inline.hpp" +#include "gc/shared/collectedHeap.hpp" +#include "gc/shared/cardTableBarrierSet.hpp" +#include "gc/shared/genOopClosures.inline.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/stubCodeGenerator.hpp" +#include "runtime/stubRoutines.hpp" + +// Macro to print instruction bits. +// numbering of instruction bits on ppc64 is (highest) 0 1 ... 30 31 (lowest). +#define print_instruction_bits(st, instruction, start_bit, end_bit) \ + { assert((start_bit) <= (end_bit), "sanity check"); \ + for (int i=(31-(start_bit));i>=(31-(end_bit));i--) { \ + (st)->print("%d", ((instruction) >> i) & 0x1); \ + } \ + } + +// Macro to decode "bo" instruction bits. +#define print_decoded_bo_bits(env, instruction, end_bit) \ + { int bo_bits = (instruction >> (31 - (end_bit))) & 0x1f; \ + if ( ((bo_bits & 0x1c) == 0x4) || ((bo_bits & 0x1c) == 0xc) ) { \ + switch (bo_bits & 0x3) { \ + case (0 << 1) | (0 << 0): env->print("[no_hint]"); break; \ + case (0 << 1) | (1 << 0): env->print("[reserved]"); break; \ + case (1 << 1) | (0 << 0): env->print("[not_taken]"); break; \ + case (1 << 1) | (1 << 0): env->print("[taken]"); break; \ + default: break; \ + } \ + } else if ( ((bo_bits & 0x14) == 0x10) ) { \ + switch (bo_bits & 0x9) { \ + case (0 << 3) | (0 << 0): env->print("[no_hint]"); break; \ + case (0 << 3) | (1 << 0): env->print("[reserved]"); break; \ + case (1 << 3) | (0 << 0): env->print("[not_taken]"); break; \ + case (1 << 3) | (1 << 0): env->print("[taken]"); break; \ + default: break; \ + } \ + } \ + } + +// Macro to decode "bh" instruction bits. +#define print_decoded_bh_bits(env, instruction, end_bit, is_bclr) \ + { int bh_bits = (instruction >> (31 - (end_bit))) & 0x3; \ + if (is_bclr) { \ + switch (bh_bits) { \ + case (0 << 1) | (0 << 0): env->print("[subroutine_return]"); break; \ + case (0 << 1) | (1 << 0): env->print("[not_return_but_same]"); break; \ + case (1 << 1) | (0 << 0): env->print("[reserved]"); break; \ + case (1 << 1) | (1 << 0): env->print("[not_predictable]"); break; \ + default: break; \ + } \ + } else { \ + switch (bh_bits) { \ + case (0 << 1) | (0 << 0): env->print("[not_return_but_same]"); break; \ + case (0 << 1) | (1 << 0): env->print("[reserved]"); break; \ + case (1 << 1) | (0 << 0): env->print("[reserved]"); break; \ + case (1 << 1) | (1 << 0): env->print("[not_predictable]"); break; \ + default: break; \ + } \ + } \ + } + +address Disassembler::find_prev_instr(address here, int n_instr) { + if (!os::is_readable_pointer(here)) return NULL; // obviously a bad location to decode + + // Find most distant possible starting point. + // Narrow down because we don't want to SEGV while printing. + address start = here - n_instr*Assembler::instr_maxlen(); // starting point can't be further away. + while ((start < here) && !os::is_readable_range(start, here)) { + start = align_down(start, os::min_page_size()) + os::min_page_size(); + } + if (start >= here) { + // Strange. Can only happen with here on page boundary. + return NULL; + } + return start; +} + +address Disassembler::decode_instruction0(address here, outputStream * st, address virtual_begin ) { + if (is_abstract()) { + // The disassembler library was not loaded (yet), + // use AbstractDisassembler's decode method. + return decode_instruction_abstract(here, st, Assembler::instr_len(here), Assembler::instr_maxlen()); + } + + // Currently, "special decoding" doesn't work when decoding error files. + // When decoding an instruction from a hs_err file, the given + // instruction address 'start' points to the instruction's virtual address + // which is not equal to the address where the instruction is located. + // Therefore, we will either crash or decode garbage. + if (is_decode_error_file()) { + return here; + } + + //---< Decode some well-known "instructions" >--- + + address next; + uint32_t instruction = *(uint32_t*)here; + + // Align at next tab position. + const uint tabspacing = 8; + const uint pos = st->position(); + const uint aligned_pos = ((pos+tabspacing-1)/tabspacing)*tabspacing; + st->fill_to(aligned_pos); + + if (instruction == 0x0) { + st->print("illtrap .data 0x0"); + next = here + Assembler::instr_len(here); + } else if (instruction == 0xbadbabe) { + st->print(".data 0xbadbabe"); + next = here + Assembler::instr_len(here); + } else if (Assembler::is_endgroup(instruction)) { + st->print("endgroup"); + next = here + Assembler::instr_len(here); + } else { + next = here; + } + return next; +} + +// print annotations (instruction control bits) +void Disassembler::annotate(address here, outputStream* st) { + // Currently, annotation doesn't work when decoding error files. + // When decoding an instruction from a hs_err file, the given + // instruction address 'start' points to the instruction's virtual address + // which is not equal to the address where the instruction is located. + // Therefore, we will either crash or decode garbage. + if (is_decode_error_file()) { + return; + } + + uint32_t instruction = *(uint32_t*)here; + + // Align at next tab position. + const uint tabspacing = 8; + const uint pos = st->position(); + const uint aligned_pos = ((pos+tabspacing-1)/tabspacing)*tabspacing; + + if (MacroAssembler::is_bcxx(instruction)) { + st->print(",bo=0b"); + print_instruction_bits(st, instruction, 6, 10); + print_decoded_bo_bits(st, instruction, 10); + } else if (MacroAssembler::is_bctr(instruction) || + MacroAssembler::is_bctrl(instruction) || + MacroAssembler::is_bclr(instruction)) { + st->fill_to(aligned_pos); + st->print("bo=0b"); + print_instruction_bits(st, instruction, 6, 10); + print_decoded_bo_bits(st, instruction, 10); + st->print(",bh=0b"); + print_instruction_bits(st, instruction, 19, 20); + print_decoded_bh_bits(st, instruction, 20, + !(MacroAssembler::is_bctr(instruction) || + MacroAssembler::is_bctrl(instruction))); + } else if (MacroAssembler::is_trap_should_not_reach_here(instruction)) { + st->fill_to(aligned_pos + tabspacing); + st->print(";trap: should not reach here"); + } else if (MacroAssembler::is_trap_null_check(instruction)) { + st->fill_to(aligned_pos + tabspacing); + st->print(";trap: null check"); + } else if (MacroAssembler::is_trap_range_check(instruction)) { + st->fill_to(aligned_pos + tabspacing); + st->print(";trap: range check"); + } else if (MacroAssembler::is_trap_ic_miss_check(instruction)) { + st->fill_to(aligned_pos + tabspacing); + st->print(";trap: ic miss check"); + } else if (MacroAssembler::is_trap_zombie_not_entrant(instruction)) { + st->fill_to(aligned_pos + tabspacing); + st->print(";trap: zombie"); + } +} diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/ppc/disassembler_ppc.hpp --- a/src/hotspot/cpu/ppc/disassembler_ppc.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/ppc/disassembler_ppc.hpp Thu May 23 11:07:37 2019 +0100 @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2013 SAP SE. All rights reserved. + * Copyright (c) 2012, 2019 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 @@ -34,4 +34,23 @@ return "ppc64"; } + // Find preceding instruction. + // + // Starting at the passed location, the n-th preceding (towards lower addresses) + // location is searched, the contents of which - if interpreted as + // instructions - has the passed location as n-th successor. + // - If no such location exists, NULL is returned. The caller should then + // terminate its search and react properly. + static address find_prev_instr(address here, int n_instr); + + // special-case instruction decoding. + // There may be cases where the binutils disassembler doesn't do + // the perfect job. In those cases, decode_instruction0 may kick in + // and do it right. + // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)" + static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL); + + // platform-specific instruction annotations (like value of loaded constants) + static void annotate(address pc, outputStream* st); + #endif // CPU_PPC_DISASSEMBLER_PPC_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/ppc/vm_version_ppc.cpp --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp Thu May 23 11:07:37 2019 +0100 @@ -67,17 +67,17 @@ // If PowerArchitecturePPC64 hasn't been specified explicitly determine from features. if (FLAG_IS_DEFAULT(PowerArchitecturePPC64)) { if (VM_Version::has_darn()) { - FLAG_SET_ERGO(uintx, PowerArchitecturePPC64, 9); + FLAG_SET_ERGO(PowerArchitecturePPC64, 9); } else if (VM_Version::has_lqarx()) { - FLAG_SET_ERGO(uintx, PowerArchitecturePPC64, 8); + FLAG_SET_ERGO(PowerArchitecturePPC64, 8); } else if (VM_Version::has_popcntw()) { - FLAG_SET_ERGO(uintx, PowerArchitecturePPC64, 7); + FLAG_SET_ERGO(PowerArchitecturePPC64, 7); } else if (VM_Version::has_cmpb()) { - FLAG_SET_ERGO(uintx, PowerArchitecturePPC64, 6); + FLAG_SET_ERGO(PowerArchitecturePPC64, 6); } else if (VM_Version::has_popcntb()) { - FLAG_SET_ERGO(uintx, PowerArchitecturePPC64, 5); + FLAG_SET_ERGO(PowerArchitecturePPC64, 5); } else { - FLAG_SET_ERGO(uintx, PowerArchitecturePPC64, 0); + FLAG_SET_ERGO(PowerArchitecturePPC64, 0); } } @@ -103,15 +103,15 @@ MSG(TrapBasedICMissChecks); MSG(TrapBasedNotEntrantChecks); MSG(TrapBasedNullChecks); - FLAG_SET_ERGO(bool, TrapBasedNotEntrantChecks, false); - FLAG_SET_ERGO(bool, TrapBasedNullChecks, false); - FLAG_SET_ERGO(bool, TrapBasedICMissChecks, false); + FLAG_SET_ERGO(TrapBasedNotEntrantChecks, false); + FLAG_SET_ERGO(TrapBasedNullChecks, false); + FLAG_SET_ERGO(TrapBasedICMissChecks, false); } #ifdef COMPILER2 if (!UseSIGTRAP) { MSG(TrapBasedRangeChecks); - FLAG_SET_ERGO(bool, TrapBasedRangeChecks, false); + FLAG_SET_ERGO(TrapBasedRangeChecks, false); } // On Power6 test for section size. @@ -123,7 +123,7 @@ if (PowerArchitecturePPC64 >= 8) { if (FLAG_IS_DEFAULT(SuperwordUseVSX)) { - FLAG_SET_ERGO(bool, SuperwordUseVSX, true); + FLAG_SET_ERGO(SuperwordUseVSX, true); } } else { if (SuperwordUseVSX) { @@ -135,10 +135,10 @@ if (PowerArchitecturePPC64 >= 9) { if (FLAG_IS_DEFAULT(UseCountTrailingZerosInstructionsPPC64)) { - FLAG_SET_ERGO(bool, UseCountTrailingZerosInstructionsPPC64, true); + FLAG_SET_ERGO(UseCountTrailingZerosInstructionsPPC64, true); } if (FLAG_IS_DEFAULT(UseCharacterCompareIntrinsics)) { - FLAG_SET_ERGO(bool, UseCharacterCompareIntrinsics, true); + FLAG_SET_ERGO(UseCharacterCompareIntrinsics, true); } } else { if (UseCountTrailingZerosInstructionsPPC64) { @@ -708,6 +708,8 @@ uint32_t *code_end = (uint32_t *)a->pc(); a->flush(); + cb.insts()->set_end((u_char*)code_end); + double loop1_seconds,loop2_seconds, rel_diff; uint64_t start1, stop1; @@ -725,10 +727,11 @@ rel_diff = (loop2_seconds - loop1_seconds) / loop1_seconds *100; - if (PrintAssembly) { + if (PrintAssembly || PrintStubCode) { ttyLocker ttyl; tty->print_cr("Decoding section size detection stub at " INTPTR_FORMAT " before execution:", p2i(code)); - Disassembler::decode((u_char*)code, (u_char*)code_end, tty); + // Use existing decode function. This enables the [MachCode] format which is needed to DecodeErrorFile. + Disassembler::decode(&cb, (u_char*)code, (u_char*)code_end, tty); tty->print_cr("Time loop1 :%f", loop1_seconds); tty->print_cr("Time loop2 :%f", loop2_seconds); tty->print_cr("(time2 - time1) / time1 = %f %%", rel_diff); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/s390/assembler_s390.hpp --- a/src/hotspot/cpu/s390/assembler_s390.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/s390/assembler_s390.hpp Thu May 23 11:07:37 2019 +0100 @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2017 SAP SE. All rights reserved. + * Copyright (c) 2016, 2019 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 @@ -1531,16 +1531,16 @@ //----------------------------------------------- // Calculate length of instruction. - static int instr_len(unsigned char *instr); + static unsigned int instr_len(unsigned char *instr); // Longest instructions are 6 bytes on z/Architecture. - static int instr_maxlen() { return 6; } + static unsigned int instr_maxlen() { return 6; } // Average instruction is 4 bytes on z/Architecture (just a guess). - static int instr_avglen() { return 4; } + static unsigned int instr_avglen() { return 4; } // Shortest instructions are 2 bytes on z/Architecture. - static int instr_minlen() { return 2; } + static unsigned int instr_minlen() { return 2; } // Move instruction at pc right-justified into passed long int. // Return instr len in bytes as function result. diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/s390/assembler_s390.inline.hpp --- a/src/hotspot/cpu/s390/assembler_s390.inline.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/s390/assembler_s390.inline.hpp Thu May 23 11:07:37 2019 +0100 @@ -1344,7 +1344,7 @@ // Instruction must start at passed address. // Extra check for illtraps with ID. -inline int Assembler::instr_len(unsigned char *instr) { +inline unsigned int Assembler::instr_len(unsigned char *instr) { switch ((*instr) >> 6) { case 0: return 2; case 1: // fallthru diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/s390/disassembler_s390.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/cpu/s390/disassembler_s390.cpp Thu May 23 11:07:37 2019 +0100 @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "asm/assembler.inline.hpp" +#include "asm/macroAssembler.hpp" +#include "code/codeCache.hpp" +#include "compiler/disassembler.hpp" +#include "depChecker_s390.hpp" +#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp" +#include "gc/cms/parOopClosures.inline.hpp" +#include "gc/shared/collectedHeap.hpp" +#include "gc/shared/cardTableBarrierSet.hpp" +#include "gc/shared/genOopClosures.inline.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/stubCodeGenerator.hpp" +#include "runtime/stubRoutines.hpp" +#include "utilities/align.hpp" + +// List of all major opcodes, as of +// Principles of Operation, Eleventh Edition, March 2015 +bool Disassembler::valid_opcodes[] = +{ true, true, false, false, true, true, true, true, // 0x00..07 + false, false, true, true, true, true, true, true, // 0x08..0f + true, true, true, true, true, true, true, true, // 0x10..17 + true, true, true, true, true, true, true, true, // 0x18..1f + true, true, true, true, true, true, true, true, // 0x20..27 + true, true, true, true, true, true, true, true, // 0x28..2f + true, true, true, true, true, true, true, true, // 0x30..37 + true, true, true, true, true, true, true, true, // 0x38..3f + true, true, true, true, true, true, true, true, // 0x40..47 + true, true, true, true, true, true, true, true, // 0x48..4f + true, true, false, false, true, true, true, true, // 0x50..57 + true, true, true, true, true, true, true, true, // 0x58..5f + true, false, false, false, false, false, false, true, // 0x60..67 + true, true, true, true, true, true, true, true, // 0x68..6f + true, true, false, false, false, false, false, false, // 0x70..77 + true, true, true, true, true, true, true, true, // 0x78..7f + true, false, true, true, true, true, true, true, // 0x80..87 + true, true, true, true, true, true, true, true, // 0x88..8f + true, true, true, true, true, true, true, true, // 0x90..97 + true, true, true, true, false, false, false, false, // 0x98..9f + false, false, false, false, false, true, false, true, // 0xa0..a7 + true, true, false, false, true, true, true, true, // 0xa8..af + false, true, true, true, false, false, true, true, // 0xb0..b7 + false, true, true, true, false, true, true, true, // 0xb8..bf + true, false, true, false, true, false, true, false, // 0xc0..c7 + true, false, false, false, true, false, false, false, // 0xc8..cf + true, true, true, true, true, true, true, true, // 0xd0..d7 + false, true, true, true, true, true, true, true, // 0xd8..df + false, true, true, true, false, true, false, true, // 0xe0..e7 + true, true, true, true, true, true, true, true, // 0xe8..ef + true, true, true, true, false, false, false, false, // 0xf0..f7 + true, true, true, true, true, true, false, false, // 0xf8..ff +}; +// Check for valid opcodes. +// +// The major opcode (one byte) at the passed location is inspected. +// If the opcode found is assigned, the function returns true, false otherwise. +// The true indication is not reliable. It may well be that the major opcode is +// assigned, but there exists a minor opcode field in the instruction which +// which has unassigned values. +bool Disassembler::is_valid_opcode_at(address here) { + return valid_opcodes[*here]; +} + +// This method does plain instruction decoding, no frills. +// It may be called before the binutils disassembler kicks in +// to handle special cases the binutils disassembler does not. +// Instruction address, comments, and the like have to be output by caller. +address Disassembler::decode_instruction0(address here, outputStream * st, address virtual_begin) { + if (is_abstract()) { + // The disassembler library was not loaded (yet), + // use AbstractDisassembler's decode-method. + return decode_instruction_abstract(here, st, Assembler::instr_len(here), Assembler::instr_maxlen()); + } + + // Currently, "special decoding" doesn't work when decoding error files. + // When decoding an instruction from a hs_err file, the given + // instruction address 'start' points to the instruction's virtual address + // which is not equal to the address where the instruction is located. + // Therefore, we will either crash or decode garbage. + if (is_decode_error_file()) { + return here; + } + + //---< Decode some well-known "instructions" >--- + + address next; + uint16_t instruction_2bytes = *(uint16_t*)here; + + if (Assembler::is_z_nop((long)instruction_2bytes)) { +#if 1 + st->print("nop "); // fill up to operand column, leads to better code comment alignment + next = here + 2; +#else + // Compact disassembler output. Does not work the easy way. + // Currently unusable, search does not terminate, risk of crash. + // TODO: rework required. + // Terminate search loop when reaching CodeEntryAlignment-aligned offset + // or, at the latest, when reaching the next page boundary. + int n_nops = 0; + while(is_same_page(here, here+2*n_nops) && Assembler::is_z_nop((long)instruction_2bytes)) { + n_nops++; + instruction_2bytes = *(uint16_t*)(here+2*n_nops); + } + if (n_nops <= 4) { // do not group few subsequent nops + st->print("nop "); // fill up to operand column, leads to better code comment alignment + next = here + 2; + } else { + st->print("nop count=%d", n_nops); + next = here + 2*n_nops; + } +#endif + } else if (Assembler::is_z_sync((long)instruction_2bytes)) { + // Specific names. Make use of lightweight sync. + st->print("sync "); + if (Assembler::is_z_sync_full((long)instruction_2bytes) ) st->print("heavyweight"); + if (Assembler::is_z_sync_light((long)instruction_2bytes)) st->print("lightweight"); + next = here + 2; + } else if (instruction_2bytes == 0x0000) { +#if 1 + st->print("illtrap .nodata"); + next = here + 2; +#else + // Compact disassembler output. Does not work the easy way. + // Currently unusable, search does not terminate, risk of crash. + // TODO: rework required. + // Terminate search loop when reaching CodeEntryAlignment-aligned offset + // or, at the latest, when reaching the next page boundary. + int n_traps = 0; + while(is_same_page(here, here+2*n_nops) && (instruction_2bytes == 0x0000)) { + n_traps++; + instruction_2bytes = *(uint16_t*)(here+2*n_traps); + } + if (n_traps <= 4) { // do not group few subsequent illtraps + st->print("illtrap .nodata"); + next = here + 2; + } else { + st->print("illtrap .nodata count=%d", n_traps); + next = here + 2*n_traps; + } +#endif + } else if ((instruction_2bytes & 0xff00) == 0x0000) { + st->print("illtrap .data 0x%2.2x", instruction_2bytes & 0x00ff); + next = here + 2; + } else { + next = here; + } + return next; +} + +// Count the instructions contained in the range [begin..end). +// The range must exactly contain the instructions, i.e. +// - the first instruction starts @begin +// - the last instruction ends @(end-1) +// The caller has to make sure that the given range is readable. +// This function performs no safety checks! +// Return value: +// - The number of instructions, if there was exact containment. +// - If there is no exact containment, a negative value is returned. +// Its absolute value is the number of instructions from begin to end, +// where the last instruction counted runs over the range end. +// - 0 (zero) is returned if there was a parameter error +// (inverted range, bad starting point). +int Disassembler::count_instr(address begin, address end) { + if (end < begin+2) return 0; // no instructions in range + if (!Disassembler::is_valid_opcode_at(begin)) return 0; // bad starting point + + address p = begin; + int n = 0; + while(p < end) { + p += Assembler::instr_len(p); + n++; + } + return (p == end) ? n : -n; +} + +// Find preceding instruction. +// +// Starting at the passed location, the n-th preceding (towards lower addresses) +// instruction is searched. With variable length instructions, there may be +// more than one solution, or no solution at all (if the passed location +// does not point to the start of an instruction or if the storage area +// does not contain instructions at all). +// instructions - has the passed location as n-th successor. +// - If multiple such locations exist between (here-n*instr_maxlen()) and here, +// the most distant location is selected. +// - If no such location exists, NULL is returned. The caller should then +// terminate its search and react properly. +// Must be placed here in disassembler_s390.cpp. It does not compile +// in the header. There the class 'Assembler' is not available. +address Disassembler::find_prev_instr(address here, int n_instr) { + if (!os::is_readable_pointer(here)) return NULL; // obviously a bad location to decode + + // Find most distant possible starting point. + // Narrow down because we don't want to SEGV while printing. + address start = here - n_instr*Assembler::instr_maxlen(); // starting point can't be further away. + while ((start < here) && !os::is_readable_range(start, here)) { + start = align_down(start, os::min_page_size()) + os::min_page_size(); + } + if (start >= here) { + // Strange. Can only happen with here on page boundary. + return NULL; + } + + //---< Find a starting point >--- + int i_count = 0; + while ((start < here) && ((i_count = count_instr(start, here)) <= 0)) start += 2; + if (i_count == 0) return NULL; // There is something seriously wrong + + //---< Narrow down distance (estimate was too large) >--- + while(i_count-- > n_instr) { + start += Assembler::instr_len(start); + } + assert(n_instr >= count_instr(start, here), "just checking"); + return start; +} + + +// Print annotations (value of loaded constant) +void Disassembler::annotate(address here, outputStream* st) { + // Currently, annotation doesn't work when decoding error files. + // When decoding an instruction from a hs_err file, the given + // instruction address 'start' points to the instruction's virtual address + // which is not equal to the address where the instruction is located. + // Therefore, we will either crash or decode garbage. + if (is_decode_error_file()) { + return; + } + + if (MacroAssembler::is_load_const(here)) { + long value = MacroAssembler::get_const(here); + const int tsize = 8; + + st->fill_to(60); + st->print(";const %p | %ld | %23.15e", (void *)value, value, (double)value); + } +} diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/s390/disassembler_s390.hpp --- a/src/hotspot/cpu/s390/disassembler_s390.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/s390/disassembler_s390.hpp Thu May 23 11:07:37 2019 +0100 @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016 SAP SE. All rights reserved. + * Copyright (c) 2016, 2019 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 @@ -27,11 +27,36 @@ #define CPU_S390_DISASSEMBLER_S390_HPP static int pd_instruction_alignment() { - return 1; + return 2; } static const char* pd_cpu_opts() { - return "zarch"; + return "s390"; } + static bool valid_opcodes[256]; + static bool is_valid_opcode_at(address here); + + // Find preceding instruction. + // + // Starting at the passed location, the n-th preceding (towards lower addresses) + // location is searched, the contents of which - if interpreted as + // instructions - has the passed location as n-th successor. + // - If multiple such locations exist between (here-n*instr_maxlen()) and here, + // the most distant location is selected. + // - If no such location exists, NULL is returned. The caller should then + // terminate its search and react properly. + static address find_prev_instr(address here, int n_instr); + static int count_instr(address begin, address end); + + // special-case instruction decoding. + // There may be cases where the binutils disassembler doesn't do + // the perfect job. In those cases, decode_instruction0 may kick in + // and do it right. + // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)" + static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL); + + // platform-specific instruction annotations (like value of loaded constants) + static void annotate(address pc, outputStream* st); + #endif // CPU_S390_DISASSEMBLER_S390_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/s390/s390.ad --- a/src/hotspot/cpu/s390/s390.ad Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/s390/s390.ad Thu May 23 11:07:37 2019 +0100 @@ -1,6 +1,6 @@ // -// Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. -// Copyright (c) 2017, SAP SE. All rights reserved. +// Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2017, 2019 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 @@ -1388,7 +1388,6 @@ __ z_br(R1_ic_miss_stub_addr); __ bind(valid); } - } uint MachUEPNode::size(PhaseRegAlloc *ra_) const { @@ -4318,7 +4317,7 @@ match(Set dst src); ins_cost(DEFAULT_COST); size(6); - format %{ "LGFI $dst,$src\t # (int)" %} + format %{ "LGFI $dst,$src\t # (int)" %} ins_encode %{ __ z_lgfi($dst$$Register, $src$$constant); %} // Sign-extend to 64 bit, it's at no cost. ins_pipe(pipe_class_dummy); %} @@ -4327,7 +4326,7 @@ match(Set dst src); ins_cost(DEFAULT_COST_LOW); size(4); - format %{ "LGHI $dst,$src\t # (int)" %} + format %{ "LGHI $dst,$src\t # (int)" %} ins_encode %{ __ z_lghi($dst$$Register, $src$$constant); %} // Sign-extend to 64 bit, it's at no cost. ins_pipe(pipe_class_dummy); %} @@ -4723,7 +4722,7 @@ match(Set dst (LoadN mem)); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); - format %{ "LoadN $dst,$mem\t# (cOop)" %} + format %{ "LoadN $dst,$mem\t # (cOop)" %} opcode(LLGF_ZOPC, LLGF_ZOPC); ins_encode(z_form_rt_mem_opt(dst, mem)); ins_pipe(pipe_class_dummy); @@ -4734,7 +4733,7 @@ match(Set dst (LoadNKlass mem)); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); - format %{ "LoadNKlass $dst,$mem\t# (klass cOop)" %} + format %{ "LoadNKlass $dst,$mem\t # (klass cOop)" %} opcode(LLGF_ZOPC, LLGF_ZOPC); ins_encode(z_form_rt_mem_opt(dst, mem)); ins_pipe(pipe_class_dummy); @@ -4787,7 +4786,7 @@ predicate(false && (CompressedOops::base()==NULL)&&(CompressedOops::shift()==0)); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); - format %{ "DecodeLoadN $dst,$mem\t# (cOop Load+Decode)" %} + format %{ "DecodeLoadN $dst,$mem\t # (cOop Load+Decode)" %} opcode(LLGF_ZOPC, LLGF_ZOPC); ins_encode(z_form_rt_mem_opt(dst, mem)); ins_pipe(pipe_class_dummy); @@ -4798,7 +4797,7 @@ predicate(false && (CompressedKlassPointers::base()==NULL)&&(CompressedKlassPointers::shift()==0)); ins_cost(MEMORY_REF_COST); size(Z_DISP3_SIZE); - format %{ "DecodeLoadNKlass $dst,$mem\t# (load/decode NKlass)" %} + format %{ "DecodeLoadNKlass $dst,$mem\t # (load/decode NKlass)" %} opcode(LLGF_ZOPC, LLGF_ZOPC); ins_encode(z_form_rt_mem_opt(dst, mem)); ins_pipe(pipe_class_dummy); @@ -4826,7 +4825,7 @@ predicate(CompressedOops::base() == NULL || !ExpandLoadingBaseDecode); ins_cost(MEMORY_REF_COST+3 * DEFAULT_COST + BRANCH_COST); // TODO: s390 port size(VARIABLE_SIZE); - format %{ "decodeN $dst,$src\t# (decode cOop)" %} + format %{ "decodeN $dst,$src\t # (decode cOop)" %} ins_encode %{ __ oop_decoder($dst$$Register, $src$$Register, true); %} ins_pipe(pipe_class_dummy); %} @@ -4850,7 +4849,7 @@ (CompressedOops::base()== NULL || !ExpandLoadingBaseDecode_NN)); ins_cost(MEMORY_REF_COST+2 * DEFAULT_COST); // TODO: s390 port size(VARIABLE_SIZE); - format %{ "decodeN $dst,$src\t# (decode cOop NN)" %} + format %{ "decodeN $dst,$src\t # (decode cOop NN)" %} ins_encode %{ __ oop_decoder($dst$$Register, $src$$Register, false); %} ins_pipe(pipe_class_dummy); %} @@ -4873,7 +4872,7 @@ effect(KILL cr); predicate(false); // TODO: s390 port size(VARIABLE_SIZE); - format %{ "decodeN $dst = ($src == 0) ? NULL : ($src << 3) + $base + pow2_offset\t# (decode cOop)" %} + format %{ "decodeN $dst = ($src == 0) ? NULL : ($src << 3) + $base + pow2_offset\t # (decode cOop)" %} ins_encode %{ __ oop_decoder($dst$$Register, $src$$Register, true, $base$$Register, (jlong)MacroAssembler::get_oop_base_pow2_offset((uint64_t)(intptr_t)CompressedOops::base())); @@ -4887,7 +4886,7 @@ effect(KILL cr); predicate(false); // TODO: s390 port size(VARIABLE_SIZE); - format %{ "decodeN $dst = ($src << 3) + $base + pow2_offset\t# (decode cOop)" %} + format %{ "decodeN $dst = ($src << 3) + $base + pow2_offset\t # (decode cOop)" %} ins_encode %{ __ oop_decoder($dst$$Register, $src$$Register, false, $base$$Register, (jlong)MacroAssembler::get_oop_base_pow2_offset((uint64_t)(intptr_t)CompressedOops::base())); @@ -4937,7 +4936,7 @@ !ExpandLoadingBaseEncode)); ins_cost(MEMORY_REF_COST+3 * DEFAULT_COST); // TODO: s390 port size(VARIABLE_SIZE); - format %{ "encodeP $dst,$src\t# (encode cOop)" %} + format %{ "encodeP $dst,$src\t # (encode cOop)" %} ins_encode %{ __ oop_encoder($dst$$Register, $src$$Register, true, Z_R1_scratch, -1, all_outs_are_Stores(this)); %} ins_pipe(pipe_class_dummy); %} @@ -4960,7 +4959,7 @@ !ExpandLoadingBaseEncode_NN)); ins_cost(MEMORY_REF_COST+3 * DEFAULT_COST); // TODO: s390 port size(VARIABLE_SIZE); - format %{ "encodeP $dst,$src\t# (encode cOop)" %} + format %{ "encodeP $dst,$src\t # (encode cOop)" %} ins_encode %{ __ oop_encoder($dst$$Register, $src$$Register, false, Z_R1_scratch, -1, all_outs_are_Stores(this)); %} ins_pipe(pipe_class_dummy); %} @@ -4972,7 +4971,7 @@ predicate(false); ins_cost(MEMORY_REF_COST+2 * DEFAULT_COST); // TODO: s390 port size(VARIABLE_SIZE); - format %{ "encodeP $dst = ($src>>3) +$base + pow2_offset\t# (encode cOop)" %} + format %{ "encodeP $dst = ($src>>3) +$base + pow2_offset\t # (encode cOop)" %} ins_encode %{ jlong offset = -(jlong)MacroAssembler::get_oop_base_pow2_offset (((uint64_t)(intptr_t)CompressedOops::base()) >> CompressedOops::shift()); @@ -4988,7 +4987,7 @@ predicate(false); ins_cost(MEMORY_REF_COST+2 * DEFAULT_COST); // TODO: s390 port size(VARIABLE_SIZE); - format %{ "encodeP $dst = ($src>>3) +$base + $pow2_offset\t# (encode cOop)" %} + format %{ "encodeP $dst = ($src>>3) +$base + $pow2_offset\t # (encode cOop)" %} ins_encode %{ __ oop_encoder($dst$$Register, $src$$Register, false, $base$$Register, $pow2_offset$$constant); %} ins_pipe(pipe_class_dummy); %} @@ -5041,7 +5040,7 @@ match(Set mem (StoreN mem src)); ins_cost(MEMORY_REF_COST); size(Z_DISP_SIZE); - format %{ "ST $src,$mem\t# (cOop)" %} + format %{ "ST $src,$mem\t # (cOop)" %} opcode(STY_ZOPC, ST_ZOPC); ins_encode(z_form_rt_mem_opt(src, mem)); ins_pipe(pipe_class_dummy); @@ -5052,7 +5051,7 @@ match(Set mem (StoreNKlass mem src)); ins_cost(MEMORY_REF_COST); size(Z_DISP_SIZE); - format %{ "ST $src,$mem\t# (cKlass)" %} + format %{ "ST $src,$mem\t # (cKlass)" %} opcode(STY_ZOPC, ST_ZOPC); ins_encode(z_form_rt_mem_opt(src, mem)); ins_pipe(pipe_class_dummy); @@ -5064,7 +5063,7 @@ match(Set cr (CmpN src1 src2)); ins_cost(DEFAULT_COST); size(2); - format %{ "CLR $src1,$src2\t# (cOop)" %} + format %{ "CLR $src1,$src2\t # (cOop)" %} opcode(CLR_ZOPC); ins_encode(z_rrform(src1, src2)); ins_pipe(pipe_class_dummy); @@ -5074,7 +5073,7 @@ match(Set cr (CmpN src1 src2)); ins_cost(DEFAULT_COST); size(6); - format %{ "CLFI $src1,$src2\t# (cOop) compare immediate narrow" %} + format %{ "CLFI $src1,$src2\t # (cOop) compare immediate narrow" %} ins_encode %{ AddressLiteral cOop = __ constant_oop_address((jobject)$src2$$constant); __ relocate(cOop.rspec(), 1); @@ -5087,7 +5086,7 @@ match(Set cr (CmpN src1 src2)); ins_cost(DEFAULT_COST); size(6); - format %{ "CLFI $src1,$src2\t# (NKlass) compare immediate narrow" %} + format %{ "CLFI $src1,$src2\t # (NKlass) compare immediate narrow" %} ins_encode %{ AddressLiteral NKlass = __ constant_metadata_address((Metadata*)$src2$$constant); __ relocate(NKlass.rspec(), 1); @@ -5100,7 +5099,7 @@ match(Set cr (CmpN src1 src2)); ins_cost(DEFAULT_COST); size(2); - format %{ "LTR $src1,$src2\t# (cOop) LTR because comparing against zero" %} + format %{ "LTR $src1,$src2\t # (cOop) LTR because comparing against zero" %} opcode(LTR_ZOPC); ins_encode(z_rrform(src1, src1)); ins_pipe(pipe_class_dummy); @@ -6795,7 +6794,7 @@ effect(KILL cr); // R1 is killed, too. ins_cost(3 * DEFAULT_COST); size(14); - format %{ "SLL $dst,$src,[$nbits] & 31\t# use RISC-like SLLG also for int" %} + format %{ "SLL $dst,$src,[$nbits] & 31\t # use RISC-like SLLG also for int" %} ins_encode %{ __ z_lgr(Z_R1_scratch, $nbits$$Register); __ z_nill(Z_R1_scratch, BitsPerJavaInteger-1); @@ -6809,7 +6808,7 @@ instruct sllI_reg_imm(iRegI dst, iRegI src, immI nbits) %{ match(Set dst (LShiftI src nbits)); size(6); - format %{ "SLL $dst,$src,$nbits\t# use RISC-like SLLG also for int" %} + format %{ "SLL $dst,$src,$nbits\t # use RISC-like SLLG also for int" %} ins_encode %{ int Nbit = $nbits$$constant; assert((Nbit & (BitsPerJavaInteger - 1)) == Nbit, "Check shift mask in ideal graph"); @@ -7125,7 +7124,7 @@ instruct overflowNegI_rReg(flagsReg cr, immI_0 zero, iRegI op2) %{ match(Set cr (OverflowSubI zero op2)); effect(DEF cr, USE op2); - format %{ "NEG $op2\t# overflow check int" %} + format %{ "NEG $op2\t # overflow check int" %} ins_encode %{ __ clear_reg(Z_R0_scratch, false, false); __ z_sr(Z_R0_scratch, $op2$$Register); @@ -7136,7 +7135,7 @@ instruct overflowNegL_rReg(flagsReg cr, immL_0 zero, iRegL op2) %{ match(Set cr (OverflowSubL zero op2)); effect(DEF cr, USE op2); - format %{ "NEGG $op2\t# overflow check long" %} + format %{ "NEGG $op2\t # overflow check long" %} ins_encode %{ __ clear_reg(Z_R0_scratch, true, false); __ z_sgr(Z_R0_scratch, $op2$$Register); @@ -9191,7 +9190,7 @@ effect(USE lbl); ins_cost(BRANCH_COST); size(4); - format %{ "branch_con_short,$cmp $cr, $lbl" %} + format %{ "branch_con_short,$cmp $lbl" %} ins_encode(z_enc_branch_con_short(cmp, lbl)); ins_pipe(pipe_class_dummy); // If set to 1 this indicates that the current instruction is a @@ -9213,7 +9212,7 @@ // Make more expensive to prefer compare_and_branch over separate instructions. ins_cost(2 * BRANCH_COST); size(6); - format %{ "branch_con_far,$cmp $cr, $lbl" %} + format %{ "branch_con_far,$cmp $lbl" %} ins_encode(z_enc_branch_con_far(cmp, lbl)); ins_pipe(pipe_class_dummy); // This is not a short variant of a branch, but the long variant.. @@ -9782,7 +9781,7 @@ match(TailCall jump_target method_oop); ins_cost(CALL_COST); size(2); - format %{ "Jmp $jump_target\t# $method_oop holds method oop" %} + format %{ "Jmp $jump_target\t # $method_oop holds method oop" %} ins_encode %{ __ z_br($jump_target$$Register); %} ins_pipe(pipe_class_dummy); %} @@ -10790,7 +10789,7 @@ predicate(UseByteReverseInstruction); // See Matcher::match_rule_supported ins_cost(DEFAULT_COST); size(4); - format %{ "LRVR $dst,$src\t# byte reverse int" %} + format %{ "LRVR $dst,$src\t # byte reverse int" %} opcode(LRVR_ZOPC); ins_encode(z_rreform(dst, src)); ins_pipe(pipe_class_dummy); @@ -10801,7 +10800,7 @@ predicate(UseByteReverseInstruction); // See Matcher::match_rule_supported ins_cost(DEFAULT_COST); // TODO: s390 port size(FIXED_SIZE); - format %{ "LRVGR $dst,$src\t# byte reverse long" %} + format %{ "LRVGR $dst,$src\t # byte reverse long" %} opcode(LRVGR_ZOPC); ins_encode(z_rreform(dst, src)); ins_pipe(pipe_class_dummy); @@ -10821,8 +10820,8 @@ effect(KILL tmp, KILL cr); ins_cost(3 * DEFAULT_COST); size(14); - format %{ "SLLG $dst,$src,32\t# no need to always count 32 zeroes first\n\t" - "IILH $dst,0x8000 \t# insert \"stop bit\" to force result 32 for zero src.\n\t" + format %{ "SLLG $dst,$src,32\t # no need to always count 32 zeroes first\n\t" + "IILH $dst,0x8000 \t # insert \"stop bit\" to force result 32 for zero src.\n\t" "FLOGR $dst,$dst" %} ins_encode %{ @@ -10859,7 +10858,7 @@ effect(KILL tmp, KILL cr); ins_cost(DEFAULT_COST); size(4); - format %{ "FLOGR $dst,$src \t# count leading zeros (long)\n\t" %} + format %{ "FLOGR $dst,$src \t # count leading zeros (long)\n\t" %} ins_encode %{ __ z_flogr($dst$$Register, $src$$Register); %} ins_pipe(pipe_class_dummy); %} @@ -10884,14 +10883,14 @@ effect(TEMP_DEF dst, TEMP tmp, KILL cr); ins_cost(8 * DEFAULT_COST); // TODO: s390 port size(FIXED_SIZE); // Emitted code depends on PreferLAoverADD being on/off. - format %{ "LLGFR $dst,$src \t# clear upper 32 bits (we are dealing with int)\n\t" - "LCGFR $tmp,$src \t# load 2's complement (32->64 bit)\n\t" - "AGHI $dst,-1 \t# tmp1 = src-1\n\t" - "AGHI $tmp,-1 \t# tmp2 = -src-1 = ~src\n\t" - "NGR $dst,$tmp \t# tmp3 = tmp1&tmp2\n\t" - "FLOGR $dst,$dst \t# count trailing zeros (int)\n\t" - "AHI $dst,-64 \t# tmp4 = 64-(trailing zeroes)-64\n\t" - "LCR $dst,$dst \t# res = -tmp4" + format %{ "LLGFR $dst,$src \t # clear upper 32 bits (we are dealing with int)\n\t" + "LCGFR $tmp,$src \t # load 2's complement (32->64 bit)\n\t" + "AGHI $dst,-1 \t # tmp1 = src-1\n\t" + "AGHI $tmp,-1 \t # tmp2 = -src-1 = ~src\n\t" + "NGR $dst,$tmp \t # tmp3 = tmp1&tmp2\n\t" + "FLOGR $dst,$dst \t # count trailing zeros (int)\n\t" + "AHI $dst,-64 \t # tmp4 = 64-(trailing zeroes)-64\n\t" + "LCR $dst,$dst \t # res = -tmp4" %} ins_encode %{ Register Rdst = $dst$$Register; @@ -10937,12 +10936,12 @@ effect(TEMP_DEF dst, KILL tmp, KILL cr); ins_cost(8 * DEFAULT_COST); // TODO: s390 port size(FIXED_SIZE); // Emitted code depends on PreferLAoverADD being on/off. - format %{ "LCGR $dst,$src \t# preserve src\n\t" - "NGR $dst,$src \t#" - "AGHI $dst,-1 \t# tmp1 = src-1\n\t" - "FLOGR $dst,$dst \t# count trailing zeros (long), kill $tmp\n\t" - "AHI $dst,-64 \t# tmp4 = 64-(trailing zeroes)-64\n\t" - "LCR $dst,$dst \t#" + format %{ "LCGR $dst,$src \t # preserve src\n\t" + "NGR $dst,$src \t #\n\t" + "AGHI $dst,-1 \t # tmp1 = src-1\n\t" + "FLOGR $dst,$dst \t # count trailing zeros (long), kill $tmp\n\t" + "AHI $dst,-64 \t # tmp4 = 64-(trailing zeroes)-64\n\t" + "LCR $dst,$dst \t #" %} ins_encode %{ Register Rdst = $dst$$Register; @@ -10969,7 +10968,7 @@ predicate(UsePopCountInstruction && VM_Version::has_PopCount()); ins_cost(DEFAULT_COST); size(24); - format %{ "POPCNT $dst,$src\t# pop count int" %} + format %{ "POPCNT $dst,$src\t # pop count int" %} ins_encode %{ Register Rdst = $dst$$Register; Register Rsrc = $src$$Register; @@ -10996,7 +10995,7 @@ predicate(UsePopCountInstruction && VM_Version::has_PopCount()); ins_cost(DEFAULT_COST); // TODO: s390 port size(FIXED_SIZE); - format %{ "POPCNT $dst,$src\t# pop count long" %} + format %{ "POPCNT $dst,$src\t # pop count long" %} ins_encode %{ Register Rdst = $dst$$Register; Register Rsrc = $src$$Register; diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/s390/vm_version_s390.cpp --- a/src/hotspot/cpu/s390/vm_version_s390.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp Thu May 23 11:07:37 2019 +0100 @@ -804,6 +804,8 @@ address code_end = a->pc(); a->flush(); + cbuf.insts()->set_end(code_end); + // Print the detection code. bool printVerbose = Verbose || PrintAssembly || PrintStubCode; if (printVerbose) { @@ -812,8 +814,8 @@ tty->print_cr("Stub length is %ld bytes, codebuffer reserves %d bytes, %ld bytes spare.", code_end-code, cbuf_size, cbuf_size-(code_end-code)); - // Use existing decode function. This enables the [Code] format which is needed to DecodeErrorFile. - Disassembler::decode((u_char*)code, (u_char*)code_end, tty); + // Use existing decode function. This enables the [MachCode] format which is needed to DecodeErrorFile. + Disassembler::decode(&cbuf, code, code_end, tty); } // Prepare for detection code execution and clear work buffer. diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/sparc/assembler_sparc.hpp --- a/src/hotspot/cpu/sparc/assembler_sparc.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/sparc/assembler_sparc.hpp Thu May 23 11:07:37 2019 +0100 @@ -335,6 +335,14 @@ Lookaside = 1 << 4 }; + //---< calculate length of instruction >--- + // With SPARC being a RISC architecture, this always is BytesPerInstWord + // instruction must start at passed address + static unsigned int instr_len(unsigned char *instr) { return BytesPerInstWord; } + + //---< longest instructions >--- + static unsigned int instr_maxlen() { return BytesPerInstWord; } + static bool is_in_wdisp_range(address a, address b, int nbits) { intptr_t d = intptr_t(b) - intptr_t(a); return is_simm(d, nbits + 2); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/sparc/c2_globals_sparc.hpp --- a/src/hotspot/cpu/sparc/c2_globals_sparc.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/sparc/c2_globals_sparc.hpp Thu May 23 11:07:37 2019 +0100 @@ -80,7 +80,7 @@ // Ergonomics related flags define_pd_global(uint64_t,MaxRAM, 128ULL*G); -define_pd_global(uintx, CodeCacheMinBlockLength, 4); +define_pd_global(uintx, CodeCacheMinBlockLength, 6); define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on sparc. diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/sparc/disassembler_sparc.hpp --- a/src/hotspot/cpu/sparc/disassembler_sparc.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/sparc/disassembler_sparc.hpp Thu May 23 11:07:37 2019 +0100 @@ -33,4 +33,24 @@ return "v9only"; } + // Returns address of n-th instruction preceding addr, + // NULL if no preceding instruction can be found. + // With SPARC being a RISC architecture, this always is BytesPerInstWord + // It might be beneficial to check "is_readable" as we do on ppc and s390. + static address find_prev_instr(address addr, int n_instr) { + return addr - BytesPerInstWord*n_instr; + } + + // special-case instruction decoding. + // There may be cases where the binutils disassembler doesn't do + // the perfect job. In those cases, decode_instruction0 may kick in + // and do it right. + // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)" + static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL) { + return here; + } + + // platform-specific instruction annotations (like value of loaded constants) + static void annotate(address pc, outputStream* st) { }; + #endif // CPU_SPARC_DISASSEMBLER_SPARC_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/sparc/sparc.ad --- a/src/hotspot/cpu/sparc/sparc.ad Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/sparc/sparc.ad Thu May 23 11:07:37 2019 +0100 @@ -1,5 +1,5 @@ // -// Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This code is free software; you can redistribute it and/or modify it @@ -1295,7 +1295,8 @@ #ifndef PRODUCT ATTRIBUTE_PRINTF(2, 3) static void print_helper(outputStream* st, const char* format, ...) { - if (st->position() > 0) { + const int tab_size = 8; + if (st->position() > tab_size) { st->cr(); st->sp(); } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/sparc/vm_version_sparc.cpp --- a/src/hotspot/cpu/sparc/vm_version_sparc.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/sparc/vm_version_sparc.cpp Thu May 23 11:07:37 2019 +0100 @@ -139,12 +139,12 @@ if (FLAG_IS_DEFAULT(AllocatePrefetchLines)) { const int ap_lns = AllocatePrefetchLines; const int ap_inc = cache_line_size < 64 ? ap_lns : (ap_lns + 1) / 2; - FLAG_SET_ERGO(intx, AllocatePrefetchLines, ap_lns + ap_inc); + FLAG_SET_ERGO(AllocatePrefetchLines, ap_lns + ap_inc); } if (FLAG_IS_DEFAULT(AllocateInstancePrefetchLines)) { const int ip_lns = AllocateInstancePrefetchLines; const int ip_inc = cache_line_size < 64 ? ip_lns : (ip_lns + 1) / 2; - FLAG_SET_ERGO(intx, AllocateInstancePrefetchLines, ip_lns + ip_inc); + FLAG_SET_ERGO(AllocateInstancePrefetchLines, ip_lns + ip_inc); } } #endif /* COMPILER2 */ diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/x86/assembler_x86.hpp --- a/src/hotspot/cpu/x86/assembler_x86.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/x86/assembler_x86.hpp Thu May 23 11:07:37 2019 +0100 @@ -630,6 +630,17 @@ _true = 7 }; + //---< calculate length of instruction >--- + // As instruction size can't be found out easily on x86/x64, + // we just use '4' for len and maxlen. + // instruction must start at passed address + static unsigned int instr_len(unsigned char *instr) { return 4; } + + //---< longest instructions >--- + // Max instruction length is not specified in architecture documentation. + // We could use a "safe enough" estimate (15), but just default to + // instruction length guess from above. + static unsigned int instr_maxlen() { return 4; } // NOTE: The general philopsophy of the declarations here is that 64bit versions // of instructions are freely declared without the need for wrapping them an ifdef. diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/x86/c2_globals_x86.hpp --- a/src/hotspot/cpu/x86/c2_globals_x86.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/x86/c2_globals_x86.hpp Thu May 23 11:07:37 2019 +0100 @@ -88,7 +88,7 @@ define_pd_global(uintx, NonProfiledCodeHeapSize, 21*M); define_pd_global(uintx, ProfiledCodeHeapSize, 22*M); define_pd_global(uintx, NonNMethodCodeHeapSize, 5*M ); -define_pd_global(uintx, CodeCacheMinBlockLength, 4); +define_pd_global(uintx, CodeCacheMinBlockLength, 6); define_pd_global(uintx, CodeCacheMinimumUseSpace, 400*K); define_pd_global(bool, TrapBasedRangeChecks, false); // Not needed on x86. diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/x86/disassembler_x86.hpp --- a/src/hotspot/cpu/x86/disassembler_x86.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/x86/disassembler_x86.hpp Thu May 23 11:07:37 2019 +0100 @@ -33,4 +33,25 @@ return ""; } + // Returns address of n-th instruction preceding addr, + // NULL if no preceding instruction can be found. + // On CISC architectures, it is difficult to impossible to step + // backwards in the instruction stream. Therefore just return NULL. + // It might be beneficial to check "is_readable" as we do on ppc and s390. + static address find_prev_instr(address addr, int n_instr) { + return NULL; + } + + // special-case instruction decoding. + // There may be cases where the binutils disassembler doesn't do + // the perfect job. In those cases, decode_instruction0 may kick in + // and do it right. + // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)" + static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL) { + return here; + } + + // platform-specific instruction annotations (like value of loaded constants) + static void annotate(address pc, outputStream* st) { }; + #endif // CPU_X86_DISASSEMBLER_X86_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/x86/rdtsc_x86.cpp --- a/src/hotspot/cpu/x86/rdtsc_x86.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/x86/rdtsc_x86.cpp Thu May 23 11:07:37 2019 +0100 @@ -145,7 +145,7 @@ static bool ergonomics() { const bool invtsc_support = Rdtsc::is_supported(); if (FLAG_IS_DEFAULT(UseFastUnorderedTimeStamps) && invtsc_support) { - FLAG_SET_ERGO(bool, UseFastUnorderedTimeStamps, true); + FLAG_SET_ERGO(UseFastUnorderedTimeStamps, true); } bool ft_enabled = UseFastUnorderedTimeStamps && invtsc_support; diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/x86/x86_64.ad --- a/src/hotspot/cpu/x86/x86_64.ad Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/x86/x86_64.ad Thu May 23 11:07:37 2019 +0100 @@ -918,19 +918,19 @@ st->print("\t"); } - st->print_cr("popq rbp"); + st->print_cr("popq rbp"); if (do_polling() && C->is_method_compilation()) { st->print("\t"); if (SafepointMechanism::uses_thread_local_poll()) { - st->print_cr("movq rscratch1, poll_offset[r15_thread] #polling_page_address\n\t" - "testl rax, [rscratch1]\t" + st->print_cr("movq rscratch1, poll_offset[r15_thread] #polling_page_address\n\t" + "testl rax, [rscratch1]\t" "# Safepoint: poll for GC"); } else if (Assembler::is_polling_page_far()) { - st->print_cr("movq rscratch1, #polling_page_address\n\t" - "testl rax, [rscratch1]\t" + st->print_cr("movq rscratch1, #polling_page_address\n\t" + "testl rax, [rscratch1]\t" "# Safepoint: poll for GC"); } else { - st->print_cr("testl rax, [rip + #offset_to_poll_page]\t" + st->print_cr("testl rax, [rip + #offset_to_poll_page]\t" "# Safepoint: poll for GC"); } } @@ -10303,10 +10303,10 @@ match(Set p (AddI (AndI (CmpLTMask p q) y) (SubI p q))); effect(KILL cr); ins_cost(300); - format %{ "subl $p,$q\t# cadd_cmpLTMask\n\t" - "jge done\n\t" - "addl $p,$y\n" - "done: " %} + format %{ "subl $p,$q\t# cadd_cmpLTMask\n\t" + "jge done\n\t" + "addl $p,$y\n" + "done: " %} ins_encode %{ Register Rp = $p$$Register; Register Rq = $q$$Register; @@ -10328,10 +10328,10 @@ ins_cost(300); - format %{ "cmpl $p, $q\t# and_cmpLTMask\n\t" - "jlt done\n\t" - "xorl $y, $y\n" - "done: " %} + format %{ "cmpl $p, $q\t# and_cmpLTMask\n\t" + "jlt done\n\t" + "xorl $y, $y\n" + "done: " %} ins_encode %{ Register Rp = $p$$Register; Register Rq = $q$$Register; @@ -11888,7 +11888,7 @@ %{ match(Set cr (CmpU src zero)); - format %{ "testl $src, $src\t# unsigned" %} + format %{ "testl $src, $src\t# unsigned" %} opcode(0x85); ins_encode(REX_reg_reg(src, src), OpcP, reg_reg(src, src)); ins_pipe(ialu_cr_reg_imm); @@ -12431,7 +12431,7 @@ effect(USE labl); ins_cost(300); - format %{ "j$cop,u $labl" %} + format %{ "j$cop,u $labl" %} size(6); ins_encode %{ Label* L = $labl$$label; @@ -12445,7 +12445,7 @@ effect(USE labl); ins_cost(200); - format %{ "j$cop,u $labl" %} + format %{ "j$cop,u $labl" %} size(6); ins_encode %{ Label* L = $labl$$label; @@ -12461,10 +12461,10 @@ ins_cost(200); format %{ $$template if ($cop$$cmpcode == Assembler::notEqual) { - $$emit$$"jp,u $labl\n\t" + $$emit$$"jp,u $labl\n\t" $$emit$$"j$cop,u $labl" } else { - $$emit$$"jp,u done\n\t" + $$emit$$"jp,u done\n\t" $$emit$$"j$cop,u $labl\n\t" $$emit$$"done:" } @@ -12666,10 +12666,10 @@ ins_cost(300); format %{ $$template if ($cop$$cmpcode == Assembler::notEqual) { - $$emit$$"jp,u,s $labl\n\t" - $$emit$$"j$cop,u,s $labl" + $$emit$$"jp,u,s $labl\n\t" + $$emit$$"j$cop,u,s $labl" } else { - $$emit$$"jp,u,s done\n\t" + $$emit$$"jp,u,s done\n\t" $$emit$$"j$cop,u,s $labl\n\t" $$emit$$"done:" } @@ -12745,7 +12745,7 @@ match(SafePoint); effect(KILL cr); - format %{ "testl rax, [rip + #offset_to_poll_page]\t" + format %{ "testl rax, [rip + #offset_to_poll_page]\t" "# Safepoint: poll for GC" %} ins_cost(125); ins_encode %{ @@ -12761,7 +12761,7 @@ match(SafePoint poll); effect(KILL cr, USE poll); - format %{ "testl rax, [$poll]\t" + format %{ "testl rax, [$poll]\t" "# Safepoint: poll for GC" %} ins_cost(125); ins_encode %{ @@ -12777,7 +12777,7 @@ match(SafePoint poll); effect(KILL cr, USE poll); - format %{ "testl rax, [$poll]\t" + format %{ "testl rax, [$poll]\t" "# Safepoint: poll for GC" %} ins_cost(125); size(4); /* setting an explicit size will cause debug builds to assert if size is incorrect */ diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/zero/assembler_zero.hpp --- a/src/hotspot/cpu/zero/assembler_zero.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/zero/assembler_zero.hpp Thu May 23 11:07:37 2019 +0100 @@ -37,6 +37,12 @@ public: void pd_patch_instruction(address branch, address target, const char* file, int line); + + //---< calculate length of instruction >--- + static unsigned int instr_len(unsigned char *instr) { return 1; } + + //---< longest instructions >--- + static unsigned int instr_maxlen() { return 1; } }; class MacroAssembler : public Assembler { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/cpu/zero/disassembler_zero.hpp --- a/src/hotspot/cpu/zero/disassembler_zero.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/cpu/zero/disassembler_zero.hpp Thu May 23 11:07:37 2019 +0100 @@ -34,4 +34,24 @@ return ""; } + // Returns address of n-th instruction preceding addr, + // NULL if no preceding instruction can be found. + // On ZERO, we assume a constant instruction length of 1 byte. + // It might be beneficial to check "is_readable" as we do on ppc and s390. + static address find_prev_instr(address addr, int n_instr) { + return addr - 1*n_instr; + } + + // special-case instruction decoding. + // There may be cases where the binutils disassembler doesn't do + // the perfect job. In those cases, decode_instruction0 may kick in + // and do it right. + // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)" + static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL) { + return here; + } + + // platform-specific instruction annotations (like value of loaded constants) + static void annotate(address pc, outputStream* st) { }; + #endif // CPU_ZERO_DISASSEMBLER_ZERO_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/os/aix/os_aix.cpp --- a/src/hotspot/os/aix/os_aix.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/os/aix/os_aix.cpp Thu May 23 11:07:37 2019 +0100 @@ -3447,7 +3447,7 @@ // fall back to 4K paged mode and use mmap for everything. trcVerbose("4K page mode"); Aix::_page_size = 4*K; - FLAG_SET_ERGO(bool, Use64KPages, false); + FLAG_SET_ERGO(Use64KPages, false); } } else { // datapsize = 64k. Data segment, thread stacks are 64k paged. @@ -3457,11 +3457,11 @@ assert0(g_multipage_support.can_use_64K_pages); Aix::_page_size = 64*K; trcVerbose("64K page mode"); - FLAG_SET_ERGO(bool, Use64KPages, true); + FLAG_SET_ERGO(Use64KPages, true); } // For now UseLargePages is just ignored. - FLAG_SET_ERGO(bool, UseLargePages, false); + FLAG_SET_ERGO(UseLargePages, false); _page_sizes[0] = 0; // debug trace diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/os/windows/os_windows.cpp --- a/src/hotspot/os/windows/os_windows.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/os/windows/os_windows.cpp Thu May 23 11:07:37 2019 +0100 @@ -4072,7 +4072,7 @@ init_page_sizes((size_t) win32::vm_page_size()); // This may be overridden later when argument processing is done. - FLAG_SET_ERGO(bool, UseLargePagesIndividualAllocation, false); + FLAG_SET_ERGO(UseLargePagesIndividualAllocation, false); // Initialize main_process and main_thread main_process = GetCurrentProcess(); // Remember main_process is a pseudo handle diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp --- a/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp Thu May 23 11:07:37 2019 +0100 @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2014 SAP SE. All rights reserved. + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019 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 @@ -92,9 +92,8 @@ return false; } -// Forte Analyzer AsyncGetCallTrace profiling support is not implemented on Linux/PPC. +// Forte Analyzer AsyncGetCallTrace profiling support. bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, bool isInJava) { - assert(this->is_Java_thread(), "must be JavaThread"); return pd_get_top_frame_for_profiling(fr_addr, ucontext, isInJava); } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp --- a/src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp Thu May 23 11:07:37 2019 +0100 @@ -90,10 +90,9 @@ return false; } -// Forte Analyzer AsyncGetCallTrace profiling support is not implemented on Linux/S390x. +// Forte Analyzer AsyncGetCallTrace profiling support. bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr, void* ucontext, bool isInJava) { - Unimplemented(); - return false; + return pd_get_top_frame_for_profiling(fr_addr, ucontext, isInJava); } void JavaThread::cache_global_variables() { } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/Xusage.txt --- a/src/hotspot/share/Xusage.txt Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/Xusage.txt Thu May 23 11:07:37 2019 +0100 @@ -13,6 +13,7 @@ -Xmx set maximum Java heap size -Xss set java thread stack size -Xfuture enable strictest checks, anticipating future default + (deprecated) -Xrs reduce use of OS signals by Java/VM (see documentation) -Xcheck:jni perform additional checks for JNI functions -Xshare:off do not attempt to use shared class data diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/aot/aotLoader.hpp --- a/src/hotspot/share/aot/aotLoader.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/aot/aotLoader.hpp Thu May 23 11:07:37 2019 +0100 @@ -54,7 +54,7 @@ static void add_heap(AOTCodeHeap *heap); static void add_library(AOTLib *lib); #endif - static void initialize() NOT_AOT({ FLAG_SET_ERGO(bool, UseAOT, false); }); + static void initialize() NOT_AOT({ FLAG_SET_ERGO(UseAOT, false); }); static void universe_init() NOT_AOT_RETURN; static void set_narrow_oop_shift() NOT_AOT_RETURN; diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/asm/codeBuffer.cpp --- a/src/hotspot/share/asm/codeBuffer.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/asm/codeBuffer.cpp Thu May 23 11:07:37 2019 +0100 @@ -86,7 +86,8 @@ // External buffer, in a predefined CodeBlob. // Important: The code_start must be taken exactly, and not realigned. CodeBuffer::CodeBuffer(CodeBlob* blob) { - initialize_misc("static buffer"); + // Provide code buffer with meaningful name + initialize_misc(blob->name()); initialize(blob->content_begin(), blob->content_size()); verify_section_allocation(); } @@ -1035,7 +1036,9 @@ } void CodeBuffer::block_comment(intptr_t offset, const char * comment) { - _code_strings.add_comment(offset, comment); + if (_collect_comments) { + _code_strings.add_comment(offset, comment); + } } const char* CodeBuffer::code_string(const char* str) { @@ -1148,15 +1151,23 @@ const char* CodeStrings::_prefix = " ;; "; // default: can be changed via set_prefix +// Check if any block comments are pending for the given offset. +bool CodeStrings::has_block_comment(intptr_t offset) const { + if (_strings == NULL) return false; + CodeString* c = find(offset); + return c != NULL; +} + void CodeStrings::print_block_comment(outputStream* stream, intptr_t offset) const { - check_valid(); - if (_strings != NULL) { + check_valid(); + if (_strings != NULL) { CodeString* c = find(offset); while (c && c->offset() == offset) { stream->bol(); stream->print("%s", _prefix); // Don't interpret as format strings since it could contain % - stream->print_raw_cr(c->string()); + stream->print_raw(c->string()); + stream->bol(); // advance to next line only if string didn't contain a cr() at the end. c = c->next_comment(); } } @@ -1186,7 +1197,7 @@ void CodeBuffer::decode() { ttyLocker ttyl; - Disassembler::decode(decode_begin(), insts_end()); + Disassembler::decode(decode_begin(), insts_end(), tty); _decode_begin = insts_end(); } @@ -1217,4 +1228,10 @@ } } +// Directly disassemble code buffer. +void CodeBuffer::decode(address start, address end) { + ttyLocker ttyl; + Disassembler::decode(this, start, end, tty); +} + #endif // PRODUCT diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/asm/codeBuffer.hpp --- a/src/hotspot/share/asm/codeBuffer.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/asm/codeBuffer.hpp Thu May 23 11:07:37 2019 +0100 @@ -289,6 +289,7 @@ const char* add_string(const char * string) PRODUCT_RETURN_(return NULL;); void add_comment(intptr_t offset, const char * comment) PRODUCT_RETURN; + bool has_block_comment(intptr_t offset) const; void print_block_comment(outputStream* stream, intptr_t offset) const PRODUCT_RETURN; // MOVE strings from other to this; invalidate other. void assign(CodeStrings& other) PRODUCT_RETURN; @@ -296,6 +297,7 @@ void copy(CodeStrings& other) PRODUCT_RETURN; // FREE strings; invalidate this. void free() PRODUCT_RETURN; + // Guarantee that _strings are used at most once; assign and free invalidate a buffer. inline void check_valid() const { #ifdef ASSERT @@ -377,6 +379,7 @@ OopRecorder* _oop_recorder; CodeStrings _code_strings; + bool _collect_comments; // Indicate if we need to collect block comments at all. OopRecorder _default_oop_recorder; // override with initialize_oop_recorder Arena* _overflow_arena; @@ -403,6 +406,14 @@ #if INCLUDE_AOT _immutable_PIC = false; #endif + + // Collect block comments, but restrict collection to cases where a disassembly is output. + _collect_comments = ( PrintAssembly + || PrintStubCode + || PrintMethodHandleStubs + || PrintInterpreter + || PrintSignatureHandlers + ); } void initialize(address code_start, csize_t code_size) { @@ -604,6 +615,23 @@ } } + // Directly disassemble code buffer. + // Print the comment associated with offset on stream, if there is one. + virtual void print_block_comment(outputStream* stream, address block_begin) { +#ifndef PRODUCT + intptr_t offset = (intptr_t)(block_begin - _total_start); // I assume total_start is not correct for all code sections. + _code_strings.print_block_comment(stream, offset); +#endif + } + bool has_block_comment(address block_begin) { +#ifndef PRODUCT + intptr_t offset = (intptr_t)(block_begin - _total_start); // I assume total_start is not correct for all code sections. + return _code_strings.has_block_comment(offset); +#else + return false; +#endif + } + // Code generation void relocate(address at, RelocationHolder const& rspec, int format = 0) { _insts.relocate(at, rspec, format); @@ -650,7 +678,8 @@ void decode(); void print(); #endif - + // Directly disassemble code buffer. + void decode(address start, address end); // The following header contains architecture-specific implementations #include CPU_HEADER(codeBuffer) diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/c1/c1_Runtime1.cpp --- a/src/hotspot/share/c1/c1_Runtime1.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/c1/c1_Runtime1.cpp Thu May 23 11:07:37 2019 +0100 @@ -575,7 +575,7 @@ tempst.print("compiled method <%s>\n" " at PC" INTPTR_FORMAT " for thread " INTPTR_FORMAT, nm->method()->print_value_string(), p2i(pc), p2i(thread)); - Exceptions::log_exception(exception, tempst); + Exceptions::log_exception(exception, tempst.as_string()); } // for AbortVMOnException flag Exceptions::debug_check_abort(exception); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/c1/c1_globals.cpp --- a/src/hotspot/share/c1/c1_globals.cpp Fri May 17 13:21:44 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2000, 2016, 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 "c1/c1_globals.hpp" - -C1_FLAGS(MATERIALIZE_DEVELOPER_FLAG, \ - MATERIALIZE_PD_DEVELOPER_FLAG, \ - MATERIALIZE_PRODUCT_FLAG, \ - MATERIALIZE_PD_PRODUCT_FLAG, \ - MATERIALIZE_DIAGNOSTIC_FLAG, \ - MATERIALIZE_PD_DIAGNOSTIC_FLAG, \ - MATERIALIZE_NOTPRODUCT_FLAG, \ - IGNORE_RANGE, \ - IGNORE_CONSTRAINT, \ - IGNORE_WRITEABLE) diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/c1/c1_globals.hpp --- a/src/hotspot/share/c1/c1_globals.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/c1/c1_globals.hpp Thu May 23 11:07:37 2019 +0100 @@ -25,7 +25,7 @@ #ifndef SHARE_C1_C1_GLOBALS_HPP #define SHARE_C1_C1_GLOBALS_HPP -#include "runtime/globals.hpp" +#include "runtime/globals_shared.hpp" #include "utilities/macros.hpp" #include CPU_HEADER(c1_globals) @@ -324,17 +324,5 @@ develop(bool, PrintCFGToFile, false, \ "print control flow graph to a separate file during compilation") \ \ -// Read default values for c1 globals - -C1_FLAGS(DECLARE_DEVELOPER_FLAG, \ - DECLARE_PD_DEVELOPER_FLAG, \ - DECLARE_PRODUCT_FLAG, \ - DECLARE_PD_PRODUCT_FLAG, \ - DECLARE_DIAGNOSTIC_FLAG, \ - DECLARE_PD_DIAGNOSTIC_FLAG, \ - DECLARE_NOTPRODUCT_FLAG, \ - IGNORE_RANGE, \ - IGNORE_CONSTRAINT, \ - IGNORE_WRITEABLE) #endif // SHARE_C1_C1_GLOBALS_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/classListParser.cpp --- a/src/hotspot/share/classfile/classListParser.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/classListParser.cpp Thu May 23 11:07:37 2019 +0100 @@ -295,14 +295,14 @@ if (!is_id_specified()) { error("If source location is specified, id must be also specified"); } - InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, CHECK_NULL); - if (strncmp(_class_name, "java/", 5) == 0) { log_info(cds)("Prohibited package for non-bootstrap classes: %s.class from %s", _class_name, _source); return NULL; } + InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, CHECK_NULL); + if (k != NULL) { if (k->local_interfaces()->length() != _interfaces->length()) { print_specified_interfaces(); @@ -461,4 +461,3 @@ ShouldNotReachHere(); return NULL; } - diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/classLoader.cpp --- a/src/hotspot/share/classfile/classLoader.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/classLoader.cpp Thu May 23 11:07:37 2019 +0100 @@ -36,6 +36,7 @@ #include "classfile/klassFactory.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" #include "compiler/compileBroker.hpp" #include "interpreter/bytecodeStream.hpp" @@ -468,7 +469,7 @@ #if INCLUDE_CDS void ClassLoader::exit_with_path_failure(const char* error, const char* message) { - assert(DumpSharedSpaces, "only called at dump time"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "only called at dump time"); tty->print_cr("Hint: enable -Xlog:class+path=info to diagnose the failure"); vm_exit_during_initialization(error, message); } @@ -534,7 +535,7 @@ trace_class_path("bootstrap loader class path=", sys_class_path); } #if INCLUDE_CDS - if (DumpSharedSpaces) { + if (DumpSharedSpaces || DynamicDumpSharedSpaces) { _shared_paths_misc_info->add_boot_classpath(sys_class_path); } #endif @@ -550,16 +551,16 @@ return _shared_paths_misc_info->buffer(); } -bool ClassLoader::check_shared_paths_misc_info(void *buf, int size) { +bool ClassLoader::check_shared_paths_misc_info(void *buf, int size, bool is_static) { SharedPathsMiscInfo* checker = new SharedPathsMiscInfo((char*)buf, size); - bool result = checker->check(); + bool result = checker->check(is_static); delete checker; return result; } void ClassLoader::setup_app_search_path(const char *class_path) { - assert(DumpSharedSpaces, "Sanity"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "Sanity"); Thread* THREAD = Thread::current(); int len = (int)strlen(class_path); @@ -587,7 +588,7 @@ void ClassLoader::add_to_module_path_entries(const char* path, ClassPathEntry* entry) { assert(entry != NULL, "ClassPathEntry should not be NULL"); - assert(DumpSharedSpaces, "dump time only"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only"); // The entry does not exist, add to the list if (_module_path_entries == NULL) { @@ -601,7 +602,7 @@ // Add a module path to the _module_path_entries list. void ClassLoader::update_module_path_entry_list(const char *path, TRAPS) { - assert(DumpSharedSpaces, "dump time only"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump time only"); struct stat st; if (os::stat(path, &st) != 0) { tty->print_cr("os::stat error %d (%s). CDS dump aborted (path was \"%s\").", @@ -709,7 +710,7 @@ bool set_base_piece = true; #if INCLUDE_CDS - if (DumpSharedSpaces) { + if (DumpSharedSpaces || DynamicDumpSharedSpaces) { if (!Arguments::has_jimage()) { vm_exit_during_initialization("CDS is not supported in exploded JDK build", NULL); } @@ -976,7 +977,7 @@ return true; } else { #if INCLUDE_CDS - if (DumpSharedSpaces) { + if (DumpSharedSpaces || DynamicDumpSharedSpaces) { _shared_paths_misc_info->add_nonexist_path(path); } #endif @@ -1334,6 +1335,10 @@ // appear in the _patch_mod_entries. The runtime shared class visibility // check will determine if a shared class is visible based on the runtime // environemnt, including the runtime --patch-module setting. + // + // DynamicDumpSharedSpaces requires UseSharedSpaces to be enabled. Since --patch-module + // is not supported with UseSharedSpaces, it is not supported with DynamicDumpSharedSpaces. + assert(!DynamicDumpSharedSpaces, "sanity"); if (!DumpSharedSpaces) { stream = search_module_entries(_patch_mod_entries, class_name, file_name, CHECK_NULL); } @@ -1423,7 +1428,7 @@ // Record the shared classpath index and loader type for classes loaded // by the builtin loaders at dump time. void ClassLoader::record_result(InstanceKlass* ik, const ClassFileStream* stream, TRAPS) { - assert(DumpSharedSpaces, "sanity"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "sanity"); assert(stream != NULL, "sanity"); if (ik->is_unsafe_anonymous()) { @@ -1513,6 +1518,8 @@ // user defined classloader. if (classpath_index < 0) { assert(ik->shared_classpath_index() < 0, "Sanity"); + ik->set_shared_classpath_index(UNREGISTERED_INDEX); + SystemDictionaryShared::set_shared_class_misc_info(ik, (ClassFileStream*)stream); return; } } else { @@ -1595,7 +1602,7 @@ load_jimage_library(); #if INCLUDE_CDS // initialize search path - if (DumpSharedSpaces) { + if (DumpSharedSpaces || DynamicDumpSharedSpaces) { _shared_paths_misc_info = new SharedPathsMiscInfo(); } #endif @@ -1604,14 +1611,14 @@ #if INCLUDE_CDS void ClassLoader::initialize_shared_path() { - if (DumpSharedSpaces) { + if (DumpSharedSpaces || DynamicDumpSharedSpaces) { ClassLoaderExt::setup_search_paths(); _shared_paths_misc_info->write_jint(0); // see comments in SharedPathsMiscInfo::check() } } void ClassLoader::initialize_module_path(TRAPS) { - if (DumpSharedSpaces) { + if (DumpSharedSpaces || DynamicDumpSharedSpaces) { ClassLoaderExt::setup_module_paths(THREAD); FileMapInfo::allocate_shared_path_table(); } @@ -1677,6 +1684,7 @@ // entries will be added to the exploded build array. if (!has_jrt_entry()) { assert(!DumpSharedSpaces, "DumpSharedSpaces not supported with exploded module builds"); + assert(!DynamicDumpSharedSpaces, "DynamicDumpSharedSpaces not supported with exploded module builds"); assert(!UseSharedSpaces, "UsedSharedSpaces not supported with exploded module builds"); // Set up the boot loader's _exploded_entries list. Note that this gets // done before loading any classes, by the same thread that will diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/classLoader.hpp --- a/src/hotspot/share/classfile/classLoader.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/classLoader.hpp Thu May 23 11:07:37 2019 +0100 @@ -398,7 +398,8 @@ // Helper function used by CDS code to get the number of module path // entries during shared classpath setup time. static int num_module_path_entries() { - assert(DumpSharedSpaces, "Should only be called at CDS dump time"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, + "Should only be called at CDS dump time"); int num_entries = 0; ClassPathEntry* e= ClassLoader::_module_path_entries; while (e != NULL) { @@ -410,7 +411,7 @@ static void finalize_shared_paths_misc_info(); static int get_shared_paths_misc_info_size(); static void* get_shared_paths_misc_info(); - static bool check_shared_paths_misc_info(void* info, int size); + static bool check_shared_paths_misc_info(void* info, int size, bool is_static); static void exit_with_path_failure(const char* error, const char* message); static char* skip_uri_protocol(char* source); static void record_result(InstanceKlass* ik, const ClassFileStream* stream, TRAPS); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/classLoader.inline.hpp --- a/src/hotspot/share/classfile/classLoader.inline.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/classLoader.inline.hpp Thu May 23 11:07:37 2019 +0100 @@ -62,7 +62,8 @@ // entries during shared classpath setup time. inline int ClassLoader::num_boot_classpath_entries() { - assert(DumpSharedSpaces, "Should only be called at CDS dump time"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, + "Should only be called at CDS dump time"); assert(has_jrt_entry(), "must have a java runtime image"); int num_entries = 1; // count the runtime image ClassPathEntry* e = ClassLoader::_first_append_entry; @@ -84,7 +85,8 @@ // Helper function used by CDS code to get the number of app classpath // entries during shared classpath setup time. inline int ClassLoader::num_app_classpath_entries() { - assert(DumpSharedSpaces, "Should only be called at CDS dump time"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, + "Should only be called at CDS dump time"); int num_entries = 0; ClassPathEntry* e= ClassLoader::_app_classpath_entries; while (e != NULL) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/classLoaderDataGraph.cpp --- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp Thu May 23 11:07:37 2019 +0100 @@ -596,14 +596,13 @@ DependencyContext::purge_dependency_contexts(); } -int ClassLoaderDataGraph::resize_if_needed() { +int ClassLoaderDataGraph::resize_dictionaries() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!"); int resized = 0; - if (Dictionary::does_any_dictionary_needs_resizing()) { - FOR_ALL_DICTIONARY(cld) { - if (cld->dictionary()->resize_if_needed()) { - resized++; - } + assert (Dictionary::does_any_dictionary_needs_resizing(), "some dictionary should need resizing"); + FOR_ALL_DICTIONARY(cld) { + if (cld->dictionary()->resize_if_needed()) { + resized++; } } return resized; diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/classLoaderDataGraph.hpp --- a/src/hotspot/share/classfile/classLoaderDataGraph.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/classLoaderDataGraph.hpp Thu May 23 11:07:37 2019 +0100 @@ -119,16 +119,14 @@ static GrowableArray* new_clds(); static void set_should_purge(bool b) { _should_purge = b; } - static void purge_if_needed() { - // Only purge the CLDG for CMS if concurrent sweep is complete. - if (_should_purge) { - purge(); - // reset for next time. - set_should_purge(false); - } + static bool should_purge_and_reset() { + bool res = _should_purge; + // reset for next time. + set_should_purge(false); + return res; } - static int resize_if_needed(); + static int resize_dictionaries(); static bool has_metaspace_oom() { return _metaspace_oom; } static void set_metaspace_oom(bool value) { _metaspace_oom = value; } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/classLoaderExt.cpp --- a/src/hotspot/share/classfile/classLoaderExt.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/classLoaderExt.cpp Thu May 23 11:07:37 2019 +0100 @@ -62,7 +62,8 @@ } void ClassLoaderExt::setup_app_search_path() { - assert(DumpSharedSpaces, "this function is only used with -Xshare:dump"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, + "this function is only used at CDS dump time"); _app_class_paths_start_index = ClassLoader::num_boot_classpath_entries(); char* app_class_path = os::strdup(Arguments::get_appclasspath()); @@ -92,7 +93,8 @@ } } void ClassLoaderExt::setup_module_paths(TRAPS) { - assert(DumpSharedSpaces, "this function is only used with -Xshare:dump"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, + "this function is only used with CDS dump time"); _app_module_paths_start_index = ClassLoader::num_boot_classpath_entries() + ClassLoader::num_app_classpath_entries(); Handle system_class_loader (THREAD, SystemDictionary::java_system_loader()); @@ -227,7 +229,7 @@ void ClassLoaderExt::record_result(const s2 classpath_index, InstanceKlass* result, TRAPS) { - assert(DumpSharedSpaces, "Sanity"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "Sanity"); // We need to remember where the class comes from during dumping. oop loader = result->class_loader(); @@ -301,8 +303,6 @@ tty->print_cr("Preload Error: Failed to load %s", class_name); return NULL; } - result->set_shared_classpath_index(UNREGISTERED_INDEX); - SystemDictionaryShared::set_shared_class_misc_info(result, stream); return result; } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/compactHashtable.cpp --- a/src/hotspot/share/classfile/compactHashtable.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/compactHashtable.cpp Thu May 23 11:07:37 2019 +0100 @@ -27,6 +27,7 @@ #include "classfile/compactHashtable.hpp" #include "classfile/javaClasses.hpp" #include "logging/logMessage.hpp" +#include "memory/dynamicArchive.hpp" #include "memory/heapShared.inline.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspaceShared.hpp" @@ -39,12 +40,14 @@ // // The compact hash table writer implementations // -CompactHashtableWriter::CompactHashtableWriter(int num_buckets, +CompactHashtableWriter::CompactHashtableWriter(int num_entries, CompactHashtableStats* stats) { - assert(DumpSharedSpaces, "dump-time only"); - assert(num_buckets > 0, "no buckets"); - _num_buckets = num_buckets; - _num_entries = 0; + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump-time only"); + assert(num_entries >= 0, "sanity"); + _num_buckets = calculate_num_buckets(num_entries); + assert(_num_buckets > 0, "no buckets"); + + _num_entries_written = 0; _buckets = NEW_C_HEAP_ARRAY(GrowableArray*, _num_buckets, mtSymbol); for (int i=0; i<_num_buckets; i++) { _buckets[i] = new (ResourceObj::C_HEAP, mtSymbol) GrowableArray(0, true, mtSymbol); @@ -67,11 +70,24 @@ FREE_C_HEAP_ARRAY(GrowableArray*, _buckets); } +size_t CompactHashtableWriter::estimate_size(int num_entries) { + int num_buckets = calculate_num_buckets(num_entries); + size_t bucket_bytes = MetaspaceShared::ro_array_bytesize(num_buckets + 1); + + // In worst case, we have no VALUE_ONLY_BUCKET_TYPE, so each entry takes 2 slots + int entries_space = 2 * num_entries; + size_t entry_bytes = MetaspaceShared::ro_array_bytesize(entries_space); + + return bucket_bytes + + entry_bytes + + SimpleCompactHashtable::calculate_header_size(); +} + // Add a symbol entry to the temporary hash table void CompactHashtableWriter::add(unsigned int hash, u4 value) { int index = hash % _num_buckets; _buckets[index]->append_if_missing(Entry(hash, value)); - _num_entries++; + _num_entries_written++; } void CompactHashtableWriter::allocate_table() { @@ -81,7 +97,7 @@ int bucket_size = bucket->length(); if (bucket_size == 1) { entries_space++; - } else { + } else if (bucket_size > 1) { entries_space += 2 * bucket_size; } } @@ -96,7 +112,7 @@ _stats->bucket_count = _num_buckets; _stats->bucket_bytes = _compact_buckets->size() * BytesPerWord; - _stats->hashentry_count = _num_entries; + _stats->hashentry_count = _num_entries_written; _stats->hashentry_bytes = _compact_entries->size() * BytesPerWord; } @@ -144,19 +160,19 @@ dump_table(&summary); int table_bytes = _stats->bucket_bytes + _stats->hashentry_bytes; - address base_address = address(MetaspaceShared::shared_rs()->base()); - cht->init(base_address, _num_entries, _num_buckets, + address base_address = address(SharedBaseAddress); + cht->init(base_address, _num_entries_written, _num_buckets, _compact_buckets->data(), _compact_entries->data()); LogMessage(cds, hashtables) msg; if (msg.is_info()) { double avg_cost = 0.0; - if (_num_entries > 0) { - avg_cost = double(table_bytes)/double(_num_entries); + if (_num_entries_written > 0) { + avg_cost = double(table_bytes)/double(_num_entries_written); } msg.info("Shared %s table stats -------- base: " PTR_FORMAT, table_name, (intptr_t)base_address); - msg.info("Number of entries : %9d", _num_entries); + msg.info("Number of entries : %9d", _num_entries_written); msg.info("Total bytes used : %9d", table_bytes); msg.info("Average bytes per entry : %9.3f", avg_cost); msg.info("Average bucket size : %9.3f", summary.avg()); @@ -174,7 +190,28 @@ // The CompactHashtable implementation // +void SimpleCompactHashtable::init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries) { + _bucket_count = bucket_count; + _entry_count = entry_count; + _base_address = base_address; + if (DynamicDumpSharedSpaces) { + _buckets = DynamicArchive::buffer_to_target(buckets); + _entries = DynamicArchive::buffer_to_target(entries); + } else { + _buckets = buckets; + _entries = entries; + } +} + +size_t SimpleCompactHashtable::calculate_header_size() { + // We have 5 fields. Each takes up sizeof(intptr_t). See WriteClosure::do_u4 + size_t bytes = sizeof(intptr_t) * 5; + return bytes; +} + void SimpleCompactHashtable::serialize_header(SerializeClosure* soc) { + // NOTE: if you change this function, you MUST change the number 5 in + // calculate_header_size() accordingly. soc->do_ptr((void**)&_base_address); soc->do_u4(&_entry_count); soc->do_u4(&_bucket_count); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/compactHashtable.hpp --- a/src/hotspot/share/classfile/compactHashtable.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/compactHashtable.hpp Thu May 23 11:07:37 2019 +0100 @@ -100,7 +100,7 @@ }; // class CompactHashtableWriter::Entry private: - int _num_entries; + int _num_entries_written; int _num_buckets; int _num_empty_buckets; int _num_value_only_buckets; @@ -112,7 +112,7 @@ public: // This is called at dump-time only - CompactHashtableWriter(int num_buckets, CompactHashtableStats* stats); + CompactHashtableWriter(int num_entries, CompactHashtableStats* stats); ~CompactHashtableWriter(); void add(unsigned int hash, u4 value); @@ -120,18 +120,16 @@ private: void allocate_table(); void dump_table(NumberSeq* summary); + static int calculate_num_buckets(int num_entries) { + int num_buckets = num_entries / SharedSymbolTableBucketSize; + // calculation of num_buckets can result in zero buckets, we need at least one + return (num_buckets < 1) ? 1 : num_buckets; + } public: void dump(SimpleCompactHashtable *cht, const char* table_name); - static int default_num_buckets(size_t num_entries) { - return default_num_buckets((int)num_entries); - } - static int default_num_buckets(int num_entries) { - int num_buckets = num_entries / SharedSymbolTableBucketSize; - // calculation of num_buckets can result in zero buckets, we need at least one - return (num_buckets < 1) ? 1 : num_buckets; - } + static size_t estimate_size(int num_entries); }; #endif // INCLUDE_CDS @@ -214,13 +212,7 @@ _entries = 0; } - void init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries) { - _base_address = base_address; - _bucket_count = bucket_count; - _entry_count = entry_count; - _buckets = buckets; - _entries = entries; - } + void init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries); // Read/Write the table's header from/to the CDS archive void serialize_header(SerializeClosure* soc) NOT_CDS_RETURN; @@ -228,6 +220,8 @@ inline bool empty() { return (_entry_count == 0); } + + static size_t calculate_header_size(); }; template < diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/dictionary.cpp --- a/src/hotspot/share/classfile/dictionary.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/dictionary.cpp Thu May 23 11:07:37 2019 +0100 @@ -246,7 +246,7 @@ // Used to scan and relocate the classes during CDS archive dump. void Dictionary::classes_do(MetaspaceClosure* it) { - assert(DumpSharedSpaces, "dump-time only"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "dump-time only"); for (int index = 0; index < table_size(); index++) { for (DictionaryEntry* probe = bucket(index); probe != NULL; @@ -312,7 +312,6 @@ } } - InstanceKlass* Dictionary::find_class(int index, unsigned int hash, Symbol* name) { assert_locked_or_safepoint(SystemDictionary_lock); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/klassFactory.cpp --- a/src/hotspot/share/classfile/klassFactory.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/klassFactory.cpp Thu May 23 11:07:37 2019 +0100 @@ -218,7 +218,7 @@ JFR_ONLY(ON_KLASS_CREATION(result, parser, THREAD);) #if INCLUDE_CDS - if (DumpSharedSpaces) { + if (DumpSharedSpaces || DynamicDumpSharedSpaces) { ClassLoader::record_result(result, stream, THREAD); } #endif // INCLUDE_CDS diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/sharedPathsMiscInfo.cpp --- a/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp Thu May 23 11:07:37 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -104,7 +104,7 @@ } } -bool SharedPathsMiscInfo::check() { +bool SharedPathsMiscInfo::check(bool is_static) { // The whole buffer must be 0 terminated so that we can use strlen and strcmp // without fear. _end_ptr -= sizeof(jint); @@ -116,9 +116,10 @@ } jshort cur_index = 0; - jshort max_cp_index = FileMapInfo::current_info()->header()->max_used_path_index(); - jshort module_paths_start_index = - FileMapInfo::current_info()->header()->app_module_paths_start_index(); + FileMapHeader* header = is_static ? FileMapInfo::current_info()->header() : + FileMapInfo::dynamic_info()->header(); + jshort max_cp_index = header->max_used_path_index(); + jshort module_paths_start_index = header->app_module_paths_start_index(); while (_cur_ptr < _end_ptr) { jint type; const char* path = _cur_ptr; @@ -136,7 +137,7 @@ } // skip checking the class path(s) which was not referenced during CDS dump if ((cur_index <= max_cp_index) || (cur_index >= module_paths_start_index)) { - if (!check(type, path)) { + if (!check(type, path, is_static)) { if (!PrintSharedArchiveAndExit) { return false; } @@ -171,7 +172,7 @@ return p; } -bool SharedPathsMiscInfo::check(jint type, const char* path) { +bool SharedPathsMiscInfo::check(jint type, const char* path, bool is_static) { assert(UseSharedSpaces, "runtime only"); switch (type) { case BOOT_PATH: @@ -196,7 +197,9 @@ char* rp = skip_first_path_entry(runtime_boot_path); char* dp = skip_first_path_entry(path); - bool relaxed_check = !FileMapInfo::current_info()->header()->has_platform_or_app_classes(); + bool relaxed_check = is_static ? + !FileMapInfo::current_info()->header()->has_platform_or_app_classes() : + !FileMapInfo::dynamic_info()->header()->has_platform_or_app_classes(); if (dp == NULL && rp == NULL) { break; // ok, both runtime and dump time boot paths have modules_images only } else if (dp == NULL && rp != NULL && relaxed_check) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/sharedPathsMiscInfo.hpp --- a/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp Thu May 23 11:07:37 2019 +0100 @@ -67,7 +67,7 @@ protected: static bool fail(const char* msg, const char* name = NULL); - bool check(jint type, const char* path); + bool check(jint type, const char* path, bool is_static); public: enum { @@ -162,7 +162,7 @@ } public: - bool check(); + bool check(bool is_static); }; #endif // SHARE_CLASSFILE_SHAREDPATHSMISCINFO_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/stringTable.cpp --- a/src/hotspot/share/classfile/stringTable.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/stringTable.cpp Thu May 23 11:07:37 2019 +0100 @@ -372,16 +372,19 @@ bool rehash_warning; do { - if (_local_table->get(THREAD, lookup, stg, &rehash_warning)) { - update_needs_rehash(rehash_warning); - return stg.get_res_oop(); - } + // Callers have already looked up the String using the jchar* name, so just go to add. WeakHandle wh = WeakHandle::create(string_h); // The hash table takes ownership of the WeakHandle, even if it's not inserted. if (_local_table->insert(THREAD, lookup, wh, &rehash_warning)) { update_needs_rehash(rehash_warning); return wh.resolve(); } + // In case another thread did a concurrent add, return value already in the table. + // This could fail if the String got gc'ed concurrently, so loop back until success. + if (_local_table->get(THREAD, lookup, stg, &rehash_warning)) { + update_needs_rehash(rehash_warning); + return stg.get_res_oop(); + } } while(true); } @@ -779,9 +782,7 @@ assert(HeapShared::is_heap_object_archiving_allowed(), "must be"); _shared_table.reset(); - int num_buckets = CompactHashtableWriter::default_num_buckets(_items_count); - CompactHashtableWriter writer(num_buckets, - &MetaspaceShared::stats()->string); + CompactHashtableWriter writer(_items_count, &MetaspaceShared::stats()->string); // Copy the interned strings into the "string space" within the java heap copy_shared_string_table(&writer); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/symbolTable.cpp --- a/src/hotspot/share/classfile/symbolTable.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/symbolTable.cpp Thu May 23 11:07:37 2019 +0100 @@ -28,6 +28,7 @@ #include "classfile/javaClasses.hpp" #include "classfile/symbolTable.hpp" #include "memory/allocation.inline.hpp" +#include "memory/dynamicArchive.hpp" #include "memory/metaspaceClosure.hpp" #include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" @@ -69,6 +70,11 @@ symbol_equals_compact_hashtable_entry > _shared_table; +static OffsetCompactHashtable< + const char*, Symbol*, + symbol_equals_compact_hashtable_entry +> _dynamic_shared_table; + // -------------------------------------------------------------------------- typedef ConcurrentHashTabledo_safepoint_scan(mpd); } @@ -287,19 +296,24 @@ return sym; } +#if INCLUDE_CDS Symbol* SymbolTable::lookup_shared(const char* name, int len, unsigned int hash) { + Symbol* sym = NULL; if (!_shared_table.empty()) { if (_alt_hash) { // hash_code parameter may use alternate hashing algorithm but the shared table // always uses the same original hash code. hash = hash_shared_symbol(name, len); } - return _shared_table.lookup(name, hash, len); - } else { - return NULL; + sym = _shared_table.lookup(name, hash, len); + if (sym == NULL && DynamicArchive::is_mapped()) { + sym = _dynamic_shared_table.lookup(name, hash, len); + } } + return sym; } +#endif Symbol* SymbolTable::lookup_common(const char* name, int len, unsigned int hash) { @@ -467,14 +481,17 @@ Thread* THREAD = Thread::current(); do { + // Callers have looked up the symbol once, insert the symbol. + sym = allocate_symbol(name, len, heap); + if (_local_table->insert(THREAD, lookup, sym, &rehash_warning, &clean_hint)) { + break; + } + // In case another thread did a concurrent add, return value already in the table. + // This could fail if the symbol got deleted concurrently, so loop back until success. if (_local_table->get(THREAD, lookup, stg, &rehash_warning)) { sym = stg.get_res_sym(); break; } - sym = allocate_symbol(name, len, heap); - if (_local_table->insert(THREAD, lookup, sym, &rehash_warning, &clean_hint)) { - break; - } } while(true); update_needs_rehash(rehash_warning); @@ -588,6 +605,9 @@ unsigned int fixed_hash = hash_shared_symbol((const char*)sym->bytes(), sym->utf8_length()); assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length(), false), "must not rehash during dumping"); + if (DynamicDumpSharedSpaces) { + sym = DynamicArchive::original_to_target(sym); + } _writer->add(fixed_hash, MetaspaceShared::object_delta_u4(sym)); return true; } @@ -598,30 +618,43 @@ _local_table->do_safepoint_scan(copy); } -void SymbolTable::write_to_archive() { - _shared_table.reset(); +size_t SymbolTable::estimate_size_for_archive() { + return CompactHashtableWriter::estimate_size(int(_items_count)); +} - int num_buckets = CompactHashtableWriter::default_num_buckets( - _items_count); - CompactHashtableWriter writer(num_buckets, +void SymbolTable::write_to_archive(bool is_static_archive) { + _shared_table.reset(); + _dynamic_shared_table.reset(); + + CompactHashtableWriter writer(int(_items_count), &MetaspaceShared::stats()->symbol); copy_shared_symbol_table(&writer); - writer.dump(&_shared_table, "symbol"); + if (is_static_archive) { + writer.dump(&_shared_table, "symbol"); - // Verify table is correct - Symbol* sym = vmSymbols::java_lang_Object(); - const char* name = (const char*)sym->bytes(); - int len = sym->utf8_length(); - unsigned int hash = hash_symbol(name, len, _alt_hash); - assert(sym == _shared_table.lookup(name, hash, len), "sanity"); + // Verify table is correct + Symbol* sym = vmSymbols::java_lang_Object(); + const char* name = (const char*)sym->bytes(); + int len = sym->utf8_length(); + unsigned int hash = hash_symbol(name, len, _alt_hash); + assert(sym == _shared_table.lookup(name, hash, len), "sanity"); + } else { + writer.dump(&_dynamic_shared_table, "symbol"); + } } -void SymbolTable::serialize_shared_table_header(SerializeClosure* soc) { - _shared_table.serialize_header(soc); - +void SymbolTable::serialize_shared_table_header(SerializeClosure* soc, + bool is_static_archive) { + OffsetCompactHashtable * table; + if (is_static_archive) { + table = &_shared_table; + } else { + table = &_dynamic_shared_table; + } + table->serialize_header(soc); if (soc->writing()) { // Sanity. Make sure we don't use the shared table at dump time - _shared_table.reset(); + table->reset(); } } #endif //INCLUDE_CDS diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/symbolTable.hpp --- a/src/hotspot/share/classfile/symbolTable.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/symbolTable.hpp Thu May 23 11:07:37 2019 +0100 @@ -137,7 +137,7 @@ const char** name, int* lengths, int* cp_indices, unsigned int* hashValues); - static Symbol* lookup_shared(const char* name, int len, unsigned int hash); + static Symbol* lookup_shared(const char* name, int len, unsigned int hash) NOT_CDS_RETURN_(NULL); static Symbol* lookup_dynamic(const char* name, int len, unsigned int hash); static Symbol* lookup_common(const char* name, int len, unsigned int hash); @@ -209,8 +209,10 @@ private: static void copy_shared_symbol_table(CompactHashtableWriter* ch_table); public: - static void write_to_archive() NOT_CDS_RETURN; - static void serialize_shared_table_header(SerializeClosure* soc) NOT_CDS_RETURN; + static size_t estimate_size_for_archive() NOT_CDS_RETURN_(0); + static void write_to_archive(bool is_static_archive = true) NOT_CDS_RETURN; + static void serialize_shared_table_header(SerializeClosure* soc, + bool is_static_archive = true) NOT_CDS_RETURN; static void metaspace_pointers_do(MetaspaceClosure* it); // Jcmd diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/systemDictionaryShared.cpp --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp Thu May 23 11:07:37 2019 +0100 @@ -44,6 +44,7 @@ #include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" +#include "memory/dynamicArchive.hpp" #include "oops/instanceKlass.hpp" #include "oops/klass.inline.hpp" #include "oops/objArrayOop.inline.hpp" @@ -61,9 +62,10 @@ objArrayOop SystemDictionaryShared::_shared_protection_domains = NULL; objArrayOop SystemDictionaryShared::_shared_jar_urls = NULL; objArrayOop SystemDictionaryShared::_shared_jar_manifests = NULL; -DEBUG_ONLY(bool SystemDictionaryShared::_checked_excluded_classes = false;) +DEBUG_ONLY(bool SystemDictionaryShared::_no_class_loading_should_happen = false;) class DumpTimeSharedClassInfo: public CHeapObj { + bool _excluded; public: struct DTConstraint { Symbol* _name; @@ -76,7 +78,6 @@ int _id; int _clsfile_size; int _clsfile_crc32; - bool _excluded; GrowableArray* _verifier_constraints; GrowableArray* _verifier_constraint_flags; @@ -115,6 +116,15 @@ } } } + + void set_excluded() { + _excluded = true; + } + + bool is_excluded() { + // _klass may become NULL due to DynamicArchiveBuilder::set_to_null + return _excluded || _klass == NULL; + } }; class DumpTimeSharedClassTable: public ResourceHashtable< @@ -131,8 +141,8 @@ DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k) { DumpTimeSharedClassInfo* p = get(k); if (p == NULL) { - assert(!SystemDictionaryShared::checked_excluded_classes(), - "no new classes can be added after check_excluded_classes"); + assert(!SystemDictionaryShared::no_class_loading_should_happen(), + "no new classes can be loaded while dumping archive"); put(k, DumpTimeSharedClassInfo()); p = get(k); assert(p != NULL, "sanity"); @@ -146,16 +156,20 @@ public: CountClassByCategory(DumpTimeSharedClassTable* table) : _table(table) {} bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { - if (SystemDictionaryShared::is_builtin(k)) { - ++ _table->_builtin_count; - } else { - ++ _table->_unregistered_count; + if (!info.is_excluded()) { + if (info.is_builtin()) { + ++ _table->_builtin_count; + } else { + ++ _table->_unregistered_count; + } } return true; // keep on iterating } }; void update_counts() { + _builtin_count = 0; + _unregistered_count = 0; CountClassByCategory counter(this); iterate(&counter); } @@ -250,26 +264,36 @@ return (char*)(address(this) + verifier_constraint_flags_offset()); } + static u4 object_delta_u4(Symbol* sym) { + if (DynamicDumpSharedSpaces) { + sym = DynamicArchive::original_to_target(sym); + } + return MetaspaceShared::object_delta_u4(sym); + } + void init(DumpTimeSharedClassInfo& info) { _klass = info._klass; - _num_constraints = info.num_constraints(); if (!SystemDictionaryShared::is_builtin(_klass)) { CrcInfo* c = crc(); c->_clsfile_size = info._clsfile_size; c->_clsfile_crc32 = info._clsfile_crc32; } + _num_constraints = info.num_constraints(); if (_num_constraints > 0) { RTConstraint* constraints = verifier_constraints(); char* flags = verifier_constraint_flags(); int i; for (i = 0; i < _num_constraints; i++) { - constraints[i]._name = MetaspaceShared::object_delta_u4(info._verifier_constraints->at(i)._name); - constraints[i]._from_name = MetaspaceShared::object_delta_u4(info._verifier_constraints->at(i)._from_name); + constraints[i]._name = object_delta_u4(info._verifier_constraints->at(i)._name); + constraints[i]._from_name = object_delta_u4(info._verifier_constraints->at(i)._from_name); } for (i = 0; i < _num_constraints; i++) { flags[i] = info._verifier_constraint_flags->at(i); } } + if (DynamicDumpSharedSpaces) { + _klass = DynamicArchive::original_to_target(info._klass); + } } bool matches(int clsfile_size, int clsfile_crc32) const { @@ -307,7 +331,12 @@ return *info_pointer_addr(klass); } static void set_for(InstanceKlass* klass, RunTimeSharedClassInfo* record) { - *info_pointer_addr(klass) = record; + if (DynamicDumpSharedSpaces) { + klass = DynamicArchive::original_to_buffer(klass); + *info_pointer_addr(klass) = DynamicArchive::buffer_to_target(record); + } else { + *info_pointer_addr(klass) = record; + } } // Used by RunTimeSharedDictionary to implement OffsetCompactHashtable::EQUALS @@ -323,8 +352,12 @@ RunTimeSharedClassInfo::EQUALS> {}; static DumpTimeSharedClassTable* _dumptime_table = NULL; +// SystemDictionaries in the base layer static archive static RunTimeSharedDictionary _builtin_dictionary; static RunTimeSharedDictionary _unregistered_dictionary; +// SystemDictionaries in the top layer dynamic archive +static RunTimeSharedDictionary _dynamic_builtin_dictionary; +static RunTimeSharedDictionary _dynamic_unregistered_dictionary; oop SystemDictionaryShared::shared_protection_domain(int index) { return _shared_protection_domains->obj_at(index); @@ -710,6 +743,17 @@ return false; } +bool SystemDictionaryShared::has_platform_or_app_classes() { + if (FileMapInfo::current_info()->header()->has_platform_or_app_classes()) { + return true; + } + if (DynamicArchive::is_mapped() && + FileMapInfo::dynamic_info()->header()->has_platform_or_app_classes()) { + return true; + } + return false; +} + // The following stack shows how this code is reached: // // [0] SystemDictionaryShared::find_or_load_shared_class() @@ -747,7 +791,7 @@ Symbol* name, Handle class_loader, TRAPS) { InstanceKlass* k = NULL; if (UseSharedSpaces) { - if (!FileMapInfo::current_info()->header()->has_platform_or_app_classes()) { + if (!has_platform_or_app_classes()) { return NULL; } @@ -864,11 +908,17 @@ const RunTimeSharedClassInfo* record = find_record(&_unregistered_dictionary, class_name); if (record == NULL) { - return NULL; + if (DynamicArchive::is_mapped()) { + record = find_record(&_dynamic_unregistered_dictionary, class_name); + } + if (record == NULL) { + return NULL; + } } int clsfile_size = cfs->length(); int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length()); + if (!record->matches(clsfile_size, clsfile_crc32)) { return NULL; } @@ -971,6 +1021,7 @@ } DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for(InstanceKlass* k) { + MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); if (_dumptime_table == NULL) { _dumptime_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeSharedClassTable(); } @@ -978,7 +1029,7 @@ } void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) { - assert(DumpSharedSpaces, "only when dumping"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "only when dumping"); assert(!is_builtin(k), "must be unregistered class"); DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k); info->_clsfile_size = cfs->length(); @@ -990,6 +1041,28 @@ } void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) { + MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); + DumpTimeSharedClassInfo* p = _dumptime_table->get(k); + if (p == NULL) { + return; + } + if (p->_verifier_constraints != NULL) { + for (int i = 0; i < p->_verifier_constraints->length(); i++) { + DumpTimeSharedClassInfo::DTConstraint constraint = p->_verifier_constraints->at(i); + if (constraint._name != NULL ) { + constraint._name->decrement_refcount(); + } + if (constraint._from_name != NULL ) { + constraint._from_name->decrement_refcount(); + } + } + FREE_C_HEAP_ARRAY(DTConstraint, p->_verifier_constraints); + p->_verifier_constraints = NULL; + } + if (p->_verifier_constraint_flags != NULL) { + FREE_C_HEAP_ARRAY(char, p->_verifier_constraint_flags); + p->_verifier_constraint_flags = NULL; + } _dumptime_table->remove(k); } @@ -1010,9 +1083,11 @@ bool SystemDictionaryShared::should_be_excluded(InstanceKlass* k) { if (k->class_loader_data()->is_unsafe_anonymous()) { + warn_excluded(k, "Unsafe anonymous class"); return true; // unsafe anonymous classes are not archived, skip } if (k->is_in_error_state()) { + warn_excluded(k, "In error state"); return true; } if (k->shared_classpath_index() < 0 && is_builtin(k)) { @@ -1036,6 +1111,44 @@ warn_excluded(k, "JFR event class"); return true; } + if (k->init_state() < InstanceKlass::linked) { + // In static dumping, we will attempt to link all classes. Those that fail to link will + // be marked as in error state. + assert(DynamicDumpSharedSpaces, "must be"); + + // TODO -- rethink how this can be handled. + // We should try to link ik, however, we can't do it here because + // 1. We are at VM exit + // 2. linking a class may cause other classes to be loaded, which means + // a custom ClassLoader.loadClass() may be called, at a point where the + // class loader doesn't expect it. + warn_excluded(k, "Not linked"); + return true; + } + if (k->major_version() < 50 /*JAVA_6_VERSION*/) { + ResourceMark rm; + log_warning(cds)("Pre JDK 6 class not supported by CDS: %u.%u %s", + k->major_version(), k->minor_version(), k->name()->as_C_string()); + return true; + } + + InstanceKlass* super = k->java_super(); + if (super != NULL && should_be_excluded(super)) { + ResourceMark rm; + log_warning(cds)("Skipping %s: super class %s is excluded", k->name()->as_C_string(), super->name()->as_C_string()); + return true; + } + + Array* interfaces = k->local_interfaces(); + int len = interfaces->length(); + for (int i = 0; i < len; i++) { + InstanceKlass* intf = interfaces->at(i); + if (should_be_excluded(intf)) { + log_warning(cds)("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string()); + return true; + } + } + return false; } @@ -1044,8 +1157,9 @@ ResourceMark rm; const char* name = k->name()->as_C_string(); DumpTimeSharedClassInfo* info = _dumptime_table->get(k); + assert(_no_class_loading_should_happen, "class loading must be disabled"); guarantee(info != NULL, "Class %s must be entered into _dumptime_table", name); - guarantee(!info->_excluded, "Should not attempt to archive excluded class %s", name); + guarantee(!info->is_excluded(), "Should not attempt to archive excluded class %s", name); if (is_builtin(k)) { guarantee(k->loader_type() != 0, "Class loader type must be set for BUILTIN class %s", name); @@ -1059,7 +1173,7 @@ public: bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { if (SystemDictionaryShared::should_be_excluded(k)) { - info._excluded = true; + info.set_excluded(); } return true; // keep on iterating } @@ -1068,13 +1182,13 @@ void SystemDictionaryShared::check_excluded_classes() { ExcludeDumpTimeSharedClasses excl; _dumptime_table->iterate(&excl); - DEBUG_ONLY(_checked_excluded_classes = true;) + _dumptime_table->update_counts(); } bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) { - assert(_checked_excluded_classes, "sanity"); - assert(DumpSharedSpaces, "only when dumping"); - return find_or_allocate_info_for(k)->_excluded; + assert(_no_class_loading_should_happen, "sanity"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "only when dumping"); + return find_or_allocate_info_for(k)->is_excluded(); } class IterateDumpTimeSharedClassTable : StackObj { @@ -1083,7 +1197,7 @@ IterateDumpTimeSharedClassTable(MetaspaceClosure* it) : _it(it) {} bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { - if (!info._excluded) { + if (!info.is_excluded()) { info.metaspace_pointers_do(_it); } return true; // keep on iterating @@ -1097,18 +1211,27 @@ bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name, Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) { - assert(DumpSharedSpaces, "called at dump time only"); + assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "called at dump time only"); DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k); info->add_verification_constraint(k, name, from_name, from_field_is_protected, from_is_array, from_is_object); - if (is_builtin(k)) { - // For builtin class loaders, we can try to complete the verification check at dump time, - // because we can resolve all the constraint classes. + + if (DynamicDumpSharedSpaces) { + // For dynamic dumping, we can resolve all the constraint classes for all class loaders during + // the initial run prior to creating the archive before vm exit. We will also perform verification + // check when running with the archive. return false; } else { - // For non-builtin class loaders, we cannot complete the verification check at dump time, - // because at dump time we don't know how to resolve classes for such loaders. - return true; + if (is_builtin(k)) { + // For builtin class loaders, we can try to complete the verification check at dump time, + // because we can resolve all the constraint classes. We will also perform verification check + // when running with the archive. + return false; + } else { + // For non-builtin class loaders, we cannot complete the verification check at dump time, + // because at dump time we don't know how to resolve classes for such loaders. + return true; + } } } @@ -1139,9 +1262,9 @@ if (log_is_enabled(Trace, cds, verification)) { ResourceMark rm; - log_trace(cds, verification)("add_verification_constraint: %s: %s must be subclass of %s", + log_trace(cds, verification)("add_verification_constraint: %s: %s must be subclass of %s [0x%x]", k->external_name(), from_name->as_klass_external_name(), - name->as_klass_external_name()); + name->as_klass_external_name(), c); } } @@ -1157,6 +1280,13 @@ Symbol* from_name = record->get_constraint_from_name(i); char c = record->get_constraint_flag(i); + if (log_is_enabled(Trace, cds, verification)) { + ResourceMark rm(THREAD); + log_trace(cds, verification)("check_verification_constraint: %s: %s must be subclass of %s [0x%x]", + klass->external_name(), from_name->as_klass_external_name(), + name->as_klass_external_name(), c); + } + bool from_field_is_protected = (c & SystemDictionaryShared::FROM_FIELD_IS_PROTECTED) ? true : false; bool from_is_array = (c & SystemDictionaryShared::FROM_IS_ARRAY) ? true : false; bool from_is_object = (c & SystemDictionaryShared::FROM_IS_OBJECT) ? true : false; @@ -1178,22 +1308,72 @@ } } +class EstimateSizeForArchive : StackObj { + size_t _shared_class_info_size; + int _num_builtin_klasses; + int _num_unregistered_klasses; + +public: + EstimateSizeForArchive() { + _shared_class_info_size = 0; + _num_builtin_klasses = 0; + _num_unregistered_klasses = 0; + } + + bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { + if (!info.is_excluded()) { + size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_constraints()); + _shared_class_info_size += align_up(byte_size, BytesPerWord); + } + return true; // keep on iterating + } + + size_t total() { + return _shared_class_info_size; + } +}; + +size_t SystemDictionaryShared::estimate_size_for_archive() { + EstimateSizeForArchive est; + _dumptime_table->iterate(&est); + return est.total() + + CompactHashtableWriter::estimate_size(_dumptime_table->count_of(true)) + + CompactHashtableWriter::estimate_size(_dumptime_table->count_of(false)); +} + class CopySharedClassInfoToArchive : StackObj { CompactHashtableWriter* _writer; bool _is_builtin; public: - CopySharedClassInfoToArchive(CompactHashtableWriter* writer, bool is_builtin) + CopySharedClassInfoToArchive(CompactHashtableWriter* writer, + bool is_builtin, + bool is_static_archive) : _writer(writer), _is_builtin(is_builtin) {} bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { - if (!info._excluded && info.is_builtin() == _is_builtin) { + if (!info.is_excluded() && info.is_builtin() == _is_builtin) { size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_constraints()); - RunTimeSharedClassInfo* record = - (RunTimeSharedClassInfo*)MetaspaceShared::read_only_space_alloc(byte_size); + RunTimeSharedClassInfo* record; + record = (RunTimeSharedClassInfo*)MetaspaceShared::read_only_space_alloc(byte_size); record->init(info); - unsigned int hash = primitive_hash(info._klass->name()); - _writer->add(hash, MetaspaceShared::object_delta_u4(record)); + unsigned int hash; + Symbol* name = info._klass->name(); + if (DynamicDumpSharedSpaces) { + name = DynamicArchive::original_to_target(name); + } + hash = primitive_hash(name); + u4 delta; + if (DynamicDumpSharedSpaces) { + delta = MetaspaceShared::object_delta_u4(DynamicArchive::buffer_to_target(record)); + } else { + delta = MetaspaceShared::object_delta_u4(record); + } + _writer->add(hash, delta); + if (log_is_enabled(Trace, cds, hashtables)) { + ResourceMark rm; + log_trace(cds,hashtables)("%s dictionary: %s", (_is_builtin ? "builtin" : "unregistered"), info._klass->external_name()); + } // Save this for quick runtime lookup of InstanceKlass* -> RunTimeSharedClassInfo* RunTimeSharedClassInfo::set_for(info._klass, record); @@ -1202,25 +1382,36 @@ } }; -void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin) { +void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, + bool is_builtin, + bool is_static_archive) { CompactHashtableStats stats; dictionary->reset(); - int num_buckets = CompactHashtableWriter::default_num_buckets(_dumptime_table->count_of(is_builtin)); - CompactHashtableWriter writer(num_buckets, &stats); - CopySharedClassInfoToArchive copy(&writer, is_builtin); + CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin), &stats); + CopySharedClassInfoToArchive copy(&writer, is_builtin, is_static_archive); _dumptime_table->iterate(©); writer.dump(dictionary, is_builtin ? "builtin dictionary" : "unregistered dictionary"); } -void SystemDictionaryShared::write_to_archive() { - _dumptime_table->update_counts(); - write_dictionary(&_builtin_dictionary, true); - write_dictionary(&_unregistered_dictionary, false); +void SystemDictionaryShared::write_to_archive(bool is_static_archive) { + if (is_static_archive) { + write_dictionary(&_builtin_dictionary, true); + write_dictionary(&_unregistered_dictionary, false); + } else { + write_dictionary(&_dynamic_builtin_dictionary, true); + write_dictionary(&_dynamic_unregistered_dictionary, false); + } } -void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc) { - _builtin_dictionary.serialize_header(soc); - _unregistered_dictionary.serialize_header(soc); +void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc, + bool is_static_archive) { + if (is_static_archive) { + _builtin_dictionary.serialize_header(soc); + _unregistered_dictionary.serialize_header(soc); + } else { + _dynamic_builtin_dictionary.serialize_header(soc); + _dynamic_unregistered_dictionary.serialize_header(soc); + } } const RunTimeSharedClassInfo* @@ -1237,9 +1428,16 @@ const RunTimeSharedClassInfo* record = find_record(&_builtin_dictionary, name); if (record) { return record->_klass; - } else { - return NULL; } + + if (DynamicArchive::is_mapped()) { + record = find_record(&_dynamic_builtin_dictionary, name); + if (record) { + return record->_klass; + } + } + + return NULL; } void SystemDictionaryShared::update_shared_entry(InstanceKlass* k, int id) { @@ -1266,14 +1464,31 @@ SharedDictionaryPrinter p(st); _builtin_dictionary.iterate(&p); _unregistered_dictionary.iterate(&p); + if (DynamicArchive::is_mapped()) { + _dynamic_builtin_dictionary.iterate(&p); + _unregistered_dictionary.iterate(&p); + } } } -void SystemDictionaryShared::print() { print_on(tty); } - void SystemDictionaryShared::print_table_statistics(outputStream* st) { if (UseSharedSpaces) { _builtin_dictionary.print_table_statistics(st, "Builtin Shared Dictionary"); _unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary"); + if (DynamicArchive::is_mapped()) { + _dynamic_builtin_dictionary.print_table_statistics(st, "Dynamic Builtin Shared Dictionary"); + _dynamic_unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary"); + } } } + +bool SystemDictionaryShared::empty_dumptime_table() { + if (_dumptime_table == NULL) { + return true; + } + _dumptime_table->update_counts(); + if (_dumptime_table->count_of(true) == 0 && _dumptime_table->count_of(false) == 0){ + return true; + } + return false; +} diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/systemDictionaryShared.hpp --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp Thu May 23 11:07:37 2019 +0100 @@ -109,6 +109,7 @@ class RunTimeSharedDictionary; class SystemDictionaryShared: public SystemDictionary { + friend class ExcludeDumpTimeSharedClasses; public: enum { FROM_FIELD_IS_PROTECTED = 1 << 0, @@ -211,16 +212,21 @@ const ClassFileStream* cfs, TRAPS); static DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k); - static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin); + static void write_dictionary(RunTimeSharedDictionary* dictionary, + bool is_builtin, + bool is_static_archive = true); static bool is_jfr_event_class(InstanceKlass *k); static void warn_excluded(InstanceKlass* k, const char* reason); + static bool should_be_excluded(InstanceKlass* k); - DEBUG_ONLY(static bool _checked_excluded_classes;) + DEBUG_ONLY(static bool _no_class_loading_should_happen;) public: static InstanceKlass* find_builtin_class(Symbol* class_name); static const RunTimeSharedClassInfo* find_record(RunTimeSharedDictionary* dict, Symbol* name); + static bool has_platform_or_app_classes(); + // Called by PLATFORM/APP loader only static InstanceKlass* find_or_load_shared_class(Symbol* class_name, Handle class_loader, @@ -288,18 +294,34 @@ static bool is_builtin(InstanceKlass* k) { return (k->shared_classpath_index() != UNREGISTERED_INDEX); } - static bool should_be_excluded(InstanceKlass* k); static void check_excluded_classes(); static void validate_before_archiving(InstanceKlass* k); static bool is_excluded_class(InstanceKlass* k); static void dumptime_classes_do(class MetaspaceClosure* it); - static void write_to_archive(); - static void serialize_dictionary_headers(class SerializeClosure* soc); - static void print(); + static size_t estimate_size_for_archive(); + static void write_to_archive(bool is_static_archive = true); + static void serialize_dictionary_headers(class SerializeClosure* soc, + bool is_static_archive = true); + static void print() { return print_on(tty); } static void print_on(outputStream* st) NOT_CDS_RETURN; static void print_table_statistics(outputStream* st) NOT_CDS_RETURN; + static bool empty_dumptime_table() NOT_CDS_RETURN_(true); - DEBUG_ONLY(static bool checked_excluded_classes() {return _checked_excluded_classes;}) + DEBUG_ONLY(static bool no_class_loading_should_happen() {return _no_class_loading_should_happen;}) + +#ifdef ASSERT + class NoClassLoadingMark: public StackObj { + public: + NoClassLoadingMark() { + assert(!_no_class_loading_should_happen, "must not be nested"); + _no_class_loading_should_happen = true; + } + ~NoClassLoadingMark() { + _no_class_loading_should_happen = false; + } + }; +#endif + }; #endif // SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/classfile/verificationType.cpp --- a/src/hotspot/share/classfile/verificationType.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/classfile/verificationType.cpp Thu May 23 11:07:37 2019 +0100 @@ -94,12 +94,14 @@ return true; } - if (DumpSharedSpaces && SystemDictionaryShared::add_verification_constraint(klass, + if (DumpSharedSpaces || DynamicDumpSharedSpaces) { + if (SystemDictionaryShared::add_verification_constraint(klass, name(), from.name(), from_field_is_protected, from.is_array(), from.is_object())) { - // If add_verification_constraint() returns true, the resolution/check should be - // delayed until runtime. - return true; + // If add_verification_constraint() returns true, the resolution/check should be + // delayed until runtime. + return true; + } } return resolve_and_check_assignability(klass, name(), from.name(), diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/codeBlob.cpp --- a/src/hotspot/share/code/codeBlob.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/codeBlob.cpp Thu May 23 11:07:37 2019 +0100 @@ -183,8 +183,14 @@ jio_snprintf(stub_id, sizeof(stub_id), "%s%s", name1, name2); if (PrintStubCode) { ttyLocker ttyl; + tty->print_cr("- - - [BEGIN] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, (intptr_t) stub); - Disassembler::decode(stub->code_begin(), stub->code_end()); + Disassembler::decode(stub->code_begin(), stub->code_end(), tty); + if ((stub->oop_maps() != NULL) && AbstractDisassembler::show_structs()) { + tty->print_cr("- - - [OOP MAPS]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + stub->oop_maps()->print(); + } + tty->print_cr("- - - [END] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); tty->cr(); } Forte::register_stub(stub_id, stub->code_begin(), stub->code_end()); @@ -263,6 +269,7 @@ } void BufferBlob::free(BufferBlob *blob) { + assert(blob != NULL, "caller must check for NULL"); ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock blob->flush(); { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/codeBlob.hpp --- a/src/hotspot/share/code/codeBlob.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/codeBlob.hpp Thu May 23 11:07:37 2019 +0100 @@ -211,7 +211,7 @@ const ImmutableOopMap* oop_map_for_return_address(address return_address); virtual void preserve_callee_argument_oops(frame fr, const RegisterMap* reg_map, OopClosure* f) = 0; - // Frame support + // Frame support. Sizes are in word units. int frame_size() const { return _frame_size; } void set_frame_size(int size) { _frame_size = size; } @@ -230,6 +230,10 @@ void dump_for_addr(address addr, outputStream* st, bool verbose) const; void print_code(); + bool has_block_comment(address block_begin) const { + intptr_t offset = (intptr_t)(block_begin - code_begin()); + return _strings.has_block_comment(offset); + } // Print the comment associated with offset on stream, if there is one virtual void print_block_comment(outputStream* stream, address block_begin) const { intptr_t offset = (intptr_t)(block_begin - code_begin()); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/codeCache.cpp --- a/src/hotspot/share/code/codeCache.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/codeCache.cpp Thu May 23 11:07:37 2019 +0100 @@ -283,9 +283,9 @@ // Verify sizes and update flag values assert(non_profiled_size + profiled_size + non_nmethod_size == cache_size, "Invalid code heap sizes"); - FLAG_SET_ERGO(uintx, NonNMethodCodeHeapSize, non_nmethod_size); - FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, profiled_size); - FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, non_profiled_size); + FLAG_SET_ERGO(NonNMethodCodeHeapSize, non_nmethod_size); + FLAG_SET_ERGO(ProfiledCodeHeapSize, profiled_size); + FLAG_SET_ERGO(NonProfiledCodeHeapSize, non_profiled_size); // If large page support is enabled, align code heaps according to large // page size to make sure that code cache is covered by large pages. @@ -941,9 +941,9 @@ initialize_heaps(); } else { // Use a single code heap - FLAG_SET_ERGO(uintx, NonNMethodCodeHeapSize, 0); - FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, 0); - FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, 0); + FLAG_SET_ERGO(NonNMethodCodeHeapSize, 0); + FLAG_SET_ERGO(ProfiledCodeHeapSize, 0); + FLAG_SET_ERGO(NonProfiledCodeHeapSize, 0); ReservedCodeSpace rs = reserve_heap_memory(ReservedCodeCacheSize); add_heap(rs, "CodeCache", CodeBlobType::All); } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/codeHeapState.cpp --- a/src/hotspot/share/code/codeHeapState.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/codeHeapState.cpp Thu May 23 11:07:37 2019 +0100 @@ -124,7 +124,7 @@ size_t _nlockedflush = 0; \ size_t _nflush_bytes = 0; \ size_t _capacity = _capa; \ - bufferedStream _sstobj = bufferedStream(_capa); \ + bufferedStream _sstobj(_capa); \ bufferedStream* _sstbuf = &_sstobj; \ outputStream* _outbuf = _outst; \ bufferedStream* _anyst = &_sstobj; /* any stream. Use this to just print - no buffer flush. */ diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/exceptionHandlerTable.cpp --- a/src/hotspot/share/code/exceptionHandlerTable.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/exceptionHandlerTable.cpp Thu May 23 11:07:37 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -185,10 +185,24 @@ } void ImplicitExceptionTable::print(address base) const { - tty->print("{"); - for( uint i=0; iprint("< " INTPTR_FORMAT ", " INTPTR_FORMAT " > ", p2i(base + *adr(i)), p2i(base + *(adr(i)+1))); - tty->print_cr("}"); + const uint n = len(); + if (n > 0) { + const uint items_per_line = 3; + uint i; + tty->print_cr("ImplicitExceptionTable (size = %d entries, %d bytes):", n, size_in_bytes()); + tty->print("{"); + for (i = 0; i < n; i++) { + if (i%items_per_line == 0) { + tty->cr(); + tty->fill_to(3); + } + tty->print("< " INTPTR_FORMAT ", " INTPTR_FORMAT " > ", p2i(base + *adr(i)), p2i(base + *(adr(i)+1))); + } + tty->bol(); + tty->print_cr("}"); + } else { + tty->print_cr("ImplicitExceptionTable is empty"); + } } ImplicitExceptionTable::ImplicitExceptionTable(const nmethod* nm) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/nmethod.cpp --- a/src/hotspot/share/code/nmethod.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/nmethod.cpp Thu May 23 11:07:37 2019 +0100 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "jvm.h" +#include "asm/assembler.inline.hpp" #include "code/codeCache.hpp" #include "code/compiledIC.hpp" #include "code/compiledMethod.inline.hpp" @@ -456,14 +457,17 @@ { MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); int native_nmethod_size = CodeBlob::allocation_size(code_buffer, sizeof(nmethod)); + CodeOffsets offsets; offsets.set_value(CodeOffsets::Verified_Entry, vep_offset); offsets.set_value(CodeOffsets::Frame_Complete, frame_complete); - nm = new (native_nmethod_size, CompLevel_none) nmethod(method(), compiler_none, native_nmethod_size, - compile_id, &offsets, - code_buffer, frame_size, - basic_lock_owner_sp_offset, - basic_lock_sp_offset, oop_maps); + nm = new (native_nmethod_size, CompLevel_none) + nmethod(method(), compiler_none, native_nmethod_size, + compile_id, &offsets, + code_buffer, frame_size, + basic_lock_owner_sp_offset, + basic_lock_sp_offset, + oop_maps); NOT_PRODUCT(if (nm != NULL) native_nmethod_stats.note_native_nmethod(nm)); } @@ -593,9 +597,9 @@ _native_basic_lock_sp_offset(basic_lock_sp_offset) { { - int scopes_data_offset = 0; - int deoptimize_offset = 0; - int deoptimize_mh_offset = 0; + int scopes_data_offset = 0; + int deoptimize_offset = 0; + int deoptimize_mh_offset = 0; debug_only(NoSafepointVerifier nsv;) assert_locked_or_safepoint(CodeCache_lock); @@ -658,18 +662,32 @@ xtty->stamp(); xtty->end_head(" address='" INTPTR_FORMAT "'", (intptr_t) this); } - // print the header part first - print(); - // then print the requested information + // Print the header part, then print the requested information. + // This is both handled in decode2(), called via print_code() -> decode() if (PrintNativeNMethods) { + tty->print_cr("-------------------------- Assembly (native nmethod) ---------------------------"); print_code(); - if (oop_maps != NULL) { - oop_maps->print(); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); +#if defined(SUPPORT_DATA_STRUCTS) + if (AbstractDisassembler::show_structs()) { + if (oop_maps != NULL) { + tty->print("oop maps:"); // oop_maps->print_on(tty) outputs a cr() at the beginning + oop_maps->print_on(tty); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + } + } +#endif + } else { + print(); // print the header part only. + } +#if defined(SUPPORT_DATA_STRUCTS) + if (AbstractDisassembler::show_structs()) { + if (PrintRelocations) { + print_relocations(); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); } } - if (PrintRelocations) { - print_relocations(); - } +#endif if (xtty != NULL) { xtty->tail("print_native_nmethod"); } @@ -746,22 +764,21 @@ } else { _deopt_mh_handler_begin = NULL; } - } else { + } else #endif - // Exception handler and deopt handler are in the stub section - assert(offsets->value(CodeOffsets::Exceptions) != -1, "must be set"); - assert(offsets->value(CodeOffsets::Deopt ) != -1, "must be set"); - - _exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions); - _deopt_handler_begin = (address) this + _stub_offset + offsets->value(CodeOffsets::Deopt); - if (offsets->value(CodeOffsets::DeoptMH) != -1) { - _deopt_mh_handler_begin = (address) this + _stub_offset + offsets->value(CodeOffsets::DeoptMH); - } else { - _deopt_mh_handler_begin = NULL; + { + // Exception handler and deopt handler are in the stub section + assert(offsets->value(CodeOffsets::Exceptions) != -1, "must be set"); + assert(offsets->value(CodeOffsets::Deopt ) != -1, "must be set"); + + _exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions); + _deopt_handler_begin = (address) this + _stub_offset + offsets->value(CodeOffsets::Deopt); + if (offsets->value(CodeOffsets::DeoptMH) != -1) { + _deopt_mh_handler_begin = (address) this + _stub_offset + offsets->value(CodeOffsets::DeoptMH); + } else { + _deopt_mh_handler_begin = NULL; + } } -#if INCLUDE_JVMCI - } -#endif if (offsets->value(CodeOffsets::UnwindHandler) != -1) { _unwind_handler_offset = code_offset() + offsets->value(CodeOffsets::UnwindHandler); } else { @@ -787,8 +804,7 @@ _verified_entry_point = code_begin() + offsets->value(CodeOffsets::Verified_Entry); _osr_entry_point = code_begin() + offsets->value(CodeOffsets::OSR_Entry); _exception_cache = NULL; - - _scopes_data_begin = (address) this + scopes_data_offset; + _scopes_data_begin = (address) this + scopes_data_offset; _pc_desc_container.reset_to(scopes_pcs_begin()); @@ -909,33 +925,72 @@ xtty->stamp(); xtty->end_head(); } - // print the header part first - print(); - // then print the requested information + // Print the header part, then print the requested information. + // This is both handled in decode2(). if (printmethod) { - print_code(); - print_pcs(); - if (oop_maps()) { - oop_maps()->print(); + HandleMark hm; + ResourceMark m; + if (is_compiled_by_c1()) { + tty->cr(); + tty->print_cr("============================= C1-compiled nmethod =============================="); + } + if (is_compiled_by_jvmci()) { + tty->cr(); + tty->print_cr("=========================== JVMCI-compiled nmethod ============================="); + } + tty->print_cr("----------------------------------- Assembly -----------------------------------"); + decode2(tty); +#if defined(SUPPORT_DATA_STRUCTS) + if (AbstractDisassembler::show_structs()) { + // Print the oops from the underlying CodeBlob as well. + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + print_oops(tty); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + print_metadata(tty); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + print_pcs(); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + if (oop_maps() != NULL) { + tty->print("oop maps:"); // oop_maps()->print_on(tty) outputs a cr() at the beginning + oop_maps()->print_on(tty); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + } + } +#endif + } else { + print(); // print the header part only. + } + +#if defined(SUPPORT_DATA_STRUCTS) + if (AbstractDisassembler::show_structs()) { + if (printmethod || PrintDebugInfo || CompilerOracle::has_option_string(_method, "PrintDebugInfo")) { + print_scopes(); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + } + if (printmethod || PrintRelocations || CompilerOracle::has_option_string(_method, "PrintRelocations")) { + print_relocations(); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + } + if (printmethod || PrintDependencies || CompilerOracle::has_option_string(_method, "PrintDependencies")) { + print_dependencies(); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + } + if (printmethod || PrintExceptionHandlers) { + print_handler_table(); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + print_nul_chk_table(); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + } + + if (printmethod) { + print_recorded_oops(); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + print_recorded_metadata(); + tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); } } - if (printmethod || PrintDebugInfo || CompilerOracle::has_option_string(_method, "PrintDebugInfo")) { - print_scopes(); - } - if (printmethod || PrintRelocations || CompilerOracle::has_option_string(_method, "PrintRelocations")) { - print_relocations(); - } - if (printmethod || PrintDependencies || CompilerOracle::has_option_string(_method, "PrintDependencies")) { - print_dependencies(); - } - if (printmethod || PrintExceptionHandlers) { - print_handler_table(); - print_nul_chk_table(); - } - if (printmethod) { - print_recorded_oops(); - print_recorded_metadata(); - } +#endif + if (xtty != NULL) { xtty->tail("print_nmethod"); } @@ -2062,10 +2117,9 @@ assert(cb != NULL && cb == this, ""); ttyLocker ttyl; tty->print_cr("implicit exception happened at " INTPTR_FORMAT, p2i(pc)); - print(); + // Print all available nmethod info. + print_nmethod(true); method()->print_codes(); - print_code(); - print_pcs(); } #endif if (cont_offset == 0) { @@ -2076,7 +2130,6 @@ } - void nmethod_init() { // make sure you didn't forget to adjust the filler fields assert(sizeof(nmethod) % oopSize == 0, "nmethod size must be multiple of a word"); @@ -2124,12 +2177,14 @@ bool ok() { return _ok; } virtual void do_oop(oop* p) { if (oopDesc::is_oop_or_null(*p)) return; + // Print diagnostic information before calling print_nmethod(). + // Assertions therein might prevent call from returning. + tty->print_cr("*** non-oop " PTR_FORMAT " found at " PTR_FORMAT " (offset %d)", + p2i(*p), p2i(p), (int)((intptr_t)p - (intptr_t)_nm)); if (_ok) { _nm->print_nmethod(true); _ok = false; } - tty->print_cr("*** non-oop " PTR_FORMAT " found at " PTR_FORMAT " (offset %d)", - p2i(*p), p2i(p), (int)((intptr_t)p - (intptr_t)_nm)); } virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); } }; @@ -2238,107 +2293,104 @@ // Printing operations void nmethod::print() const { + ttyLocker ttyl; // keep the following output all in one block + print(tty); +} + +void nmethod::print(outputStream* st) const { ResourceMark rm; - ttyLocker ttyl; // keep the following output all in one block - - tty->print("Compiled method "); + + st->print("Compiled method "); if (is_compiled_by_c1()) { - tty->print("(c1) "); + st->print("(c1) "); } else if (is_compiled_by_c2()) { - tty->print("(c2) "); + st->print("(c2) "); } else if (is_compiled_by_jvmci()) { - tty->print("(JVMCI) "); + st->print("(JVMCI) "); } else { - tty->print("(nm) "); + st->print("(n/a) "); } print_on(tty, NULL); if (WizardMode) { - tty->print("((nmethod*) " INTPTR_FORMAT ") ", p2i(this)); - tty->print(" for method " INTPTR_FORMAT , p2i(method())); - tty->print(" { "); - tty->print_cr("%s ", state()); - tty->print_cr("}:"); + st->print("((nmethod*) " INTPTR_FORMAT ") ", p2i(this)); + st->print(" for method " INTPTR_FORMAT , p2i(method())); + st->print(" { "); + st->print_cr("%s ", state()); + st->print_cr("}:"); } - if (size () > 0) tty->print_cr(" total in heap [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(this), - p2i(this) + size(), - size()); - if (relocation_size () > 0) tty->print_cr(" relocation [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(relocation_begin()), - p2i(relocation_end()), - relocation_size()); - if (consts_size () > 0) tty->print_cr(" constants [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(consts_begin()), - p2i(consts_end()), - consts_size()); - if (insts_size () > 0) tty->print_cr(" main code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(insts_begin()), - p2i(insts_end()), - insts_size()); - if (stub_size () > 0) tty->print_cr(" stub code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(stub_begin()), - p2i(stub_end()), - stub_size()); - if (oops_size () > 0) tty->print_cr(" oops [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(oops_begin()), - p2i(oops_end()), - oops_size()); - if (metadata_size () > 0) tty->print_cr(" metadata [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(metadata_begin()), - p2i(metadata_end()), - metadata_size()); - if (scopes_data_size () > 0) tty->print_cr(" scopes data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(scopes_data_begin()), - p2i(scopes_data_end()), - scopes_data_size()); - if (scopes_pcs_size () > 0) tty->print_cr(" scopes pcs [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(scopes_pcs_begin()), - p2i(scopes_pcs_end()), - scopes_pcs_size()); - if (dependencies_size () > 0) tty->print_cr(" dependencies [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(dependencies_begin()), - p2i(dependencies_end()), - dependencies_size()); - if (handler_table_size() > 0) tty->print_cr(" handler table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(handler_table_begin()), - p2i(handler_table_end()), - handler_table_size()); - if (nul_chk_table_size() > 0) tty->print_cr(" nul chk table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(nul_chk_table_begin()), - p2i(nul_chk_table_end()), - nul_chk_table_size()); + if (size () > 0) st->print_cr(" total in heap [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(this), + p2i(this) + size(), + size()); + if (relocation_size () > 0) st->print_cr(" relocation [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(relocation_begin()), + p2i(relocation_end()), + relocation_size()); + if (consts_size () > 0) st->print_cr(" constants [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(consts_begin()), + p2i(consts_end()), + consts_size()); + if (insts_size () > 0) st->print_cr(" main code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(insts_begin()), + p2i(insts_end()), + insts_size()); + if (stub_size () > 0) st->print_cr(" stub code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(stub_begin()), + p2i(stub_end()), + stub_size()); + if (oops_size () > 0) st->print_cr(" oops [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(oops_begin()), + p2i(oops_end()), + oops_size()); + if (metadata_size () > 0) st->print_cr(" metadata [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(metadata_begin()), + p2i(metadata_end()), + metadata_size()); + if (scopes_data_size () > 0) st->print_cr(" scopes data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(scopes_data_begin()), + p2i(scopes_data_end()), + scopes_data_size()); + if (scopes_pcs_size () > 0) st->print_cr(" scopes pcs [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(scopes_pcs_begin()), + p2i(scopes_pcs_end()), + scopes_pcs_size()); + if (dependencies_size () > 0) st->print_cr(" dependencies [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(dependencies_begin()), + p2i(dependencies_end()), + dependencies_size()); + if (handler_table_size() > 0) st->print_cr(" handler table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(handler_table_begin()), + p2i(handler_table_end()), + handler_table_size()); + if (nul_chk_table_size() > 0) st->print_cr(" nul chk table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(nul_chk_table_begin()), + p2i(nul_chk_table_end()), + nul_chk_table_size()); #if INCLUDE_JVMCI - if (speculations_size () > 0) tty->print_cr(" speculations [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(speculations_begin()), - p2i(speculations_end()), - speculations_size()); - if (jvmci_data_size () > 0) tty->print_cr(" JVMCI data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", - p2i(jvmci_data_begin()), - p2i(jvmci_data_end()), - jvmci_data_size()); + if (speculations_size () > 0) st->print_cr(" speculations [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(speculations_begin()), + p2i(speculations_end()), + speculations_size()); + if (jvmci_data_size () > 0) st->print_cr(" JVMCI data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d", + p2i(jvmci_data_begin()), + p2i(jvmci_data_end()), + jvmci_data_size()); #endif } -#ifndef PRODUCT - -void nmethod::print_scopes() { - // Find the first pc desc for all scopes in the code and print it. - ResourceMark rm; - for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) { - if (p->scope_decode_offset() == DebugInformationRecorder::serialized_null) - continue; - - ScopeDesc* sd = scope_desc_at(p->real_pc(this)); - while (sd != NULL) { - sd->print_on(tty, p); - sd = sd->sender(); - } - } +void nmethod::print_code() { + HandleMark hm; + ResourceMark m; + ttyLocker ttyl; + // Call the specialized decode method of this class. + decode(tty); } +#ifndef PRODUCT // called InstanceKlass methods are available only then. Declared as PRODUCT_RETURN + void nmethod::print_dependencies() { ResourceMark rm; ttyLocker ttyl; // keep the following output all in one block @@ -2354,57 +2406,379 @@ deps.log_dependency(); // put it into the xml log also } } - - +#endif + +#if defined(SUPPORT_DATA_STRUCTS) + +// Print the oops from the underlying CodeBlob. +void nmethod::print_oops(outputStream* st) { + HandleMark hm; + ResourceMark m; + st->print("Oops:"); + if (oops_begin() < oops_end()) { + st->cr(); + for (oop* p = oops_begin(); p < oops_end(); p++) { + Disassembler::print_location((unsigned char*)p, (unsigned char*)oops_begin(), (unsigned char*)oops_end(), st, true, false); + st->print(PTR_FORMAT " ", *((uintptr_t*)p)); + if (*p == Universe::non_oop_word()) { + st->print_cr("NON_OOP"); + continue; // skip non-oops + } + if (*p == NULL) { + st->print_cr("NULL-oop"); + continue; // skip non-oops + } + (*p)->print_value_on(st); + st->cr(); + } + } else { + st->print_cr(" "); + } +} + +// Print metadata pool. +void nmethod::print_metadata(outputStream* st) { + HandleMark hm; + ResourceMark m; + st->print("Metadata:"); + if (metadata_begin() < metadata_end()) { + st->cr(); + for (Metadata** p = metadata_begin(); p < metadata_end(); p++) { + Disassembler::print_location((unsigned char*)p, (unsigned char*)metadata_begin(), (unsigned char*)metadata_end(), st, true, false); + st->print(PTR_FORMAT " ", *((uintptr_t*)p)); + if (*p && *p != Universe::non_oop_word()) { + (*p)->print_value_on(st); + } + st->cr(); + } + } else { + st->print_cr(" "); + } +} + +#ifndef PRODUCT // ScopeDesc::print_on() is available only then. Declared as PRODUCT_RETURN +void nmethod::print_scopes_on(outputStream* st) { + // Find the first pc desc for all scopes in the code and print it. + ResourceMark rm; + st->print("scopes:"); + if (scopes_pcs_begin() < scopes_pcs_end()) { + st->cr(); + for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) { + if (p->scope_decode_offset() == DebugInformationRecorder::serialized_null) + continue; + + ScopeDesc* sd = scope_desc_at(p->real_pc(this)); + while (sd != NULL) { + sd->print_on(st, p); // print output ends with a newline + sd = sd->sender(); + } + } + } else { + st->print_cr(" "); + } +} +#endif + +#ifndef PRODUCT // RelocIterator does support printing only then. void nmethod::print_relocations() { ResourceMark m; // in case methods get printed via the debugger tty->print_cr("relocations:"); RelocIterator iter(this); iter.print(); } - - -void nmethod::print_pcs() { +#endif + +void nmethod::print_pcs_on(outputStream* st) { ResourceMark m; // in case methods get printed via debugger - tty->print_cr("pc-bytecode offsets:"); - for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) { - p->print(this); + st->print("pc-bytecode offsets:"); + if (scopes_pcs_begin() < scopes_pcs_end()) { + st->cr(); + for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) { + p->print_on(st, this); // print output ends with a newline + } + } else { + st->print_cr(" "); } } +void nmethod::print_handler_table() { + ExceptionHandlerTable(this).print(); +} + +void nmethod::print_nul_chk_table() { + ImplicitExceptionTable(this).print(code_begin()); +} + void nmethod::print_recorded_oops() { - tty->print_cr("Recorded oops:"); - for (int i = 0; i < oops_count(); i++) { - oop o = oop_at(i); - tty->print("#%3d: " INTPTR_FORMAT " ", i, p2i(o)); - if (o == Universe::non_oop_word()) { - tty->print("non-oop word"); - } else { - if (o != NULL) { - o->print_value(); + const int n = oops_count(); + const int log_n = (n<10) ? 1 : (n<100) ? 2 : (n<1000) ? 3 : (n<10000) ? 4 : 6; + tty->print("Recorded oops:"); + if (n > 0) { + tty->cr(); + for (int i = 0; i < n; i++) { + oop o = oop_at(i); + tty->print("#%*d: " INTPTR_FORMAT " ", log_n, i, p2i(o)); + if (o == (oop)Universe::non_oop_word()) { + tty->print("non-oop word"); + } else if (o == NULL) { + tty->print("NULL-oop"); } else { - tty->print_cr("NULL"); + o->print_value_on(tty); } + tty->cr(); } - tty->cr(); + } else { + tty->print_cr(" "); } } void nmethod::print_recorded_metadata() { - tty->print_cr("Recorded metadata:"); - for (int i = 0; i < metadata_count(); i++) { - Metadata* m = metadata_at(i); - tty->print("#%3d: " INTPTR_FORMAT " ", i, p2i(m)); - if (m == (Metadata*)Universe::non_oop_word()) { - tty->print("non-metadata word"); - } else { - Metadata::print_value_on_maybe_null(tty, m); + const int n = metadata_count(); + const int log_n = (n<10) ? 1 : (n<100) ? 2 : (n<1000) ? 3 : (n<10000) ? 4 : 6; + tty->print("Recorded metadata:"); + if (n > 0) { + tty->cr(); + for (int i = 0; i < n; i++) { + Metadata* m = metadata_at(i); + tty->print("#%*d: " INTPTR_FORMAT " ", log_n, i, p2i(m)); + if (m == (Metadata*)Universe::non_oop_word()) { + tty->print("non-metadata word"); + } else if (m == NULL) { + tty->print("NULL-oop"); + } else { + Metadata::print_value_on_maybe_null(tty, m); + } + tty->cr(); } - tty->cr(); + } else { + tty->print_cr(" "); } } - -#endif // PRODUCT +#endif + +#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) + +void nmethod::print_constant_pool(outputStream* st) { + //----------------------------------- + //---< Print the constant pool >--- + //----------------------------------- + int consts_size = this->consts_size(); + if ( consts_size > 0 ) { + unsigned char* cstart = this->consts_begin(); + unsigned char* cp = cstart; + unsigned char* cend = cp + consts_size; + unsigned int bytes_per_line = 4; + unsigned int CP_alignment = 8; + unsigned int n; + + st->cr(); + + //---< print CP header to make clear what's printed >--- + if( ((uintptr_t)cp&(CP_alignment-1)) == 0 ) { + n = bytes_per_line; + st->print_cr("[Constant Pool]"); + Disassembler::print_location(cp, cstart, cend, st, true, true); + Disassembler::print_hexdata(cp, n, st, true); + st->cr(); + } else { + n = (uintptr_t)cp&(bytes_per_line-1); + st->print_cr("[Constant Pool (unaligned)]"); + } + + //---< print CP contents, bytes_per_line at a time >--- + while (cp < cend) { + Disassembler::print_location(cp, cstart, cend, st, true, false); + Disassembler::print_hexdata(cp, n, st, false); + cp += n; + n = bytes_per_line; + st->cr(); + } + + //---< Show potential alignment gap between constant pool and code >--- + cend = code_begin(); + if( cp < cend ) { + n = 4; + st->print_cr("[Code entry alignment]"); + while (cp < cend) { + Disassembler::print_location(cp, cstart, cend, st, false, false); + cp += n; + st->cr(); + } + } + } else { + st->print_cr("[Constant Pool (empty)]"); + } + st->cr(); +} + +#endif + +// Disassemble this nmethod. +// Print additional debug information, if requested. This could be code +// comments, block comments, profiling counters, etc. +// The undisassembled format is useful no disassembler library is available. +// The resulting hex dump (with markers) can be disassembled later, or on +// another system, when/where a disassembler library is available. +void nmethod::decode2(outputStream* ost) const { + + // Called from frame::back_trace_with_decode without ResourceMark. + ResourceMark rm; + + // Make sure we have a valid stream to print on. + outputStream* st = ost ? ost : tty; + +#if defined(SUPPORT_ABSTRACT_ASSEMBLY) && ! defined(SUPPORT_ASSEMBLY) + const bool use_compressed_format = true; + const bool compressed_with_comments = use_compressed_format && (AbstractDisassembler::show_comment() || + AbstractDisassembler::show_block_comment()); +#else + const bool use_compressed_format = Disassembler::is_abstract(); + const bool compressed_with_comments = use_compressed_format && (AbstractDisassembler::show_comment() || + AbstractDisassembler::show_block_comment()); +#endif + + st->cr(); + this->print(st); + st->cr(); + +#if defined(SUPPORT_ASSEMBLY) + //---------------------------------- + //---< Print real disassembly >--- + //---------------------------------- + if (! use_compressed_format) { + Disassembler::decode(const_cast(this), st); + return; + } +#endif + +#if defined(SUPPORT_ABSTRACT_ASSEMBLY) + + // Compressed undisassembled disassembly format. + // The following stati are defined/supported: + // = 0 - currently at bol() position, nothing printed yet on current line. + // = 1 - currently at position after print_location(). + // > 1 - in the midst of printing instruction stream bytes. + int compressed_format_idx = 0; + int code_comment_column = 0; + const int instr_maxlen = Assembler::instr_maxlen(); + const uint tabspacing = 8; + unsigned char* start = this->code_begin(); + unsigned char* p = this->code_begin(); + unsigned char* end = this->code_end(); + unsigned char* pss = p; // start of a code section (used for offsets) + + if ((start == NULL) || (end == NULL)) { + st->print_cr("PrintAssembly not possible due to uninitialized section pointers"); + return; + } +#endif + +#if defined(SUPPORT_ABSTRACT_ASSEMBLY) + //---< plain abstract disassembly, no comments or anything, just section headers >--- + if (use_compressed_format && ! compressed_with_comments) { + const_cast(this)->print_constant_pool(st); + + //---< Open the output (Marker for post-mortem disassembler) >--- + st->print_cr("[MachCode]"); + const char* header = NULL; + address p0 = p; + while (p < end) { + address pp = p; + while ((p < end) && (header == NULL)) { + header = nmethod_section_label(p); + pp = p; + p += Assembler::instr_len(p); + } + if (pp > p0) { + AbstractDisassembler::decode_range_abstract(p0, pp, start, end, st, Assembler::instr_maxlen()); + p0 = pp; + p = pp; + header = NULL; + } else if (header != NULL) { + st->bol(); + st->print_cr("%s", header); + header = NULL; + } + } + //---< Close the output (Marker for post-mortem disassembler) >--- + st->bol(); + st->print_cr("[/MachCode]"); + return; + } +#endif + +#if defined(SUPPORT_ABSTRACT_ASSEMBLY) + //---< abstract disassembly with comments and section headers merged in >--- + if (compressed_with_comments) { + const_cast(this)->print_constant_pool(st); + + //---< Open the output (Marker for post-mortem disassembler) >--- + st->print_cr("[MachCode]"); + while ((p < end) && (p != NULL)) { + const int instruction_size_in_bytes = Assembler::instr_len(p); + + //---< Block comments for nmethod. Interrupts instruction stream, if any. >--- + // Outputs a bol() before and a cr() after, but only if a comment is printed. + // Prints nmethod_section_label as well. + if (AbstractDisassembler::show_block_comment()) { + print_block_comment(st, p); + if (st->position() == 0) { + compressed_format_idx = 0; + } + } + + //---< New location information after line break >--- + if (compressed_format_idx == 0) { + code_comment_column = Disassembler::print_location(p, pss, end, st, false, false); + compressed_format_idx = 1; + } + + //---< Code comment for current instruction. Address range [p..(p+len)) >--- + unsigned char* p_end = p + (ssize_t)instruction_size_in_bytes; + S390_ONLY(if (p_end > end) p_end = end;) // avoid getting past the end + + if (AbstractDisassembler::show_comment() && const_cast(this)->has_code_comment(p, p_end)) { + //---< interrupt instruction byte stream for code comment >--- + if (compressed_format_idx > 1) { + st->cr(); // interrupt byte stream + st->cr(); // add an empty line + code_comment_column = Disassembler::print_location(p, pss, end, st, false, false); + } + const_cast(this)->print_code_comment_on(st, code_comment_column, p, p_end ); + st->bol(); + compressed_format_idx = 0; + } + + //---< New location information after line break >--- + if (compressed_format_idx == 0) { + code_comment_column = Disassembler::print_location(p, pss, end, st, false, false); + compressed_format_idx = 1; + } + + //---< Nicely align instructions for readability >--- + if (compressed_format_idx > 1) { + Disassembler::print_delimiter(st); + } + + //---< Now, finally, print the actual instruction bytes >--- + unsigned char* p0 = p; + p = Disassembler::decode_instruction_abstract(p, st, instruction_size_in_bytes, instr_maxlen); + compressed_format_idx += p - p0; + + if (Disassembler::start_newline(compressed_format_idx-1)) { + st->cr(); + compressed_format_idx = 0; + } + } + //---< Close the output (Marker for post-mortem disassembler) >--- + st->bol(); + st->print_cr("[/MachCode]"); + return; + } +#endif +} + +#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) const char* nmethod::reloc_string_for(u_char* begin, u_char* end) { RelocIterator iter(this, begin, end); @@ -2414,7 +2788,9 @@ switch (iter.type()) { case relocInfo::none: return "no_reloc"; case relocInfo::oop_type: { - stringStream st; + // Get a non-resizable resource-allocated stringStream. + // Our callees make use of (nested) ResourceMarks. + stringStream st(NEW_RESOURCE_ARRAY(char, 1024), 1024); oop_Relocation* r = iter.oop_reloc(); oop obj = r->oop_value(); st.print("oop("); @@ -2516,17 +2892,28 @@ return NULL; } -void nmethod::print_nmethod_labels(outputStream* stream, address block_begin) const { - if (block_begin == entry_point()) stream->print_cr("[Entry Point]"); - if (block_begin == verified_entry_point()) stream->print_cr("[Verified Entry Point]"); - if (JVMCI_ONLY(_exception_offset >= 0 &&) block_begin == exception_begin()) stream->print_cr("[Exception Handler]"); - if (block_begin == stub_begin()) stream->print_cr("[Stub Code]"); - if (JVMCI_ONLY(_deopt_handler_begin != NULL &&) block_begin == deopt_handler_begin()) stream->print_cr("[Deopt Handler Code]"); - - if (has_method_handle_invokes()) - if (block_begin == deopt_mh_handler_begin()) stream->print_cr("[Deopt MH Handler Code]"); - - if (block_begin == consts_begin()) stream->print_cr("[Constants]"); +const char* nmethod::nmethod_section_label(address pos) const { + const char* label = NULL; + if (pos == code_begin()) label = "[Instructions begin]"; + if (pos == entry_point()) label = "[Entry Point]"; + if (pos == verified_entry_point()) label = "[Verified Entry Point]"; + if (has_method_handle_invokes() && (pos == deopt_mh_handler_begin())) label = "[Deopt MH Handler Code]"; + if (pos == consts_begin() && pos != insts_begin()) label = "[Constants]"; + // Check stub_code before checking exception_handler or deopt_handler. + if (pos == this->stub_begin()) label = "[Stub Code]"; + if (JVMCI_ONLY(_exception_offset >= 0 &&) pos == exception_begin()) label = "[Exception Handler]"; + if (JVMCI_ONLY(_deopt_handler_begin != NULL &&) pos == deopt_handler_begin()) label = "[Deopt Handler Code]"; + return label; +} + +void nmethod::print_nmethod_labels(outputStream* stream, address block_begin, bool print_section_labels) const { + if (print_section_labels) { + const char* label = nmethod_section_label(block_begin); + if (label != NULL) { + stream->bol(); + stream->print_cr("%s", label); + } + } if (block_begin == entry_point()) { methodHandle m = method(); @@ -2623,7 +3010,24 @@ } } -void nmethod::print_code_comment_on(outputStream* st, int column, u_char* begin, u_char* end) { +// Returns whether this nmethod has code comments. +bool nmethod::has_code_comment(address begin, address end) { + // scopes? + ScopeDesc* sd = scope_desc_in(begin, end); + if (sd != NULL) return true; + + // relocations? + const char* str = reloc_string_for(begin, end); + if (str != NULL) return true; + + // implicit exceptions? + int cont_offset = ImplicitExceptionTable(this).at(begin - code_begin()); + if (cont_offset != 0) return true; + + return false; +} + +void nmethod::print_code_comment_on(outputStream* st, int column, address begin, address end) { // First, find an oopmap in (begin, end]. // We use the odd half-closed interval so that oop maps and scope descs // which are tied to the byte after a call are printed with the call itself. @@ -2636,7 +3040,7 @@ address pc = base + pair->pc_offset(); if (pc > begin) { if (pc <= end) { - st->move_to(column); + st->move_to(column, 6, 0); st->print("; "); om->print_on(st); } @@ -2648,7 +3052,7 @@ // Print any debug info present at this pc. ScopeDesc* sd = scope_desc_in(begin, end); if (sd != NULL) { - st->move_to(column); + st->move_to(column, 6, 0); if (sd->bci() == SynchronizationEntryBCI) { st->print(";*synchronization entry"); } else if (sd->bci() == AfterBci) { @@ -2704,8 +3108,11 @@ // Print all scopes for (;sd != NULL; sd = sd->sender()) { - st->move_to(column); + st->move_to(column, 6, 0); st->print("; -"); + if (sd->should_reexecute()) { + st->print(" (reexecute)"); + } if (sd->method() == NULL) { st->print("method is NULL"); } else { @@ -2722,20 +3129,24 @@ } // Print relocation information + // Prevent memory leak: allocating without ResourceMark. + ResourceMark rm; const char* str = reloc_string_for(begin, end); if (str != NULL) { if (sd != NULL) st->cr(); - st->move_to(column); + st->move_to(column, 6, 0); st->print("; {%s}", str); } int cont_offset = ImplicitExceptionTable(this).at(begin - code_begin()); if (cont_offset != 0) { - st->move_to(column); + st->move_to(column, 6, 0); st->print("; implicit exception: dispatches to " INTPTR_FORMAT, p2i(code_begin() + cont_offset)); } } +#endif + class DirectNativeCallWrapper: public NativeCallWrapper { private: NativeCall* _call; @@ -2842,12 +3253,14 @@ return CompiledDirectStaticCall::before(return_addr); } -#ifndef PRODUCT - +#if defined(SUPPORT_DATA_STRUCTS) void nmethod::print_value_on(outputStream* st) const { st->print("nmethod"); print_on(st, NULL); } +#endif + +#ifndef PRODUCT void nmethod::print_calls(outputStream* st) { RelocIterator iter(this); @@ -2869,14 +3282,6 @@ } } -void nmethod::print_handler_table() { - ExceptionHandlerTable(this).print(); -} - -void nmethod::print_nul_chk_table() { - ImplicitExceptionTable(this).print(code_begin()); -} - void nmethod::print_statistics() { ttyLocker ttyl; if (xtty != NULL) xtty->head("statistics type='nmethod'"); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/nmethod.hpp --- a/src/hotspot/share/code/nmethod.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/nmethod.hpp Thu May 23 11:07:37 2019 +0100 @@ -377,6 +377,7 @@ void make_unloaded(); bool has_dependencies() { return dependencies_size() != 0; } + void print_dependencies() PRODUCT_RETURN; void flush_dependencies(bool delete_immediately); bool has_flushed_dependencies() { return _has_flushed_dependencies; } void set_has_flushed_dependencies() { @@ -505,18 +506,40 @@ void verify_scopes(); void verify_interrupt_point(address interrupt_point); + // Disassemble this nmethod with additional debug information, e.g. information about blocks. + void decode2(outputStream* st) const; + void print_constant_pool(outputStream* st); + + // Avoid hiding of parent's 'decode(outputStream*)' method. + void decode(outputStream* st) const { decode2(st); } // just delegate here. + // printing support void print() const; + void print(outputStream* st) const; + void print_code(); + +#if defined(SUPPORT_DATA_STRUCTS) + // print output in opt build for disassembler library void print_relocations() PRODUCT_RETURN; - void print_pcs() PRODUCT_RETURN; - void print_scopes() PRODUCT_RETURN; - void print_dependencies() PRODUCT_RETURN; - void print_value_on(outputStream* st) const PRODUCT_RETURN; + void print_pcs() { print_pcs_on(tty); } + void print_pcs_on(outputStream* st); + void print_scopes() { print_scopes_on(tty); } + void print_scopes_on(outputStream* st) PRODUCT_RETURN; + void print_value_on(outputStream* st) const; + void print_handler_table(); + void print_nul_chk_table(); + void print_recorded_oops(); + void print_recorded_metadata(); + + void print_oops(outputStream* st); // oops from the underlying CodeBlob. + void print_metadata(outputStream* st); // metadata in metadata pool. +#else + // void print_pcs() PRODUCT_RETURN; + void print_pcs() { return; } +#endif + void print_calls(outputStream* st) PRODUCT_RETURN; - void print_handler_table() PRODUCT_RETURN; - void print_nul_chk_table() PRODUCT_RETURN; - void print_recorded_oops() PRODUCT_RETURN; - void print_recorded_metadata() PRODUCT_RETURN; + static void print_statistics() PRODUCT_RETURN; void maybe_print_nmethod(DirectiveSet* directive); void print_nmethod(bool print_code); @@ -532,14 +555,21 @@ // Prints block-level comments, including nmethod specific block labels: virtual void print_block_comment(outputStream* stream, address block_begin) const { +#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) print_nmethod_labels(stream, block_begin); CodeBlob::print_block_comment(stream, block_begin); +#endif } - void print_nmethod_labels(outputStream* stream, address block_begin) const; + bool has_block_comment(address block_begin) { + return CodeBlob::has_block_comment(block_begin); + } + void print_nmethod_labels(outputStream* stream, address block_begin, bool print_section_labels=true) const; + const char* nmethod_section_label(address pos) const; + // returns whether this nmethod has code comments. + bool has_code_comment(address begin, address end); // Prints a comment for one native instruction (reloc info, pc desc) void print_code_comment_on(outputStream* st, int column, address begin, address end); - static void print_statistics() PRODUCT_RETURN; // Compiler task identification. Note that all OSR methods // are numbered in an independent sequence if CICountOSR is true, diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/pcDesc.cpp --- a/src/hotspot/share/code/pcDesc.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/pcDesc.cpp Thu May 23 11:07:37 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,19 +40,24 @@ return code->code_begin() + pc_offset(); } -void PcDesc::print(CompiledMethod* code) { +void PcDesc::print_on(outputStream* st, CompiledMethod* code) { #ifndef PRODUCT ResourceMark rm; - tty->print_cr("PcDesc(pc=" PTR_FORMAT " offset=%x bits=%x):", p2i(real_pc(code)), pc_offset(), _flags); + st->print("PcDesc(pc=" PTR_FORMAT " offset=%x bits=%x):", p2i(real_pc(code)), pc_offset(), _flags); if (scope_decode_offset() == DebugInformationRecorder::serialized_null) { + st->cr(); return; } + int tab = 8; + int pos = st->position() + 2; // current column plus two spaces + pos = ((pos+tab-1)/tab)*tab; + for (ScopeDesc* sd = code->scope_desc_at(real_pc(code)); sd != NULL; sd = sd->sender()) { - sd->print_on(tty); + sd->print_on(st); } #endif } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/pcDesc.hpp --- a/src/hotspot/share/code/pcDesc.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/pcDesc.hpp Thu May 23 11:07:37 2019 +0100 @@ -92,7 +92,8 @@ // Returns the real pc address real_pc(const CompiledMethod* code) const; - void print(CompiledMethod* code); + void print(CompiledMethod* code) { print_on(tty, code); } + void print_on(outputStream* st, CompiledMethod* code); bool verify(CompiledMethod* code); }; diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/vmreg.cpp --- a/src/hotspot/share/code/vmreg.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/vmreg.cpp Thu May 23 11:07:37 2019 +0100 @@ -39,7 +39,7 @@ void VMRegImpl::print_on(outputStream* st) const { if( is_reg() ) { - assert( VMRegImpl::regName[value()], "" ); + assert(VMRegImpl::regName[value()], "VMRegImpl::regName[" INTPTR_FORMAT "] returns NULL", value()); st->print("%s",VMRegImpl::regName[value()]); } else if (is_stack()) { int stk = value() - stack0->value(); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/code/vtableStubs.cpp --- a/src/hotspot/share/code/vtableStubs.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/code/vtableStubs.cpp Thu May 23 11:07:37 2019 +0100 @@ -80,7 +80,7 @@ void VtableStub::print_on(outputStream* st) const { - st->print("vtable stub (index = %d, receiver_location = " INTX_FORMAT ", code = [" INTPTR_FORMAT ", " INTPTR_FORMAT "[)", + st->print("vtable stub (index = %d, receiver_location = " INTX_FORMAT ", code = [" INTPTR_FORMAT ", " INTPTR_FORMAT "])", index(), p2i(receiver_location()), p2i(code_begin()), p2i(code_end())); } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/compiler/abstractDisassembler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/compiler/abstractDisassembler.cpp Thu May 23 11:07:37 2019 +0100 @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +// AbstractDisassembler is the base class for +// platform-specific Disassembler classes. + +#include "precompiled.hpp" +#include "asm/assembler.inline.hpp" +#include "compiler/abstractDisassembler.hpp" +#include "oops/oop.inline.hpp" +#include "utilities/debug.hpp" +#include "utilities/ostream.hpp" + +// Default values for what is being printed as line prefix when disassembling a single instruction. +// Can be overridden by command line parameter PrintAssemblyOptions. +bool AbstractDisassembler::_show_data_hex = true; +bool AbstractDisassembler::_show_data_int = false; +bool AbstractDisassembler::_show_data_float = false; +bool AbstractDisassembler::_align_instr = false; +bool AbstractDisassembler::_show_pc = true; +bool AbstractDisassembler::_show_offset = false; +bool AbstractDisassembler::_show_structs = false; +bool AbstractDisassembler::_show_comment = false; +bool AbstractDisassembler::_show_block_comment = false; + +// set "true" to see what's in memory bit by bit +// might prove cumbersome on platforms where instr_len is hard to find out +bool AbstractDisassembler::_show_bytes = false; + +// Return #bytes printed. Callers may use that for output alignment. +// Print instruction address, and offset from blob begin. +// Offset width (2, 4, 6, 8 bytes) is adapted to size of blob. +// Working assumption: we are at st->bol() upon entry. If not, it's the +// caller's responsibility to guarantee proper alignment. +int AbstractDisassembler::print_location(address here, address begin, address end, outputStream* st, bool align, bool print_header) { + const int pos_0 = st->position(); + + if (show_pc() || show_offset()) { + st->print(" "); + } + + if (show_pc()) { + if (print_header) { + st->print(" %*s", 18, "Address"); + } else { + st->print(" " PTR_FORMAT, p2i(here)); + } + } + + if (show_offset()) { +#ifdef ASSERT + if ((uintptr_t)begin > (uintptr_t)here) st->print(">>begin(" PTR_FORMAT ") > here(" PTR_FORMAT ")<<", p2i(begin), p2i(here)); + if ((uintptr_t)end < (uintptr_t)here) st->print(">> end(" PTR_FORMAT ") < here(" PTR_FORMAT ")<<", p2i(end), p2i(here)); + assert((uintptr_t)begin <= (uintptr_t)end, "inverted address range"); +#endif + const int blob_len = end - begin; + const int offset = here - begin; + const int width = (blob_len < (1<< 8)) ? 2 : (blob_len < (1<<16)) ? 4 : (blob_len < (1<<24)) ? 6 : 8; + if (print_header) { + st->print(" %*s", width+5, "offset"); + } else { + st->print(" (+0x%*.*x)", width, width, offset); + } + } + + if ((show_pc() || show_offset()) && !print_header) { + st->print(": "); + } + + if (align) { + const uint tabspacing = 8; + const uint pos = st->position(); + const uint aligned_pos = ((pos+tabspacing-1)/tabspacing)*tabspacing /* - 1 */; + st->fill_to(aligned_pos); + } + + return st->position() - pos_0; +} + + +// Return #bytes printed. Callers may use that for output alignment. +// Print instruction in hexadecimal representation, using 2-byte blocks. +// Used with real disassemblies. Not so useful with abstract disassemblies. +int AbstractDisassembler::print_instruction(address here, int len, int max_len, outputStream* st, bool align, bool print_header) { + if (show_bytes()) { + const int block_bytes = 2; + const int pos_0 = st->position(); + address pos = here; + + //---< print instruction bytes in blocks >--- + // must print byte by byte: address might be unaligned. + for (; pos <= here + len - block_bytes; pos += block_bytes) { + for (address byte = pos; byte < pos + block_bytes; byte++) { + st->print("%2.2x", *byte); + } + st->print(" "); + } + + //---< Print the remaining bytes of the instruction >--- + if ((len & (block_bytes - 1)) != 0) { + for (; pos < here + len; pos++) { + st->print("%2.2x", *pos); + } + } + + //---< filler for shorter than max_len instructions >--- + for (int i = len+1; i < max_len; i++) { + st->print(" "); + } + + st->print(" "); // separator space. + print_delimiter(st); + return st->position() - pos_0; + } + + if (align) { + const uint tabspacing = 8; + const uint pos = st->position(); + const uint aligned_pos = ((pos+tabspacing-1)/tabspacing)*tabspacing /* - 1 */; + st->fill_to(aligned_pos); + } + + return 0; +} + + +// Return #bytes printed. Callers may use that for output alignment. +// Print data (e.g. constant pool entries) in hex format. +// Depending on the alignment, short, int, and long entities are printed. +// If selected, data is formatted as int/long and float/double values in addition. +int AbstractDisassembler::print_hexdata(address here, int len, outputStream* st, bool print_header) { + const int tsize = 8; + const int pos_0 = st->position(); + int pos = pos_0; + int align = ((pos+tsize-1)/tsize)*tsize; + st->fill_to(align); + + //---< printing hex data >--- + if (show_data_hex()) { + switch (len) { + case 1: if (print_header) { + st->print("hex1"); + } else { + st->print("0x%02x", *here); + } + st->fill_to(align += tsize); + case 2: if (print_header) { + st->print(" hex2"); + } else { + if (((uintptr_t)(here)&0x01) == 0) { + st->print("0x%04x", *((jushort*)here)); + } + } + st->fill_to(align += tsize); + case 4: if (print_header) { + st->print(" hex4"); + } else { + if (((uintptr_t)(here)&0x03) == 0) { + st->print("0x%08x", *((juint*)here)); + } + } + st->fill_to(align += 2*tsize); + case 8: if (print_header) { + st->print(" hex8"); + } else { + if (((uintptr_t)(here)&0x07) == 0) { + st->print(PTR_FORMAT, *((uintptr_t*)here)); + } + } + st->fill_to(align += 3*tsize); + break; + default: ; + } + pos = st->position(); + align = ((pos+tsize-1)/tsize)*tsize; + st->fill_to(align); + } + + //---< printing int/long data >--- + if (show_data_int()) { + switch (len) { + case 4: if (print_header) { + st->print(" int"); + } else { + if (((uintptr_t)(here)&0x03) == 0) { + st->print("%12.1d", *((jint*)here)); + } + } + st->fill_to(align += 2*tsize); + case 8: if (print_header) { + st->print(" long"); + } else { + if (((uintptr_t)(here)&0x07) == 0) { + st->print("%23.1ld", *((jlong*)here)); + } + } + st->fill_to(align += 3*tsize); + break; + default: ; + } + pos = st->position(); + align = ((pos+tsize-1)/tsize)*tsize; + st->fill_to(align); + } + + //---< printing float/double data >--- + if (show_data_float()) { + switch (len) { + case 4: if (print_header) { + st->print(" float"); + } else { + if (((uintptr_t)(here)&0x03) == 0) { + st->print("%15.7e", (double)*((float*)here)); + } + } + st->fill_to(align += 2*tsize); + case 8: if (print_header) { + st->print(" double"); + } else { + if (((uintptr_t)(here)&0x07) == 0) { + st->print("%23.15e", *((double*)here)); + } + } + st->fill_to(align += 3*tsize); + break; + default: ; + } + } + + return st->position() - pos_0; +} + + +// Return #bytes printed. Callers may use that for output alignment. +// Print an instruction delimiter. +int AbstractDisassembler::print_delimiter(outputStream* st) { + if (align_instr()) { st->print("| "); return 2; } + else return 0; +} + + +// Decodes the one instruction at address start in a platform-independent format. +// Returns the start of the next instruction (which is 'start' plus 'instruction_size_in_bytes'). +// The parameter max_instr_size_in_bytes is used for output alignment purposes only. +address AbstractDisassembler::decode_instruction_abstract(address start, + outputStream* st, + const int instruction_size_in_bytes, + const int max_instr_size_in_bytes) { + assert(instruction_size_in_bytes > 0, "no zero-size instructions!"); + assert(max_instr_size_in_bytes >= instruction_size_in_bytes, "inconsistent call parameters"); + + //---< current instruction is at the start address >--- + unsigned char* current = (unsigned char*) start; + int filler_limit = align_instr() ? max_instr_size_in_bytes : ((instruction_size_in_bytes+abstract_instruction_bytes_per_block-1)/abstract_instruction_bytes_per_block) + *abstract_instruction_bytes_per_block; + + //---< print the instruction's bytes >--- + for (int i = 1; i <= instruction_size_in_bytes; i++) { + st->print("%02x", *current); + ++current; + if (abstract_instruction_bytes_per_block <= max_instr_size_in_bytes) { + if (i%abstract_instruction_bytes_per_block == 0) st->print(" "); + } else { + if (i == instruction_size_in_bytes) st->print(" "); + } + } + + //---< print some filler spaces to column-align instructions >--- + for (int i = instruction_size_in_bytes+1; i <= filler_limit; i++) { + st->print(" "); + if (abstract_instruction_bytes_per_block <= max_instr_size_in_bytes) { + if (i%abstract_instruction_bytes_per_block == 0) st->print(" "); + } else { + if (i == instruction_size_in_bytes) st->print(" "); + } + } + + //---< the address of the next instruction >--- + return (address) current; +} + + +// Decodes all instructions in the given range [start..end) +// calling decode_instruction_abstract for each instruction. +// The format is platform dependent only to the extend that +// it respects the actual instruction length where possible. +// Does not print any markers or decorators. +void AbstractDisassembler::decode_range_abstract(address range_start, address range_end, + address start, address end, + outputStream* st, + const int max_instr_size_in_bytes) { + assert(st != NULL, "need an output stream (no default)!"); + int idx = 0; + address pos = range_start; + + while ((pos != NULL) && (pos < range_end)) { + int instr_size_in_bytes = Assembler::instr_len(pos); + + if (idx == 0) print_location(pos, start, end, st, false, false); + else print_delimiter(st); + + //---< print the instruction's bytes >--- + // don't access storage beyond end of range + if (pos + instr_size_in_bytes <= range_end) { + pos = decode_instruction_abstract(pos, st, instr_size_in_bytes, max_instr_size_in_bytes); + } else { + // If the range to be decoded contains garbage at the end (e.g. 0xcc initializer bytes), + // instruction size calculation may run out of sync. Just terminate in that case. + pos = range_end; + } + + idx += instr_size_in_bytes; + if (start_newline(idx)) { + st->cr(); + idx = 0; + } + } +} + + +// Decodes all instructions in the given range [start..end). +// The output is enclosed in [MachCode] and [/MachCode] tags for later recognition. +// The format is platform dependent only to the extend that +// it respects the actual instruction length where possible. +void AbstractDisassembler::decode_abstract(address start, address end, outputStream* ost, + const int max_instr_size_in_bytes) { + int idx = 0; + address pos = start; + + outputStream* st = (ost == NULL) ? tty : ost; + + //---< Open the output (Marker for post-mortem disassembler) >--- + st->bol(); + st->print_cr("[MachCode]"); + + decode_range_abstract(start, end, start, end, st, max_instr_size_in_bytes); + + //---< Close the output (Marker for post-mortem disassembler) >--- + st->bol(); + st->print_cr("[/MachCode]"); +} diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/compiler/abstractDisassembler.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/compiler/abstractDisassembler.hpp Thu May 23 11:07:37 2019 +0100 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_COMPILER_ABSTRACTDISASSEMBLER_HPP +#define SHARE_COMPILER_ABSTRACTDISASSEMBLER_HPP + +// AbstractDisassembler is the base class for +// platform-specific Disassembler classes. + +#include "utilities/globalDefinitions.hpp" + +class AbstractDisassembler { + + private: + // These are some general settings which control + // abstract disassembly output. + enum { + // that many bytes are dumped in one line. + abstract_instruction_bytes_per_line = 32, + // instruction bytes are grouped in blocks of that many bytes. + abstract_instruction_bytes_per_block = 2, + // instructions have this default len. + abstract_instruction_size_in_bytes = 1, + // instructions have this maximum len. + abstract_instruction_maxsize_in_bytes = 1 + }; + + static bool _align_instr; // vertical alignment of instructions in abstract disassembly + static bool _show_pc; // print the instruction address + static bool _show_offset; // print the instruction offset (from start of blob) + static bool _show_bytes; // print instruction bytes + static bool _show_data_hex; // print instruction bytes + static bool _show_data_int; // print instruction bytes + static bool _show_data_float; // print instruction bytes + static bool _show_structs; // print compiler data structures (relocations, oop maps, scopes, metadata, ...) + static bool _show_comment; // print instruction comments + static bool _show_block_comment; // print block comments + + public: + // Platform-independent location and instruction formatting. + // All functions return #characters printed. + static int print_location(address here, address begin, address end, outputStream* st, bool align, bool print_header); + static int print_instruction(address here, int len, int max_len, outputStream* st, bool align, bool print_header); + static int print_hexdata(address here, int len, outputStream* st, bool print_header = false); + static int print_delimiter(outputStream* st); + static bool start_newline(int byte_count) { return byte_count >= abstract_instruction_bytes_per_line; } + + static void toggle_align_instr() { _align_instr = !_align_instr; } + static void toggle_show_pc() { _show_pc = !_show_pc; } + static void toggle_show_offset() { _show_offset = !_show_offset; } + static void toggle_show_bytes() { _show_bytes = !_show_bytes; } + static void toggle_show_data_hex() { _show_data_hex = !_show_data_hex; } + static void toggle_show_data_int() { _show_data_int = !_show_data_int; } + static void toggle_show_data_float() { _show_data_float = !_show_data_float; } + static void toggle_show_structs() { _show_structs = !_show_structs; } + static void toggle_show_comment() { _show_comment = !_show_comment; } + static void toggle_show_block_comment() { _show_block_comment = !_show_block_comment; } + + static bool align_instr() { return _align_instr; } + static bool show_pc() { return _show_pc; } + static bool show_offset() { return _show_offset; } + static bool show_bytes() { return _show_bytes; } + static bool show_data_hex() { return _show_data_hex; } + static bool show_data_int() { return _show_data_int; } + static bool show_data_float() { return _show_data_float; } + static bool show_structs() { return _show_structs; } + static bool show_comment() { return _show_comment; } + static bool show_block_comment() { return _show_block_comment; } + + // Decodes the one instruction at address start in a platform-independent + // format. Returns the start of the next instruction (which is + // 'start' plus 'instruction_size_in_bytes'). The parameter max_instr_size_in_bytes + // is used for output alignment purposes only. + static address decode_instruction_abstract(address start, + outputStream* st, + const int instruction_size_in_bytes, + const int max_instr_size_in_bytes = abstract_instruction_maxsize_in_bytes); + + // Decodes all instructions in the given range [start..end) + // calling decode_instruction_abstract for each instruction. + // The format is platform dependent only to the extend that + // it respects the actual instruction length where possible. + // Does not print any markers or decorators. + static void decode_range_abstract(address range_start, address range_end, + address start, address end, + outputStream* st, + const int max_instr_size_in_bytes = abstract_instruction_maxsize_in_bytes); + + // Decodes all instructions in the given range in a platform-independent + // format, calling decode_instruction_abstract for each instruction. + static void decode_abstract(address start, address end, + outputStream* st, + const int max_instr_size_in_bytes = abstract_instruction_maxsize_in_bytes); +}; + +#endif // SHARE_COMPILER_ABSTRACTDISASSEMBLER_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/compiler/compileBroker.cpp --- a/src/hotspot/share/compiler/compileBroker.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/compiler/compileBroker.cpp Thu May 23 11:07:37 2019 +0100 @@ -192,7 +192,7 @@ void log_compile(JavaThread* thread, CompileTask* task) { StringLogMessage lm; - stringStream sstr = lm.stream(); + stringStream sstr(lm.buffer(), lm.size()); // msg.time_stamp().update_to(tty->time_stamp().ticks()); task->print(&sstr, NULL, true, false); log(thread, "%s", (const char*)lm); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/compiler/compilerDefinitions.cpp --- a/src/hotspot/share/compiler/compilerDefinitions.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/compiler/compilerDefinitions.cpp Thu May 23 11:07:37 2019 +0100 @@ -117,38 +117,38 @@ Compilation_mode = CompMode_client; CompLevel_highest_tier = CompLevel_simple; CompLevel_initial_compile = CompLevel_simple; - FLAG_SET_ERGO(bool, TieredCompilation, false); - FLAG_SET_ERGO(bool, ProfileInterpreter, false); + FLAG_SET_ERGO(TieredCompilation, false); + FLAG_SET_ERGO(ProfileInterpreter, false); #if INCLUDE_JVMCI - FLAG_SET_ERGO(bool, EnableJVMCI, false); - FLAG_SET_ERGO(bool, UseJVMCICompiler, false); + FLAG_SET_ERGO(EnableJVMCI, false); + FLAG_SET_ERGO(UseJVMCICompiler, false); #endif #if INCLUDE_AOT - FLAG_SET_ERGO(bool, UseAOT, false); + FLAG_SET_ERGO(UseAOT, false); #endif if (FLAG_IS_DEFAULT(NeverActAsServerClassMachine)) { - FLAG_SET_ERGO(bool, NeverActAsServerClassMachine, true); + FLAG_SET_ERGO(NeverActAsServerClassMachine, true); } if (FLAG_IS_DEFAULT(InitialCodeCacheSize)) { - FLAG_SET_ERGO(uintx, InitialCodeCacheSize, 160*K); + FLAG_SET_ERGO(InitialCodeCacheSize, 160*K); } if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) { - FLAG_SET_ERGO(uintx, ReservedCodeCacheSize, 32*M); + FLAG_SET_ERGO(ReservedCodeCacheSize, 32*M); } if (FLAG_IS_DEFAULT(NonProfiledCodeHeapSize)) { - FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, 27*M); + FLAG_SET_ERGO(NonProfiledCodeHeapSize, 27*M); } if (FLAG_IS_DEFAULT(ProfiledCodeHeapSize)) { - FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, 0); + FLAG_SET_ERGO(ProfiledCodeHeapSize, 0); } if (FLAG_IS_DEFAULT(NonNMethodCodeHeapSize)) { - FLAG_SET_ERGO(uintx, NonNMethodCodeHeapSize, 5*M); + FLAG_SET_ERGO(NonNMethodCodeHeapSize, 5*M); } if (FLAG_IS_DEFAULT(CodeCacheExpansionSize)) { - FLAG_SET_ERGO(uintx, CodeCacheExpansionSize, 32*K); + FLAG_SET_ERGO(CodeCacheExpansionSize, 32*K); } if (FLAG_IS_DEFAULT(MetaspaceSize)) { - FLAG_SET_ERGO(size_t, MetaspaceSize, MIN2(12*M, MaxMetaspaceSize)); + FLAG_SET_ERGO(MetaspaceSize, MIN2(12*M, MaxMetaspaceSize)); } if (FLAG_IS_DEFAULT(MaxRAM)) { // Do not use FLAG_SET_ERGO to update MaxRAM, as this will impact @@ -156,13 +156,13 @@ FLAG_SET_DEFAULT(MaxRAM, 1ULL*G); } if (FLAG_IS_DEFAULT(CompileThreshold)) { - FLAG_SET_ERGO(intx, CompileThreshold, 1500); + FLAG_SET_ERGO(CompileThreshold, 1500); } if (FLAG_IS_DEFAULT(OnStackReplacePercentage)) { - FLAG_SET_ERGO(intx, OnStackReplacePercentage, 933); + FLAG_SET_ERGO(OnStackReplacePercentage, 933); } if (FLAG_IS_DEFAULT(CICompilerCount)) { - FLAG_SET_ERGO(intx, CICompilerCount, 1); + FLAG_SET_ERGO(CICompilerCount, 1); } } @@ -177,7 +177,7 @@ void select_compilation_mode_ergonomically() { #if defined(_WINDOWS) && !defined(_LP64) if (FLAG_IS_DEFAULT(NeverActAsServerClassMachine)) { - FLAG_SET_ERGO(bool, NeverActAsServerClassMachine, true); + FLAG_SET_ERGO(NeverActAsServerClassMachine, true); } #endif if (NeverActAsServerClassMachine) { @@ -198,14 +198,14 @@ } // Increase the code cache size - tiered compiles a lot more. if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) { - FLAG_SET_ERGO(uintx, ReservedCodeCacheSize, + FLAG_SET_ERGO(ReservedCodeCacheSize, MIN2(CODE_CACHE_DEFAULT_LIMIT, (size_t)ReservedCodeCacheSize * 5)); } // Enable SegmentedCodeCache if TieredCompilation is enabled, ReservedCodeCacheSize >= 240M // and the code cache contains at least 8 pages (segmentation disables advantage of huge pages). if (FLAG_IS_DEFAULT(SegmentedCodeCache) && ReservedCodeCacheSize >= 240*M && 8 * CodeCache::page_size() <= ReservedCodeCacheSize) { - FLAG_SET_ERGO(bool, SegmentedCodeCache, true); + FLAG_SET_ERGO(SegmentedCodeCache, true); } if (!UseInterpreter) { // -Xcomp Tier3InvokeNotifyFreqLog = 0; @@ -219,29 +219,29 @@ // Scale tiered compilation thresholds. // CompileThresholdScaling == 0.0 is equivalent to -Xint and leaves compilation thresholds unchanged. if (!FLAG_IS_DEFAULT(CompileThresholdScaling) && CompileThresholdScaling > 0.0) { - FLAG_SET_ERGO(intx, Tier0InvokeNotifyFreqLog, scaled_freq_log(Tier0InvokeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier0BackedgeNotifyFreqLog, scaled_freq_log(Tier0BackedgeNotifyFreqLog)); + FLAG_SET_ERGO(Tier0InvokeNotifyFreqLog, scaled_freq_log(Tier0InvokeNotifyFreqLog)); + FLAG_SET_ERGO(Tier0BackedgeNotifyFreqLog, scaled_freq_log(Tier0BackedgeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier3InvocationThreshold, scaled_compile_threshold(Tier3InvocationThreshold)); - FLAG_SET_ERGO(intx, Tier3MinInvocationThreshold, scaled_compile_threshold(Tier3MinInvocationThreshold)); - FLAG_SET_ERGO(intx, Tier3CompileThreshold, scaled_compile_threshold(Tier3CompileThreshold)); - FLAG_SET_ERGO(intx, Tier3BackEdgeThreshold, scaled_compile_threshold(Tier3BackEdgeThreshold)); + FLAG_SET_ERGO(Tier3InvocationThreshold, scaled_compile_threshold(Tier3InvocationThreshold)); + FLAG_SET_ERGO(Tier3MinInvocationThreshold, scaled_compile_threshold(Tier3MinInvocationThreshold)); + FLAG_SET_ERGO(Tier3CompileThreshold, scaled_compile_threshold(Tier3CompileThreshold)); + FLAG_SET_ERGO(Tier3BackEdgeThreshold, scaled_compile_threshold(Tier3BackEdgeThreshold)); // Tier2{Invocation,MinInvocation,Compile,Backedge}Threshold should be scaled here // once these thresholds become supported. - FLAG_SET_ERGO(intx, Tier2InvokeNotifyFreqLog, scaled_freq_log(Tier2InvokeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier2BackedgeNotifyFreqLog, scaled_freq_log(Tier2BackedgeNotifyFreqLog)); + FLAG_SET_ERGO(Tier2InvokeNotifyFreqLog, scaled_freq_log(Tier2InvokeNotifyFreqLog)); + FLAG_SET_ERGO(Tier2BackedgeNotifyFreqLog, scaled_freq_log(Tier2BackedgeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier3InvokeNotifyFreqLog, scaled_freq_log(Tier3InvokeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier3BackedgeNotifyFreqLog, scaled_freq_log(Tier3BackedgeNotifyFreqLog)); + FLAG_SET_ERGO(Tier3InvokeNotifyFreqLog, scaled_freq_log(Tier3InvokeNotifyFreqLog)); + FLAG_SET_ERGO(Tier3BackedgeNotifyFreqLog, scaled_freq_log(Tier3BackedgeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier23InlineeNotifyFreqLog, scaled_freq_log(Tier23InlineeNotifyFreqLog)); + FLAG_SET_ERGO(Tier23InlineeNotifyFreqLog, scaled_freq_log(Tier23InlineeNotifyFreqLog)); - FLAG_SET_ERGO(intx, Tier4InvocationThreshold, scaled_compile_threshold(Tier4InvocationThreshold)); - FLAG_SET_ERGO(intx, Tier4MinInvocationThreshold, scaled_compile_threshold(Tier4MinInvocationThreshold)); - FLAG_SET_ERGO(intx, Tier4CompileThreshold, scaled_compile_threshold(Tier4CompileThreshold)); - FLAG_SET_ERGO(intx, Tier4BackEdgeThreshold, scaled_compile_threshold(Tier4BackEdgeThreshold)); + FLAG_SET_ERGO(Tier4InvocationThreshold, scaled_compile_threshold(Tier4InvocationThreshold)); + FLAG_SET_ERGO(Tier4MinInvocationThreshold, scaled_compile_threshold(Tier4MinInvocationThreshold)); + FLAG_SET_ERGO(Tier4CompileThreshold, scaled_compile_threshold(Tier4CompileThreshold)); + FLAG_SET_ERGO(Tier4BackEdgeThreshold, scaled_compile_threshold(Tier4BackEdgeThreshold)); } } @@ -256,7 +256,7 @@ if (TieredStopAtLevel != CompLevel_full_optimization) { // Currently JVMCI compiler can only work at the full optimization level warning("forcing TieredStopAtLevel to full optimization because JVMCI is enabled"); - FLAG_SET_ERGO(intx, TieredStopAtLevel, CompLevel_full_optimization); + FLAG_SET_ERGO(TieredStopAtLevel, CompLevel_full_optimization); } if (FLAG_IS_DEFAULT(TypeProfileLevel)) { FLAG_SET_DEFAULT(TypeProfileLevel, 0); @@ -338,7 +338,7 @@ if (!FLAG_IS_DEFAULT(BackgroundCompilation)) { warning("BackgroundCompilation disabled due to ReplayCompiles option."); } - FLAG_SET_CMDLINE(bool, BackgroundCompilation, false); + FLAG_SET_CMDLINE(BackgroundCompilation, false); } #ifdef COMPILER2 @@ -346,7 +346,7 @@ if (!FLAG_IS_DEFAULT(PostLoopMultiversioning)) { warning("PostLoopMultiversioning disabled because RangeCheckElimination is disabled."); } - FLAG_SET_CMDLINE(bool, PostLoopMultiversioning, false); + FLAG_SET_CMDLINE(PostLoopMultiversioning, false); } if (UseCountedLoopSafepoints && LoopStripMiningIter == 0) { if (!FLAG_IS_DEFAULT(UseCountedLoopSafepoints) || !FLAG_IS_DEFAULT(LoopStripMiningIter)) { @@ -366,27 +366,27 @@ if (!FLAG_IS_DEFAULT(UseCompiler)) { warning("UseCompiler disabled due to -Xint."); } - FLAG_SET_CMDLINE(bool, UseCompiler, false); + FLAG_SET_CMDLINE(UseCompiler, false); } if (ProfileInterpreter) { if (!FLAG_IS_DEFAULT(ProfileInterpreter)) { warning("ProfileInterpreter disabled due to -Xint."); } - FLAG_SET_CMDLINE(bool, ProfileInterpreter, false); + FLAG_SET_CMDLINE(ProfileInterpreter, false); } if (TieredCompilation) { if (!FLAG_IS_DEFAULT(TieredCompilation)) { warning("TieredCompilation disabled due to -Xint."); } - FLAG_SET_CMDLINE(bool, TieredCompilation, false); + FLAG_SET_CMDLINE(TieredCompilation, false); } #if INCLUDE_JVMCI if (EnableJVMCI) { if (!FLAG_IS_DEFAULT(EnableJVMCI) || !FLAG_IS_DEFAULT(UseJVMCICompiler)) { warning("JVMCI Compiler disabled due to -Xint."); } - FLAG_SET_CMDLINE(bool, EnableJVMCI, false); - FLAG_SET_CMDLINE(bool, UseJVMCICompiler, false); + FLAG_SET_CMDLINE(EnableJVMCI, false); + FLAG_SET_CMDLINE(UseJVMCICompiler, false); } #endif } else { @@ -434,7 +434,7 @@ // Scale CompileThreshold // CompileThresholdScaling == 0.0 is equivalent to -Xint and leaves CompileThreshold unchanged. if (!FLAG_IS_DEFAULT(CompileThresholdScaling) && CompileThresholdScaling > 0.0) { - FLAG_SET_ERGO(intx, CompileThreshold, scaled_compile_threshold(CompileThreshold)); + FLAG_SET_ERGO(CompileThreshold, scaled_compile_threshold(CompileThreshold)); } } @@ -455,7 +455,7 @@ AlwaysIncrementalInline = false; } if (PrintIdealGraphLevel > 0) { - FLAG_SET_ERGO(bool, PrintIdealGraph, true); + FLAG_SET_ERGO(PrintIdealGraph, true); } #endif if (!UseTypeSpeculation && FLAG_IS_DEFAULT(TypeProfileLevel)) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/compiler/compiler_globals.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/compiler/compiler_globals.hpp Thu May 23 11:07:37 2019 +0100 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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_COMPILER_COMPILER_GLOBALS_HPP +#define SHARE_COMPILER_COMPILER_GLOBALS_HPP + +#include "runtime/globals_shared.hpp" +#ifdef COMPILER1 +#include "c1/c1_globals.hpp" +#endif // COMPILER1 +#ifdef COMPILER2 +#include "opto/c2_globals.hpp" +#endif // COMPILER2 +#if INCLUDE_JVMCI +#include "jvmci/jvmci_globals.hpp" +#endif + +#if !defined(COMPILER1) && !defined(COMPILER2) && !INCLUDE_JVMCI +define_pd_global(bool, BackgroundCompilation, false); +define_pd_global(bool, UseTLAB, false); +define_pd_global(bool, CICompileOSR, false); +define_pd_global(bool, UseTypeProfile, false); +define_pd_global(bool, UseOnStackReplacement, false); +define_pd_global(bool, InlineIntrinsics, false); +define_pd_global(bool, PreferInterpreterNativeStubs, true); +define_pd_global(bool, ProfileInterpreter, false); +define_pd_global(bool, ProfileTraps, false); +define_pd_global(bool, TieredCompilation, false); + +define_pd_global(intx, CompileThreshold, 0); + +define_pd_global(intx, OnStackReplacePercentage, 0); +define_pd_global(bool, ResizeTLAB, false); +define_pd_global(intx, FreqInlineSize, 0); +define_pd_global(size_t, NewSizeThreadIncrease, 4*K); +define_pd_global(bool, InlineClassNatives, true); +define_pd_global(bool, InlineUnsafeOps, true); +define_pd_global(uintx, InitialCodeCacheSize, 160*K); +define_pd_global(uintx, ReservedCodeCacheSize, 32*M); +define_pd_global(uintx, NonProfiledCodeHeapSize, 0); +define_pd_global(uintx, ProfiledCodeHeapSize, 0); +define_pd_global(uintx, NonNMethodCodeHeapSize, 32*M); + +define_pd_global(uintx, CodeCacheExpansionSize, 32*K); +define_pd_global(uintx, CodeCacheMinBlockLength, 1); +define_pd_global(uintx, CodeCacheMinimumUseSpace, 200*K); +define_pd_global(size_t, MetaspaceSize, ScaleForWordSize(4*M)); +define_pd_global(bool, NeverActAsServerClassMachine, true); +define_pd_global(uint64_t,MaxRAM, 1ULL*G); +#define CI_COMPILER_COUNT 0 +#else + +#if COMPILER2_OR_JVMCI +#define CI_COMPILER_COUNT 2 +#else +#define CI_COMPILER_COUNT 1 +#endif // COMPILER2_OR_JVMCI + +#endif // no compilers + +#endif // SHARE_COMPILER_COMPILER_GLOBALS_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/compiler/disassembler.cpp --- a/src/hotspot/share/compiler/disassembler.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/compiler/disassembler.cpp Thu May 23 11:07:37 2019 +0100 @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "asm/assembler.inline.hpp" #include "asm/macroAssembler.hpp" #include "ci/ciUtilities.hpp" #include "classfile/javaClasses.hpp" @@ -43,6 +44,7 @@ void* Disassembler::_library = NULL; bool Disassembler::_tried_to_load_library = false; +bool Disassembler::_library_usable = false; // This routine is in the shared library: Disassembler::decode_func_virtual Disassembler::_decode_instructions_virtual = NULL; @@ -55,127 +57,46 @@ #define COMMENT_COLUMN 52 LP64_ONLY(+8) /*could be an option*/ #define BYTES_COMMENT ";..." /* funky byte display comment */ -bool Disassembler::load_library() { - if (_decode_instructions_virtual != NULL || _decode_instructions != NULL) { - // Already succeeded. - return true; - } - if (_tried_to_load_library) { - // Do not try twice. - // To force retry in debugger: assign _tried_to_load_library=0 - return false; - } - // Try to load it. - char ebuf[1024]; - char buf[JVM_MAXPATHLEN]; - os::jvm_path(buf, sizeof(buf)); - int jvm_offset = -1; - int lib_offset = -1; -#ifdef STATIC_BUILD - char* p = strrchr(buf, '/'); - *p = '\0'; - strcat(p, "/lib/"); - lib_offset = jvm_offset = strlen(buf); -#else - { - // Match "jvm[^/]*" in jvm_path. - const char* base = buf; - const char* p = strrchr(buf, *os::file_separator()); - if (p != NULL) lib_offset = p - base + 1; - p = strstr(p ? p : base, "jvm"); - if (p != NULL) jvm_offset = p - base; - } -#endif - // Find the disassembler shared library. - // Search for several paths derived from libjvm, in this order: - // 1. /jre/lib///libhsdis-.so (for compatibility) - // 2. /jre/lib///hsdis-.so - // 3. /jre/lib//hsdis-.so - // 4. hsdis-.so (using LD_LIBRARY_PATH) - if (jvm_offset >= 0) { - // 1. /jre/lib///libhsdis-.so - strcpy(&buf[jvm_offset], hsdis_library_name); - strcat(&buf[jvm_offset], os::dll_file_extension()); - _library = os::dll_load(buf, ebuf, sizeof ebuf); - if (_library == NULL && lib_offset >= 0) { - // 2. /jre/lib///hsdis-.so - strcpy(&buf[lib_offset], hsdis_library_name); - strcat(&buf[lib_offset], os::dll_file_extension()); - _library = os::dll_load(buf, ebuf, sizeof ebuf); - } - if (_library == NULL && lib_offset > 0) { - // 3. /jre/lib//hsdis-.so - buf[lib_offset - 1] = '\0'; - const char* p = strrchr(buf, *os::file_separator()); - if (p != NULL) { - lib_offset = p - buf + 1; - strcpy(&buf[lib_offset], hsdis_library_name); - strcat(&buf[lib_offset], os::dll_file_extension()); - _library = os::dll_load(buf, ebuf, sizeof ebuf); - } - } - } - if (_library == NULL) { - // 4. hsdis-.so (using LD_LIBRARY_PATH) - strcpy(&buf[0], hsdis_library_name); - strcat(&buf[0], os::dll_file_extension()); - _library = os::dll_load(buf, ebuf, sizeof ebuf); - } - if (_library != NULL) { - _decode_instructions_virtual = CAST_TO_FN_PTR(Disassembler::decode_func_virtual, - os::dll_lookup(_library, decode_instructions_virtual_name)); - } - if (_decode_instructions_virtual == NULL && _library != NULL) { - // could not spot in new version, try old version - _decode_instructions = CAST_TO_FN_PTR(Disassembler::decode_func, - os::dll_lookup(_library, decode_instructions_name)); - use_new_version = false; - } else { - use_new_version = true; - } - _tried_to_load_library = true; - if (_decode_instructions_virtual == NULL && _decode_instructions == NULL) { - tty->print_cr("Could not load %s; %s; %s", buf, - ((_library != NULL) - ? "entry point is missing" - : (WizardMode || PrintMiscellaneous) - ? (const char*)ebuf - : "library not loadable"), - "PrintAssembly is disabled"); - return false; - } - - // Success. - tty->print_cr("Loaded disassembler from %s", buf); - return true; -} - - class decode_env { private: - nmethod* _nm; - CodeBlob* _code; + outputStream* _output; // where the disassembly is directed to + CodeBuffer* _codeBuffer; // != NULL only when decoding a CodeBuffer + CodeBlob* _codeBlob; // != NULL only when decoding a CodeBlob + nmethod* _nm; // != NULL only when decoding a nmethod CodeStrings _strings; - outputStream* _output; - address _start, _end; - ptrdiff_t _offset; + address _start; // != NULL when decoding a range of unknown type + address _end; // != NULL when decoding a range of unknown type char _option_buf[512]; char _print_raw; - bool _print_pc; - bool _print_bytes; - address _cur_insn; - int _bytes_per_line; // arch-specific formatting option + address _cur_insn; // address of instruction currently being decoded + int _bytes_per_line; // arch-specific formatting option + int _pre_decode_alignment; + int _post_decode_alignment; bool _print_file_name; + bool _print_help; + bool _helpPrinted; + static bool _optionsParsed; + enum { + tabspacing = 8 + }; + + // Check if the event matches the expected tag + // The tag must be a substring of the event, and + // the tag must be a token in the event, i.e. separated by delimiters static bool match(const char* event, const char* tag) { - size_t taglen = strlen(tag); - if (strncmp(event, tag, taglen) != 0) + size_t eventlen = strlen(event); + size_t taglen = strlen(tag); + if (eventlen < taglen) // size mismatch + return false; + if (strncmp(event, tag, taglen) != 0) // string mismatch return false; char delim = event[taglen]; return delim == '\0' || delim == ' ' || delim == '/' || delim == '='; } + // Merge new option string with previously recorded options void collect_options(const char* p) { if (p == NULL || p[0] == '\0') return; size_t opt_so_far = strlen(_option_buf); @@ -187,14 +108,56 @@ char* q = fillp; while ((q = strpbrk(q, " \t\n")) != NULL) *q++ = ','; - // Note that multiple PrintAssemblyOptions flags accumulate with \n, - // which we want to be changed to a comma... } + void process_options(outputStream* ost); + void print_insn_labels(); - void print_insn_bytes(address pc0, address pc); + void print_insn_prefix(); void print_address(address value); + // Properly initializes _start/_end. Overwritten too often if + // printing of instructions is called for each instruction. + void set_start(address s) { _start = s; } + void set_end (address e) { _end = e; } + void set_nm (nmethod* nm) { _nm = nm; } + void set_output(outputStream* st) { _output = st; } + +#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) + // The disassembler library (sometimes) uses tabs to nicely align the instruction operands. + // Depending on the mnemonic length and the column position where the + // mnemonic is printed, alignment may turn out to be not so nice. + // To improve, we assume 8-character tab spacing and left-align the mnemonic on a tab position. + // Instruction comments are aligned 4 tab positions to the right of the mnemonic. + void calculate_alignment() { + _pre_decode_alignment = ((output()->position()+tabspacing-1)/tabspacing)*tabspacing; + _post_decode_alignment = _pre_decode_alignment + 4*tabspacing; + } + + void start_insn(address pc) { + _cur_insn = pc; + output()->bol(); + print_insn_labels(); + print_insn_prefix(); + } + + void end_insn(address pc) { + address pc0 = cur_insn(); + outputStream* st = output(); + + if (AbstractDisassembler::show_comment()) { + if ((_nm != NULL) && _nm->has_code_comment(pc0, pc)) { + _nm->print_code_comment_on(st, _post_decode_alignment, pc0, pc); + // this calls reloc_string_for which calls oop::print_value_on + } + print_hook_comments(pc0, _nm != NULL); + } + Disassembler::annotate(pc0, output()); + // follow each complete insn by a nice newline + st->bol(); + } +#endif + struct SourceFileInfo { struct Link : public CHeapObj { const char* file; @@ -241,40 +204,28 @@ static GrowableArray* _cached_src_lines; public: - decode_env(CodeBlob* code, outputStream* output, - CodeStrings c = CodeStrings(), ptrdiff_t offset = 0); - - address decode_instructions(address start, address end); - - void start_insn(address pc) { - _cur_insn = pc; - output()->bol(); - print_insn_labels(); - } + decode_env(CodeBuffer* code, outputStream* output); + decode_env(CodeBlob* code, outputStream* output, CodeStrings c = CodeStrings() /* , ptrdiff_t offset */); + decode_env(nmethod* code, outputStream* output, CodeStrings c = CodeStrings()); + // Constructor for a 'decode_env' to decode an arbitrary + // piece of memory, hopefully containing code. + decode_env(address start, address end, outputStream* output); - void end_insn(address pc) { - address pc0 = cur_insn(); - outputStream* st = output(); - if (_print_bytes && pc > pc0) - print_insn_bytes(pc0, pc); - if (_nm != NULL) { - _nm->print_code_comment_on(st, COMMENT_COLUMN, pc0, pc); - // this calls reloc_string_for which calls oop::print_value_on - } - print_hook_comments(pc0, _nm != NULL); - // follow each complete insn by a nice newline - st->cr(); - } + // Add 'original_start' argument which is the the original address + // the instructions were located at (if this is not equal to 'start'). + address decode_instructions(address start, address end, address original_start = NULL); address handle_event(const char* event, address arg); - outputStream* output() { return _output; } - address cur_insn() { return _cur_insn; } - const char* options() { return _option_buf; } - static void hook(const char* file, int line, address pc); + outputStream* output() { return _output; } + address cur_insn() { return _cur_insn; } + const char* options() { return _option_buf; } + static void hook(const char* file, int line, address pc); void print_hook_comments(address pc, bool newline); }; +bool decode_env::_optionsParsed = false; + decode_env::SourceFileInfoTable decode_env::_src_table; const char* decode_env::_cached_src = NULL; GrowableArray* decode_env::_cached_src_lines = NULL; @@ -361,50 +312,185 @@ } } -decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c, - ptrdiff_t offset) : _nm(NULL), - _start(NULL), - _end(NULL), - _option_buf(), - _print_raw('\0'), - _cur_insn(NULL) { +decode_env::decode_env(CodeBuffer* code, outputStream* output) { + memset(this, 0, sizeof(*this)); + _output = output ? output : tty; + _codeBlob = NULL; + _codeBuffer = code; + _helpPrinted = false; + + process_options(_output); +} + +decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c) { + memset(this, 0, sizeof(*this)); // Beware, this zeroes bits of fields. + _output = output ? output : tty; + _codeBlob = code; + _codeBuffer = NULL; + _helpPrinted = false; + if (_codeBlob != NULL && _codeBlob->is_nmethod()) { + _nm = (nmethod*) code; + } + _strings.copy(c); + + process_options(_output); +} + +decode_env::decode_env(nmethod* code, outputStream* output, CodeStrings c) { + memset(this, 0, sizeof(*this)); // Beware, this zeroes bits of fields. _output = output ? output : tty; - _code = code; - if (code != NULL && code->is_nmethod()) - _nm = (nmethod*) code; + _codeBlob = NULL; + _codeBuffer = NULL; + _nm = code; + _start = _nm->code_begin(); + _end = _nm->code_end(); + _helpPrinted = false; _strings.copy(c); - _offset = offset; + + process_options(_output); +} +// Constructor for a 'decode_env' to decode a memory range [start, end) +// of unknown origin, assuming it contains code. +decode_env::decode_env(address start, address end, outputStream* output) { + assert(start < end, "Range must have a positive size, [" PTR_FORMAT ".." PTR_FORMAT ").", p2i(start), p2i(end)); + memset(this, 0, sizeof(*this)); + _output = output ? output : tty; + _codeBlob = NULL; + _codeBuffer = NULL; + _start = start; + _end = end; + _helpPrinted = false; + + process_options(_output); +} + +void decode_env::process_options(outputStream* ost) { // by default, output pc but not bytes: - _print_pc = true; - _print_bytes = false; - _bytes_per_line = Disassembler::pd_instruction_alignment(); - _print_file_name= true; + _print_help = false; + _bytes_per_line = Disassembler::pd_instruction_alignment(); + _print_file_name = true; + + if (_optionsParsed) return; // parse only once // parse the global option string: collect_options(Disassembler::pd_cpu_opts()); collect_options(PrintAssemblyOptions); - if (strstr(options(), "hsdis-")) { - if (strstr(options(), "hsdis-print-raw")) - _print_raw = (strstr(options(), "xml") ? 2 : 1); - if (strstr(options(), "hsdis-print-pc")) - _print_pc = !_print_pc; - if (strstr(options(), "hsdis-print-bytes")) - _print_bytes = !_print_bytes; + if (strstr(options(), "print-raw")) { + _print_raw = (strstr(options(), "xml") ? 2 : 1); + } + + if (strstr(options(), "help")) { + _print_help = true; + } + if (strstr(options(), "align-instr")) { + AbstractDisassembler::toggle_align_instr(); + } + if (strstr(options(), "show-pc")) { + AbstractDisassembler::toggle_show_pc(); + } + if (strstr(options(), "show-offset")) { + AbstractDisassembler::toggle_show_offset(); + } + if (strstr(options(), "show-bytes")) { + AbstractDisassembler::toggle_show_bytes(); + } + if (strstr(options(), "show-data-hex")) { + AbstractDisassembler::toggle_show_data_hex(); + } + if (strstr(options(), "show-data-int")) { + AbstractDisassembler::toggle_show_data_int(); + } + if (strstr(options(), "show-data-float")) { + AbstractDisassembler::toggle_show_data_float(); } - if (strstr(options(), "help")) { - tty->print_cr("PrintAssemblyOptions help:"); - tty->print_cr(" hsdis-print-raw test plugin by requesting raw output"); - tty->print_cr(" hsdis-print-raw-xml test plugin by requesting raw xml"); - tty->print_cr(" hsdis-print-pc turn off PC printing (on by default)"); - tty->print_cr(" hsdis-print-bytes turn on instruction byte output"); - tty->print_cr("combined options: %s", options()); + if (strstr(options(), "show-structs")) { + AbstractDisassembler::toggle_show_structs(); + } + if (strstr(options(), "show-comment")) { + AbstractDisassembler::toggle_show_comment(); + } + if (strstr(options(), "show-block-comment")) { + AbstractDisassembler::toggle_show_block_comment(); + } + _optionsParsed = true; + + if (_print_help && ! _helpPrinted) { + _helpPrinted = true; + ost->print_cr("PrintAssemblyOptions help:"); + ost->print_cr(" print-raw test plugin by requesting raw output"); + ost->print_cr(" print-raw-xml test plugin by requesting raw xml"); + ost->cr(); + ost->print_cr(" show-pc toggle printing current pc, currently %s", AbstractDisassembler::show_pc() ? "ON" : "OFF"); + ost->print_cr(" show-offset toggle printing current offset, currently %s", AbstractDisassembler::show_offset() ? "ON" : "OFF"); + ost->print_cr(" show-bytes toggle printing instruction bytes, currently %s", AbstractDisassembler::show_bytes() ? "ON" : "OFF"); + ost->print_cr(" show-data-hex toggle formatting data as hex, currently %s", AbstractDisassembler::show_data_hex() ? "ON" : "OFF"); + ost->print_cr(" show-data-int toggle formatting data as int, currently %s", AbstractDisassembler::show_data_int() ? "ON" : "OFF"); + ost->print_cr(" show-data-float toggle formatting data as float, currently %s", AbstractDisassembler::show_data_float() ? "ON" : "OFF"); + ost->print_cr(" show-structs toggle compiler data structures, currently %s", AbstractDisassembler::show_structs() ? "ON" : "OFF"); + ost->print_cr(" show-comment toggle instruction comments, currently %s", AbstractDisassembler::show_comment() ? "ON" : "OFF"); + ost->print_cr(" show-block-comment toggle block comments, currently %s", AbstractDisassembler::show_block_comment() ? "ON" : "OFF"); + ost->print_cr(" align-instr toggle instruction alignment, currently %s", AbstractDisassembler::align_instr() ? "ON" : "OFF"); + ost->print_cr("combined options: %s", options()); } } +// Disassembly Event Handler. +// This method receives events from the disassembler library hsdis +// via event_to_env for each decoding step (installed by +// Disassembler::decode_instructions(), replacing the default +// callback method). This enables dumping additional info +// and custom line formatting. +// In a future extension, calling a custom decode method will be +// supported. We can use such a method to decode instructions the +// binutils decoder does not handle to our liking (suboptimal +// formatting, incomplete information, ...). +// Returns: +// - NULL for all standard invocations. The function result is not +// examined (as of now, 20190409) by the hsdis decoder loop. +// - next for 'insn0' invocations. +// next == arg: the custom decoder didn't do anything. +// next > arg: the custom decoder did decode the instruction. +// next points to the next undecoded instruction +// (continuation point for decoder loop). +// +// "Normal" sequence of events: +// insns - start of instruction stream decoding +// mach - display architecture +// format - display bytes-per-line +// for each instruction: +// insn - start of instruction decoding +// insn0 - custom decoder invocation (if any) +// addr - print address value +// /insn - end of instruction decoding +// /insns - premature end of instruction stream due to no progress +// address decode_env::handle_event(const char* event, address arg) { - if (match(event, "insn")) { + +#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) + + //---< Event: end decoding loop (error, no progress) >--- + if (decode_env::match(event, "/insns")) { + // Nothing to be done here. + return NULL; + } + + //---< Event: start decoding loop >--- + if (decode_env::match(event, "insns")) { + // Nothing to be done here. + return NULL; + } + + //---< Event: finish decoding an instruction >--- + if (decode_env::match(event, "/insn")) { + output()->fill_to(_post_decode_alignment); + end_insn(arg); + return NULL; + } + + //---< Event: start decoding an instruction >--- + if (decode_env::match(event, "insn")) { start_insn(arg); } else if (match(event, "/insn")) { end_insn(arg); @@ -413,26 +499,59 @@ print_address(arg); return arg; } - } else if (match(event, "mach")) { - static char buffer[32] = { 0, }; - if (strcmp(buffer, (const char*)arg) != 0 || - strlen((const char*)arg) > sizeof(buffer) - 1) { + calculate_alignment(); + output()->fill_to(_pre_decode_alignment); + return NULL; + } + + //---< Event: call custom decoder (platform specific) >--- + if (decode_env::match(event, "insn0")) { + return Disassembler::decode_instruction0(arg, output(), arg); + } + + //---< Event: Print address >--- + if (decode_env::match(event, "addr")) { + print_address(arg); + return arg; + } + + //---< Event: mach (inform about machine architecture) >--- + // This event is problematic because it messes up the output. + // The event is fired after the instruction address has already + // been printed. The decoded instruction (event "insn") is + // printed afterwards. That doesn't look nice. + if (decode_env::match(event, "mach")) { + guarantee(arg != NULL, "event_to_env - arg must not be NULL for event 'mach'"); + static char buffer[64] = { 0, }; + // Output suppressed because it messes up disassembly. + // Only print this when the mach changes. + if (false && (strcmp(buffer, (const char*)arg) != 0 || + strlen((const char*)arg) > sizeof(buffer) - 1)) { // Only print this when the mach changes strncpy(buffer, (const char*)arg, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = '\0'; - output()->print_cr("[Disassembling for mach='%s']", arg); + output()->print_cr("[Disassembling for mach='%s']", (const char*)arg); } - } else if (match(event, "format bytes-per-line")) { + return NULL; + } + + //---< Event: format bytes-per-line >--- + if (decode_env::match(event, "format bytes-per-line")) { _bytes_per_line = (int) (intptr_t) arg; - } else { - // ignore unrecognized markup + return NULL; } +#endif return NULL; } +static void* event_to_env(void* env_pv, const char* event, void* arg) { + decode_env* env = (decode_env*) env_pv; + return env->handle_event(event, (address) arg); +} + // called by the disassembler to print out jump targets and data addresses void decode_env::print_address(address adr) { - outputStream* st = _output; + outputStream* st = output(); if (adr == NULL) { st->print("NULL"); @@ -477,9 +596,11 @@ if (_nm == NULL) { // Don't do this for native methods, as the function name will be printed in // nmethod::reloc_string_for(). - ResourceMark rm; + // Allocate the buffer on the stack instead of as RESOURCE array. + // In case we do DecodeErrorFile, Thread will not be initialized, + // causing a "assert(current != __null) failed" failure. const int buflen = 1024; - char* buf = NEW_RESOURCE_ARRAY(char, buflen); + char buf[buflen]; int offset; if (os::dll_address_to_function_name(adr, buf, buflen, &offset)) { st->print(PTR_FORMAT " = %s", p2i(adr), buf); @@ -495,54 +616,31 @@ } void decode_env::print_insn_labels() { - address p = cur_insn(); - outputStream* st = output(); - CodeBlob* cb = _code; - if (cb != NULL) { - cb->print_block_comment(st, p); - } - _strings.print_block_comment(st, (intptr_t)(p - _start + _offset)); - if (_print_pc) { - st->print(" " PTR_FORMAT ": ", p2i(p)); + if (AbstractDisassembler::show_block_comment()) { + address p = cur_insn(); + outputStream* st = output(); + + //---< Block comments for nmethod >--- + // Outputs a bol() before and a cr() after, but only if a comment is printed. + // Prints nmethod_section_label as well. + if (_nm != NULL) { + _nm->print_block_comment(st, p); + } + if (_codeBlob != NULL) { + _codeBlob->print_block_comment(st, p); + } + if (_codeBuffer != NULL) { + _codeBuffer->print_block_comment(st, p); + } + _strings.print_block_comment(st, (intptr_t)(p - _start)); } } -void decode_env::print_insn_bytes(address pc, address pc_limit) { +void decode_env::print_insn_prefix() { + address p = cur_insn(); outputStream* st = output(); - size_t incr = 1; - size_t perline = _bytes_per_line; - if ((size_t) Disassembler::pd_instruction_alignment() >= sizeof(int) - && !((uintptr_t)pc % sizeof(int)) - && !((uintptr_t)pc_limit % sizeof(int))) { - incr = sizeof(int); - if (perline % incr) perline += incr - (perline % incr); - } - while (pc < pc_limit) { - // tab to the desired column: - st->move_to(COMMENT_COLUMN); - address pc0 = pc; - address pc1 = pc + perline; - if (pc1 > pc_limit) pc1 = pc_limit; - for (; pc < pc1; pc += incr) { - if (pc == pc0) { - st->print(BYTES_COMMENT); - } else if ((uint)(pc - pc0) % sizeof(int) == 0) { - st->print(" "); // put out a space on word boundaries - } - if (incr == sizeof(int)) { - st->print("%08x", *(int*)pc); - } else { - st->print("%02x", (*pc)&0xFF); - } - } - st->cr(); - } -} - - -static void* event_to_env(void* env_pv, const char* event, void* arg) { - decode_env* env = (decode_env*) env_pv; - return env->handle_event(event, (address) arg); + AbstractDisassembler::print_location(p, _start, _end, st, false, false); + AbstractDisassembler::print_instruction(p, Assembler::instr_len(p), Assembler::instr_maxlen(), st, true, false); } ATTRIBUTE_PRINTF(2, 3) @@ -575,16 +673,31 @@ return (int)(cnt1 - cnt0); } -address decode_env::decode_instructions(address start, address end) { - _start = start; _end = end; - - assert(((((intptr_t)start | (intptr_t)end) % Disassembler::pd_instruction_alignment()) == 0), "misaligned insn addr"); +// The 'original_start' argument holds the the original address where +// the instructions were located in the originating system. If zero (NULL) +// is passed in, there is no original address. +address decode_env::decode_instructions(address start, address end, address original_start /* = 0*/) { + // CodeComment in Stubs. + // Properly initialize _start/_end. Overwritten too often if + // printing of instructions is called for each instruction. + assert((_start == NULL) || (start == NULL) || (_start == start), "don't overwrite CTOR values"); + assert((_end == NULL) || (end == NULL) || (_end == end ), "don't overwrite CTOR values"); + if (start != NULL) set_start(start); + if (end != NULL) set_end(end); + if (original_start == NULL) { + original_start = start; + } - const int show_bytes = false; // for disassembler debugging + //---< Check (and correct) alignment >--- + // Don't check alignment of end, it is not aligned. + if (((uint64_t)start & ((uint64_t)Disassembler::pd_instruction_alignment() - 1)) != 0) { + output()->print_cr("Decode range start:" PTR_FORMAT ": ... (unaligned)", p2i(start)); + start = (address)((uint64_t)start & ~((uint64_t)Disassembler::pd_instruction_alignment() - 1)); + } - //_version = Disassembler::pd_cpu_version(); - - if (!Disassembler::can_decode()) { + // Trying to decode instructions doesn't make sense if we + // couldn't load the disassembler library. + if (Disassembler::is_abstract()) { return NULL; } @@ -625,16 +738,177 @@ options()); } +// ---------------------------------------------------------------------------- +// Disassembler +// Used as a static wrapper for decode_env. +// Each method will create a decode_env before decoding. +// You can call the decode_env methods directly if you already have one. -void Disassembler::decode(CodeBlob* cb, outputStream* st) { - ttyLocker ttyl; - if (!load_library()) return; - if (cb->is_nmethod()) { - decode((nmethod*)cb, st); + +bool Disassembler::load_library(outputStream* st) { + // Do not try to load multiple times. Failed once -> fails always. + // To force retry in debugger: assign _tried_to_load_library=0 + if (_tried_to_load_library) { + return _library_usable; + } + +#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) + // Print to given stream, if any. + // Print to tty if Verbose is on and no stream given. + st = ((st == NULL) && Verbose) ? tty : st; + + // Compute fully qualified library name. + char ebuf[1024]; + char buf[JVM_MAXPATHLEN]; + os::jvm_path(buf, sizeof(buf)); + int jvm_offset = -1; + int lib_offset = -1; +#ifdef STATIC_BUILD + char* p = strrchr(buf, '/'); + *p = '\0'; + strcat(p, "/lib/"); + lib_offset = jvm_offset = strlen(buf); +#else + { + // Match "libjvm" instead of "jvm" on *nix platforms. Creates better matches. + // Match "[lib]jvm[^/]*" in jvm_path. + const char* base = buf; + const char* p = strrchr(buf, *os::file_separator()); +#ifdef _WIN32 + p = strstr(p ? p : base, "jvm"); +#else + p = strstr(p ? p : base, "libjvm"); +#endif + if (p != NULL) lib_offset = p - base + 1; + if (p != NULL) jvm_offset = p - base; + } +#endif + + // Find the disassembler shared library. + // Search for several paths derived from libjvm, in this order: + // 1. /jre/lib///libhsdis-.so (for compatibility) + // 2. /jre/lib///hsdis-.so + // 3. /jre/lib//hsdis-.so + // 4. hsdis-.so (using LD_LIBRARY_PATH) + if (jvm_offset >= 0) { + // 1. /jre/lib///libhsdis-.so + strcpy(&buf[jvm_offset], hsdis_library_name); + strcat(&buf[jvm_offset], os::dll_file_extension()); + _library = os::dll_load(buf, ebuf, sizeof ebuf); + if (_library == NULL && lib_offset >= 0) { + // 2. /jre/lib///hsdis-.so + strcpy(&buf[lib_offset], hsdis_library_name); + strcat(&buf[lib_offset], os::dll_file_extension()); + _library = os::dll_load(buf, ebuf, sizeof ebuf); + } + if (_library == NULL && lib_offset > 0) { + // 3. /jre/lib//hsdis-.so + buf[lib_offset - 1] = '\0'; + const char* p = strrchr(buf, *os::file_separator()); + if (p != NULL) { + lib_offset = p - buf + 1; + strcpy(&buf[lib_offset], hsdis_library_name); + strcat(&buf[lib_offset], os::dll_file_extension()); + _library = os::dll_load(buf, ebuf, sizeof ebuf); + } + } + } + if (_library == NULL) { + // 4. hsdis-.so (using LD_LIBRARY_PATH) + strcpy(&buf[0], hsdis_library_name); + strcat(&buf[0], os::dll_file_extension()); + _library = os::dll_load(buf, ebuf, sizeof ebuf); + } + + // load the decoder function to use (new or old version). + if (_library != NULL) { + _decode_instructions_virtual = CAST_TO_FN_PTR(Disassembler::decode_func_virtual, + os::dll_lookup(_library, decode_instructions_virtual_name)); + } + if (_decode_instructions_virtual == NULL && _library != NULL) { + // could not spot in new version, try old version + _decode_instructions = CAST_TO_FN_PTR(Disassembler::decode_func, + os::dll_lookup(_library, decode_instructions_name)); + use_new_version = false; + } else { + use_new_version = true; + } + _tried_to_load_library = true; + _library_usable = _decode_instructions_virtual != NULL || _decode_instructions != NULL; + + // Create a dummy environment to initialize PrintAssemblyOptions. + // The PrintAssemblyOptions must be known for abstract disassemblies as well. + decode_env dummy((unsigned char*)(&buf[0]), (unsigned char*)(&buf[1]), st); + + // Report problems during dll_load or dll_lookup, if any. + if (st != NULL) { + // Success. + if (_library_usable) { + st->print_cr("Loaded disassembler from %s", buf); + } else { + st->print_cr("Could not load %s; %s; %s", + buf, + ((_library != NULL) + ? "entry point is missing" + : ((WizardMode || PrintMiscellaneous) + ? (const char*)ebuf + : "library not loadable")), + "PrintAssembly defaults to abstract disassembly."); + } + } +#endif + return _library_usable; +} + + +// Directly disassemble code buffer. +void Disassembler::decode(CodeBuffer* cb, address start, address end, outputStream* st) { +#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) + //---< Test memory before decoding >--- + if (!(cb->contains(start) && cb->contains(end))) { + //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- + if (st != NULL) { + st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not contained in CodeBuffer", p2i(start), p2i(end)); + } return; } + if (!os::is_readable_range(start, end)) { + //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- + if (st != NULL) { + st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not readable", p2i(start), p2i(end)); + } + return; + } + decode_env env(cb, st); - env.output()->print_cr("----------------------------------------------------------------------"); + env.output()->print_cr("--------------------------------------------------------------------------------"); + env.output()->print("Decoding CodeBuffer (" PTR_FORMAT ")", p2i(cb)); + if (cb->name() != NULL) { + env.output()->print(", name: %s,", cb->name()); + } + env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(start), p2i(end), ((jlong)(end - start))); + + if (is_abstract()) { + AbstractDisassembler::decode_abstract(start, end, env.output(), Assembler::instr_maxlen()); + } else { + env.decode_instructions(start, end); + } + env.output()->print_cr("--------------------------------------------------------------------------------"); +#endif +} + +// Directly disassemble code blob. +void Disassembler::decode(CodeBlob* cb, outputStream* st, CodeStrings c) { +#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) + if (cb->is_nmethod()) { + // If we have an nmethod at hand, + // call the specialized decoder directly. + decode((nmethod*)cb, st, c); + return; + } + + decode_env env(cb, st); + env.output()->print_cr("--------------------------------------------------------------------------------"); if (cb->is_aot()) { env.output()->print("A "); if (cb->is_compiled()) { @@ -648,57 +922,78 @@ env.output()->print_cr("%s", cb->name()); } } else { - env.output()->print_cr("%s", cb->name()); + env.output()->print("Decoding CodeBlob"); + if (cb->name() != NULL) { + env.output()->print(", name: %s,", cb->name()); + } } - env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(cb->code_begin()), p2i(cb->code_end()), ((jlong)(cb->code_end() - cb->code_begin())) * sizeof(unsigned char*)); - env.decode_instructions(cb->code_begin(), cb->code_end()); -} + env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(cb->code_begin()), p2i(cb->code_end()), ((jlong)(cb->code_end() - cb->code_begin()))); -void Disassembler::decode(address start, address end, outputStream* st, CodeStrings c, - ptrdiff_t offset) { - ttyLocker ttyl; - if (!load_library()) return; - decode_env env(CodeCache::find_blob_unsafe(start), st, c, offset); - env.decode_instructions(start, end); + if (is_abstract()) { + AbstractDisassembler::decode_abstract(cb->code_begin(), cb->code_end(), env.output(), Assembler::instr_maxlen()); + } else { + env.decode_instructions(cb->code_begin(), cb->code_end()); + } + env.output()->print_cr("--------------------------------------------------------------------------------"); +#endif } -void Disassembler::decode(nmethod* nm, outputStream* st) { +// Decode a nmethod. +// This includes printing the constant pool and all code segments. +// The nmethod data structures (oop maps, relocations and the like) are not printed. +void Disassembler::decode(nmethod* nm, outputStream* st, CodeStrings c) { +#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) ttyLocker ttyl; - if (!load_library()) return; + decode_env env(nm, st); - env.output()->print_cr("----------------------------------------------------------------------"); - - unsigned char* p = nm->code_begin(); - unsigned char* end = nm->code_end(); + env.output()->print_cr("--------------------------------------------------------------------------------"); + nm->print_constant_pool(env.output()); + env.output()->print_cr("--------------------------------------------------------------------------------"); + env.output()->cr(); + if (is_abstract()) { + AbstractDisassembler::decode_abstract(nm->code_begin(), nm->code_end(), env.output(), Assembler::instr_maxlen()); + } else { + env.decode_instructions(nm->code_begin(), nm->code_end()); + } + env.output()->print_cr("--------------------------------------------------------------------------------"); +#endif +} - nm->method()->method_holder()->name()->print_symbol_on(env.output()); - env.output()->print("."); - nm->method()->name()->print_symbol_on(env.output()); - nm->method()->signature()->print_symbol_on(env.output()); -#if INCLUDE_JVMCI +// Decode a range, given as [start address, end address) +void Disassembler::decode(address start, address end, outputStream* st, CodeStrings c /*, ptrdiff_t offset */) { +#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) + //---< Test memory before decoding >--- + if (!os::is_readable_range(start, end)) { + //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- + if (st != NULL) { + st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not readable", p2i(start), p2i(end)); + } + return; + } + + if (is_abstract()) { + AbstractDisassembler::decode_abstract(start, end, st, Assembler::instr_maxlen()); + return; + } + +// Don't do that fancy stuff. If we just have two addresses, live with it +// and treat the memory contents as "amorphic" piece of code. +#if 0 + CodeBlob* cb = CodeCache::find_blob_unsafe(start); + if (cb != NULL) { + // If we have an CodeBlob at hand, + // call the specialized decoder directly. + decode(cb, st, c); + } else +#endif { - const char* jvmciName = nm->jvmci_name(); - if (jvmciName != NULL) { - env.output()->print(" (%s)", jvmciName); - } + // This seems to be just a chunk of memory. + decode_env env(start, end, st); + env.output()->print_cr("--------------------------------------------------------------------------------"); + env.decode_instructions(start, end); + env.output()->print_cr("--------------------------------------------------------------------------------"); } #endif - env.output()->print_cr(" [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(p), p2i(end), ((jlong)(end - p))); - - // Print constant table. - if (nm->consts_size() > 0) { - nm->print_nmethod_labels(env.output(), nm->consts_begin()); - int offset = 0; - for (address p = nm->consts_begin(); p < nm->consts_end(); p += 4, offset += 4) { - if ((offset % 8) == 0) { - env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT " " PTR64_FORMAT, p2i(p), offset, *((int32_t*) p), *((int64_t*) p)); - } else { - env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT, p2i(p), offset, *((int32_t*) p)); - } - } - } - - env.decode_instructions(p, end); } // To prevent excessive code expansion in the interpreter generator, we diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/compiler/disassembler.hpp --- a/src/hotspot/share/compiler/disassembler.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/compiler/disassembler.hpp Thu May 23 11:07:37 2019 +0100 @@ -25,7 +25,11 @@ #ifndef SHARE_COMPILER_DISASSEMBLER_HPP #define SHARE_COMPILER_DISASSEMBLER_HPP +#include "utilities/globalDefinitions.hpp" + +#include "asm/assembler.hpp" #include "asm/codeBuffer.hpp" +#include "compiler/abstractDisassembler.hpp" #include "runtime/globals.hpp" #include "utilities/macros.hpp" @@ -34,7 +38,8 @@ // The disassembler prints out assembly code annotated // with Java specific information. -class Disassembler { +// Disassembler inherits from AbstractDisassembler +class Disassembler : public AbstractDisassembler { friend class decode_env; private: // this is the type of the dll entry point: @@ -57,26 +62,58 @@ static void* _library; // bailout static bool _tried_to_load_library; + static bool _library_usable; // points to the decode function. static decode_func_virtual _decode_instructions_virtual; static decode_func _decode_instructions; - // tries to load library and return whether it succedded. - static bool load_library(); + + // tries to load library and return whether it succeeded. + // Allow (diagnostic) output redirection. + // No output at all if stream is NULL. Can be overridden + // with -Verbose flag, in which case output goes to tty. + static bool load_library(outputStream* st = NULL); + + // Check if the two addresses are on the same page. + static bool is_same_page(address a1, address a2) { + return (((uintptr_t)a1 ^ (uintptr_t)a2) & (~0x0fffUL)) == 0L; + } // Machine dependent stuff #include CPU_HEADER(disassembler) public: - static bool can_decode() { - ttyLocker tl; - return (_decode_instructions_virtual != NULL) || - (_decode_instructions != NULL) || - load_library(); + // We can always decode code blobs. + // Either we have a disassembler library available (successfully loaded) + // or we will resort to the abstract disassembler. This method informs + // about which decoding format is used. + // We can also enforce using the abstract disassembler. + static bool is_abstract() { + if (!_tried_to_load_library /* && !UseAbstractDisassembler */) { + load_library(); + } + return ! _library_usable /* || UseAbstractDisassembler */; // Not available until DecodeErrorFile is supported. } - static void decode(CodeBlob *cb, outputStream* st = NULL); - static void decode(nmethod* nm, outputStream* st = NULL); - static void decode(address begin, address end, outputStream* st = NULL, - CodeStrings c = CodeStrings(), ptrdiff_t offset = 0); + + // Check out if we are doing a live disassembly or a post-mortem + // disassembly where the binary data was loaded from a hs_err file. + static bool is_decode_error_file() { +// Activate once post-mortem disassembly (from hs-err file) is available. +#if 0 + return DecodeErrorFile && (strlen(DecodeErrorFile) != 0); +#else + return false; +#endif + } + + // Directly disassemble code buffer. + static void decode(CodeBuffer* cb, address start, address end, outputStream* st = NULL); + // Directly disassemble code blob. + static void decode(CodeBlob *cb, outputStream* st = NULL, CodeStrings c = CodeStrings()); + // Directly disassemble nmethod. + static void decode(nmethod* nm, outputStream* st = NULL, CodeStrings c = CodeStrings()); + // Disassemble an arbitrary memory range. + static void decode(address start, address end, outputStream* st = NULL, CodeStrings c = CodeStrings() /* , ptrdiff_t offset */); + static void _hook(const char* file, int line, class MacroAssembler* masm); // This functions makes it easy to generate comments in the generated diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/compiler/oopMap.cpp --- a/src/hotspot/share/compiler/oopMap.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/compiler/oopMap.cpp Thu May 23 11:07:37 2019 +0100 @@ -511,7 +511,7 @@ void ImmutableOopMap::print_on(outputStream* st) const { OopMapValue omv; - st->print("ImmutableOopMap{"); + st->print("ImmutableOopMap {"); for(OopMapStream oms(this); !oms.is_done(); oms.next()) { omv = oms.current(); omv.print_on(st); @@ -523,44 +523,51 @@ void OopMap::print_on(outputStream* st) const { OopMapValue omv; - st->print("OopMap{"); + st->print("OopMap {"); for(OopMapStream oms((OopMap*)this); !oms.is_done(); oms.next()) { omv = oms.current(); omv.print_on(st); } - st->print("off=%d}", (int) offset()); + // Print hex offset in addition. + st->print("off=%d/0x%x}", (int) offset(), (int) offset()); } void OopMap::print() const { print_on(tty); } void ImmutableOopMapSet::print_on(outputStream* st) const { const ImmutableOopMap* last = NULL; - for (int i = 0; i < _count; ++i) { + const int len = count(); + + st->print_cr("ImmutableOopMapSet contains %d OopMaps", len); + + for (int i = 0; i < len; i++) { const ImmutableOopMapPair* pair = pair_at(i); const ImmutableOopMap* map = pair->get_from(this); if (map != last) { st->cr(); map->print_on(st); - st->print("pc offsets: "); + st->print(" pc offsets: "); } last = map; st->print("%d ", pair->pc_offset()); } + st->cr(); } void ImmutableOopMapSet::print() const { print_on(tty); } void OopMapSet::print_on(outputStream* st) const { - int i, len = om_count(); + const int len = om_count(); - st->print_cr("OopMapSet contains %d OopMaps\n",len); + st->print_cr("OopMapSet contains %d OopMaps", len); - for( i = 0; i < len; i++) { + for( int i = 0; i < len; i++) { OopMap* m = at(i); st->print_cr("#%d ",i); m->print_on(st); st->cr(); } + st->cr(); } void OopMapSet::print() const { print_on(tty); } @@ -580,15 +587,17 @@ const ImmutableOopMap* ImmutableOopMapSet::find_map_at_offset(int pc_offset) const { ImmutableOopMapPair* pairs = get_pairs(); + ImmutableOopMapPair* last = NULL; - int i; - for (i = 0; i < _count; ++i) { + for (int i = 0; i < _count; ++i) { if (pairs[i].pc_offset() >= pc_offset) { + last = &pairs[i]; break; } } - ImmutableOopMapPair* last = &pairs[i]; + // Heal Coverity issue: potential index out of bounds access. + guarantee(last != NULL, "last may not be null"); assert(last->pc_offset() == pc_offset, "oopmap not found"); return last->get_from(this); } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/cms/cmsArguments.cpp --- a/src/hotspot/share/gc/cms/cmsArguments.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/cms/cmsArguments.cpp Thu May 23 11:07:37 2019 +0100 @@ -106,7 +106,7 @@ } if (!ClassUnloading) { - FLAG_SET_CMDLINE(bool, CMSClassUnloadingEnabled, false); + FLAG_SET_CMDLINE(CMSClassUnloadingEnabled, false); } // Set CMS global values @@ -142,9 +142,9 @@ // NewSize was set on the command line and it is larger than // preferred_max_new_size. if (!FLAG_IS_DEFAULT(NewSize)) { // NewSize explicitly set at command-line - FLAG_SET_ERGO(size_t, MaxNewSize, MAX2(NewSize, preferred_max_new_size)); + FLAG_SET_ERGO(MaxNewSize, MAX2(NewSize, preferred_max_new_size)); } else { - FLAG_SET_ERGO(size_t, MaxNewSize, preferred_max_new_size); + FLAG_SET_ERGO(MaxNewSize, preferred_max_new_size); } log_trace(gc, heap)("CMS ergo set MaxNewSize: " SIZE_FORMAT, MaxNewSize); @@ -159,15 +159,15 @@ // Unless explicitly requested otherwise, make young gen // at least min_new, and at most preferred_max_new_size. if (FLAG_IS_DEFAULT(NewSize)) { - FLAG_SET_ERGO(size_t, NewSize, MAX2(NewSize, min_new)); - FLAG_SET_ERGO(size_t, NewSize, MIN2(preferred_max_new_size, NewSize)); + FLAG_SET_ERGO(NewSize, MAX2(NewSize, min_new)); + FLAG_SET_ERGO(NewSize, MIN2(preferred_max_new_size, NewSize)); log_trace(gc, heap)("CMS ergo set NewSize: " SIZE_FORMAT, NewSize); } // Unless explicitly requested otherwise, size old gen // so it's NewRatio x of NewSize. if (FLAG_IS_DEFAULT(OldSize)) { if (max_heap > NewSize) { - FLAG_SET_ERGO(size_t, OldSize, MIN2(NewRatio*NewSize, max_heap - NewSize)); + FLAG_SET_ERGO(OldSize, MIN2(NewRatio*NewSize, max_heap - NewSize)); log_trace(gc, heap)("CMS ergo set OldSize: " SIZE_FORMAT, OldSize); } } @@ -177,14 +177,14 @@ // promote all objects surviving "tenuring_default" scavenges. if (FLAG_IS_DEFAULT(MaxTenuringThreshold) && FLAG_IS_DEFAULT(SurvivorRatio)) { - FLAG_SET_ERGO(uintx, MaxTenuringThreshold, tenuring_default); + FLAG_SET_ERGO(MaxTenuringThreshold, tenuring_default); } // If we decided above (or user explicitly requested) // `promote all' (via MaxTenuringThreshold := 0), // prefer minuscule survivor spaces so as not to waste // space for (non-existent) survivors if (FLAG_IS_DEFAULT(SurvivorRatio) && MaxTenuringThreshold == 0) { - FLAG_SET_ERGO(uintx, SurvivorRatio, MAX2((uintx)1024, SurvivorRatio)); + FLAG_SET_ERGO(SurvivorRatio, MAX2((uintx)1024, SurvivorRatio)); } // OldPLABSize is interpreted in CMS as not the size of the PLAB in words, @@ -195,7 +195,7 @@ // OldPLAB sizing manually turned off: Use a larger default setting, // unless it was manually specified. This is because a too-low value // will slow down scavenges. - FLAG_SET_ERGO(size_t, OldPLABSize, CompactibleFreeListSpaceLAB::_default_static_old_plab_size); // default value before 6631166 + FLAG_SET_ERGO(OldPLABSize, CompactibleFreeListSpaceLAB::_default_static_old_plab_size); // default value before 6631166 } else { FLAG_SET_DEFAULT(OldPLABSize, CompactibleFreeListSpaceLAB::_default_dynamic_old_plab_size); // old CMSParPromoteBlocksToClaim default } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/epsilon/epsilonHeap.hpp --- a/src/hotspot/share/gc/epsilon/epsilonHeap.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/epsilon/epsilonHeap.hpp Thu May 23 11:07:37 2019 +0100 @@ -29,7 +29,6 @@ #include "gc/shared/space.hpp" #include "gc/epsilon/epsilonMonitoringSupport.hpp" #include "gc/epsilon/epsilonBarrierSet.hpp" -#include "gc/epsilon/epsilon_globals.hpp" #include "services/memoryManager.hpp" class EpsilonHeap : public CollectedHeap { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/epsilon/epsilon_globals.hpp --- a/src/hotspot/share/gc/epsilon/epsilon_globals.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/epsilon/epsilon_globals.hpp Thu May 23 11:07:37 2019 +0100 @@ -25,7 +25,8 @@ #ifndef SHARE_GC_EPSILON_EPSILON_GLOBALS_HPP #define SHARE_GC_EPSILON_EPSILON_GLOBALS_HPP -#include "runtime/globals.hpp" +#include "runtime/globals_shared.hpp" + // // Defines all globals flags used by the Epsilon GC. // diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1Arguments.cpp --- a/src/hotspot/share/gc/g1/g1Arguments.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1Arguments.cpp Thu May 23 11:07:37 2019 +0100 @@ -110,11 +110,11 @@ // triggering a full collection. To get as low fragmentation as // possible we only use one worker thread. if (DumpSharedSpaces) { - FLAG_SET_ERGO(uint, ParallelGCThreads, 1); + FLAG_SET_ERGO(ParallelGCThreads, 1); } if (FLAG_IS_DEFAULT(G1ConcRefinementThreads)) { - FLAG_SET_ERGO(uint, G1ConcRefinementThreads, ParallelGCThreads); + FLAG_SET_ERGO(G1ConcRefinementThreads, ParallelGCThreads); } // MarkStackSize will be set (if it hasn't been set by the user) @@ -162,7 +162,7 @@ // By default do not let the target stack size to be more than 1/4 of the entries if (FLAG_IS_DEFAULT(GCDrainStackTargetSize)) { - FLAG_SET_ERGO(uintx, GCDrainStackTargetSize, MIN2(GCDrainStackTargetSize, (uintx)TASKQUEUE_SIZE / 4)); + FLAG_SET_ERGO(GCDrainStackTargetSize, MIN2(GCDrainStackTargetSize, (uintx)TASKQUEUE_SIZE / 4)); } #ifdef COMPILER2 diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1CollectedHeap.hpp --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp Thu May 23 11:07:37 2019 +0100 @@ -1111,7 +1111,7 @@ public: - inline G1HeapRegionAttr region_attr(const oop obj); + inline G1HeapRegionAttr region_attr(const void* obj); // Return "TRUE" iff the given object address is in the reserved // region of g1. diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp Thu May 23 11:07:37 2019 +0100 @@ -29,6 +29,7 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1Policy.hpp" +#include "gc/g1/g1RemSet.hpp" #include "gc/g1/heapRegionManager.inline.hpp" #include "gc/g1/heapRegionRemSet.hpp" #include "gc/g1/heapRegionSet.inline.hpp" @@ -162,8 +163,8 @@ return _region_attr.is_in_cset_or_humongous((HeapWord*)obj); } -G1HeapRegionAttr G1CollectedHeap::region_attr(const oop obj) { - return _region_attr.at((HeapWord*)obj); +G1HeapRegionAttr G1CollectedHeap::region_attr(const void* addr) { + return _region_attr.at((HeapWord*)addr); } void G1CollectedHeap::register_humongous_region_with_region_attr(uint index) { @@ -176,6 +177,7 @@ void G1CollectedHeap::register_old_region_with_region_attr(HeapRegion* r) { _region_attr.set_in_old(r->hrm_index(), r->rem_set()->is_tracked()); + _rem_set->prepare_for_scan_rem_set(r->hrm_index()); } void G1CollectedHeap::register_optional_region_with_region_attr(HeapRegion* r) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1ConcurrentMark.cpp --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp Thu May 23 11:07:37 2019 +0100 @@ -425,7 +425,7 @@ // Calculate the number of concurrent worker threads by scaling // the number of parallel GC threads. uint marking_thread_num = scale_concurrent_worker_threads(ParallelGCThreads); - FLAG_SET_ERGO(uint, ConcGCThreads, marking_thread_num); + FLAG_SET_ERGO(ConcGCThreads, marking_thread_num); } assert(ConcGCThreads > 0, "ConcGCThreads have been set."); @@ -456,7 +456,7 @@ mark_stack_size, MarkStackSizeMax); return; } - FLAG_SET_ERGO(size_t, MarkStackSize, mark_stack_size); + FLAG_SET_ERGO(MarkStackSize, mark_stack_size); } else { // Verify MarkStackSize is in range. if (FLAG_IS_CMDLINE(MarkStackSize)) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp --- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp Thu May 23 11:07:37 2019 +0100 @@ -66,8 +66,21 @@ flush(); } +void G1DirtyCardQueue::handle_completed_buffer() { + assert(_buf != NULL, "precondition"); + BufferNode* node = BufferNode::make_node_from_buffer(_buf, index()); + G1DirtyCardQueueSet* dcqs = dirty_card_qset(); + if (dcqs->process_or_enqueue_completed_buffer(node)) { + reset(); // Buffer fully processed, reset index. + } else { + allocate_buffer(); // Buffer enqueued, get a new one. + } +} + G1DirtyCardQueueSet::G1DirtyCardQueueSet(bool notify_when_complete) : PtrQueueSet(notify_when_complete), + _max_completed_buffers(MaxCompletedBuffersUnlimited), + _completed_buffers_padding(0), _free_ids(NULL), _processed_buffers_mut(0), _processed_buffers_rs_thread(0), @@ -136,6 +149,24 @@ } while (0) #endif // ASSERT +bool G1DirtyCardQueueSet::process_or_enqueue_completed_buffer(BufferNode* node) { + if (Thread::current()->is_Java_thread()) { + // If the number of buffers exceeds the limit, make this Java + // thread do the processing itself. We don't lock to access + // buffer count or padding; it is fine to be imprecise here. The + // add of padding could overflow, which is treated as unlimited. + size_t max_buffers = max_completed_buffers(); + size_t limit = max_buffers + completed_buffers_padding(); + if ((completed_buffers_num() > limit) && (limit >= max_buffers)) { + if (mut_process_buffer(node)) { + return true; + } + } + } + enqueue_completed_buffer(node); + return false; +} + bool G1DirtyCardQueueSet::mut_process_buffer(BufferNode* node) { guarantee(_free_ids != NULL, "must be"); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp --- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp Thu May 23 11:07:37 2019 +0100 @@ -47,6 +47,9 @@ // A ptrQueue whose elements are "oops", pointers to object heads. class G1DirtyCardQueue: public PtrQueue { +protected: + virtual void handle_completed_buffer(); + public: G1DirtyCardQueue(G1DirtyCardQueueSet* qset); @@ -57,6 +60,8 @@ // Process queue entries and release resources. void flush() { flush_impl(); } + inline G1DirtyCardQueueSet* dirty_card_qset() const; + // Compiler support. static ByteSize byte_offset_of_index() { return PtrQueue::byte_offset_of_index(); @@ -102,6 +107,12 @@ bool mut_process_buffer(BufferNode* node); + // If the queue contains more buffers than configured here, the + // mutator must start doing some of the concurrent refinement work, + size_t _max_completed_buffers; + size_t _completed_buffers_padding; + static const size_t MaxCompletedBuffersUnlimited = ~size_t(0); + G1FreeIdSet* _free_ids; // The number of completed buffers processed by mutator and rs thread, @@ -126,6 +137,11 @@ static void handle_zero_index_for_thread(Thread* t); + // Either process the entire buffer and return true, or enqueue the + // buffer and return false. If the buffer is completely processed, + // it can be reused in place. + bool process_or_enqueue_completed_buffer(BufferNode* node); + // Apply G1RefineCardConcurrentlyClosure to completed buffers until there are stop_at // completed buffers remaining. bool refine_completed_buffer_concurrently(uint worker_i, size_t stop_at); @@ -147,6 +163,20 @@ // If any threads have partial logs, add them to the global list of logs. void concatenate_logs(); + void set_max_completed_buffers(size_t m) { + _max_completed_buffers = m; + } + size_t max_completed_buffers() const { + return _max_completed_buffers; + } + + void set_completed_buffers_padding(size_t padding) { + _completed_buffers_padding = padding; + } + size_t completed_buffers_padding() const { + return _completed_buffers_padding; + } + jint processed_buffers_mut() { return _processed_buffers_mut; } @@ -156,4 +186,8 @@ }; +inline G1DirtyCardQueueSet* G1DirtyCardQueue::dirty_card_qset() const { + return static_cast(qset()); +} + #endif // SHARE_GC_G1_G1DIRTYCARDQUEUE_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1EvacFailure.cpp --- a/src/hotspot/share/gc/g1/g1EvacFailure.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1EvacFailure.cpp Thu May 23 11:07:37 2019 +0100 @@ -30,7 +30,6 @@ #include "gc/g1/g1EvacFailure.hpp" #include "gc/g1/g1HeapVerifier.hpp" #include "gc/g1/g1OopClosures.inline.hpp" -#include "gc/g1/g1_globals.hpp" #include "gc/g1/heapRegion.hpp" #include "gc/g1/heapRegionRemSet.hpp" #include "gc/shared/preservedMarks.inline.hpp" diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1EvacStats.cpp --- a/src/hotspot/share/gc/g1/g1EvacStats.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1EvacStats.cpp Thu May 23 11:07:37 2019 +0100 @@ -23,7 +23,6 @@ */ #include "precompiled.hpp" -#include "gc/g1/g1_globals.hpp" #include "gc/g1/g1EvacStats.hpp" #include "gc/shared/gcId.hpp" #include "logging/log.hpp" diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1HotCardCache.hpp --- a/src/hotspot/share/gc/g1/g1HotCardCache.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1HotCardCache.hpp Thu May 23 11:07:37 2019 +0100 @@ -26,7 +26,6 @@ #define SHARE_GC_G1_G1HOTCARDCACHE_HPP #include "gc/g1/g1CardCounts.hpp" -#include "gc/g1/g1_globals.hpp" #include "memory/allocation.hpp" #include "runtime/safepoint.hpp" #include "runtime/thread.hpp" diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1OopClosures.hpp --- a/src/hotspot/share/gc/g1/g1OopClosures.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1OopClosures.hpp Thu May 23 11:07:37 2019 +0100 @@ -73,9 +73,10 @@ // Used during Optional RS scanning to make sure we trim the queues in a timely manner. class G1ScanRSForOptionalClosure : public OopClosure { + G1CollectedHeap* _g1h; G1ScanCardClosure* _scan_cl; public: - G1ScanRSForOptionalClosure(G1ScanCardClosure* cl) : _scan_cl(cl) { } + G1ScanRSForOptionalClosure(G1CollectedHeap* g1h, G1ScanCardClosure* cl) : _g1h(g1h), _scan_cl(cl) { } template void do_oop_work(T* p); virtual void do_oop(oop* p) { do_oop_work(p); } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1OopClosures.inline.hpp --- a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp Thu May 23 11:07:37 2019 +0100 @@ -169,11 +169,14 @@ check_obj_during_refinement(p, obj); - // We can not check for references from the collection set: the remembered sets - // may contain such entries and we do not filter them before. + assert(!_g1h->is_in_cset((HeapWord*)p), + "Oop originates from " PTR_FORMAT " (region: %u) which is in the collection set.", + p2i(p), _g1h->addr_to_region((HeapWord*)p)); const G1HeapRegionAttr region_attr = _g1h->region_attr(obj); if (region_attr.is_in_cset()) { + // Since the source is always from outside the collection set, here we implicitly know + // that this is a cross-region reference too. prefetch_and_push(p, obj); } else if (!HeapRegion::is_in_same_region(p, obj)) { handle_non_cset_obj_common(region_attr, p, obj); @@ -183,6 +186,13 @@ template inline void G1ScanRSForOptionalClosure::do_oop_work(T* p) { + const G1HeapRegionAttr region_attr = _g1h->region_attr(p); + // Entries in the optional collection set may start to originate from the collection + // set after one or more increments. In this case, previously optional regions + // became actual collection set regions. Filter them out here. + if (region_attr.is_in_cset()) { + return; + } _scan_cl->do_oop_work(p); _scan_cl->trim_queue_partially(); } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp Thu May 23 11:07:37 2019 +0100 @@ -208,6 +208,8 @@ inline void G1ParScanThreadState::remember_root_into_optional_region(T* p) { oop o = RawAccess::oop_load(p); uint index = _g1h->heap_region_containing(o)->index_in_opt_cset(); + assert(index < _num_optional_regions, + "Trying to access optional region idx %u beyond " SIZE_FORMAT, index, _num_optional_regions); _oops_into_optional_regions[index].push_root(p); } @@ -215,11 +217,16 @@ inline void G1ParScanThreadState::remember_reference_into_optional_region(T* p) { oop o = RawAccess::oop_load(p); uint index = _g1h->heap_region_containing(o)->index_in_opt_cset(); + assert(index < _num_optional_regions, + "Trying to access optional region idx %u beyond " SIZE_FORMAT, index, _num_optional_regions); _oops_into_optional_regions[index].push_oop(p); DEBUG_ONLY(verify_ref(p);) } G1OopStarChunkedList* G1ParScanThreadState::oops_into_optional_region(const HeapRegion* hr) { + assert(hr->index_in_opt_cset() < _num_optional_regions, + "Trying to access optional region idx %u beyond " SIZE_FORMAT " " HR_FORMAT, + hr->index_in_opt_cset(), _num_optional_regions, HR_FORMAT_PARAMS(hr)); return &_oops_into_optional_regions[hr->index_in_opt_cset()]; } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1RemSet.cpp --- a/src/hotspot/share/gc/g1/g1RemSet.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp Thu May 23 11:07:37 2019 +0100 @@ -188,7 +188,7 @@ void reset() { for (uint i = 0; i < _max_regions; i++) { _iter_states[i] = Unclaimed; - _scan_top[i] = NULL; + clear_scan_top(i); } G1ResetScanTopClosure cl(_scan_top); @@ -253,6 +253,10 @@ return _scan_top[region_idx]; } + void clear_scan_top(uint region_idx) { + _scan_top[region_idx] = NULL; + } + // Clear the card table of "dirty" regions. void clear_card_table(WorkGang* workers) { if (_cur_dirty_region == 0) { @@ -304,158 +308,198 @@ _scan_state->initialize(max_regions); } -G1ScanRSForRegionClosure::G1ScanRSForRegionClosure(G1RemSetScanState* scan_state, - G1ScanCardClosure* scan_obj_on_card, - G1ParScanThreadState* pss, - G1GCPhaseTimes::GCParPhases phase, - uint worker_i) : - _g1h(G1CollectedHeap::heap()), - _ct(_g1h->card_table()), - _pss(pss), - _scan_objs_on_card_cl(scan_obj_on_card), - _scan_state(scan_state), - _phase(phase), - _worker_i(worker_i), - _opt_refs_scanned(0), - _opt_refs_memory_used(0), - _cards_scanned(0), - _cards_claimed(0), - _cards_skipped(0), - _rem_set_root_scan_time(), - _rem_set_trim_partially_time(), - _strong_code_root_scan_time(), - _strong_code_trim_partially_time() { -} +class G1ScanRSForRegionClosure : public HeapRegionClosure { + G1CollectedHeap* _g1h; + G1CardTable *_ct; -void G1ScanRSForRegionClosure::claim_card(size_t card_index, const uint region_idx_for_card){ - _ct->set_card_claimed(card_index); - _scan_state->add_dirty_region(region_idx_for_card); -} + G1ParScanThreadState* _pss; + G1ScanCardClosure* _scan_objs_on_card_cl; + + G1RemSetScanState* _scan_state; + + G1GCPhaseTimes::GCParPhases _phase; + + uint _worker_i; -void G1ScanRSForRegionClosure::scan_card(MemRegion mr, uint region_idx_for_card) { - HeapRegion* const card_region = _g1h->region_at(region_idx_for_card); - assert(!card_region->is_young(), "Should not scan card in young region %u", region_idx_for_card); - card_region->oops_on_card_seq_iterate_careful(mr, _scan_objs_on_card_cl); - _scan_objs_on_card_cl->trim_queue_partially(); - _cards_scanned++; -} + size_t _opt_refs_scanned; + size_t _opt_refs_memory_used; -void G1ScanRSForRegionClosure::scan_opt_rem_set_roots(HeapRegion* r) { - EventGCPhaseParallel event; - - G1OopStarChunkedList* opt_rem_set_list = _pss->oops_into_optional_region(r); + size_t _cards_scanned; + size_t _cards_claimed; + size_t _cards_skipped; - G1ScanCardClosure scan_cl(_g1h, _pss); - G1ScanRSForOptionalClosure cl(&scan_cl); - _opt_refs_scanned += opt_rem_set_list->oops_do(&cl, _pss->closures()->raw_strong_oops()); - _opt_refs_memory_used += opt_rem_set_list->used_memory(); - - event.commit(GCId::current(), _worker_i, G1GCPhaseTimes::phase_name(_phase)); -} + Tickspan _rem_set_root_scan_time; + Tickspan _rem_set_trim_partially_time; -void G1ScanRSForRegionClosure::scan_rem_set_roots(HeapRegion* r) { - EventGCPhaseParallel event; - uint const region_idx = r->hrm_index(); + Tickspan _strong_code_root_scan_time; + Tickspan _strong_code_trim_partially_time; - if (_scan_state->claim_iter(region_idx)) { - // If we ever free the collection set concurrently, we should also - // clear the card table concurrently therefore we won't need to - // add regions of the collection set to the dirty cards region. - _scan_state->add_dirty_region(region_idx); + void claim_card(size_t card_index, const uint region_idx_for_card) { + _ct->set_card_claimed(card_index); + _scan_state->add_dirty_region(region_idx_for_card); } - if (r->rem_set()->cardset_is_empty()) { - return; + void scan_card(MemRegion mr, uint region_idx_for_card) { + HeapRegion* const card_region = _g1h->region_at(region_idx_for_card); + assert(!card_region->is_young(), "Should not scan card in young region %u", region_idx_for_card); + card_region->oops_on_card_seq_iterate_careful(mr, _scan_objs_on_card_cl); + _scan_objs_on_card_cl->trim_queue_partially(); + _cards_scanned++; + } + + void scan_opt_rem_set_roots(HeapRegion* r) { + EventGCPhaseParallel event; + + G1OopStarChunkedList* opt_rem_set_list = _pss->oops_into_optional_region(r); + + G1ScanCardClosure scan_cl(_g1h, _pss); + G1ScanRSForOptionalClosure cl(_g1h, &scan_cl); + _opt_refs_scanned += opt_rem_set_list->oops_do(&cl, _pss->closures()->raw_strong_oops()); + _opt_refs_memory_used += opt_rem_set_list->used_memory(); + + event.commit(GCId::current(), _worker_i, G1GCPhaseTimes::phase_name(_phase)); } - // We claim cards in blocks so as to reduce the contention. - size_t const block_size = G1RSetScanBlockSize; - - HeapRegionRemSetIterator iter(r->rem_set()); - size_t card_index; + void scan_rem_set_roots(HeapRegion* r) { + EventGCPhaseParallel event; + uint const region_idx = r->hrm_index(); - size_t claimed_card_block = _scan_state->iter_claimed_next(region_idx, block_size); - for (size_t current_card = 0; iter.has_next(card_index); current_card++) { - if (current_card >= claimed_card_block + block_size) { - claimed_card_block = _scan_state->iter_claimed_next(region_idx, block_size); - } - if (current_card < claimed_card_block) { - _cards_skipped++; - continue; - } - _cards_claimed++; - - HeapWord* const card_start = _g1h->bot()->address_for_index_raw(card_index); - uint const region_idx_for_card = _g1h->addr_to_region(card_start); - -#ifdef ASSERT - HeapRegion* hr = _g1h->region_at_or_null(region_idx_for_card); - assert(hr == NULL || hr->is_in_reserved(card_start), - "Card start " PTR_FORMAT " to scan outside of region %u", p2i(card_start), _g1h->region_at(region_idx_for_card)->hrm_index()); -#endif - HeapWord* const top = _scan_state->scan_top(region_idx_for_card); - if (card_start >= top) { - continue; + if (_scan_state->claim_iter(region_idx)) { + // If we ever free the collection set concurrently, we should also + // clear the card table concurrently therefore we won't need to + // add regions of the collection set to the dirty cards region. + _scan_state->add_dirty_region(region_idx); } - // If the card is dirty, then G1 will scan it during Update RS. - if (_ct->is_card_claimed(card_index) || _ct->is_card_dirty(card_index)) { - continue; + if (r->rem_set()->cardset_is_empty()) { + return; } - // We claim lazily (so races are possible but they're benign), which reduces the - // number of duplicate scans (the rsets of the regions in the cset can intersect). - // Claim the card after checking bounds above: the remembered set may contain - // random cards into current survivor, and we would then have an incorrectly - // claimed card in survivor space. Card table clear does not reset the card table - // of survivor space regions. - claim_card(card_index, region_idx_for_card); + // We claim cards in blocks so as to reduce the contention. + size_t const block_size = G1RSetScanBlockSize; + + HeapRegionRemSetIterator iter(r->rem_set()); + size_t card_index; - MemRegion const mr(card_start, MIN2(card_start + BOTConstants::N_words, top)); + size_t claimed_card_block = _scan_state->iter_claimed_next(region_idx, block_size); + for (size_t current_card = 0; iter.has_next(card_index); current_card++) { + if (current_card >= claimed_card_block + block_size) { + claimed_card_block = _scan_state->iter_claimed_next(region_idx, block_size); + } + if (current_card < claimed_card_block) { + _cards_skipped++; + continue; + } + _cards_claimed++; - scan_card(mr, region_idx_for_card); - } - event.commit(GCId::current(), _worker_i, G1GCPhaseTimes::phase_name(_phase)); -} + HeapWord* const card_start = _g1h->bot()->address_for_index_raw(card_index); + uint const region_idx_for_card = _g1h->addr_to_region(card_start); -void G1ScanRSForRegionClosure::scan_strong_code_roots(HeapRegion* r) { - EventGCPhaseParallel event; - // We pass a weak code blobs closure to the remembered set scanning because we want to avoid - // treating the nmethods visited to act as roots for concurrent marking. - // We only want to make sure that the oops in the nmethods are adjusted with regard to the - // objects copied by the current evacuation. - r->strong_code_roots_do(_pss->closures()->weak_codeblobs()); - event.commit(GCId::current(), _worker_i, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::CodeRoots)); -} +#ifdef ASSERT + HeapRegion* hr = _g1h->region_at_or_null(region_idx_for_card); + assert(hr == NULL || hr->is_in_reserved(card_start), + "Card start " PTR_FORMAT " to scan outside of region %u", p2i(card_start), _g1h->region_at(region_idx_for_card)->hrm_index()); +#endif + HeapWord* const top = _scan_state->scan_top(region_idx_for_card); + if (card_start >= top) { + continue; + } -bool G1ScanRSForRegionClosure::do_heap_region(HeapRegion* r) { - assert(r->in_collection_set(), "Region %u is not in the collection set.", r->hrm_index()); - uint const region_idx = r->hrm_index(); + // If the card is dirty, then G1 will scan it during Update RS. + if (_ct->is_card_claimed(card_index) || _ct->is_card_dirty(card_index)) { + continue; + } - // The individual references for the optional remembered set are per-worker, so we - // always need to scan them. - if (r->has_index_in_opt_cset()) { - G1EvacPhaseWithTrimTimeTracker timer(_pss, _rem_set_root_scan_time, _rem_set_trim_partially_time); - scan_opt_rem_set_roots(r); + // We claim lazily (so races are possible but they're benign), which reduces the + // number of duplicate scans (the rsets of the regions in the cset can intersect). + // Claim the card after checking bounds above: the remembered set may contain + // random cards into current survivor, and we would then have an incorrectly + // claimed card in survivor space. Card table clear does not reset the card table + // of survivor space regions. + claim_card(card_index, region_idx_for_card); + + MemRegion const mr(card_start, MIN2(card_start + BOTConstants::N_words, top)); + + scan_card(mr, region_idx_for_card); + } + event.commit(GCId::current(), _worker_i, G1GCPhaseTimes::phase_name(_phase)); } - // Do an early out if we know we are complete. - if (_scan_state->iter_is_complete(region_idx)) { + void scan_strong_code_roots(HeapRegion* r) { + EventGCPhaseParallel event; + // We pass a weak code blobs closure to the remembered set scanning because we want to avoid + // treating the nmethods visited to act as roots for concurrent marking. + // We only want to make sure that the oops in the nmethods are adjusted with regard to the + // objects copied by the current evacuation. + r->strong_code_roots_do(_pss->closures()->weak_codeblobs()); + event.commit(GCId::current(), _worker_i, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::CodeRoots)); + } + +public: + G1ScanRSForRegionClosure(G1RemSetScanState* scan_state, + G1ScanCardClosure* scan_obj_on_card, + G1ParScanThreadState* pss, + G1GCPhaseTimes::GCParPhases phase, + uint worker_i) : + _g1h(G1CollectedHeap::heap()), + _ct(_g1h->card_table()), + _pss(pss), + _scan_objs_on_card_cl(scan_obj_on_card), + _scan_state(scan_state), + _phase(phase), + _worker_i(worker_i), + _opt_refs_scanned(0), + _opt_refs_memory_used(0), + _cards_scanned(0), + _cards_claimed(0), + _cards_skipped(0), + _rem_set_root_scan_time(), + _rem_set_trim_partially_time(), + _strong_code_root_scan_time(), + _strong_code_trim_partially_time() { } + + bool do_heap_region(HeapRegion* r) { + assert(r->in_collection_set(), "Region %u is not in the collection set.", r->hrm_index()); + uint const region_idx = r->hrm_index(); + + // The individual references for the optional remembered set are per-worker, so we + // always need to scan them. + if (r->has_index_in_opt_cset()) { + G1EvacPhaseWithTrimTimeTracker timer(_pss, _rem_set_root_scan_time, _rem_set_trim_partially_time); + scan_opt_rem_set_roots(r); + } + + // Do an early out if we know we are complete. + if (_scan_state->iter_is_complete(region_idx)) { + return false; + } + + { + G1EvacPhaseWithTrimTimeTracker timer(_pss, _rem_set_root_scan_time, _rem_set_trim_partially_time); + scan_rem_set_roots(r); + } + + if (_scan_state->set_iter_complete(region_idx)) { + G1EvacPhaseWithTrimTimeTracker timer(_pss, _strong_code_root_scan_time, _strong_code_trim_partially_time); + // Scan the strong code root list attached to the current region + scan_strong_code_roots(r); + } return false; } - { - G1EvacPhaseWithTrimTimeTracker timer(_pss, _rem_set_root_scan_time, _rem_set_trim_partially_time); - scan_rem_set_roots(r); - } + Tickspan rem_set_root_scan_time() const { return _rem_set_root_scan_time; } + Tickspan rem_set_trim_partially_time() const { return _rem_set_trim_partially_time; } + + Tickspan strong_code_root_scan_time() const { return _strong_code_root_scan_time; } + Tickspan strong_code_root_trim_partially_time() const { return _strong_code_trim_partially_time; } - if (_scan_state->set_iter_complete(region_idx)) { - G1EvacPhaseWithTrimTimeTracker timer(_pss, _strong_code_root_scan_time, _strong_code_trim_partially_time); - // Scan the strong code root list attached to the current region - scan_strong_code_roots(r); - } - return false; -} + size_t cards_scanned() const { return _cards_scanned; } + size_t cards_claimed() const { return _cards_claimed; } + size_t cards_skipped() const { return _cards_skipped; } + + size_t opt_refs_scanned() const { return _opt_refs_scanned; } + size_t opt_refs_memory_used() const { return _opt_refs_memory_used; } +}; void G1RemSet::scan_rem_set(G1ParScanThreadState* pss, uint worker_i, @@ -550,6 +594,10 @@ _scan_state->reset(); } +void G1RemSet::prepare_for_scan_rem_set(uint region_idx) { + _scan_state->clear_scan_top(region_idx); +} + void G1RemSet::cleanup_after_scan_rem_set() { G1GCPhaseTimes* phase_times = _g1h->phase_times(); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1RemSet.hpp --- a/src/hotspot/share/gc/g1/g1RemSet.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1RemSet.hpp Thu May 23 11:07:37 2019 +0100 @@ -98,10 +98,12 @@ // into the collection set or update the remembered set. void update_rem_set(G1ParScanThreadState* pss, uint worker_i); - // Prepare for and cleanup after scanning the remembered sets. Must be called + // Prepare for and cleanup after scanning the remembered sets. Must be called // once before and after in sequential code. void prepare_for_scan_rem_set(); void cleanup_after_scan_rem_set(); + // Prepares the given region for remembered set scanning. + void prepare_for_scan_rem_set(uint region_idx); G1RemSetScanState* scan_state() const { return _scan_state; } @@ -128,59 +130,4 @@ void rebuild_rem_set(G1ConcurrentMark* cm, WorkGang* workers, uint worker_id_offset); }; -class G1ScanRSForRegionClosure : public HeapRegionClosure { - G1CollectedHeap* _g1h; - G1CardTable *_ct; - - G1ParScanThreadState* _pss; - G1ScanCardClosure* _scan_objs_on_card_cl; - - G1RemSetScanState* _scan_state; - - G1GCPhaseTimes::GCParPhases _phase; - - uint _worker_i; - - size_t _opt_refs_scanned; - size_t _opt_refs_memory_used; - - size_t _cards_scanned; - size_t _cards_claimed; - size_t _cards_skipped; - - Tickspan _rem_set_root_scan_time; - Tickspan _rem_set_trim_partially_time; - - Tickspan _strong_code_root_scan_time; - Tickspan _strong_code_trim_partially_time; - - void claim_card(size_t card_index, const uint region_idx_for_card); - void scan_card(MemRegion mr, uint region_idx_for_card); - - void scan_opt_rem_set_roots(HeapRegion* r); - void scan_rem_set_roots(HeapRegion* r); - void scan_strong_code_roots(HeapRegion* r); -public: - G1ScanRSForRegionClosure(G1RemSetScanState* scan_state, - G1ScanCardClosure* scan_obj_on_card, - G1ParScanThreadState* pss, - G1GCPhaseTimes::GCParPhases phase, - uint worker_i); - - bool do_heap_region(HeapRegion* r); - - Tickspan rem_set_root_scan_time() const { return _rem_set_root_scan_time; } - Tickspan rem_set_trim_partially_time() const { return _rem_set_trim_partially_time; } - - Tickspan strong_code_root_scan_time() const { return _strong_code_root_scan_time; } - Tickspan strong_code_root_trim_partially_time() const { return _strong_code_trim_partially_time; } - - size_t cards_scanned() const { return _cards_scanned; } - size_t cards_claimed() const { return _cards_claimed; } - size_t cards_skipped() const { return _cards_skipped; } - - size_t opt_refs_scanned() const { return _opt_refs_scanned; } - size_t opt_refs_memory_used() const { return _opt_refs_memory_used; } -}; - #endif // SHARE_GC_G1_G1REMSET_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1YoungGenSizer.cpp --- a/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp Thu May 23 11:07:37 2019 +0100 @@ -48,7 +48,7 @@ "A new max generation size of " SIZE_FORMAT "k will be used.", NewSize/K, MaxNewSize/K, NewSize/K); } - FLAG_SET_ERGO(size_t, MaxNewSize, NewSize); + FLAG_SET_ERGO(MaxNewSize, NewSize); } if (FLAG_IS_CMDLINE(NewSize)) { @@ -121,7 +121,7 @@ size_t max_young_size = result * HeapRegion::GrainBytes; if (max_young_size != MaxNewSize) { - FLAG_SET_ERGO(size_t, MaxNewSize, max_young_size); + FLAG_SET_ERGO(MaxNewSize, max_young_size); } } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/g1_globals.hpp --- a/src/hotspot/share/gc/g1/g1_globals.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/g1_globals.hpp Thu May 23 11:07:37 2019 +0100 @@ -25,7 +25,8 @@ #ifndef SHARE_GC_G1_G1_GLOBALS_HPP #define SHARE_GC_G1_G1_GLOBALS_HPP -#include // for DBL_MAX +#include "runtime/globals_shared.hpp" + // // Defines all globals flags used by the garbage-first compiler. // diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/g1/heapRegion.cpp --- a/src/hotspot/share/gc/g1/heapRegion.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/g1/heapRegion.cpp Thu May 23 11:07:37 2019 +0100 @@ -106,7 +106,7 @@ CardsPerRegion = GrainBytes >> G1CardTable::card_shift; if (G1HeapRegionSize != GrainBytes) { - FLAG_SET_ERGO(size_t, G1HeapRegionSize, GrainBytes); + FLAG_SET_ERGO(G1HeapRegionSize, GrainBytes); } } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/parallel/parallelArguments.cpp --- a/src/hotspot/share/gc/parallel/parallelArguments.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp Thu May 23 11:07:37 2019 +0100 @@ -113,11 +113,11 @@ // default gc, which adds 2 to the ratio value. We need to // make sure the values are valid before using them. if (MinSurvivorRatio < 3) { - FLAG_SET_ERGO(uintx, MinSurvivorRatio, 3); + FLAG_SET_ERGO(MinSurvivorRatio, 3); } if (InitialSurvivorRatio < 3) { - FLAG_SET_ERGO(uintx, InitialSurvivorRatio, 3); + FLAG_SET_ERGO(InitialSurvivorRatio, 3); } } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/gcArguments.cpp --- a/src/hotspot/share/gc/shared/gcArguments.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/gcArguments.cpp Thu May 23 11:07:37 2019 +0100 @@ -54,12 +54,12 @@ if (MinHeapFreeRatio == 100) { // Keeping the heap 100% free is hard ;-) so limit it to 99%. - FLAG_SET_ERGO(uintx, MinHeapFreeRatio, 99); + FLAG_SET_ERGO(MinHeapFreeRatio, 99); } if (!ClassUnloading) { // If class unloading is disabled, also disable concurrent class unloading. - FLAG_SET_CMDLINE(bool, ClassUnloadingWithConcurrentMark, false); + FLAG_SET_CMDLINE(ClassUnloadingWithConcurrentMark, false); } if (!FLAG_IS_DEFAULT(AllocateOldGenAt)) { @@ -172,10 +172,10 @@ // Write back to flags if the values changed if (aligned_initial_heap_size != InitialHeapSize) { - FLAG_SET_ERGO(size_t, InitialHeapSize, aligned_initial_heap_size); + FLAG_SET_ERGO(InitialHeapSize, aligned_initial_heap_size); } if (aligned_max_heap_size != MaxHeapSize) { - FLAG_SET_ERGO(size_t, MaxHeapSize, aligned_max_heap_size); + FLAG_SET_ERGO(MaxHeapSize, aligned_max_heap_size); } if (FLAG_IS_CMDLINE(InitialHeapSize) && MinHeapSize != 0 && @@ -183,15 +183,15 @@ vm_exit_during_initialization("Incompatible minimum and initial heap sizes specified"); } if (!FLAG_IS_DEFAULT(InitialHeapSize) && InitialHeapSize > MaxHeapSize) { - FLAG_SET_ERGO(size_t, MaxHeapSize, InitialHeapSize); + FLAG_SET_ERGO(MaxHeapSize, InitialHeapSize); } else if (!FLAG_IS_DEFAULT(MaxHeapSize) && InitialHeapSize > MaxHeapSize) { - FLAG_SET_ERGO(size_t, InitialHeapSize, MaxHeapSize); + FLAG_SET_ERGO(InitialHeapSize, MaxHeapSize); if (InitialHeapSize < MinHeapSize) { MinHeapSize = InitialHeapSize; } } - FLAG_SET_ERGO(size_t, MinHeapDeltaBytes, align_up(MinHeapDeltaBytes, SpaceAlignment)); + FLAG_SET_ERGO(MinHeapDeltaBytes, align_up(MinHeapDeltaBytes, SpaceAlignment)); DEBUG_ONLY(assert_flags();) } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/gcCause.cpp --- a/src/hotspot/share/gc/shared/gcCause.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/gcCause.cpp Thu May 23 11:07:37 2019 +0100 @@ -141,6 +141,9 @@ case _z_proactive: return "Proactive"; + case _z_high_usage: + return "High Usage"; + case _last_gc_cause: return "ILLEGAL VALUE - last gc cause - ILLEGAL VALUE"; diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/gcCause.hpp --- a/src/hotspot/share/gc/shared/gcCause.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/gcCause.hpp Thu May 23 11:07:37 2019 +0100 @@ -91,6 +91,7 @@ _z_allocation_rate, _z_allocation_stall, _z_proactive, + _z_high_usage, _last_gc_cause }; diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/gcConfig.cpp --- a/src/hotspot/share/gc/shared/gcConfig.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/gcConfig.cpp Thu May 23 11:07:37 2019 +0100 @@ -109,15 +109,15 @@ void GCConfig::select_gc_ergonomically() { if (os::is_server_class_machine()) { #if INCLUDE_G1GC - FLAG_SET_ERGO_IF_DEFAULT(bool, UseG1GC, true); + FLAG_SET_ERGO_IF_DEFAULT(UseG1GC, true); #elif INCLUDE_PARALLELGC - FLAG_SET_ERGO_IF_DEFAULT(bool, UseParallelGC, true); + FLAG_SET_ERGO_IF_DEFAULT(UseParallelGC, true); #elif INCLUDE_SERIALGC - FLAG_SET_ERGO_IF_DEFAULT(bool, UseSerialGC, true); + FLAG_SET_ERGO_IF_DEFAULT(UseSerialGC, true); #endif } else { #if INCLUDE_SERIALGC - FLAG_SET_ERGO_IF_DEFAULT(bool, UseSerialGC, true); + FLAG_SET_ERGO_IF_DEFAULT(UseSerialGC, true); #endif } } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/gc_globals.hpp --- a/src/hotspot/share/gc/shared/gc_globals.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/gc_globals.hpp Thu May 23 11:07:37 2019 +0100 @@ -25,6 +25,7 @@ #ifndef SHARE_GC_SHARED_GC_GLOBALS_HPP #define SHARE_GC_SHARED_GC_GLOBALS_HPP +#include "runtime/globals_shared.hpp" #include "utilities/macros.hpp" #if INCLUDE_CMSGC #include "gc/cms/cms_globals.hpp" diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/genArguments.cpp --- a/src/hotspot/share/gc/shared/genArguments.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/genArguments.cpp Thu May 23 11:07:37 2019 +0100 @@ -86,20 +86,20 @@ size_t smallest_heap_size = align_up(smallest_new_size + old_gen_size_lower_bound(), HeapAlignment); if (MaxHeapSize < smallest_heap_size) { - FLAG_SET_ERGO(size_t, MaxHeapSize, smallest_heap_size); + FLAG_SET_ERGO(MaxHeapSize, smallest_heap_size); } // If needed, synchronize MinHeapSize size and InitialHeapSize if (MinHeapSize < smallest_heap_size) { MinHeapSize = smallest_heap_size; if (InitialHeapSize < MinHeapSize) { - FLAG_SET_ERGO(size_t, InitialHeapSize, smallest_heap_size); + FLAG_SET_ERGO(InitialHeapSize, smallest_heap_size); } } // Make sure NewSize allows an old generation to fit even if set on the command line if (FLAG_IS_CMDLINE(NewSize) && NewSize >= InitialHeapSize) { log_warning(gc, ergo)("NewSize was set larger than initial heap size, will use initial heap size."); - FLAG_SET_ERGO(size_t, NewSize, bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment)); + FLAG_SET_ERGO(NewSize, bound_minus_alignment(NewSize, InitialHeapSize, GenAlignment)); } // Now take the actual NewSize into account. We will silently increase NewSize @@ -107,7 +107,7 @@ size_t bounded_new_size = bound_minus_alignment(NewSize, MaxHeapSize, GenAlignment); bounded_new_size = MAX2(smallest_new_size, align_down(bounded_new_size, GenAlignment)); if (bounded_new_size != NewSize) { - FLAG_SET_ERGO(size_t, NewSize, bounded_new_size); + FLAG_SET_ERGO(NewSize, bounded_new_size); } MinNewSize = smallest_new_size; @@ -120,14 +120,14 @@ "heap (" SIZE_FORMAT "k). A new max generation size of " SIZE_FORMAT "k will be used.", MaxNewSize/K, MaxHeapSize/K, smaller_max_new_size/K); } - FLAG_SET_ERGO(size_t, MaxNewSize, smaller_max_new_size); + FLAG_SET_ERGO(MaxNewSize, smaller_max_new_size); if (NewSize > MaxNewSize) { - FLAG_SET_ERGO(size_t, NewSize, MaxNewSize); + FLAG_SET_ERGO(NewSize, MaxNewSize); } } else if (MaxNewSize < NewSize) { - FLAG_SET_ERGO(size_t, MaxNewSize, NewSize); + FLAG_SET_ERGO(MaxNewSize, NewSize); } else if (!is_aligned(MaxNewSize, GenAlignment)) { - FLAG_SET_ERGO(size_t, MaxNewSize, align_down(MaxNewSize, GenAlignment)); + FLAG_SET_ERGO(MaxNewSize, align_down(MaxNewSize, GenAlignment)); } } @@ -139,7 +139,7 @@ "A new max generation size of " SIZE_FORMAT "k will be used.", NewSize/K, MaxNewSize/K, NewSize/K); } - FLAG_SET_ERGO(size_t, MaxNewSize, NewSize); + FLAG_SET_ERGO(MaxNewSize, NewSize); } if (SurvivorRatio < 1 || NewRatio < 1) { @@ -147,10 +147,10 @@ } if (OldSize < old_gen_size_lower_bound()) { - FLAG_SET_ERGO(size_t, OldSize, old_gen_size_lower_bound()); + FLAG_SET_ERGO(OldSize, old_gen_size_lower_bound()); } if (!is_aligned(OldSize, GenAlignment)) { - FLAG_SET_ERGO(size_t, OldSize, align_down(OldSize, GenAlignment)); + FLAG_SET_ERGO(OldSize, align_down(OldSize, GenAlignment)); } if (FLAG_IS_CMDLINE(OldSize) && FLAG_IS_DEFAULT(MaxHeapSize)) { @@ -161,8 +161,8 @@ size_t calculated_heapsize = (OldSize / NewRatio) * (NewRatio + 1); calculated_heapsize = align_up(calculated_heapsize, HeapAlignment); - FLAG_SET_ERGO(size_t, MaxHeapSize, calculated_heapsize); - FLAG_SET_ERGO(size_t, InitialHeapSize, calculated_heapsize); + FLAG_SET_ERGO(MaxHeapSize, calculated_heapsize); + FLAG_SET_ERGO(InitialHeapSize, calculated_heapsize); } // Adjust NewSize and OldSize or MaxHeapSize to match each other @@ -173,15 +173,15 @@ size_t calculated_size = NewSize + OldSize; double shrink_factor = (double) MaxHeapSize / calculated_size; size_t smaller_new_size = align_down((size_t)(NewSize * shrink_factor), GenAlignment); - FLAG_SET_ERGO(size_t, NewSize, MAX2(young_gen_size_lower_bound(), smaller_new_size)); + FLAG_SET_ERGO(NewSize, MAX2(young_gen_size_lower_bound(), smaller_new_size)); // OldSize is already aligned because above we aligned MaxHeapSize to // HeapAlignment, and we just made sure that NewSize is aligned to // GenAlignment. In initialize_flags() we verified that HeapAlignment // is a multiple of GenAlignment. - FLAG_SET_ERGO(size_t, OldSize, MaxHeapSize - NewSize); + FLAG_SET_ERGO(OldSize, MaxHeapSize - NewSize); } else { - FLAG_SET_ERGO(size_t, MaxHeapSize, align_up(NewSize + OldSize, HeapAlignment)); + FLAG_SET_ERGO(MaxHeapSize, align_up(NewSize + OldSize, HeapAlignment)); } } @@ -191,7 +191,7 @@ if (OldSize < InitialHeapSize) { size_t new_size = InitialHeapSize - OldSize; if (new_size >= MinNewSize && new_size <= MaxNewSize) { - FLAG_SET_ERGO(size_t, NewSize, new_size); + FLAG_SET_ERGO(NewSize, new_size); } } } @@ -341,15 +341,15 @@ // Write back to flags if necessary. if (NewSize != initial_young_size) { - FLAG_SET_ERGO(size_t, NewSize, initial_young_size); + FLAG_SET_ERGO(NewSize, initial_young_size); } if (MaxNewSize != max_young_size) { - FLAG_SET_ERGO(size_t, MaxNewSize, max_young_size); + FLAG_SET_ERGO(MaxNewSize, max_young_size); } if (OldSize != initial_old_size) { - FLAG_SET_ERGO(size_t, OldSize, initial_old_size); + FLAG_SET_ERGO(OldSize, initial_old_size); } log_trace(gc, heap)("Minimum old " SIZE_FORMAT " Initial old " SIZE_FORMAT " Maximum old " SIZE_FORMAT, diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp --- a/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/jvmFlagConstraintsGC.cpp Thu May 23 11:07:37 2019 +0100 @@ -45,12 +45,6 @@ #if INCLUDE_PARALLELGC #include "gc/parallel/jvmFlagConstraintsParallel.hpp" #endif -#ifdef COMPILER1 -#include "c1/c1_globals.hpp" -#endif // COMPILER1 -#ifdef COMPILER2 -#include "opto/c2_globals.hpp" -#endif // COMPILER2 // Some flags that have default values that indicate that the // JVM should automatically determine an appropriate value diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/ptrQueue.cpp --- a/src/hotspot/share/gc/shared/ptrQueue.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/ptrQueue.cpp Thu May 23 11:07:37 2019 +0100 @@ -62,7 +62,6 @@ } } - void PtrQueue::enqueue_known_active(void* ptr) { while (_index == 0) { handle_zero_index(); @@ -75,6 +74,35 @@ _buf[index()] = ptr; } +void PtrQueue::handle_zero_index() { + assert(index() == 0, "precondition"); + + if (_buf != NULL) { + handle_completed_buffer(); + } else { + // Bootstrapping kludge; lazily initialize capacity. The initial + // thread's queues are constructed before the second phase of the + // two-phase initialization of the associated qsets. As a result, + // we can't initialize _capacity_in_bytes in the queue constructor. + if (_capacity_in_bytes == 0) { + _capacity_in_bytes = index_to_byte_index(qset()->buffer_size()); + } + allocate_buffer(); + } +} + +void PtrQueue::allocate_buffer() { + _buf = qset()->allocate_buffer(); + reset(); +} + +void PtrQueue::enqueue_completed_buffer() { + assert(_buf != NULL, "precondition"); + BufferNode* node = BufferNode::make_node_from_buffer(_buf, index()); + qset()->enqueue_completed_buffer(node); + allocate_buffer(); +} + BufferNode* BufferNode::allocate(size_t size) { size_t byte_size = size * sizeof(void*); void* data = NEW_C_HEAP_ARRAY(char, buffer_offset() + byte_size, mtGC); @@ -231,8 +259,6 @@ _process_completed_buffers_threshold(ProcessCompletedBuffersThresholdNever), _process_completed_buffers(false), _notify_when_complete(notify_when_complete), - _max_completed_buffers(MaxCompletedBuffersUnlimited), - _completed_buffers_padding(0), _all_active(false) {} @@ -258,52 +284,6 @@ _allocator->release(node); } -void PtrQueue::handle_zero_index() { - assert(index() == 0, "precondition"); - - // This thread records the full buffer and allocates a new one (while - // holding the lock if there is one). - if (_buf != NULL) { - if (!should_enqueue_buffer()) { - assert(index() > 0, "the buffer can only be re-used if it's not full"); - return; - } - - BufferNode* node = BufferNode::make_node_from_buffer(_buf, index()); - if (qset()->process_or_enqueue_completed_buffer(node)) { - // Recycle the buffer. No allocation. - assert(_buf == BufferNode::make_buffer_from_node(node), "invariant"); - assert(capacity() == qset()->buffer_size(), "invariant"); - reset(); - return; - } - } - // Set capacity in case this is the first allocation. - set_capacity(qset()->buffer_size()); - // Allocate a new buffer. - _buf = qset()->allocate_buffer(); - reset(); -} - -bool PtrQueueSet::process_or_enqueue_completed_buffer(BufferNode* node) { - if (Thread::current()->is_Java_thread()) { - // If the number of buffers exceeds the limit, make this Java - // thread do the processing itself. We don't lock to access - // buffer count or padding; it is fine to be imprecise here. The - // add of padding could overflow, which is treated as unlimited. - size_t limit = _max_completed_buffers + _completed_buffers_padding; - if ((_n_completed_buffers > limit) && (limit >= _max_completed_buffers)) { - if (mut_process_buffer(node)) { - // Successfully processed; return true to allow buffer reuse. - return true; - } - } - } - // The buffer will be enqueued. The caller will have to get a new one. - enqueue_completed_buffer(node); - return false; -} - void PtrQueueSet::enqueue_completed_buffer(BufferNode* cbn) { MutexLocker x(_cbl_mon, Mutex::_no_safepoint_check_flag); cbn->set_next(NULL); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/ptrQueue.hpp --- a/src/hotspot/share/gc/shared/ptrQueue.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/ptrQueue.hpp Thu May 23 11:07:37 2019 +0100 @@ -71,14 +71,6 @@ return _capacity_in_bytes; } - void set_capacity(size_t entries) { - size_t byte_capacity = index_to_byte_index(entries); - assert(_capacity_in_bytes == 0 || _capacity_in_bytes == byte_capacity, - "changing capacity " SIZE_FORMAT " -> " SIZE_FORMAT, - _capacity_in_bytes, byte_capacity); - _capacity_in_bytes = byte_capacity; - } - static size_t byte_index_to_index(size_t ind) { assert(is_aligned(ind, _element_size), "precondition"); return ind / _element_size; @@ -106,11 +98,20 @@ return byte_index_to_index(capacity_in_bytes()); } - PtrQueueSet* qset() { return _qset; } + PtrQueueSet* qset() const { return _qset; } // Process queue entries and release resources. void flush_impl(); + // Process (some of) the buffer and leave it in place for further use, + // or enqueue the buffer and allocate a new one. + virtual void handle_completed_buffer() = 0; + + void allocate_buffer(); + + // Enqueue the current buffer in the qset and allocate a new buffer. + void enqueue_completed_buffer(); + // Initialize this queue to contain a null buffer, and be part of the // given PtrQueueSet. PtrQueue(PtrQueueSet* qset, bool active = false); @@ -137,14 +138,6 @@ else enqueue_known_active(ptr); } - // This method is called when we're doing the zero index handling - // and gives a chance to the queues to do any pre-enqueueing - // processing they might want to do on the buffer. It should return - // true if the buffer should be enqueued, or false if enough - // entries were cleared from it so that it can be re-used. It should - // not return false if the buffer is still full (otherwise we can - // get into an infinite loop). - virtual bool should_enqueue_buffer() { return true; } void handle_zero_index(); void enqueue_known_active(void* ptr); @@ -306,7 +299,7 @@ Monitor* _cbl_mon; // Protects the fields below. BufferNode* _completed_buffers_head; BufferNode* _completed_buffers_tail; - size_t _n_completed_buffers; + volatile size_t _n_completed_buffers; size_t _process_completed_buffers_threshold; volatile bool _process_completed_buffers; @@ -314,24 +307,11 @@ // If true, notify_all on _cbl_mon when the threshold is reached. bool _notify_when_complete; - // Maximum number of elements allowed on completed queue: after that, - // enqueuer does the work itself. - size_t _max_completed_buffers; - size_t _completed_buffers_padding; - void assert_completed_buffers_list_len_correct_locked() NOT_DEBUG_RETURN; protected: bool _all_active; - // A mutator thread does the the work of processing a buffer. - // Returns "true" iff the work is complete (and the buffer may be - // deallocated). - virtual bool mut_process_buffer(BufferNode* node) { - ShouldNotReachHere(); - return false; - } - // Create an empty ptr queue set. PtrQueueSet(bool notify_when_complete = false); ~PtrQueueSet(); @@ -365,9 +345,6 @@ // return a completed buffer from the list. Otherwise, return NULL. BufferNode* get_completed_buffer(size_t stop_at = 0); - // To be invoked by the mutator. - bool process_or_enqueue_completed_buffer(BufferNode* node); - bool process_completed_buffers() { return _process_completed_buffers; } void set_process_completed_buffers(bool x) { _process_completed_buffers = x; } @@ -392,21 +369,6 @@ void merge_bufferlists(PtrQueueSet* src); - void set_max_completed_buffers(size_t m) { - _max_completed_buffers = m; - } - size_t max_completed_buffers() const { - return _max_completed_buffers; - } - static const size_t MaxCompletedBuffersUnlimited = ~size_t(0); - - void set_completed_buffers_padding(size_t padding) { - _completed_buffers_padding = padding; - } - size_t completed_buffers_padding() const { - return _completed_buffers_padding; - } - // Notify the consumer if the number of buffers crossed the threshold void notify_if_necessary(); }; diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/satbMarkQueue.cpp --- a/src/hotspot/share/gc/shared/satbMarkQueue.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/satbMarkQueue.cpp Thu May 23 11:07:37 2019 +0100 @@ -56,7 +56,7 @@ // retains a small enough collection in the buffer, we can continue to // use the buffer as-is, instead of enqueueing and replacing it. -bool SATBMarkQueue::should_enqueue_buffer() { +void SATBMarkQueue::handle_completed_buffer() { // This method should only be called if there is a non-NULL buffer // that is full. assert(index() == 0, "pre-condition"); @@ -64,15 +64,18 @@ filter(); - SATBMarkQueueSet* satb_qset = static_cast(qset()); - size_t threshold = satb_qset->buffer_enqueue_threshold(); + size_t threshold = satb_qset()->buffer_enqueue_threshold(); // Ensure we'll enqueue completely full buffers. assert(threshold > 0, "enqueue threshold = 0"); // Ensure we won't enqueue empty buffers. assert(threshold <= capacity(), "enqueue threshold " SIZE_FORMAT " exceeds capacity " SIZE_FORMAT, threshold, capacity()); - return index() < threshold; + + if (index() < threshold) { + // Buffer is sufficiently full; enqueue and allocate a new one. + enqueue_completed_buffer(); + } // Else continue to accumulate in buffer. } void SATBMarkQueue::apply_closure_and_empty(SATBBufferClosure* cl) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shared/satbMarkQueue.hpp --- a/src/hotspot/share/gc/shared/satbMarkQueue.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shared/satbMarkQueue.hpp Thu May 23 11:07:37 2019 +0100 @@ -54,20 +54,21 @@ template inline void apply_filter(Filter filter_out); +protected: + virtual void handle_completed_buffer(); + public: SATBMarkQueue(SATBMarkQueueSet* qset); // Process queue entries and free resources. void flush(); + inline SATBMarkQueueSet* satb_qset() const; + // Apply cl to the active part of the buffer. // Prerequisite: Must be at a safepoint. void apply_closure_and_empty(SATBBufferClosure* cl); - // Overrides PtrQueue::should_enqueue_buffer(). See the method's - // definition for more information. - virtual bool should_enqueue_buffer(); - #ifndef PRODUCT // Helpful for debugging void print(const char* name); @@ -140,8 +141,12 @@ void abandon_partial_marking(); }; +inline SATBMarkQueueSet* SATBMarkQueue::satb_qset() const { + return static_cast(qset()); +} + inline void SATBMarkQueue::filter() { - static_cast(qset())->filter(this); + satb_qset()->filter(this); } // Removes entries from the buffer that are no longer needed, as diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp Thu May 23 11:07:37 2019 +0100 @@ -81,10 +81,10 @@ template class ShenandoahInitMarkRootsTask : public AbstractGangTask { private: - ShenandoahRootProcessor* _rp; + ShenandoahAllRootScanner* _rp; bool _process_refs; public: - ShenandoahInitMarkRootsTask(ShenandoahRootProcessor* rp, bool process_refs) : + ShenandoahInitMarkRootsTask(ShenandoahAllRootScanner* rp, bool process_refs) : AbstractGangTask("Shenandoah init mark roots task"), _rp(rp), _process_refs(process_refs) { @@ -115,45 +115,21 @@ // cache, because there could be the case of embedded class/oop in the generated code, // which we will never visit during mark. Without code cache invalidation, as in (a), // we risk executing that code cache blob, and crashing. - // c. With ShenandoahConcurrentScanCodeRoots, we avoid scanning the entire code cache here, - // and instead do that in concurrent phase under the relevant lock. This saves init mark - // pause time. - - CLDToOopClosure clds_cl(oops, ClassLoaderData::_claim_strong); - MarkingCodeBlobClosure blobs_cl(oops, ! CodeBlobToOopClosure::FixRelocations); - - ResourceMark m; if (heap->unload_classes()) { - _rp->process_strong_roots(oops, &clds_cl, &blobs_cl, NULL, worker_id); + _rp->strong_roots_do(worker_id, oops); } else { - if (ShenandoahConcurrentScanCodeRoots) { - CodeBlobClosure* code_blobs = NULL; -#ifdef ASSERT - ShenandoahAssertToSpaceClosure assert_to_space_oops; - CodeBlobToOopClosure assert_to_space(&assert_to_space_oops, !CodeBlobToOopClosure::FixRelocations); - // If conc code cache evac is disabled, code cache should have only to-space ptrs. - // Otherwise, it should have to-space ptrs only if mark does not update refs. - if (!heap->has_forwarded_objects()) { - code_blobs = &assert_to_space; - } -#endif - _rp->process_all_roots(oops, &clds_cl, code_blobs, NULL, worker_id); - } else { - _rp->process_all_roots(oops, &clds_cl, &blobs_cl, NULL, worker_id); - } + _rp->roots_do(worker_id, oops); } } }; class ShenandoahUpdateRootsTask : public AbstractGangTask { private: - ShenandoahRootProcessor* _rp; - const bool _update_code_cache; + ShenandoahRootUpdater* _root_updater; public: - ShenandoahUpdateRootsTask(ShenandoahRootProcessor* rp, bool update_code_cache) : + ShenandoahUpdateRootsTask(ShenandoahRootUpdater* root_updater) : AbstractGangTask("Shenandoah update roots task"), - _rp(rp), - _update_code_cache(update_code_cache) { + _root_updater(root_updater) { } void work(uint worker_id) { @@ -162,22 +138,8 @@ ShenandoahHeap* heap = ShenandoahHeap::heap(); ShenandoahUpdateRefsClosure cl; - CLDToOopClosure cldCl(&cl, ClassLoaderData::_claim_strong); - - CodeBlobClosure* code_blobs; - CodeBlobToOopClosure update_blobs(&cl, CodeBlobToOopClosure::FixRelocations); -#ifdef ASSERT - ShenandoahAssertToSpaceClosure assert_to_space_oops; - CodeBlobToOopClosure assert_to_space(&assert_to_space_oops, !CodeBlobToOopClosure::FixRelocations); -#endif - if (_update_code_cache) { - code_blobs = &update_blobs; - } else { - code_blobs = - DEBUG_ONLY(&assert_to_space) - NOT_DEBUG(NULL); - } - _rp->update_all_roots(&cl, &cldCl, code_blobs, NULL, worker_id); + AlwaysTrueClosure always_true; + _root_updater->roots_do(worker_id, &always_true, &cl); } }; @@ -265,9 +227,12 @@ rp = NULL; } - // Degenerated cycle may bypass concurrent cycle, so code roots might not be scanned, - // let's check here. - _cm->concurrent_scan_code_roots(worker_id, rp); + if (heap->is_degenerated_gc_in_progress()) { + // Degenerated cycle may bypass concurrent cycle, so code roots might not be scanned, + // let's check here. + _cm->concurrent_scan_code_roots(worker_id, rp); + } + _cm->mark_loop(worker_id, _terminator, rp, false, // not cancellable _dedup_string); @@ -289,7 +254,7 @@ assert(nworkers <= task_queues()->size(), "Just check"); - ShenandoahRootProcessor root_proc(heap, nworkers, root_phase); + ShenandoahAllRootScanner root_proc(nworkers, root_phase); TASKQUEUE_STATS_ONLY(task_queues()->reset_taskqueue_stats()); task_queues()->reserve(nworkers); @@ -333,8 +298,8 @@ uint nworkers = _heap->workers()->active_workers(); - ShenandoahRootProcessor root_proc(_heap, nworkers, root_phase); - ShenandoahUpdateRootsTask update_roots(&root_proc, update_code_cache); + ShenandoahRootUpdater root_updater(nworkers, root_phase, update_code_cache); + ShenandoahUpdateRootsTask update_roots(&root_updater); _heap->workers()->run_task(&update_roots); #if defined(COMPILER2) || INCLUDE_JVMCI @@ -342,6 +307,34 @@ #endif } +class ShenandoahUpdateThreadRootsTask : public AbstractGangTask { +private: + ShenandoahThreadRoots _thread_roots; + ShenandoahPhaseTimings::Phase _phase; +public: + ShenandoahUpdateThreadRootsTask(bool is_par, ShenandoahPhaseTimings::Phase phase) : + AbstractGangTask("Shenandoah Update Thread Roots"), + _thread_roots(is_par), + _phase(phase) { + ShenandoahHeap::heap()->phase_timings()->record_workers_start(_phase); + } + + ~ShenandoahUpdateThreadRootsTask() { + ShenandoahHeap::heap()->phase_timings()->record_workers_end(_phase); + } + void work(uint worker_id) { + ShenandoahUpdateRefsClosure cl; + _thread_roots.oops_do(&cl, NULL, worker_id); + } +}; + +void ShenandoahConcurrentMark::update_thread_roots(ShenandoahPhaseTimings::Phase root_phase) { + WorkGang* workers = _heap->workers(); + bool is_par = workers->active_workers() > 1; + ShenandoahUpdateThreadRootsTask task(is_par, root_phase); + workers->run_task(&task); +} + void ShenandoahConcurrentMark::initialize(uint workers) { _heap = ShenandoahHeap::heap(); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.hpp Thu May 23 11:07:37 2019 +0100 @@ -79,6 +79,7 @@ void mark_roots(ShenandoahPhaseTimings::Phase root_phase); void update_roots(ShenandoahPhaseTimings::Phase root_phase); + void update_thread_roots(ShenandoahPhaseTimings::Phase root_phase); // ---------- Weak references // diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Thu May 23 11:07:37 2019 +0100 @@ -52,7 +52,7 @@ #include "gc/shenandoah/shenandoahMonitoringSupport.hpp" #include "gc/shenandoah/shenandoahOopClosures.inline.hpp" #include "gc/shenandoah/shenandoahPacer.inline.hpp" -#include "gc/shenandoah/shenandoahRootProcessor.hpp" +#include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahStringDedup.hpp" #include "gc/shenandoah/shenandoahTaskqueue.hpp" #include "gc/shenandoah/shenandoahUtils.hpp" @@ -67,6 +67,10 @@ #include "gc/shenandoah/heuristics/shenandoahPassiveHeuristics.hpp" #include "gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp" #include "gc/shenandoah/heuristics/shenandoahTraversalHeuristics.hpp" +#if INCLUDE_JFR +#include "gc/shenandoah/shenandoahJfrSupport.hpp" +#endif + #include "memory/metaspace.hpp" #include "oops/compressedOops.inline.hpp" #include "runtime/globals.hpp" @@ -596,6 +600,8 @@ ref_processing_init(); _heuristics->initialize(); + + JFR_ONLY(ShenandoahJFRSupport::register_jfr_type_serializers()); } size_t ShenandoahHeap::used() const { @@ -1111,7 +1117,7 @@ ShenandoahEvacOOMScope oom_evac_scope; ShenandoahEvacuateUpdateRootsClosure cl; MarkingCodeBlobClosure blobsCl(&cl, CodeBlobToOopClosure::FixRelocations); - _rp->process_evacuate_roots(&cl, &blobsCl, worker_id); + _rp->roots_do(worker_id, &cl); } }; @@ -1122,7 +1128,7 @@ assert(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Only iterate roots while world is stopped"); { - ShenandoahRootEvacuator rp(this, workers()->active_workers(), ShenandoahPhaseTimings::init_evac); + ShenandoahRootEvacuator rp(workers()->active_workers(), ShenandoahPhaseTimings::init_evac); ShenandoahEvacuateUpdateRootsTask roots_task(&rp); workers()->run_task(&roots_task); } @@ -1326,11 +1332,9 @@ Stack oop_stack; // First, we process all GC roots. This populates the work stack with initial objects. - ShenandoahRootProcessor rp(this, 1, ShenandoahPhaseTimings::_num_phases); + ShenandoahAllRootScanner rp(1, ShenandoahPhaseTimings::_num_phases); ObjectIterateScanRootClosure oops(&_aux_bit_map, &oop_stack); - CLDToOopClosure clds(&oops, ClassLoaderData::_claim_none); - CodeBlobToOopClosure blobs(&oops, false); - rp.process_all_roots(&oops, &clds, &blobs, NULL, 0); + rp.roots_do(0, &oops); // Work through the oop stack to traverse heap. while (! oop_stack.is_empty()) { @@ -1501,10 +1505,14 @@ if (!cancelled_gc()) { concurrent_mark()->finish_mark_from_roots(/* full_gc = */ false); - // Degen may be caused by failed evacuation of roots - if (is_degenerated_gc_in_progress() && has_forwarded_objects()) { - concurrent_mark()->update_roots(ShenandoahPhaseTimings::degen_gc_update_roots); - } + if (has_forwarded_objects()) { + // Degen may be caused by failed evacuation of roots + if (is_degenerated_gc_in_progress()) { + concurrent_mark()->update_roots(ShenandoahPhaseTimings::degen_gc_update_roots); + } else { + concurrent_mark()->update_thread_roots(ShenandoahPhaseTimings::update_roots); + } + } if (ShenandoahVerify) { verifier()->verify_roots_no_forwarded(); @@ -2202,9 +2210,11 @@ verifier()->verify_roots_no_forwarded_except(ShenandoahRootVerifier::ThreadRoots); } - concurrent_mark()->update_roots(is_degenerated_gc_in_progress() ? - ShenandoahPhaseTimings::degen_gc_update_roots: - ShenandoahPhaseTimings::final_update_refs_roots); + if (is_degenerated_gc_in_progress()) { + concurrent_mark()->update_roots(ShenandoahPhaseTimings::degen_gc_update_roots); + } else { + concurrent_mark()->update_thread_roots(ShenandoahPhaseTimings::final_update_refs_roots); + } ShenandoahGCPhase final_update_refs(ShenandoahPhaseTimings::final_update_refs_recycle); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Thu May 23 11:07:37 2019 +0100 @@ -40,6 +40,7 @@ class ShenandoahCollectorPolicy; class ShenandoahControlThread; class ShenandoahGCSession; +class ShenandoahGCStateResetter; class ShenandoahHeuristics; class ShenandoahMarkingContext; class ShenandoahPhaseTimings; @@ -111,6 +112,7 @@ friend class ShenandoahAsserts; friend class VMStructs; friend class ShenandoahGCSession; + friend class ShenandoahGCStateResetter; // ---------- Locks that guard important data structures in Heap // diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp Thu May 23 11:07:37 2019 +0100 @@ -30,6 +30,7 @@ #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" #include "gc/shenandoah/shenandoahTraversalGC.hpp" #include "gc/shared/space.inline.hpp" +#include "jfr/jfrEvents.hpp" #include "memory/iterator.inline.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" @@ -93,7 +94,7 @@ case _empty_uncommitted: do_commit(); case _empty_committed: - _state = _regular; + set_state(_regular); case _regular: case _pinned: return; @@ -114,10 +115,10 @@ case _cset: case _humongous_start: case _humongous_cont: - _state = _regular; + set_state(_regular); return; case _pinned_cset: - _state = _pinned; + set_state(_pinned); return; case _regular: case _pinned: @@ -133,7 +134,7 @@ case _empty_uncommitted: do_commit(); case _empty_committed: - _state = _humongous_start; + set_state(_humongous_start); return; default: report_illegal_transition("humongous start allocation"); @@ -149,7 +150,7 @@ case _regular: case _humongous_start: case _humongous_cont: - _state = _humongous_start; + set_state(_humongous_start); return; default: report_illegal_transition("humongous start bypass"); @@ -162,7 +163,7 @@ case _empty_uncommitted: do_commit(); case _empty_committed: - _state = _humongous_cont; + set_state(_humongous_cont); return; default: report_illegal_transition("humongous continuation allocation"); @@ -178,7 +179,7 @@ case _regular: case _humongous_start: case _humongous_cont: - _state = _humongous_cont; + set_state(_humongous_cont); return; default: report_illegal_transition("humongous continuation bypass"); @@ -190,14 +191,14 @@ switch (_state) { case _regular: assert (_critical_pins == 0, "sanity"); - _state = _pinned; + set_state(_pinned); case _pinned_cset: case _pinned: _critical_pins++; return; case _humongous_start: assert (_critical_pins == 0, "sanity"); - _state = _pinned_humongous_start; + set_state(_pinned_humongous_start); case _pinned_humongous_start: _critical_pins++; return; @@ -219,7 +220,7 @@ assert (_critical_pins > 0, "sanity"); _critical_pins--; if (_critical_pins == 0) { - _state = _regular; + set_state(_regular); } return; case _regular: @@ -231,14 +232,14 @@ assert (_critical_pins > 0, "sanity"); _critical_pins--; if (_critical_pins == 0) { - _state = _cset; + set_state(_cset); } return; case _pinned_humongous_start: assert (_critical_pins > 0, "sanity"); _critical_pins--; if (_critical_pins == 0) { - _state = _humongous_start; + set_state(_humongous_start); } return; default: @@ -250,7 +251,7 @@ _heap->assert_heaplock_owned_by_current_thread(); switch (_state) { case _regular: - _state = _cset; + set_state(_cset); case _cset: return; default: @@ -268,7 +269,7 @@ // Reclaiming humongous regions case _regular: // Immediate region reclaim - _state = _trash; + set_state(_trash); return; default: report_illegal_transition("trashing"); @@ -287,7 +288,7 @@ _heap->assert_heaplock_owned_by_current_thread(); switch (_state) { case _trash: - _state = _empty_committed; + set_state(_empty_committed); _empty_time = os::elapsedTime(); return; default: @@ -300,7 +301,7 @@ switch (_state) { case _empty_committed: do_uncommit(); - _state = _empty_uncommitted; + set_state(_empty_uncommitted); return; default: report_illegal_transition("uncommiting"); @@ -314,7 +315,7 @@ switch (_state) { case _empty_uncommitted: do_commit(); - _state = _empty_committed; + set_state(_empty_committed); return; default: report_illegal_transition("commit bypass"); @@ -679,3 +680,16 @@ } _heap->decrease_committed(ShenandoahHeapRegion::region_size_bytes()); } + +void ShenandoahHeapRegion::set_state(RegionState to) { + EventShenandoahHeapRegionStateChange evt; + if (evt.should_commit()){ + evt.set_index((unsigned)region_number()); + evt.set_start((uintptr_t)bottom()); + evt.set_used(used()); + evt.set_from(_state); + evt.set_to(to); + evt.commit(); + } + _state = to; +} diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.hpp Thu May 23 11:07:37 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Red Hat, Inc. All rights reserved. + * Copyright (c) 2013, 2019, Red Hat, Inc. All rights reserved. * * 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 @@ -32,9 +32,11 @@ #include "utilities/sizes.hpp" class VMStructs; +class ShenandoahHeapRegionStateConstant; class ShenandoahHeapRegion : public ContiguousSpace { friend class VMStructs; + friend class ShenandoahHeapRegionStateConstant; private: /* Region state is described by a state machine. Transitions are guarded by @@ -114,10 +116,11 @@ _cset, // region is in collection set _pinned, // region is pinned _pinned_cset, // region is pinned and in cset (evac failure path) - _trash // region contains only trash + _trash, // region contains only trash + _REGION_STATES_NUM // last }; - const char* region_state_to_string(RegionState s) const { + static const char* region_state_to_string(RegionState s) { switch (s) { case _empty_uncommitted: return "Empty Uncommitted"; case _empty_committed: return "Empty Committed"; @@ -157,6 +160,10 @@ void report_illegal_transition(const char* method); public: + static const int region_states_num() { + return _REGION_STATES_NUM; + } + // Allowed transitions from the outside code: void make_regular_allocation(); void make_regular_bypass(); @@ -424,6 +431,8 @@ void oop_iterate_humongous(OopIterateClosure* cl); inline void internal_increase_live_data(size_t s); + + void set_state(RegionState to); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHHEAPREGION_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.cpp Thu May 23 11:07:37 2019 +0100 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019, Red Hat, Inc. All rights reserved. + * + * 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 "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahJfrSupport.hpp" +#include "jfr/jfrEvents.hpp" +#if INCLUDE_JFR +#include "jfr/metadata/jfrSerializer.hpp" +#endif + +#if INCLUDE_JFR + +class ShenandoahHeapRegionStateConstant : public JfrSerializer { + friend class ShenandoahHeapRegion; +public: + virtual void serialize(JfrCheckpointWriter& writer) { + static const u4 nof_entries = ShenandoahHeapRegion::region_states_num(); + writer.write_count(nof_entries); + for (u4 i = 0; i < nof_entries; ++i) { + writer.write_key(i); + writer.write(ShenandoahHeapRegion::region_state_to_string((ShenandoahHeapRegion::RegionState)i)); + } + } +}; + +void ShenandoahJFRSupport::register_jfr_type_serializers() { + JfrSerializer::register_serializer(TYPE_SHENANDOAHHEAPREGIONSTATE, + false, + true, + new ShenandoahHeapRegionStateConstant()); +} +#endif + +class ShenandoahDumpHeapRegionInfoClosure : public ShenandoahHeapRegionClosure { +public: + virtual void heap_region_do(ShenandoahHeapRegion* r) { + EventShenandoahHeapRegionInformation evt; + evt.set_index((unsigned)r->region_number()); + evt.set_state((u8)r->state()); + evt.set_start((uintptr_t)r->bottom()); + evt.set_used(r->used()); + evt.commit(); + } +}; + +void VM_ShenandoahSendHeapRegionInfoEvents::doit() { + ShenandoahDumpHeapRegionInfoClosure c; + ShenandoahHeap::heap()->heap_region_iterate(&c); +} diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shenandoah/shenandoahJfrSupport.hpp Thu May 23 11:07:37 2019 +0100 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019, Red Hat, Inc. All rights reserved. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHJFRSUPPORT_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHJFRSUPPORT_HPP + +#include "runtime/vmOperations.hpp" + +class VM_ShenandoahSendHeapRegionInfoEvents : public VM_Operation { +public: + virtual void doit(); + virtual VMOp_Type type() const { return VMOp_HeapIterateOperation; } +}; + +class ShenandoahJFRSupport { +public: + static void register_jfr_type_serializers(); +}; + +#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHJFRSUPPORT_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahMarkCompact.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkCompact.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkCompact.cpp Thu May 23 11:07:37 2019 +0100 @@ -561,22 +561,16 @@ class ShenandoahAdjustRootPointersTask : public AbstractGangTask { private: - ShenandoahRootProcessor* _rp; + ShenandoahRootAdjuster* _rp; public: - ShenandoahAdjustRootPointersTask(ShenandoahRootProcessor* rp) : + ShenandoahAdjustRootPointersTask(ShenandoahRootAdjuster* rp) : AbstractGangTask("Shenandoah Adjust Root Pointers Task"), _rp(rp) {} void work(uint worker_id) { ShenandoahAdjustPointersClosure cl; - CLDToOopClosure adjust_cld_closure(&cl, ClassLoaderData::_claim_strong); - MarkingCodeBlobClosure adjust_code_closure(&cl, - CodeBlobToOopClosure::FixRelocations); - - _rp->update_all_roots(&cl, - &adjust_cld_closure, - &adjust_code_closure, NULL, worker_id); + _rp->roots_do(worker_id, &cl); } }; @@ -592,7 +586,7 @@ #if COMPILER2_OR_JVMCI DerivedPointerTable::clear(); #endif - ShenandoahRootProcessor rp(heap, nworkers, ShenandoahPhaseTimings::full_gc_roots); + ShenandoahRootAdjuster rp(nworkers, ShenandoahPhaseTimings::full_gc_roots); ShenandoahAdjustRootPointersTask task(&rp); workers->run_task(&task); #if COMPILER2_OR_JVMCI diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp Thu May 23 11:07:37 2019 +0100 @@ -28,8 +28,9 @@ #include "classfile/systemDictionary.hpp" #include "code/codeCache.hpp" #include "gc/shenandoah/shenandoahClosures.inline.hpp" -#include "gc/shenandoah/shenandoahRootProcessor.hpp" +#include "gc/shenandoah/shenandoahRootProcessor.inline.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahStringDedup.hpp" #include "gc/shenandoah/shenandoahTimingTracker.hpp" @@ -43,248 +44,157 @@ #include "runtime/thread.hpp" #include "services/management.hpp" -ShenandoahRootProcessor::ShenandoahRootProcessor(ShenandoahHeap* heap, uint n_workers, - ShenandoahPhaseTimings::Phase phase) : - _process_strong_tasks(new SubTasksDone(SHENANDOAH_RP_PS_NumElements)), - _srs(n_workers), - _phase(phase), - _coderoots_all_iterator(ShenandoahCodeRoots::iterator()), - _weak_processor_timings(n_workers), - _weak_processor_task(&_weak_processor_timings, n_workers), - _processed_weak_roots(false) { - heap->phase_timings()->record_workers_start(_phase); +ShenandoahSerialRoot::ShenandoahSerialRoot(ShenandoahSerialRoot::OopsDo oops_do, ShenandoahPhaseTimings::GCParPhases phase) : + _claimed(false), _oops_do(oops_do), _phase(phase) { +} + +void ShenandoahSerialRoot::oops_do(OopClosure* cl, uint worker_id) { + if (!_claimed && Atomic::cmpxchg(true, &_claimed, false) == false) { + ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); + ShenandoahWorkerTimingsTracker timer(worker_times, _phase, worker_id); + _oops_do(cl); + } +} + +ShenandoahSerialRoots::ShenandoahSerialRoots() : + _universe_root(&Universe::oops_do, ShenandoahPhaseTimings::UniverseRoots), + _object_synchronizer_root(&ObjectSynchronizer::oops_do, ShenandoahPhaseTimings::ObjectSynchronizerRoots), + _management_root(&Management::oops_do, ShenandoahPhaseTimings::ManagementRoots), + _system_dictionary_root(&SystemDictionary::oops_do, ShenandoahPhaseTimings::SystemDictionaryRoots), + _jvmti_root(&JvmtiExport::oops_do, ShenandoahPhaseTimings::JVMTIRoots), + _jni_handle_root(&JNIHandles::oops_do, ShenandoahPhaseTimings::JNIRoots) { +} + +void ShenandoahSerialRoots::oops_do(OopClosure* cl, uint worker_id) { + _universe_root.oops_do(cl, worker_id); + _object_synchronizer_root.oops_do(cl, worker_id); + _management_root.oops_do(cl, worker_id); + _system_dictionary_root.oops_do(cl, worker_id); + _jvmti_root.oops_do(cl, worker_id); + _jni_handle_root.oops_do(cl, worker_id); +} +ShenandoahThreadRoots::ShenandoahThreadRoots(bool is_par) : _is_par(is_par) { + Threads::change_thread_claim_token(); +} + +void ShenandoahThreadRoots::oops_do(OopClosure* oops_cl, CodeBlobClosure* code_cl, uint worker_id) { + ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); + ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::ThreadRoots, worker_id); + ResourceMark rm; + Threads::possibly_parallel_oops_do(_is_par, oops_cl, code_cl); +} + +void ShenandoahThreadRoots::threads_do(ThreadClosure* tc, uint worker_id) { + ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); + ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::ThreadRoots, worker_id); + ResourceMark rm; + Threads::possibly_parallel_threads_do(_is_par, tc); +} + +ShenandoahThreadRoots::~ShenandoahThreadRoots() { + Threads::assert_all_threads_claimed(); +} + +ShenandoahWeakRoots::ShenandoahWeakRoots(uint n_workers) : + _process_timings(n_workers), + _task(&_process_timings, n_workers) { +} + +ShenandoahWeakRoots::~ShenandoahWeakRoots() { + ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); + ShenandoahTimingConverter::weak_processing_timing_to_shenandoah_timing(&_process_timings, + worker_times); +} + +ShenandoahStringDedupRoots::ShenandoahStringDedupRoots() { if (ShenandoahStringDedup::is_enabled()) { StringDedup::gc_prologue(false); } } -ShenandoahRootProcessor::~ShenandoahRootProcessor() { - delete _process_strong_tasks; +ShenandoahStringDedupRoots::~ShenandoahStringDedupRoots() { if (ShenandoahStringDedup::is_enabled()) { StringDedup::gc_epilogue(); } - - if (_processed_weak_roots) { - assert(_weak_processor_timings.max_threads() == n_workers(), "Must match"); - ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); - ShenandoahTimingConverter::weak_processing_timing_to_shenandoah_timing(&_weak_processor_timings, - worker_times); - } - - ShenandoahHeap::heap()->phase_timings()->record_workers_end(_phase); -} - -void ShenandoahRootProcessor::process_strong_roots(OopClosure* oops, - CLDClosure* clds, - CodeBlobClosure* blobs, - ThreadClosure* thread_cl, - uint worker_id) { - - process_java_roots(oops, clds, NULL, blobs, thread_cl, worker_id); - process_vm_roots(oops, worker_id); - - _process_strong_tasks->all_tasks_completed(n_workers()); } -void ShenandoahRootProcessor::process_all_roots(OopClosure* oops, - CLDClosure* clds, - CodeBlobClosure* blobs, - ThreadClosure* thread_cl, - uint worker_id) { - - ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); - process_java_roots(oops, clds, clds, blobs, thread_cl, worker_id); - process_vm_roots(oops, worker_id); - - if (blobs != NULL) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::CodeCacheRoots, worker_id); - _coderoots_all_iterator.possibly_parallel_blobs_do(blobs); - } - - _process_strong_tasks->all_tasks_completed(n_workers()); - -} - -class ShenandoahParallelOopsDoThreadClosure : public ThreadClosure { -private: - OopClosure* _f; - CodeBlobClosure* _cf; - ThreadClosure* _thread_cl; -public: - ShenandoahParallelOopsDoThreadClosure(OopClosure* f, CodeBlobClosure* cf, ThreadClosure* thread_cl) : - _f(f), _cf(cf), _thread_cl(thread_cl) {} - - void do_thread(Thread* t) { - if (_thread_cl != NULL) { - _thread_cl->do_thread(t); - } - t->oops_do(_f, _cf); - } -}; - -void ShenandoahRootProcessor::process_java_roots(OopClosure* strong_roots, - CLDClosure* strong_clds, - CLDClosure* weak_clds, - CodeBlobClosure* strong_code, - ThreadClosure* thread_cl, - uint worker_id) -{ - ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); - // Iterating over the CLDG and the Threads are done early to allow us to - // first process the strong CLDs and nmethods and then, after a barrier, - // let the thread process the weak CLDs and nmethods. - { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::CLDGRoots, worker_id); - _cld_iterator.root_cld_do(strong_clds, weak_clds); - } - - { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::ThreadRoots, worker_id); - bool is_par = n_workers() > 1; - ResourceMark rm; - ShenandoahParallelOopsDoThreadClosure cl(strong_roots, strong_code, thread_cl); - Threads::possibly_parallel_threads_do(is_par, &cl); +void ShenandoahStringDedupRoots::oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, uint worker_id) { + if (ShenandoahStringDedup::is_enabled()) { + ShenandoahStringDedup::parallel_oops_do(is_alive, keep_alive, worker_id); } } -void ShenandoahRootProcessor::process_vm_roots(OopClosure* strong_roots, - uint worker_id) { - ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); - if (_process_strong_tasks->try_claim_task(SHENANDOAH_RP_PS_Universe_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::UniverseRoots, worker_id); - Universe::oops_do(strong_roots); - } - - if (_process_strong_tasks->try_claim_task(SHENANDOAH_RP_PS_JNIHandles_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::JNIRoots, worker_id); - JNIHandles::oops_do(strong_roots); - } - if (_process_strong_tasks->try_claim_task(SHENANDOAH_RP_PS_Management_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::ManagementRoots, worker_id); - Management::oops_do(strong_roots); - } - if (_process_strong_tasks->try_claim_task(SHENANDOAH_RP_PS_jvmti_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::JVMTIRoots, worker_id); - JvmtiExport::oops_do(strong_roots); - } - if (_process_strong_tasks->try_claim_task(SHENANDOAH_RP_PS_SystemDictionary_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::SystemDictionaryRoots, worker_id); - SystemDictionary::oops_do(strong_roots); - } - - { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::ObjectSynchronizerRoots, worker_id); - if (_process_strong_tasks->try_claim_task(SHENANDOAH_RP_PS_ObjectSynchronizer_oops_do)) { - ObjectSynchronizer::oops_do(strong_roots); - } - } -} - -uint ShenandoahRootProcessor::n_workers() const { - return _srs.n_threads(); -} - -ShenandoahRootEvacuator::ShenandoahRootEvacuator(ShenandoahHeap* heap, uint n_workers, ShenandoahPhaseTimings::Phase phase) : - _evacuation_tasks(new SubTasksDone(SHENANDOAH_EVAC_NumElements)), - _srs(n_workers), - _phase(phase), - _coderoots_cset_iterator(ShenandoahCodeRoots::cset_iterator()), - _weak_processor_timings(n_workers), - _weak_processor_task(&_weak_processor_timings, n_workers) { - heap->phase_timings()->record_workers_start(_phase); - if (ShenandoahStringDedup::is_enabled()) { - StringDedup::gc_prologue(false); - } -} - -ShenandoahRootEvacuator::~ShenandoahRootEvacuator() { - delete _evacuation_tasks; - if (ShenandoahStringDedup::is_enabled()) { - StringDedup::gc_epilogue(); - } - - ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); - assert(_weak_processor_timings.max_threads() == n_workers(), "Must match"); - ShenandoahTimingConverter::weak_processing_timing_to_shenandoah_timing(&_weak_processor_timings, - worker_times); - - ShenandoahHeap::heap()->phase_timings()->record_workers_end(_phase); -} - -void ShenandoahRootEvacuator::process_evacuate_roots(OopClosure* oops, - CodeBlobClosure* blobs, - uint worker_id) { - - AlwaysTrueClosure always_true; - ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); - { - bool is_par = n_workers() > 1; - ResourceMark rm; - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::ThreadRoots, worker_id); - Threads::possibly_parallel_oops_do(is_par, oops, NULL); - } - - { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::CLDGRoots, worker_id); - CLDToOopClosure clds(oops, ClassLoaderData::_claim_strong); - _cld_iterator.root_cld_do(&clds, &clds); - } - - if (blobs != NULL) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::CodeCacheRoots, worker_id); - _coderoots_cset_iterator.possibly_parallel_blobs_do(blobs); - } - - if (ShenandoahStringDedup::is_enabled()) { - ShenandoahStringDedup::parallel_oops_do(&always_true, oops, worker_id); - } - - if (_evacuation_tasks->try_claim_task(SHENANDOAH_EVAC_Universe_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::UniverseRoots, worker_id); - Universe::oops_do(oops); - } - - if (_evacuation_tasks->try_claim_task(SHENANDOAH_EVAC_Management_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::ManagementRoots, worker_id); - Management::oops_do(oops); - } - - if (_evacuation_tasks->try_claim_task(SHENANDOAH_EVAC_jvmti_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::JVMTIRoots, worker_id); - JvmtiExport::oops_do(oops); - ShenandoahForwardedIsAliveClosure is_alive; - JvmtiExport::weak_oops_do(&is_alive, oops); - } - - if (_evacuation_tasks->try_claim_task(SHENANDOAH_EVAC_JNIHandles_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::JNIRoots, worker_id); - JNIHandles::oops_do(oops); - } - - if (_evacuation_tasks->try_claim_task(SHENANDOAH_EVAC_SystemDictionary_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::SystemDictionaryRoots, worker_id); - SystemDictionary::oops_do(oops); - } - - if (_evacuation_tasks->try_claim_task(SHENANDOAH_EVAC_ObjectSynchronizer_oops_do)) { - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::ObjectSynchronizerRoots, worker_id); - ObjectSynchronizer::oops_do(oops); - } - - _weak_processor_task.work(worker_id, &always_true, oops); -} - -uint ShenandoahRootEvacuator::n_workers() const { - return _srs.n_threads(); -} - -// Implemenation of ParallelCLDRootIterator -ParallelCLDRootIterator::ParallelCLDRootIterator() { - assert(SafepointSynchronize::is_at_safepoint(), "Must at safepoint"); +ShenandoahClassLoaderDataRoots::ShenandoahClassLoaderDataRoots() { ClassLoaderDataGraph::clear_claimed_marks(); } -void ParallelCLDRootIterator::root_cld_do(CLDClosure* strong, CLDClosure* weak) { - ClassLoaderDataGraph::roots_cld_do(strong, weak); +void ShenandoahClassLoaderDataRoots::clds_do(CLDClosure* strong_clds, CLDClosure* weak_clds, uint worker_id) { + ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); + ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::CLDGRoots, worker_id); + ClassLoaderDataGraph::roots_cld_do(strong_clds, weak_clds); +} + +ShenandoahRootProcessor::ShenandoahRootProcessor(ShenandoahPhaseTimings::Phase phase) : + _heap(ShenandoahHeap::heap()), + _phase(phase) { + assert(SafepointSynchronize::is_at_safepoint(), "Must at safepoint"); + _heap->phase_timings()->record_workers_start(_phase); +} + +ShenandoahRootProcessor::~ShenandoahRootProcessor() { + assert(SafepointSynchronize::is_at_safepoint(), "Must at safepoint"); + _heap->phase_timings()->record_workers_end(_phase); +} + +ShenandoahRootEvacuator::ShenandoahRootEvacuator(uint n_workers, ShenandoahPhaseTimings::Phase phase) : + ShenandoahRootProcessor(phase), + _thread_roots(n_workers > 1), + _weak_roots(n_workers) { } + +void ShenandoahRootEvacuator::roots_do(uint worker_id, OopClosure* oops) { + MarkingCodeBlobClosure blobsCl(oops, CodeBlobToOopClosure::FixRelocations); + CLDToOopClosure clds(oops, ClassLoaderData::_claim_strong); + CLDToOopClosure* weak_clds = ShenandoahHeap::heap()->unload_classes() ? NULL : &clds; + + AlwaysTrueClosure always_true; + + _serial_roots.oops_do(oops, worker_id); + + _thread_roots.oops_do(oops, NULL, worker_id); + _cld_roots.clds_do(&clds, &clds, worker_id); + _code_roots.code_blobs_do(&blobsCl, worker_id); + + _weak_roots.oops_do(&always_true, oops, worker_id); + _dedup_roots.oops_do(&always_true, oops, worker_id); +} + +ShenandoahRootUpdater::ShenandoahRootUpdater(uint n_workers, ShenandoahPhaseTimings::Phase phase, bool update_code_cache) : + ShenandoahRootProcessor(phase), + _thread_roots(n_workers > 1), + _weak_roots(n_workers), + _update_code_cache(update_code_cache) { +} + +ShenandoahRootAdjuster::ShenandoahRootAdjuster(uint n_workers, ShenandoahPhaseTimings::Phase phase) : + ShenandoahRootProcessor(phase), + _thread_roots(n_workers > 1), + _weak_roots(n_workers) { + assert(ShenandoahHeap::heap()->is_full_gc_in_progress(), "Full GC only"); +} + +void ShenandoahRootAdjuster::roots_do(uint worker_id, OopClosure* oops) { + CodeBlobToOopClosure adjust_code_closure(oops, CodeBlobToOopClosure::FixRelocations); + CLDToOopClosure adjust_cld_closure(oops, ClassLoaderData::_claim_strong); + AlwaysTrueClosure always_true; + + _serial_roots.oops_do(oops, worker_id); + + _thread_roots.oops_do(oops, NULL, worker_id); + _cld_roots.clds_do(&adjust_cld_closure, NULL, worker_id); + _code_roots.code_blobs_do(&adjust_code_closure, worker_id); + + _weak_roots.oops_do(&always_true, oops, worker_id); + _dedup_roots.oops_do(&always_true, oops, worker_id); +} diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp Thu May 23 11:07:37 2019 +0100 @@ -36,110 +36,164 @@ #include "memory/allocation.hpp" #include "memory/iterator.hpp" -class ParallelCLDRootIterator { +class ShenandoahSerialRoot { +public: + typedef void (*OopsDo)(OopClosure*); +private: + volatile bool _claimed; + const OopsDo _oops_do; + const ShenandoahPhaseTimings::GCParPhases _phase; + public: - ParallelCLDRootIterator(); - void root_cld_do(CLDClosure* strong, CLDClosure* weak); + ShenandoahSerialRoot(OopsDo oops_do, ShenandoahPhaseTimings::GCParPhases); + void oops_do(OopClosure* cl, uint worker_id); +}; + +class ShenandoahSerialRoots { +private: + ShenandoahSerialRoot _universe_root; + ShenandoahSerialRoot _object_synchronizer_root; + ShenandoahSerialRoot _management_root; + ShenandoahSerialRoot _system_dictionary_root; + ShenandoahSerialRoot _jvmti_root; + ShenandoahSerialRoot _jni_handle_root; +public: + ShenandoahSerialRoots(); + void oops_do(OopClosure* cl, uint worker_id); +}; + +class ShenandoahThreadRoots { +private: + const bool _is_par; +public: + ShenandoahThreadRoots(bool is_par); + ~ShenandoahThreadRoots(); + + void oops_do(OopClosure* oops_cl, CodeBlobClosure* code_cl, uint worker_id); + void threads_do(ThreadClosure* tc, uint worker_id); }; -enum Shenandoah_process_roots_tasks { - SHENANDOAH_RP_PS_Universe_oops_do, - SHENANDOAH_RP_PS_JNIHandles_oops_do, - SHENANDOAH_RP_PS_ObjectSynchronizer_oops_do, - SHENANDOAH_RP_PS_Management_oops_do, - SHENANDOAH_RP_PS_SystemDictionary_oops_do, - SHENANDOAH_RP_PS_jvmti_oops_do, - // Leave this one last. - SHENANDOAH_RP_PS_NumElements +class ShenandoahWeakRoots { +private: + WeakProcessorPhaseTimes _process_timings; + WeakProcessor::Task _task; +public: + ShenandoahWeakRoots(uint n_workers); + ~ShenandoahWeakRoots(); + + template + void oops_do(IsAlive* is_alive, KeepAlive* keep_alive, uint worker_id); +}; + +class ShenandoahStringDedupRoots { +public: + ShenandoahStringDedupRoots(); + ~ShenandoahStringDedupRoots(); + + void oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, uint worker_id); +}; + +template +class ShenandoahCodeCacheRoots { +private: + ITR _coderoots_iterator; +public: + ShenandoahCodeCacheRoots(); + ~ShenandoahCodeCacheRoots(); + + void code_blobs_do(CodeBlobClosure* blob_cl, uint worker_id); +}; + +class ShenandoahClassLoaderDataRoots { +public: + ShenandoahClassLoaderDataRoots(); + + void clds_do(CLDClosure* strong_clds, CLDClosure* weak_clds, uint worker_id); }; class ShenandoahRootProcessor : public StackObj { - SubTasksDone* _process_strong_tasks; - StrongRootsScope _srs; - ShenandoahPhaseTimings::Phase _phase; - ParallelCLDRootIterator _cld_iterator; - ShenandoahAllCodeRootsIterator _coderoots_all_iterator; - CodeBlobClosure* _threads_nmethods_cl; - WeakProcessorPhaseTimes _weak_processor_timings; - WeakProcessor::Task _weak_processor_task; - bool _processed_weak_roots; - - void process_java_roots(OopClosure* scan_non_heap_roots, - CLDClosure* scan_strong_clds, - CLDClosure* scan_weak_clds, - CodeBlobClosure* scan_strong_code, - ThreadClosure* thread_cl, - uint worker_i); - - void process_vm_roots(OopClosure* scan_non_heap_roots, - uint worker_i); - - void weak_processor_timing_to_shenandoah_timing(const WeakProcessorPhases::Phase wpp, - const ShenandoahPhaseTimings::GCParPhases spp, - ShenandoahWorkerTimings* worker_times) const; - +private: + ShenandoahHeap* const _heap; + const ShenandoahPhaseTimings::Phase _phase; public: - ShenandoahRootProcessor(ShenandoahHeap* heap, uint n_workers, - ShenandoahPhaseTimings::Phase phase); + ShenandoahRootProcessor(ShenandoahPhaseTimings::Phase phase); ~ShenandoahRootProcessor(); - // Apply oops, clds and blobs to all strongly reachable roots in the system. - // Optionally, apply class loader closure to weak clds, depending on class unloading - // for the particular GC cycles. - void process_strong_roots(OopClosure* oops, - CLDClosure* clds, - CodeBlobClosure* blobs, - ThreadClosure* thread_cl, - uint worker_id); + ShenandoahHeap* heap() const { return _heap; } +}; - // Apply oops, clds and blobs to strongly reachable roots in the system - void process_all_roots(OopClosure* oops, - CLDClosure* clds, - CodeBlobClosure* blobs, - ThreadClosure* thread_cl, - uint worker_id); +template +class ShenandoahRootScanner : public ShenandoahRootProcessor { +private: + ShenandoahSerialRoots _serial_roots; + ShenandoahClassLoaderDataRoots _cld_roots; + ShenandoahThreadRoots _thread_roots; + ShenandoahCodeCacheRoots _code_roots; +public: + ShenandoahRootScanner(uint n_workers, ShenandoahPhaseTimings::Phase phase); - // Apply oops, clds and blobs to strongly and weakly reachable roots in the system - template - void update_all_roots(OopClosure* oops, - CLDClosure* clds, - CodeBlobClosure* blobs, - ThreadClosure* thread_cl, - uint worker_id); + // Apply oops, clds and blobs to all strongly reachable roots in the system, + // during class unloading cycle + void strong_roots_do(uint worker_id, OopClosure* cl); + void strong_roots_do(uint worker_id, OopClosure* oops, CLDClosure* clds, CodeBlobClosure* code, ThreadClosure* tc = NULL); - // Number of worker threads used by the root processor. - uint n_workers() const; + // Apply oops, clds and blobs to all strongly reachable roots and weakly reachable + // roots when class unloading is disabled during this cycle + void roots_do(uint worker_id, OopClosure* cl); + void roots_do(uint worker_id, OopClosure* oops, CLDClosure* clds, CodeBlobClosure* code, ThreadClosure* tc = NULL); }; -class ShenandoahRootEvacuator : public StackObj { - SubTasksDone* _evacuation_tasks; - StrongRootsScope _srs; - ShenandoahPhaseTimings::Phase _phase; - ShenandoahCsetCodeRootsIterator _coderoots_cset_iterator; - ParallelCLDRootIterator _cld_iterator; - WeakProcessorPhaseTimes _weak_processor_timings; - WeakProcessor::Task _weak_processor_task; +typedef ShenandoahRootScanner ShenandoahAllRootScanner; +typedef ShenandoahRootScanner ShenandoahCSetRootScanner; + +// Evacuate all roots at a safepoint +class ShenandoahRootEvacuator : public ShenandoahRootProcessor { +private: + ShenandoahSerialRoots _serial_roots; + ShenandoahClassLoaderDataRoots _cld_roots; + ShenandoahThreadRoots _thread_roots; + ShenandoahWeakRoots _weak_roots; + ShenandoahStringDedupRoots _dedup_roots; + ShenandoahCodeCacheRoots _code_roots; + +public: + ShenandoahRootEvacuator(uint n_workers, ShenandoahPhaseTimings::Phase phase); + + void roots_do(uint worker_id, OopClosure* oops); +}; - enum Shenandoah_evacuate_roots_tasks { - SHENANDOAH_EVAC_Universe_oops_do, - SHENANDOAH_EVAC_ObjectSynchronizer_oops_do, - SHENANDOAH_EVAC_Management_oops_do, - SHENANDOAH_EVAC_SystemDictionary_oops_do, - SHENANDOAH_EVAC_jvmti_oops_do, - SHENANDOAH_EVAC_JNIHandles_oops_do, - // Leave this one last. - SHENANDOAH_EVAC_NumElements - }; +// Update all roots at a safepoint +class ShenandoahRootUpdater : public ShenandoahRootProcessor { +private: + ShenandoahSerialRoots _serial_roots; + ShenandoahClassLoaderDataRoots _cld_roots; + ShenandoahThreadRoots _thread_roots; + ShenandoahWeakRoots _weak_roots; + ShenandoahStringDedupRoots _dedup_roots; + ShenandoahCodeCacheRoots _code_roots; + const bool _update_code_cache; + public: - ShenandoahRootEvacuator(ShenandoahHeap* heap, uint n_workers, - ShenandoahPhaseTimings::Phase phase); - ~ShenandoahRootEvacuator(); + ShenandoahRootUpdater(uint n_workers, ShenandoahPhaseTimings::Phase phase, bool update_code_cache); + + template + void roots_do(uint worker_id, IsAlive* is_alive, KeepAlive* keep_alive); +}; - void process_evacuate_roots(OopClosure* oops, - CodeBlobClosure* blobs, - uint worker_id); +// Adjuster all roots at a safepoint during full gc +class ShenandoahRootAdjuster : public ShenandoahRootProcessor { +private: + ShenandoahSerialRoots _serial_roots; + ShenandoahClassLoaderDataRoots _cld_roots; + ShenandoahThreadRoots _thread_roots; + ShenandoahWeakRoots _weak_roots; + ShenandoahStringDedupRoots _dedup_roots; + ShenandoahCodeCacheRoots _code_roots; - // Number of worker threads used by the root processor. - uint n_workers() const; +public: + ShenandoahRootAdjuster(uint n_workers, ShenandoahPhaseTimings::Phase phase); + + void roots_do(uint worker_id, OopClosure* oops); }; + #endif // SHARE_GC_SHENANDOAH_SHENANDOAHROOTPROCESSOR_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.inline.hpp Thu May 23 11:07:37 2019 +0100 @@ -24,23 +24,118 @@ #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHROOTPROCESSOR_INLINE_HPP #define SHARE_GC_SHENANDOAH_SHENANDOAHROOTPROCESSOR_INLINE_HPP +#include "gc/shenandoah/shenandoahHeuristics.hpp" #include "gc/shenandoah/shenandoahRootProcessor.hpp" +#include "gc/shenandoah/shenandoahTimingTracker.hpp" +#include "memory/resourceArea.hpp" + +template +void ShenandoahWeakRoots::oops_do(IsAlive* is_alive, KeepAlive* keep_alive, uint worker_id) { + _task.work(worker_id, is_alive, keep_alive); +} + +template +ShenandoahCodeCacheRoots::ShenandoahCodeCacheRoots() { + nmethod::oops_do_marking_prologue(); +} + +template +void ShenandoahCodeCacheRoots::code_blobs_do(CodeBlobClosure* blob_cl, uint worker_id) { + ShenandoahWorkerTimings* worker_times = ShenandoahHeap::heap()->phase_timings()->worker_times(); + ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::CodeCacheRoots, worker_id); + _coderoots_iterator.possibly_parallel_blobs_do(blob_cl); +} + +template +ShenandoahCodeCacheRoots::~ShenandoahCodeCacheRoots() { + nmethod::oops_do_marking_epilogue(); +} + +class ShenandoahParallelOopsDoThreadClosure : public ThreadClosure { +private: + OopClosure* _f; + CodeBlobClosure* _cf; + ThreadClosure* _thread_cl; +public: + ShenandoahParallelOopsDoThreadClosure(OopClosure* f, CodeBlobClosure* cf, ThreadClosure* thread_cl) : + _f(f), _cf(cf), _thread_cl(thread_cl) {} -template -void ShenandoahRootProcessor::update_all_roots(OopClosure* oops, - CLDClosure* clds, - CodeBlobClosure* blobs, - ThreadClosure* thread_cl, - uint worker_id) { - process_all_roots(oops, clds, blobs, thread_cl, worker_id); + void do_thread(Thread* t) { + if (_thread_cl != NULL) { + _thread_cl->do_thread(t); + } + t->oops_do(_f, _cf); + } +}; + +template +ShenandoahRootScanner::ShenandoahRootScanner(uint n_workers, ShenandoahPhaseTimings::Phase phase) : + ShenandoahRootProcessor(phase), + _thread_roots(n_workers > 1) { +} + +template +void ShenandoahRootScanner::roots_do(uint worker_id, OopClosure* oops) { + CLDToOopClosure clds_cl(oops, ClassLoaderData::_claim_strong); + MarkingCodeBlobClosure blobs_cl(oops, !CodeBlobToOopClosure::FixRelocations); + roots_do(worker_id, oops, &clds_cl, &blobs_cl); +} - IsAlive is_alive; - _weak_processor_task.work(worker_id, &is_alive, oops); - _processed_weak_roots = true; +template +void ShenandoahRootScanner::strong_roots_do(uint worker_id, OopClosure* oops) { + CLDToOopClosure clds_cl(oops, ClassLoaderData::_claim_strong); + MarkingCodeBlobClosure blobs_cl(oops, !CodeBlobToOopClosure::FixRelocations); + strong_roots_do(worker_id, oops, &clds_cl, &blobs_cl); +} - if (ShenandoahStringDedup::is_enabled()) { - ShenandoahStringDedup::parallel_oops_do(&is_alive, oops, worker_id); +template +void ShenandoahRootScanner::roots_do(uint worker_id, OopClosure* oops, CLDClosure* clds, CodeBlobClosure* code, ThreadClosure *tc) { + assert(!ShenandoahHeap::heap()->unload_classes() || + ShenandoahHeap::heap()->heuristics()->can_do_traversal_gc(), + "No class unloading or traversal GC"); + ShenandoahParallelOopsDoThreadClosure tc_cl(oops, code, tc); + ResourceMark rm; + + _serial_roots.oops_do(oops, worker_id); + _cld_roots.clds_do(clds, clds, worker_id); + _thread_roots.threads_do(&tc_cl, worker_id); + + // With ShenandoahConcurrentScanCodeRoots, we avoid scanning the entire code cache here, + // and instead do that in concurrent phase under the relevant lock. This saves init mark + // pause time. + if (code != NULL && !ShenandoahConcurrentScanCodeRoots) { + _code_roots.code_blobs_do(code, worker_id); } } +template +void ShenandoahRootScanner::strong_roots_do(uint worker_id, OopClosure* oops, CLDClosure* clds, CodeBlobClosure* code, ThreadClosure* tc) { + assert(ShenandoahHeap::heap()->unload_classes(), "Should be used during class unloading"); + ShenandoahParallelOopsDoThreadClosure tc_cl(oops, code, tc); + ResourceMark rm; + + _serial_roots.oops_do(oops, worker_id); + _cld_roots.clds_do(clds, NULL, worker_id); + _thread_roots.threads_do(&tc_cl, worker_id); +} + +template +void ShenandoahRootUpdater::roots_do(uint worker_id, IsAlive* is_alive, KeepAlive* keep_alive) { + CodeBlobToOopClosure update_blobs(keep_alive, CodeBlobToOopClosure::FixRelocations); + CLDToOopClosure clds(keep_alive, ClassLoaderData::_claim_strong); + CLDToOopClosure* weak_clds = ShenandoahHeap::heap()->unload_classes() ? NULL : &clds; + + _serial_roots.oops_do(keep_alive, worker_id); + + _thread_roots.oops_do(keep_alive, NULL, worker_id); + _cld_roots.clds_do(&clds, weak_clds, worker_id); + + if(_update_code_cache) { + _code_roots.code_blobs_do(&update_blobs, worker_id); + } + + _weak_roots.oops_do(is_alive, keep_alive, worker_id); + _dedup_roots.oops_do(is_alive, keep_alive, worker_id); +} + #endif // SHARE_GC_SHENANDOAH_SHENANDOAHROOTPROCESSOR_INLINE_HPP diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahSATBMarkQueueSet.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahSATBMarkQueueSet.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahSATBMarkQueueSet.cpp Thu May 23 11:07:37 2019 +0100 @@ -70,19 +70,17 @@ } } -bool ShenandoahSATBMarkQueue::should_enqueue_buffer() { - bool should_enqueue = SATBMarkQueue::should_enqueue_buffer(); - size_t cap = capacity(); - Thread* t = Thread::current(); - if (ShenandoahThreadLocalData::is_force_satb_flush(t)) { - if (!should_enqueue && cap != index()) { +void ShenandoahSATBMarkQueue::handle_completed_buffer() { + SATBMarkQueue::handle_completed_buffer(); + if (!is_empty()) { + Thread* t = Thread::current(); + if (ShenandoahThreadLocalData::is_force_satb_flush(t)) { // Non-empty buffer is compacted, and we decided not to enqueue it. // We still want to know about leftover work in that buffer eventually. // This avoid dealing with these leftovers during the final-mark, after // the buffers are drained completely. See JDK-8205353 for more discussion. - should_enqueue = true; + ShenandoahThreadLocalData::set_force_satb_flush(t, false); + enqueue_completed_buffer(); } - ShenandoahThreadLocalData::set_force_satb_flush(t, false); } - return should_enqueue; } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahSATBMarkQueueSet.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahSATBMarkQueueSet.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahSATBMarkQueueSet.hpp Thu May 23 11:07:37 2019 +0100 @@ -30,9 +30,10 @@ #include "runtime/thread.hpp" class ShenandoahSATBMarkQueue: public SATBMarkQueue { +protected: + virtual void handle_completed_buffer(); public: ShenandoahSATBMarkQueue(SATBMarkQueueSet* qset) : SATBMarkQueue(qset) {} - virtual bool should_enqueue_buffer(); }; class ShenandoahSATBMarkQueueSet : public SATBMarkQueueSet { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Thu May 23 11:07:37 2019 +0100 @@ -161,15 +161,16 @@ class ShenandoahInitTraversalCollectionTask : public AbstractGangTask { private: - ShenandoahRootProcessor* _rp; + ShenandoahCSetRootScanner* _rp; ShenandoahHeap* _heap; ShenandoahCsetCodeRootsIterator* _cset_coderoots; + ShenandoahStringDedupRoots _dedup_roots; + public: - ShenandoahInitTraversalCollectionTask(ShenandoahRootProcessor* rp, ShenandoahCsetCodeRootsIterator* cset_coderoots) : + ShenandoahInitTraversalCollectionTask(ShenandoahCSetRootScanner* rp) : AbstractGangTask("Shenandoah Init Traversal Collection"), _rp(rp), - _heap(ShenandoahHeap::heap()), - _cset_coderoots(cset_coderoots) {} + _heap(ShenandoahHeap::heap()) {} void work(uint worker_id) { ShenandoahParallelWorkerSession worker_session(worker_id); @@ -191,18 +192,13 @@ ShenandoahMarkCLDClosure cld_cl(&roots_cl); MarkingCodeBlobClosure code_cl(&roots_cl, CodeBlobToOopClosure::FixRelocations); if (unload_classes) { - _rp->process_strong_roots(&roots_cl, &cld_cl, NULL, NULL, worker_id); - // Need to pre-evac code roots here. Otherwise we might see from-space constants. - ShenandoahWorkerTimings* worker_times = _heap->phase_timings()->worker_times(); - ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::CodeCacheRoots, worker_id); - _cset_coderoots->possibly_parallel_blobs_do(&code_cl); + _rp->roots_do(worker_id, &roots_cl, NULL, &code_cl); } else { - _rp->process_all_roots(&roots_cl, &cld_cl, &code_cl, NULL, worker_id); + _rp->roots_do(worker_id, &roots_cl, &cld_cl, &code_cl); } - if (ShenandoahStringDedup::is_enabled()) { - AlwaysTrueClosure is_alive; - ShenandoahStringDedup::parallel_oops_do(&is_alive, &roots_cl, worker_id); - } + + AlwaysTrueClosure is_alive; + _dedup_roots.oops_do(&is_alive, &roots_cl, worker_id); } } }; @@ -230,11 +226,11 @@ class ShenandoahFinalTraversalCollectionTask : public AbstractGangTask { private: - ShenandoahRootProcessor* _rp; + ShenandoahAllRootScanner* _rp; ShenandoahTaskTerminator* _terminator; ShenandoahHeap* _heap; public: - ShenandoahFinalTraversalCollectionTask(ShenandoahRootProcessor* rp, ShenandoahTaskTerminator* terminator) : + ShenandoahFinalTraversalCollectionTask(ShenandoahAllRootScanner* rp, ShenandoahTaskTerminator* terminator) : AbstractGangTask("Shenandoah Final Traversal Collection"), _rp(rp), _terminator(terminator), @@ -273,23 +269,23 @@ // roots here. if (!_heap->is_degenerated_gc_in_progress()) { ShenandoahTraversalClosure roots_cl(q, rp); - CLDToOopClosure cld_cl(&roots_cl, ClassLoaderData::_claim_strong); ShenandoahTraversalSATBThreadsClosure tc(&satb_cl); if (unload_classes) { ShenandoahRemarkCLDClosure remark_cld_cl(&roots_cl); - _rp->process_strong_roots(&roots_cl, &remark_cld_cl, NULL, &tc, worker_id); + _rp->strong_roots_do(worker_id, &roots_cl, &remark_cld_cl, NULL, &tc); } else { - _rp->process_all_roots(&roots_cl, &cld_cl, NULL, &tc, worker_id); + CLDToOopClosure cld_cl(&roots_cl, ClassLoaderData::_claim_strong); + _rp->roots_do(worker_id, &roots_cl, &cld_cl, NULL, &tc); } } else { ShenandoahTraversalDegenClosure roots_cl(q, rp); - CLDToOopClosure cld_cl(&roots_cl, ClassLoaderData::_claim_strong); ShenandoahTraversalSATBThreadsClosure tc(&satb_cl); if (unload_classes) { ShenandoahRemarkCLDClosure remark_cld_cl(&roots_cl); - _rp->process_strong_roots(&roots_cl, &remark_cld_cl, NULL, &tc, worker_id); + _rp->strong_roots_do(worker_id, &roots_cl, &remark_cld_cl, NULL, &tc); } else { - _rp->process_all_roots(&roots_cl, &cld_cl, NULL, &tc, worker_id); + CLDToOopClosure cld_cl(&roots_cl, ClassLoaderData::_claim_strong); + _rp->roots_do(worker_id, &roots_cl, &cld_cl, NULL, &tc); } } @@ -309,6 +305,9 @@ _task_queues(new ShenandoahObjToScanQueueSet(heap->max_workers())), _traversal_set(ShenandoahHeapRegionSet()) { + // Traversal does not support concurrent code root scanning + FLAG_SET_DEFAULT(ShenandoahConcurrentScanCodeRoots, false); + uint num_queues = heap->max_workers(); for (uint i = 0; i < num_queues; ++i) { ShenandoahObjToScanQueue* task_queue = new ShenandoahObjToScanQueue(); @@ -411,11 +410,8 @@ { uint nworkers = _heap->workers()->active_workers(); task_queues()->reserve(nworkers); - ShenandoahRootProcessor rp(_heap, nworkers, ShenandoahPhaseTimings::init_traversal_gc_work); - - ShenandoahCsetCodeRootsIterator cset_coderoots = ShenandoahCodeRoots::cset_iterator(); - - ShenandoahInitTraversalCollectionTask traversal_task(&rp, &cset_coderoots); + ShenandoahCSetRootScanner rp(nworkers, ShenandoahPhaseTimings::init_traversal_gc_work); + ShenandoahInitTraversalCollectionTask traversal_task(&rp); _heap->workers()->run_task(&traversal_task); } @@ -584,7 +580,7 @@ task_queues()->reserve(nworkers); // Finish traversal - ShenandoahRootProcessor rp(_heap, nworkers, ShenandoahPhaseTimings::final_traversal_gc_work); + ShenandoahAllRootScanner rp(nworkers, ShenandoahPhaseTimings::final_traversal_gc_work); ShenandoahTerminationTracker term(ShenandoahPhaseTimings::final_traversal_gc_termination); ShenandoahTaskTerminator terminator(nworkers, task_queues()); @@ -693,10 +689,10 @@ class ShenandoahTraversalFixRootsTask : public AbstractGangTask { private: - ShenandoahRootProcessor* _rp; + ShenandoahRootUpdater* _rp; public: - ShenandoahTraversalFixRootsTask(ShenandoahRootProcessor* rp) : + ShenandoahTraversalFixRootsTask(ShenandoahRootUpdater* rp) : AbstractGangTask("Shenandoah traversal fix roots"), _rp(rp) { assert(ShenandoahHeap::heap()->has_forwarded_objects(), "Must be"); @@ -705,9 +701,8 @@ void work(uint worker_id) { ShenandoahParallelWorkerSession worker_session(worker_id); ShenandoahTraversalFixRootsClosure cl; - MarkingCodeBlobClosure blobsCl(&cl, CodeBlobToOopClosure::FixRelocations); - CLDToOopClosure cldCl(&cl, ClassLoaderData::_claim_strong); - _rp->update_all_roots(&cl, &cldCl, &blobsCl, NULL, worker_id); + ShenandoahForwardedIsAliveClosure is_alive; + _rp->roots_do(worker_id, &is_alive, &cl); } }; @@ -715,7 +710,7 @@ #if defined(COMPILER2) || INCLUDE_JVMCI DerivedPointerTable::clear(); #endif - ShenandoahRootProcessor rp(_heap, _heap->workers()->active_workers(), ShenandoahPhaseTimings::final_traversal_update_roots); + ShenandoahRootUpdater rp(_heap->workers()->active_workers(), ShenandoahPhaseTimings::final_traversal_update_roots, true /* update code cache */); ShenandoahTraversalFixRootsTask update_roots_task(&rp); _heap->workers()->run_task(&update_roots_task); #if defined(COMPILER2) || INCLUDE_JVMCI diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp Thu May 23 11:07:37 2019 +0100 @@ -604,6 +604,23 @@ } }; +class ShenandoahGCStateResetter : public StackObj { +private: + ShenandoahHeap* const _heap; + char _gc_state; + +public: + ShenandoahGCStateResetter() : _heap(ShenandoahHeap::heap()) { + _gc_state = _heap->gc_state(); + _heap->_gc_state.clear(); + } + + ~ShenandoahGCStateResetter() { + _heap->_gc_state.set(_gc_state); + assert(_heap->gc_state() == _gc_state, "Should be restored"); + } +}; + void ShenandoahVerifier::verify_at_safepoint(const char *label, VerifyForwarded forwarded, VerifyMarked marked, VerifyCollectionSet cset, @@ -653,6 +670,9 @@ } } + // Deactivate barriers temporarily: Verifier wants plain heap accesses + ShenandoahGCStateResetter resetter; + // Heap size checks { ShenandoahHeapLocker lock(_heap->lock()); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/z/zDirector.cpp --- a/src/hotspot/share/gc/z/zDirector.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/z/zDirector.cpp Thu May 23 11:07:37 2019 +0100 @@ -181,6 +181,25 @@ return time_until_gc <= 0; } +bool ZDirector::rule_high_usage() const { + // Perform GC if the amount of free memory is 5% or less. This is a preventive + // meassure in the case where the application has a very low allocation rate, + // such that the allocation rate rule doesn't trigger, but the amount of free + // memory is still slowly but surely heading towards zero. In this situation, + // we start a GC cycle to avoid a potential allocation stall later. + + // Calculate amount of free memory available to Java threads. Note that + // the heap reserve is not available to Java threads and is therefore not + // considered part of the free memory. + const size_t max_capacity = ZHeap::heap()->current_max_capacity(); + const size_t max_reserve = ZHeap::heap()->max_reserve(); + const size_t used = ZHeap::heap()->used(); + const size_t free_with_reserve = max_capacity - used; + const size_t free = free_with_reserve - MIN2(free_with_reserve, max_reserve); + + return percent_of(free, max_capacity) <= 5.0; +} + GCCause::Cause ZDirector::make_gc_decision() const { // Rule 0: Timer if (rule_timer()) { @@ -202,6 +221,11 @@ return GCCause::_z_proactive; } + // Rule 4: High usage + if (rule_high_usage()) { + return GCCause::_z_high_usage; + } + // No GC return GCCause::_no_gc; } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/z/zDirector.hpp --- a/src/hotspot/share/gc/z/zDirector.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/z/zDirector.hpp Thu May 23 11:07:37 2019 +0100 @@ -43,6 +43,7 @@ bool rule_warmup() const; bool rule_allocation_rate() const; bool rule_proactive() const; + bool rule_high_usage() const; GCCause::Cause make_gc_decision() const; protected: diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/gc/z/zDriver.cpp --- a/src/hotspot/share/gc/z/zDriver.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/gc/z/zDriver.cpp Thu May 23 11:07:37 2019 +0100 @@ -234,6 +234,7 @@ case GCCause::_z_allocation_rate: case GCCause::_z_allocation_stall: case GCCause::_z_proactive: + case GCCause::_z_high_usage: case GCCause::_metadata_GC_threshold: // Start asynchronous GC _gc_cycle_port.send_async(cause); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/include/cds.h --- a/src/hotspot/share/include/cds.h Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/include/cds.h Thu May 23 11:07:37 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ #define NUM_CDS_REGIONS 9 #define CDS_ARCHIVE_MAGIC 0xf00baba2 +#define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8 #define CURRENT_CDS_ARCHIVE_VERSION 5 #define INVALID_CDS_ARCHIVE_VERSION -1 diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/interpreter/bytecodeInterpreter.cpp --- a/src/hotspot/share/interpreter/bytecodeInterpreter.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/interpreter/bytecodeInterpreter.cpp Thu May 23 11:07:37 2019 +0100 @@ -2871,7 +2871,7 @@ METHOD->print_value_string(), (int)(istate->bcp() - METHOD->code_base()), (int)continuation_bci, p2i(THREAD)); - Exceptions::log_exception(except_oop, tempst); + Exceptions::log_exception(except_oop, tempst.as_string()); } // for AbortVMOnException flag Exceptions::debug_check_abort(except_oop); @@ -2888,7 +2888,7 @@ METHOD->print_value_string(), (int)(istate->bcp() - METHOD->code_base()), p2i(THREAD)); - Exceptions::log_exception(except_oop, tempst); + Exceptions::log_exception(except_oop, tempst.as_string()); } // for AbortVMOnException flag Exceptions::debug_check_abort(except_oop); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/interpreter/interpreterRuntime.cpp --- a/src/hotspot/share/interpreter/interpreterRuntime.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp Thu May 23 11:07:37 2019 +0100 @@ -543,7 +543,7 @@ tempst.print("interpreter method <%s>\n" " at bci %d for thread " INTPTR_FORMAT " (%s)", h_method->print_value_string(), current_bci, p2i(thread), thread->name()); - Exceptions::log_exception(h_exception, tempst); + Exceptions::log_exception(h_exception, tempst.as_string()); } // Don't go paging in something which won't be used. // else if (extable->length() == 0) { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/metadata/metadata.xml --- a/src/hotspot/share/jfr/metadata/metadata.xml Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/metadata/metadata.xml Thu May 23 11:07:37 2019 +0100 @@ -983,6 +983,27 @@ + + + + + + + + + + + + + + + + + + + diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/periodic/jfrPeriodic.cpp --- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp Thu May 23 11:07:37 2019 +0100 @@ -65,7 +65,9 @@ #include "services/threadService.hpp" #include "utilities/exceptions.hpp" #include "utilities/globalDefinitions.hpp" - +#if INCLUDE_SHENANDOAHGC +#include "gc/shenandoah/shenandoahJfrSupport.hpp" +#endif /** * JfrPeriodic class * Implementation of declarations in @@ -626,3 +628,14 @@ event.set_flushingEnabled(UseCodeCacheFlushing); event.commit(); } + + +TRACE_REQUEST_FUNC(ShenandoahHeapRegionInformation) { +#if INCLUDE_SHENANDOAHGC + if (UseShenandoahGC) { + VM_ShenandoahSendHeapRegionInfoEvents op; + VMThread::execute(&op); + } +#endif +} + diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp --- a/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/recorder/checkpoint/jfrCheckpointManager.cpp Thu May 23 11:07:37 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -136,6 +136,8 @@ void JfrCheckpointManager::register_full(BufferPtr t, Thread* thread) { // nothing here at the moment + assert(t != NULL, "invariant"); + assert(t->acquired_by(thread), "invariant"); assert(t->retired(), "invariant"); } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/recorder/jfrRecorder.cpp --- a/src/hotspot/share/jfr/recorder/jfrRecorder.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/recorder/jfrRecorder.cpp Thu May 23 11:07:37 2019 +0100 @@ -57,7 +57,7 @@ static bool enable() { assert(!_enabled, "invariant"); - FLAG_SET_MGMT(bool, FlightRecorder, true); + FLAG_SET_MGMT(FlightRecorder, true); _enabled = FlightRecorder; assert(_enabled, "invariant"); return _enabled; @@ -168,7 +168,7 @@ static bool is_cds_dump_requested() { // we will not be able to launch recordings if a cds dump is being requested - if (DumpSharedSpaces && (JfrOptionSet::startup_recording_options() != NULL)) { + if ((DumpSharedSpaces || DynamicDumpSharedSpaces) && (JfrOptionSet::startup_recording_options() != NULL)) { warning("JFR will be disabled during CDS dumping"); teardown_startup_support(); return true; diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp --- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.cpp Thu May 23 11:07:37 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -136,6 +136,14 @@ _identity = NULL; } +bool JfrBuffer::acquired_by(const void* id) const { + return identity() == id; +} + +bool JfrBuffer::acquired_by_self() const { + return acquired_by(Thread::current()); +} + #ifdef ASSERT static bool validate_to(const JfrBuffer* const to, size_t size) { assert(to != NULL, "invariant"); @@ -153,10 +161,6 @@ assert(t->top() + size <= t->pos(), "invariant"); return true; } - -bool JfrBuffer::acquired_by_self() const { - return identity() == Thread::current(); -} #endif // ASSERT void JfrBuffer::move(JfrBuffer* const to, size_t size) { @@ -183,7 +187,6 @@ set_concurrent_top(start()); } -// flags enum FLAG { RETIRED = 1, TRANSIENT = 2, diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp --- a/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/recorder/storage/jfrBuffer.hpp Thu May 23 11:07:37 2019 +0100 @@ -57,7 +57,6 @@ u4 _size; const u1* stable_top() const; - void clear_flags(); public: JfrBuffer(); @@ -150,6 +149,8 @@ void acquire(const void* id); bool try_acquire(const void* id); + bool acquired_by(const void* id) const; + bool acquired_by_self() const; void release(); void move(JfrBuffer* const to, size_t size); @@ -166,8 +167,6 @@ bool retired() const; void set_retired(); void clear_retired(); - - debug_only(bool acquired_by_self() const;) }; class JfrAgeNode : public JfrBuffer { diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp --- a/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/recorder/storage/jfrMemorySpace.inline.hpp Thu May 23 11:07:37 2019 +0100 @@ -346,19 +346,19 @@ template inline bool ReleaseOp::process(typename Mspace::Type* t) { assert(t != NULL, "invariant"); - if (t->retired() || t->try_acquire(_thread)) { - if (t->transient()) { - if (_release_full) { - mspace_release_full_critical(t, _mspace); - } else { - mspace_release_free_critical(t, _mspace); - } - return true; + // assumes some means of exclusive access to t + if (t->transient()) { + if (_release_full) { + mspace_release_full_critical(t, _mspace); + } else { + mspace_release_free_critical(t, _mspace); } - t->reinitialize(); - assert(t->empty(), "invariant"); - t->release(); // publish + return true; } + t->reinitialize(); + assert(t->empty(), "invariant"); + assert(!t->retired(), "invariant"); + t->release(); // publish return true; } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp --- a/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/recorder/storage/jfrStorage.cpp Thu May 23 11:07:37 2019 +0100 @@ -339,9 +339,9 @@ void JfrStorage::register_full(BufferPtr buffer, Thread* thread) { assert(buffer != NULL, "invariant"); assert(buffer->retired(), "invariant"); + assert(buffer->acquired_by(thread), "invariant"); if (!full_buffer_registration(buffer, _age_mspace, control(), thread)) { handle_registration_failure(buffer); - buffer->release(); } if (control().should_post_buffer_full_message()) { _post_box.post(MSG_FULLBUFFER); @@ -376,8 +376,8 @@ } } assert(buffer->empty(), "invariant"); + assert(buffer->identity() != NULL, "invariant"); control().increment_dead(); - buffer->release(); buffer->set_retired(); } @@ -738,13 +738,14 @@ Scavenger(JfrStorageControl& control, Mspace* mspace) : _control(control), _mspace(mspace), _count(0), _amount(0) {} bool process(Type* t) { if (t->retired()) { + assert(t->identity() != NULL, "invariant"); + assert(t->empty(), "invariant"); assert(!t->transient(), "invariant"); assert(!t->lease(), "invariant"); - assert(t->empty(), "invariant"); - assert(t->identity() == NULL, "invariant"); ++_count; _amount += t->total_size(); t->clear_retired(); + t->release(); _control.decrement_dead(); mspace_release_full_critical(t, _mspace); } diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.hpp --- a/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.hpp Thu May 23 11:07:37 2019 +0100 @@ -92,7 +92,6 @@ size_t processed() const { return ConcurrentWriteOp::processed(); } }; - template class MutexedWriteOp { private: @@ -104,6 +103,15 @@ size_t processed() const { return _operation.processed(); } }; +template +class ExclusiveOp : private MutexedWriteOp { + public: + typedef typename Operation::Type Type; + ExclusiveOp(Operation& operation) : MutexedWriteOp(operation) {} + bool process(Type* t); + size_t processed() const { return MutexedWriteOp::processed(); } +}; + enum jfr_operation_mode { mutexed = 1, concurrent diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp --- a/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/recorder/storage/jfrStorageUtils.inline.hpp Thu May 23 11:07:37 2019 +0100 @@ -26,6 +26,7 @@ #define SHARE_JFR_RECORDER_STORAGE_JFRSTORAGEUTILS_INLINE_HPP #include "jfr/recorder/storage/jfrStorageUtils.hpp" +#include "runtime/thread.inline.hpp" template inline bool UnBufferedWriteToChunk::write(T* t, const u1* data, size_t size) { @@ -75,6 +76,28 @@ return result; } +template +static void retired_sensitive_acquire(Type* t) { + assert(t != NULL, "invariant"); + if (t->retired()) { + return; + } + Thread* const thread = Thread::current(); + while (!t->try_acquire(thread)) { + if (t->retired()) { + return; + } + } +} + +template +inline bool ExclusiveOp::process(typename Operation::Type* t) { + retired_sensitive_acquire(t); + assert(t->acquired_by_self() || t->retired(), "invariant"); + // User is required to ensure proper release of the acquisition + return MutexedWriteOp::process(t); +} + template inline bool DiscardOp::process(typename Operation::Type* t) { assert(t != NULL, "invariant"); diff -r f0a1d9760c5e -r 276d3e540268 src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp --- a/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp Fri May 17 13:21:44 2019 +0100 +++ b/src/hotspot/share/jfr/recorder/stringpool/jfrStringPool.cpp Thu May 23 11:07:37 2019 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -140,93 +140,76 @@ return current_epoch; } -class StringPoolWriteOp { +template