OpenJDK / jdk / jdk
changeset 49091:0fa50be70f7a
Merge
author | prr |
---|---|
date | Fri, 16 Feb 2018 13:49:07 -0800 |
parents | 82c1fe23c469 54b423e1c4cf |
children | 6dc5e0cdb44c |
files | src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc.test/src/org/graalvm/compiler/core/sparc/test/SPARCAllocatorTest.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/ArrayRangeWriteNode.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphSnippets.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.util.test/src/org/graalvm/util/test/CollectionTest.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/EconomicMap.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/EconomicSet.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/Equivalence.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/MapCursor.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/Pair.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/UnmodifiableEconomicMap.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/UnmodifiableEconomicSet.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/UnmodifiableMapCursor.java src/jdk.internal.vm.compiler/share/classes/org.graalvm.util/src/org/graalvm/util/impl/EconomicMapImpl.java test/jdk/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.jks.base64 test/jdk/sun/security/tools/jarsigner/warnings/bad_netscape_cert_type.sh |
diffstat | 452 files changed, 10825 insertions(+), 5529 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags Fri Feb 16 12:24:38 2018 -0800 +++ b/.hgtags Fri Feb 16 13:49:07 2018 -0800 @@ -469,3 +469,4 @@ 3eae36c6baa5f916a3024cf1513e22357e00185d jdk-10+41 4b62b815b4f49970b91a952929cf50115c263cb3 jdk-10+42 107413b070b92c88bde6230ceb4a19b579781068 jdk-10+43 +dfa46cfe56346884a61efdc30dc50f7505d66761 jdk-11+1
--- a/make/CompileJavaModules.gmk Fri Feb 16 12:24:38 2018 -0800 +++ b/make/CompileJavaModules.gmk Fri Feb 16 13:49:07 2018 -0800 @@ -443,6 +443,7 @@ # jdk.internal.vm.compiler_EXCLUDES += \ + org.graalvm.collections.test \ org.graalvm.compiler.core.match.processor \ org.graalvm.compiler.nodeinfo.processor \ org.graalvm.compiler.options.processor \ @@ -461,6 +462,7 @@ org.graalvm.compiler.graph.test \ org.graalvm.compiler.hotspot.amd64.test \ org.graalvm.compiler.hotspot.lir.test \ + org.graalvm.compiler.hotspot.sparc.test \ org.graalvm.compiler.hotspot.test \ org.graalvm.compiler.jtt \ org.graalvm.compiler.lir.jtt \
--- a/make/CompileToolsHotspot.gmk Fri Feb 16 12:24:38 2018 -0800 +++ b/make/CompileToolsHotspot.gmk Fri Feb 16 13:49:07 2018 -0800 @@ -48,6 +48,7 @@ SETUP := GENERATE_OLDBYTECODE, \ SRC := \ $(SRC_DIR)/org.graalvm.word/src \ + $(SRC_DIR)/org.graalvm.collections/src \ $(SRC_DIR)/org.graalvm.compiler.core/src \ $(SRC_DIR)/org.graalvm.compiler.core.common/src \ $(SRC_DIR)/org.graalvm.compiler.core.match.processor/src \ @@ -101,6 +102,7 @@ $(eval $(call SetupJavaCompilation, BUILD_VM_COMPILER_OPTIONS_PROCESSOR, \ SETUP := GENERATE_OLDBYTECODE, \ SRC := \ + $(SRC_DIR)/org.graalvm.collections/src \ $(SRC_DIR)/org.graalvm.compiler.options/src \ $(SRC_DIR)/org.graalvm.compiler.options.processor/src \ $(SRC_DIR)/org.graalvm.util/src \ @@ -117,6 +119,7 @@ SETUP := GENERATE_OLDBYTECODE, \ SRC := \ $(SRC_DIR)/org.graalvm.word/src \ + $(SRC_DIR)/org.graalvm.collections/src \ $(SRC_DIR)/org.graalvm.compiler.replacements.verifier/src \ $(SRC_DIR)/org.graalvm.compiler.api.replacements/src \ $(SRC_DIR)/org.graalvm.compiler.code/src \
--- a/make/Docs.gmk Fri Feb 16 12:24:38 2018 -0800 +++ b/make/Docs.gmk Fri Feb 16 13:49:07 2018 -0800 @@ -445,9 +445,9 @@ ################################################################################ # Setup generation of the Java SE API documentation (javadoc + modulegraph) -# The Java SE module scope is just java.se.ee and its transitive indirect +# The Java SE module scope is just java.se and its transitive indirect # exports. -JAVASE_MODULES := java.se.ee +JAVASE_MODULES := java.se $(eval $(call SetupApiDocsGeneration, JAVASE_API, \ MODULES := $(JAVASE_MODULES), \
--- a/make/InitSupport.gmk Fri Feb 16 12:24:38 2018 -0800 +++ b/make/InitSupport.gmk Fri Feb 16 13:49:07 2018 -0800 @@ -361,6 +361,12 @@ BUILD_LOG_PIPE := > >($(TEE) -a $(BUILD_LOG)) 2> >($(TEE) -a $(BUILD_LOG) >&2) && wait + ifneq ($(CUSTOM_ROOT), ) + topdir=$(CUSTOM_ROOT) + else + topdir=$(TOPDIR) + endif + # Parse COMPARE_BUILD into COMPARE_BUILD_* # Syntax: COMPARE_BUILD=CONF=<configure options>:PATCH=<patch file>: # MAKE=<make targets>:COMP_OPTS=<compare script options>: @@ -373,7 +379,7 @@ # FAIL can be set to false to have the return value of compare be ignored. define ParseCompareBuild ifneq ($$(COMPARE_BUILD), ) - COMPARE_BUILD_OUTPUTDIR := $(TOPDIR)/build/compare-build/$(CONF_NAME) + COMPARE_BUILD_OUTPUTDIR := $(topdir)/build/compare-build/$(CONF_NAME) COMPARE_BUILD_FAIL := true ifneq ($$(findstring :, $$(COMPARE_BUILD)), ) @@ -412,9 +418,9 @@ endif endif ifneq ($$(COMPARE_BUILD_PATCH), ) - ifneq ($$(wildcard $$(TOPDIR)/$$(COMPARE_BUILD_PATCH)), ) + ifneq ($$(wildcard $$(topdir)/$$(COMPARE_BUILD_PATCH)), ) # Assume relative path, if file exists - COMPARE_BUILD_PATCH := $$(wildcard $$(TOPDIR)/$$(COMPARE_BUILD_PATCH)) + COMPARE_BUILD_PATCH := $$(wildcard $$(topdir)/$$(COMPARE_BUILD_PATCH)) else ifeq ($$(wildcard $$(COMPARE_BUILD_PATCH)), ) $$(error Patch file $$(COMPARE_BUILD_PATCH) does not exist) endif @@ -431,9 +437,9 @@ # Apply patch, if any $(if $(COMPARE_BUILD_PATCH), $(PATCH) -p1 < $(COMPARE_BUILD_PATCH)) # Move the first build away temporarily - $(RM) -r $(TOPDIR)/build/.compare-build-temp - $(MKDIR) -p $(TOPDIR)/build/.compare-build-temp - $(MV) $(OUTPUTDIR) $(TOPDIR)/build/.compare-build-temp + $(RM) -r $(topdir)/build/.compare-build-temp + $(MKDIR) -p $(topdir)/build/.compare-build-temp + $(MV) $(OUTPUTDIR) $(topdir)/build/.compare-build-temp # Restore an old compare-build, or create a new compare-build directory. if test -d $(COMPARE_BUILD_OUTPUTDIR); then \ $(MV) $(COMPARE_BUILD_OUTPUTDIR) $(OUTPUTDIR); \ @@ -443,7 +449,7 @@ # Re-run configure with the same arguments (and possibly some additional), # must be done after patching. ( cd $(OUTPUTDIR) && PATH="$(ORIGINAL_PATH)" \ - $(BASH) $(TOPDIR)/configure $(CONFIGURE_COMMAND_LINE) $(COMPARE_BUILD_CONF)) + $(BASH) $(topdir)/configure $(CONFIGURE_COMMAND_LINE) $(COMPARE_BUILD_CONF)) endef # Cleanup after a compare build @@ -451,10 +457,10 @@ # If running with a COMPARE_BUILD patch, reverse-apply it $(if $(COMPARE_BUILD_PATCH), $(PATCH) -R -p1 < $(COMPARE_BUILD_PATCH)) # Move this build away and restore the original build - $(MKDIR) -p $(TOPDIR)/build/compare-build + $(MKDIR) -p $(topdir)/build/compare-build $(MV) $(OUTPUTDIR) $(COMPARE_BUILD_OUTPUTDIR) - $(MV) $(TOPDIR)/build/.compare-build-temp/$(CONF_NAME) $(OUTPUTDIR) - $(RM) -r $(TOPDIR)/build/.compare-build-temp + $(MV) $(topdir)/build/.compare-build-temp/$(CONF_NAME) $(OUTPUTDIR) + $(RM) -r $(topdir)/build/.compare-build-temp endef # Do the actual comparison of two builds
--- a/make/autoconf/flags.m4 Fri Feb 16 12:24:38 2018 -0800 +++ b/make/autoconf/flags.m4 Fri Feb 16 13:49:07 2018 -0800 @@ -175,6 +175,16 @@ $1SYSROOT_CFLAGS="-I-xbuiltin -I[$]$1SYSROOT/usr/include" $1SYSROOT_LDFLAGS="-L[$]$1SYSROOT/usr/lib$OPENJDK_TARGET_CPU_ISADIR \ -L[$]$1SYSROOT/lib$OPENJDK_TARGET_CPU_ISADIR" + # If the devkit contains the ld linker, make sure we use it. + AC_PATH_PROG(SOLARIS_LD, ld, , $DEVKIT_TOOLCHAIN_PATH:$DEVKIT_EXTRA_PATH) + # Make sure this ld is runnable. + if test -f "$SOLARIS_LD"; then + if "$SOLARIS_LD" -V > /dev/null 2> /dev/null; then + $1SYSROOT_LDFLAGS="[$]$1SYSROOT_LDFLAGS -Yl,$(dirname $SOLARIS_LD)" + else + AC_MSG_WARN([Could not run $SOLARIS_LD found in devkit, reverting to system ld]) + fi + fi fi elif test "x$TOOLCHAIN_TYPE" = xgcc; then $1SYSROOT_CFLAGS="--sysroot=[$]$1SYSROOT" @@ -1030,9 +1040,9 @@ -qalias=noansi -qstrict -qtls=default -qlanglvl=c99vla \ -qlanglvl=noredefmac -qnortti -qnoeh -qignerrno" # We need '-qminimaltoc' or '-qpic=large -bbigtoc' if the TOC overflows. - # Hotspot now overflows its 64K TOC (currently only for slowdebug), - # so for slowdebug we build with '-qpic=large -bbigtoc'. - if test "x$DEBUG_LEVEL" = xslowdebug; then + # Hotspot now overflows its 64K TOC (currently only for debug), + # so for debug we build with '-qpic=large -bbigtoc'. + if test "x$DEBUG_LEVEL" = xslowdebug || test "x$DEBUG_LEVEL" = xfastdebug; then $2JVM_CFLAGS="[$]$2JVM_CFLAGS -qpic=large" fi elif test "x$OPENJDK_$1_OS" = xbsd; then @@ -1284,9 +1294,9 @@ $2LDFLAGS_JDK="${$2LDFLAGS_JDK} $LDFLAGS_XLC" $2JVM_LDFLAGS="[$]$2JVM_LDFLAGS $LDFLAGS_XLC" # We need '-qminimaltoc' or '-qpic=large -bbigtoc' if the TOC overflows. - # Hotspot now overflows its 64K TOC (currently only for slowdebug), - # so for slowdebug we build with '-qpic=large -bbigtoc'. - if test "x$DEBUG_LEVEL" = xslowdebug; then + # Hotspot now overflows its 64K TOC (currently only for debug), + # so we build with '-qpic=large -bbigtoc'. + if test "x$DEBUG_LEVEL" = xslowdebug || test "x$DEBUG_LEVEL" = xfastdebug; then $2JVM_LDFLAGS="[$]$2JVM_LDFLAGS -bbigtoc" fi fi
--- a/make/conf/jib-profiles.js Fri Feb 16 12:24:38 2018 -0800 +++ b/make/conf/jib-profiles.js Fri Feb 16 13:49:07 2018 -0800 @@ -768,7 +768,7 @@ linux_x64: "gcc4.9.2-OEL6.4+1.2", macosx_x64: "Xcode6.3-MacOSX10.9+1.0", solaris_x64: "SS12u4-Solaris11u1+1.0", - solaris_sparcv9: "SS12u4-Solaris11u1+1.0", + solaris_sparcv9: "SS12u4-Solaris11u1+1.1", windows_x64: "VS2013SP4+1.0", linux_aarch64: "gcc-linaro-aarch64-linux-gnu-4.8-2013.11_linux+1.0", linux_arm: (input.profile != null && input.profile.indexOf("hflt") >= 0
--- a/make/devkit/createSolarisDevkit.sh Fri Feb 16 12:24:38 2018 -0800 +++ b/make/devkit/createSolarisDevkit.sh Fri Feb 16 13:49:07 2018 -0800 @@ -73,7 +73,8 @@ DEVKIT_ROOT=${BUILD_DIR}/${DEVKIT_NAME} BUNDLE_NAME=${DEVKIT_NAME}.tar.gz BUNDLE=${BUILD_DIR}/${BUNDLE_NAME} -INSTALL_ROOT=${BUILD_DIR}/install-root +INSTALL_ROOT=${BUILD_DIR}/install-root-$SOLARIS_VERSION +INSTALL_ROOT_TOOLS=${BUILD_DIR}/install-root-tools-$SOLARIS_VERSION SYSROOT=${DEVKIT_ROOT}/sysroot SOLARIS_STUDIO_SUBDIR=SS${SOLARIS_STUDIO_VERSION} SOLARIS_STUDIO_DIR=${DEVKIT_ROOT}/${SOLARIS_STUDIO_SUBDIR} @@ -92,17 +93,27 @@ echo "Skipping installing packages" fi +# Since we have implicitly been running 11.2 tools for a long time, we need +# to pick them for the tools dir in the devkit. Create a separate install-root +# for it. +if [ ! -d $INSTALL_ROOT_TOOLS ]; then + echo "Creating $INSTALL_ROOT_TOOLS and installing packages" + pkg image-create $INSTALL_ROOT_TOOLS + pkg -R $INSTALL_ROOT_TOOLS set-publisher -P -g ${PUBLISHER_URI} solaris + sudo pkg -R $INSTALL_ROOT_TOOLS install --accept \ + entire@0.5.11-0.175.2.5.0.5.0 \ + system/linker \ + developer/base-developer-utilities \ + developer/gnu-binutils +else + echo "Skipping installing tools packages" +fi + if [ ! -d $SYSROOT ]; then echo "Copying from $INSTALL_ROOT to $SYSROOT" mkdir -p $SYSROOT cp -rH $INSTALL_ROOT/lib $SYSROOT/ - mkdir $SYSROOT/usr $DEVKIT_ROOT/gnu - # Some of the tools in sysroot are needed in the OpenJDK build but cannot be - # run from their current location due to relative runtime paths in the - # binaries. Move the sysroot/usr/bin directory to the outer bin and have them - # be runnable from there to force them to link to the system libraries - cp -rH $INSTALL_ROOT/usr/bin $DEVKIT_ROOT - cp -rH $INSTALL_ROOT/usr/gnu/bin $DEVKIT_ROOT/gnu/ + mkdir $SYSROOT/usr cp -rH $INSTALL_ROOT/usr/lib $SYSROOT/usr/ cp -rH $INSTALL_ROOT/usr/include $SYSROOT/usr/ pkg -R $INSTALL_ROOT list > $SYSROOT/pkg-list.txt @@ -110,10 +121,36 @@ echo "Skipping copying to $SYSROOT" fi +if [ ! -d $DEVKIT_ROOT/tools ]; then + echo "Copying from $INSTALL_ROOT_TOOLS to $DEVKIT_ROOT/tools" + # Some of the tools in sysroot are needed in the OpenJDK build. We need + # to copy them into a tools dir, including their specific libraries. + mkdir -p $DEVKIT_ROOT/tools/usr/bin/sparcv9 $DEVKIT_ROOT/tools/lib/sparcv9 \ + $DEVKIT_ROOT/tools/usr/gnu/bin + cp $INSTALL_ROOT_TOOLS/usr/bin/{ar,nm,strip,ld,ldd} \ + $DEVKIT_ROOT/tools/usr/bin/ + cp $INSTALL_ROOT_TOOLS/usr/bin/sparcv9/{ar,nm,strip,ld,ldd} \ + $DEVKIT_ROOT/tools/usr/bin/sparcv9/ + cp $INSTALL_ROOT_TOOLS/usr/sbin/dtrace $DEVKIT_ROOT/tools/usr/bin/ + cp $INSTALL_ROOT_TOOLS/usr/sbin/sparcv9/dtrace $DEVKIT_ROOT/tools/usr/bin/sparcv9/ + cp -rH $INSTALL_ROOT_TOOLS/usr/gnu/bin/* $DEVKIT_ROOT/tools/usr/gnu/bin/ + cp $INSTALL_ROOT_TOOLS/lib/{libelf.so*,libld.so*,liblddbg.so*} \ + $DEVKIT_ROOT/tools/lib/ + cp $INSTALL_ROOT_TOOLS/lib/sparcv9/{libelf.so*,libld.so*,liblddbg.so*} \ + $DEVKIT_ROOT/tools/lib/sparcv9/ + for t in $(ls $DEVKIT_ROOT/tools/usr/gnu/bin); do + if [ -f $DEVKIT_ROOT/tools/usr/gnu/bin/$t ]; then + ln -s ../gnu/bin/$t $DEVKIT_ROOT/tools/usr/bin/g$t + fi + done +else + echo "Skipping copying to tools dir $DEVKIT_ROOT/tools" +fi + if [ ! -d $SOLARIS_STUDIO_DIR ]; then echo "Copying Solaris Studio from $SOLARIS_STUDIO_SRC" - cp -rH $SOLARIS_STUDIO_SRC ${SOLARIS_STUDIO_DIR%/*} - mv ${SOLARIS_STUDIO_DIR%/*}/${SOLARIS_STUDIO_SRC##*/} $SOLARIS_STUDIO_DIR + mkdir -p ${SOLARIS_STUDIO_DIR} + cp -rH $SOLARIS_STUDIO_SRC/. ${SOLARIS_STUDIO_DIR}/ # Solaris Studio 12.4 requires /lib/libmmheap.so.1 to run, but this lib is not # installed by default on all Solaris systems. Sneak it in from the sysroot to # make it run OOTB on more systems. @@ -123,10 +160,9 @@ fi echo "Copying gnu make to $DEVKIT_ROOT/bin" -mkdir -p $DEVKIT_ROOT/bin -cp $GNU_MAKE $DEVKIT_ROOT/bin/ -if [ ! -e $DEVKIT_ROOT/bin/gmake ]; then - ln -s make $DEVKIT_ROOT/bin/gmake +cp $GNU_MAKE $DEVKIT_ROOT/tools/usr/bin/ +if [ ! -e $DEVKIT_ROOT/tools/usr/bin/gmake ]; then + ln -s make $DEVKIT_ROOT/tools/usr/bin/gmake fi # Create the devkit.info file @@ -136,7 +172,7 @@ echo "# This file describes to configure how to interpret the contents of this devkit" >> $INFO_FILE echo "DEVKIT_NAME=\"Solaris Studio $SOLARIS_STUDIO_VERSION - Solaris $SOLARIS_VERSION - $ARCH\"" >> $INFO_FILE echo "DEVKIT_TOOLCHAIN_PATH=\"\$DEVKIT_ROOT/$SOLARIS_STUDIO_SUBDIR/bin:\$DEVKIT_ROOT/bin\"" >> $INFO_FILE -echo "DEVKIT_EXTRA_PATH=\"\$DEVKIT_ROOT/bin\"" >> $INFO_FILE +echo "DEVKIT_EXTRA_PATH=\"\$DEVKIT_ROOT/tools/usr/bin\"" >> $INFO_FILE echo "DEVKIT_SYSROOT=\"\$DEVKIT_ROOT/sysroot\"" >> $INFO_FILE if [ ! -e $BUNDLE ]; then
--- a/make/jdk/src/classes/build/tools/taglet/ModuleGraph.java Fri Feb 16 12:24:38 2018 -0800 +++ b/make/jdk/src/classes/build/tools/taglet/ModuleGraph.java Fri Feb 16 13:49:07 2018 -0800 @@ -64,7 +64,7 @@ } String moduleName = ((ModuleElement) element).getQualifiedName().toString(); - String imageFile = moduleName + "/module-graph.png"; + String imageFile = "module-graph.png"; int thumbnailHeight = -1; String hoverImage = ""; if (!moduleName.equals("java.base")) {
--- a/make/launcher/Launcher-jdk.jcmd.gmk Fri Feb 16 12:24:38 2018 -0800 +++ b/make/launcher/Launcher-jdk.jcmd.gmk Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -60,3 +60,6 @@ $(eval $(call SetupBuildLauncher, jcmd, \ MAIN_CLASS := sun.tools.jcmd.JCmd, \ )) + +# Hook to include the corresponding custom file, if present. +$(eval $(call IncludeCustomExtension, launcher/Launcher-jdk.jcmd-post.gmk))
--- a/make/launcher/Launcher-jdk.jstatd.gmk Fri Feb 16 12:24:38 2018 -0800 +++ b/make/launcher/Launcher-jdk.jstatd.gmk Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -28,3 +28,6 @@ $(eval $(call SetupBuildLauncher, jstatd, \ MAIN_CLASS := sun.tools.jstatd.Jstatd, \ )) + +# Hook to include the corresponding custom file, if present. +$(eval $(call IncludeCustomExtension, launcher/Launcher-jdk.jstatd-post.gmk))
--- a/make/test/JtregNativeHotspot.gmk Fri Feb 16 12:24:38 2018 -0800 +++ b/make/test/JtregNativeHotspot.gmk Fri Feb 16 13:49:07 2018 -0800 @@ -67,6 +67,7 @@ $(TOPDIR)/test/hotspot/jtreg/compiler/calls \ $(TOPDIR)/test/hotspot/jtreg/compiler/runtime/criticalnatives/lookup \ $(TOPDIR)/test/hotspot/jtreg/compiler/runtime/criticalnatives/argumentcorruption \ + $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/CanGenerateAllClassHook \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorStackDepthInfo \ $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/GetNamedModule \ @@ -101,6 +102,7 @@ ifeq ($(TOOLCHAIN_TYPE), solstudio) BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_liboverflow := -lc BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libSimpleClassFileLoadHook := -lc + BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libCanGenerateAllClassHook := -lc BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libGetOwnedMonitorInfoTest := -lc BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libGetOwnedMonitorStackDepthInfoTest := -lc BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libGetNamedModuleTest := -lc
--- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2532,7 +2532,7 @@ ciMethodData* md = method->method_data_or_null(); assert(md != NULL, "Sanity"); ciProfileData* data = md->bci_to_data(bci); - assert(data->is_CounterData(), "need CounterData for calls"); + assert(data != NULL && data->is_CounterData(), "need CounterData for calls"); assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); Register mdo = op->mdo()->as_register(); __ mov_metadata(mdo, md->constant_encoding());
--- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -3150,7 +3150,7 @@ ciMethodData* md = method->method_data_or_null(); assert(md != NULL, "Sanity"); ciProfileData* data = md->bci_to_data(bci); - assert(data->is_CounterData(), "need CounterData for calls"); + assert(data != NULL && data->is_CounterData(), "need CounterData for calls"); assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); Register mdo = op->mdo()->as_register(); assert(op->tmp1()->is_register(), "tmp1 must be allocated");
--- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2017, SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2746,7 +2746,7 @@ ciMethodData* md = method->method_data_or_null(); assert(md != NULL, "Sanity"); ciProfileData* data = md->bci_to_data(bci); - assert(data->is_CounterData(), "need CounterData for calls"); + assert(data != NULL && data->is_CounterData(), "need CounterData for calls"); assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); Register mdo = op->mdo()->as_register(); #ifdef _LP64
--- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2017, SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -2715,7 +2715,7 @@ ciMethodData* md = method->method_data_or_null(); assert(md != NULL, "Sanity"); ciProfileData* data = md->bci_to_data(bci); - assert(data->is_CounterData(), "need CounterData for calls"); + assert(data != NULL && data->is_CounterData(), "need CounterData for calls"); assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); Register mdo = op->mdo()->as_register(); assert(op->tmp1()->is_double_cpu(), "tmp1 must be allocated");
--- a/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -2761,7 +2761,7 @@ ciMethodData* md = method->method_data_or_null(); assert(md != NULL, "Sanity"); ciProfileData* data = md->bci_to_data(bci); - assert(data->is_CounterData(), "need CounterData for calls"); + assert(data != NULL && data->is_CounterData(), "need CounterData for calls"); assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); Register mdo = op->mdo()->as_register(); assert(op->tmp1()->is_double_cpu(), "tmp1 must be allocated");
--- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -3504,7 +3504,7 @@ ciMethodData* md = method->method_data_or_null(); assert(md != NULL, "Sanity"); ciProfileData* data = md->bci_to_data(bci); - assert(data->is_CounterData(), "need CounterData for calls"); + assert(data != NULL && data->is_CounterData(), "need CounterData for calls"); assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); Register mdo = op->mdo()->as_register(); __ mov_metadata(mdo, md->constant_encoding());
--- a/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -676,6 +676,7 @@ assert_different_registers(start, count); BarrierSet* bs = Universe::heap()->barrier_set(); switch (bs->kind()) { +#if INCLUDE_ALL_GCS case BarrierSet::G1SATBCTLogging: // With G1, don't generate the call if we statically know that the target in uninitialized if (!uninitialized_target) { @@ -703,6 +704,7 @@ __ bind(filtered); } break; +#endif // INCLUDE_ALL_GCS case BarrierSet::CardTableForRS: case BarrierSet::CardTableExtension: case BarrierSet::ModRef: @@ -726,6 +728,7 @@ BarrierSet* bs = Universe::heap()->barrier_set(); assert_different_registers(start, count); switch (bs->kind()) { +#if INCLUDE_ALL_GCS case BarrierSet::G1SATBCTLogging: { __ pusha(); // push registers @@ -734,6 +737,7 @@ __ popa(); } break; +#endif // INCLUDE_ALL_GCS case BarrierSet::CardTableForRS: case BarrierSet::CardTableExtension:
--- a/src/hotspot/os/aix/os_aix.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/os/aix/os_aix.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -188,6 +188,7 @@ //////////////////////////////////////////////////////////////////////////////// // local variables +static volatile jlong max_real_time = 0; static jlong initial_time_count = 0; static int clock_tics_per_sec = 100; static sigset_t check_signal_done; // For diagnostics to print a message once (see run_periodic_checks) @@ -1076,32 +1077,50 @@ nanos = jlong(time.tv_usec) * 1000; } +// We use mread_real_time here. +// On AIX: If the CPU has a time register, the result will be RTC_POWER and +// it has to be converted to real time. AIX documentations suggests to do +// this unconditionally, so we do it. +// +// See: https://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.basetrf2/read_real_time.htm +// +// On PASE: mread_real_time will always return RTC_POWER_PC data, so no +// conversion is necessary. However, mread_real_time will not return +// monotonic results but merely matches read_real_time. So we need a tweak +// to ensure monotonic results. +// +// For PASE no public documentation exists, just word by IBM jlong os::javaTimeNanos() { + timebasestruct_t time; + int rc = mread_real_time(&time, TIMEBASE_SZ); if (os::Aix::on_pase()) { - - timeval time; - int status = gettimeofday(&time, NULL); - assert(status != -1, "PASE error at gettimeofday()"); - jlong usecs = jlong((unsigned long long) time.tv_sec * (1000 * 1000) + time.tv_usec); - return 1000 * usecs; - + assert(rc == RTC_POWER, "expected time format RTC_POWER from mread_real_time in PASE"); + jlong now = jlong(time.tb_high) * NANOSECS_PER_SEC + jlong(time.tb_low); + jlong prev = max_real_time; + if (now <= prev) { + return prev; // same or retrograde time; + } + jlong obsv = Atomic::cmpxchg(now, &max_real_time, prev); + assert(obsv >= prev, "invariant"); // Monotonicity + // If the CAS succeeded then we're done and return "now". + // If the CAS failed and the observed value "obsv" is >= now then + // we should return "obsv". If the CAS failed and now > obsv > prv then + // some other thread raced this thread and installed a new value, in which case + // we could either (a) retry the entire operation, (b) retry trying to install now + // or (c) just return obsv. We use (c). No loop is required although in some cases + // we might discard a higher "now" value in deference to a slightly lower but freshly + // installed obsv value. That's entirely benign -- it admits no new orderings compared + // to (a) or (b) -- and greatly reduces coherence traffic. + // We might also condition (c) on the magnitude of the delta between obsv and now. + // Avoiding excessive CAS operations to hot RW locations is critical. + // See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate + return (prev == obsv) ? now : obsv; } else { - // On AIX use the precision of processors real time clock - // or time base registers. - timebasestruct_t time; - int rc; - - // If the CPU has a time register, it will be used and - // we have to convert to real time first. After convertion we have following data: - // time.tb_high [seconds since 00:00:00 UTC on 1.1.1970] - // time.tb_low [nanoseconds after the last full second above] - // We better use mread_real_time here instead of read_real_time - // to ensure that we will get a monotonic increasing time. - if (mread_real_time(&time, TIMEBASE_SZ) != RTC_POWER) { + if (rc != RTC_POWER) { rc = time_base_to_time(&time, TIMEBASE_SZ); - assert(rc != -1, "aix error at time_base_to_time()"); + assert(rc != -1, "error calling time_base_to_time()"); } - return jlong(time.tb_high) * (1000 * 1000 * 1000) + jlong(time.tb_low); + return jlong(time.tb_high) * NANOSECS_PER_SEC + jlong(time.tb_low); } }
--- a/src/hotspot/os/bsd/jvm_bsd.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/os/bsd/jvm_bsd.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,6 +51,12 @@ case SIGILL: case SIGSEGV: +#if defined(__APPLE__) + /* On Darwin, memory access errors commonly results in SIGBUS instead + * of SIGSEGV. */ + case SIGBUS: +#endif + /* The following signal is used by the VM to dump thread stacks unless ReduceSignalUsage is set, in which case the user is allowed to set his own _native_ handler for this signal; thus, in either case,
--- a/src/hotspot/os/bsd/os_bsd.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/os/bsd/os_bsd.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -2206,7 +2206,7 @@ bool os::release_memory_special(char* base, size_t bytes) { if (MemTracker::tracking_level() > NMT_minimal) { - Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + Tracker tkr(Tracker::release); // detaching the SHM segment will also delete it, see reserve_memory_special() int rslt = shmdt(base); if (rslt == 0) {
--- a/src/hotspot/os/linux/osContainer_linux.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/os/linux/osContainer_linux.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -122,35 +122,40 @@ char file[MAXPATHLEN+1]; char buf[MAXPATHLEN+1]; - if (c != NULL && c->subsystem_path() != NULL) { - strncpy(file, c->subsystem_path(), MAXPATHLEN); - file[MAXPATHLEN-1] = '\0'; - int filelen = strlen(file); - if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) { - log_debug(os, container)("File path too long %s, %s", file, filename); - return OSCONTAINER_ERROR; - } - strncat(file, filename, MAXPATHLEN-filelen); - log_trace(os, container)("Path to %s is %s", filename, file); - fp = fopen(file, "r"); - if (fp != NULL) { - p = fgets(buf, MAXPATHLEN, fp); - if (p != NULL) { - int matched = sscanf(p, scan_fmt, returnval); - if (matched == 1) { - fclose(fp); - return 0; - } else { - log_debug(os, container)("Type %s not found in file %s", - scan_fmt , file); - } + if (c == NULL) { + log_debug(os, container)("subsystem_file_contents: CgroupSubsytem* is NULL"); + return OSCONTAINER_ERROR; + } + if (c->subsystem_path() == NULL) { + log_debug(os, container)("subsystem_file_contents: subsystem path is NULL"); + return OSCONTAINER_ERROR; + } + + strncpy(file, c->subsystem_path(), MAXPATHLEN); + file[MAXPATHLEN-1] = '\0'; + int filelen = strlen(file); + if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) { + log_debug(os, container)("File path too long %s, %s", file, filename); + return OSCONTAINER_ERROR; + } + strncat(file, filename, MAXPATHLEN-filelen); + log_trace(os, container)("Path to %s is %s", filename, file); + fp = fopen(file, "r"); + if (fp != NULL) { + p = fgets(buf, MAXPATHLEN, fp); + if (p != NULL) { + int matched = sscanf(p, scan_fmt, returnval); + if (matched == 1) { + fclose(fp); + return 0; } else { - log_debug(os, container)("Empty file %s", file); + log_debug(os, container)("Type %s not found in file %s", scan_fmt, file); } } else { - log_debug(os, container)("Open of file %s failed, %s", file, - os::strerror(errno)); + log_debug(os, container)("Empty file %s", file); } + } else { + log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno)); } if (fp != NULL) fclose(fp); @@ -273,7 +278,7 @@ else { log_debug(os, container)("Incompatible str containing cgroup and cpuset: %s", p); } - } else if (strstr(p, "cpu,cpuacct") != NULL) { + } else if (strstr(p, "cpu,cpuacct") != NULL || strstr(p, "cpuacct,cpu") != NULL) { int matched = sscanf(p, "%d %d %d:%d %s %s", &mountid, &parentid, @@ -322,8 +327,20 @@ fclose(mntinfo); - if (memory == NULL || cpuset == NULL || cpu == NULL || cpuacct == NULL) { - log_debug(os, container)("Required cgroup subsystems not found"); + if (memory == NULL) { + log_debug(os, container)("Required cgroup memory subsystem not found"); + return; + } + if (cpuset == NULL) { + log_debug(os, container)("Required cgroup cpuset subsystem not found"); + return; + } + if (cpu == NULL) { + log_debug(os, container)("Required cgroup cpu subsystem not found"); + return; + } + if (cpuacct == NULL) { + log_debug(os, container)("Required cgroup cpuacct subsystem not found"); return; } @@ -374,7 +391,7 @@ memory->set_subsystem_path(base); } else if (strstr(controller, "cpuset") != NULL) { cpuset->set_subsystem_path(base); - } else if (strstr(controller, "cpu,cpuacct") != NULL) { + } else if (strstr(controller, "cpu,cpuacct") != NULL || strstr(controller, "cpuacct,cpu") != NULL) { cpu->set_subsystem_path(base); cpuacct->set_subsystem_path(base); } else if (strstr(controller, "cpuacct") != NULL) {
--- a/src/hotspot/os/linux/os_linux.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/os/linux/os_linux.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -3862,7 +3862,7 @@ bool os::release_memory_special(char* base, size_t bytes) { bool res; if (MemTracker::tracking_level() > NMT_minimal) { - Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + Tracker tkr(Tracker::release); res = os::Linux::release_memory_special_impl(base, bytes); if (res) { tkr.record((address)base, bytes);
--- a/src/hotspot/os/windows/perfMemory_windows.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/os/windows/perfMemory_windows.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1840,7 +1840,7 @@ if (MemTracker::tracking_level() > NMT_minimal) { // it does not go through os api, the operation has to record from here - Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + Tracker tkr(Tracker::release); remove_file_mapping(addr); tkr.record((address)addr, bytes); } else {
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1556,8 +1556,6 @@ } if (profile_return() && x->type()->is_object_kind()) { ciMethod* caller = state()->scope()->method(); - ciMethodData* md = caller->method_data_or_null(); - ciProfileData* data = md->bci_to_data(invoke_bci); profile_return_type(x, method(), caller, invoke_bci); } }
--- a/src/hotspot/share/classfile/classLoaderData.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/classfile/classLoaderData.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -80,6 +80,9 @@ #include "trace/tracing.hpp" #endif +volatile size_t ClassLoaderDataGraph::_num_array_classes = 0; +volatile size_t ClassLoaderDataGraph::_num_instance_classes = 0; + ClassLoaderData * ClassLoaderData::_the_null_class_loader_data = NULL; ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool is_anonymous, Dependencies dependencies) : @@ -329,36 +332,36 @@ ClassLoaderData * const from_cld = this; ClassLoaderData * const to_cld = k->class_loader_data(); - // Dependency to the null class loader data doesn't need to be recorded - // because the null class loader data never goes away. - if (to_cld->is_the_null_class_loader_data()) { + // Do not need to record dependency if the dependency is to a class whose + // class loader data is never freed. (i.e. the dependency's class loader + // is one of the three builtin class loaders and the dependency is not + // anonymous.) + if (to_cld->is_permanent_class_loader_data()) { return; } oop to; if (to_cld->is_anonymous()) { + // Just return if an anonymous class is attempting to record a dependency + // to itself. (Note that every anonymous class has its own unique class + // loader data.) + if (to_cld == from_cld) { + return; + } // Anonymous class dependencies are through the mirror. to = k->java_mirror(); } else { to = to_cld->class_loader(); + oop from = from_cld->class_loader(); - // If from_cld is anonymous, even if it's class_loader is a parent of 'to' - // we still have to add it. The class_loader won't keep from_cld alive. - if (!from_cld->is_anonymous()) { - // Check that this dependency isn't from the same or parent class_loader - oop from = from_cld->class_loader(); - - oop curr = from; - while (curr != NULL) { - if (curr == to) { - return; // this class loader is in the parent list, no need to add it. - } - curr = java_lang_ClassLoader::parent(curr); - } + // Just return if this dependency is to a class with the same or a parent + // class_loader. + if (from == to || java_lang_ClassLoader::isAncestor(from, to)) { + return; // this class loader is in the parent list, no need to add it. } } - // It's a dependency we won't find through GC, add it. This is relatively rare + // It's a dependency we won't find through GC, add it. This is relatively rare. // Must handle over GC point. Handle dependency(THREAD, to); from_cld->_dependencies.add(dependency, CHECK); @@ -443,6 +446,11 @@ // Link the new item into the list, making sure the linked class is stable // since the list can be walked without a lock OrderAccess::release_store(&_klasses, k); + if (k->is_array_klass()) { + ClassLoaderDataGraph::inc_array_classes(1); + } else { + ClassLoaderDataGraph::inc_instance_classes(1); + } } if (publicize && k->class_loader_data() != NULL) { @@ -468,9 +476,9 @@ InstanceKlass* try_get_next_class() { assert(SafepointSynchronize::is_at_safepoint(), "only called at safepoint"); - int max_classes = InstanceKlass::number_of_instance_classes(); + size_t max_classes = ClassLoaderDataGraph::num_instance_classes(); assert(max_classes > 0, "should not be called with no instance classes"); - for (int i = 0; i < max_classes; ) { + for (size_t i = 0; i < max_classes; ) { if (_current_class_entry != NULL) { Klass* k = _current_class_entry; @@ -545,6 +553,13 @@ Klass* next = k->next_link(); prev->set_next_link(next); } + + if (k->is_array_klass()) { + ClassLoaderDataGraph::dec_array_classes(1); + } else { + ClassLoaderDataGraph::dec_instance_classes(1); + } + return; } prev = k; @@ -639,9 +654,34 @@ return alive; } +class ReleaseKlassClosure: public KlassClosure { +private: + size_t _instance_class_released; + size_t _array_class_released; +public: + ReleaseKlassClosure() : _instance_class_released(0), _array_class_released(0) { } + + size_t instance_class_released() const { return _instance_class_released; } + size_t array_class_released() const { return _array_class_released; } + + void do_klass(Klass* k) { + if (k->is_array_klass()) { + _array_class_released ++; + } else { + assert(k->is_instance_klass(), "Must be"); + _instance_class_released ++; + InstanceKlass::release_C_heap_structures(InstanceKlass::cast(k)); + } + } +}; + ClassLoaderData::~ClassLoaderData() { // Release C heap structures for all the classes. - classes_do(InstanceKlass::release_C_heap_structures); + ReleaseKlassClosure cl; + classes_do(&cl); + + ClassLoaderDataGraph::dec_array_classes(cl.array_class_released()); + ClassLoaderDataGraph::dec_instance_classes(cl.instance_class_released()); // Release C heap allocated hashtable for all the packages. if (_packages != NULL) { @@ -693,25 +733,37 @@ } } -// Returns true if this class loader data is for the system class loader. +// Returns true if this class loader data is for the app class loader +// or a user defined system class loader. (Note that the class loader +// data may be anonymous.) bool ClassLoaderData::is_system_class_loader_data() const { return SystemDictionary::is_system_class_loader(class_loader()); } // Returns true if this class loader data is for the platform class loader. +// (Note that the class loader data may be anonymous.) bool ClassLoaderData::is_platform_class_loader_data() const { return SystemDictionary::is_platform_class_loader(class_loader()); } -// Returns true if this class loader data is one of the 3 builtin -// (boot, application/system or platform) class loaders. Note, the -// builtin loaders are not freed by a GC. +// Returns true if the class loader for this class loader data is one of +// the 3 builtin (boot application/system or platform) class loaders, +// including a user-defined system class loader. Note that if the class +// loader data is for an anonymous class then it may get freed by a GC +// even if its class loader is one of these loaders. bool ClassLoaderData::is_builtin_class_loader_data() const { - return (is_the_null_class_loader_data() || + return (is_boot_class_loader_data() || SystemDictionary::is_system_class_loader(class_loader()) || SystemDictionary::is_platform_class_loader(class_loader())); } +// Returns true if this class loader data is a class loader data +// that is not ever freed by a GC. It must be one of the builtin +// class loaders and not anonymous. +bool ClassLoaderData::is_permanent_class_loader_data() const { + return is_builtin_class_loader_data() && !is_anonymous(); +} + Metaspace* ClassLoaderData::metaspace_non_null() { // If the metaspace has not been allocated, create a new one. Might want // to create smaller arena for Reflection class loaders also.
--- a/src/hotspot/share/classfile/classLoaderData.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/classfile/classLoaderData.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,6 +80,9 @@ // allocations until class unloading static bool _metaspace_oom; + static volatile size_t _num_instance_classes; + static volatile size_t _num_array_classes; + static ClassLoaderData* add(Handle class_loader, bool anonymous, TRAPS); static void post_class_unload_events(); public: @@ -154,6 +157,15 @@ static void print_creation(outputStream* out, Handle loader, ClassLoaderData* cld, TRAPS); static bool unload_list_contains(const void* x); + + // instance and array class counters + static inline size_t num_instance_classes(); + static inline size_t num_array_classes(); + static inline void inc_instance_classes(size_t count); + static inline void dec_instance_classes(size_t count); + static inline void inc_array_classes(size_t count); + static inline void dec_array_classes(size_t count); + #ifndef PRODUCT static bool contains_loader_data(ClassLoaderData* loader_data); #endif @@ -344,7 +356,15 @@ } bool is_system_class_loader_data() const; bool is_platform_class_loader_data() const; + + // Returns true if this class loader data is for the boot class loader. + // (Note that the class loader data may be anonymous.) + bool is_boot_class_loader_data() const { + return class_loader() == NULL; + } + bool is_builtin_class_loader_data() const; + bool is_permanent_class_loader_data() const; // The Metaspace is created lazily so may be NULL. This // method will allocate a Metaspace if needed.
--- a/src/hotspot/share/classfile/classLoaderData.inline.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/classfile/classLoaderData.inline.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,3 +50,29 @@ } return ClassLoaderDataGraph::add(loader, false, THREAD); } + +size_t ClassLoaderDataGraph::num_instance_classes() { + return _num_instance_classes; +} + +size_t ClassLoaderDataGraph::num_array_classes() { + return _num_array_classes; +} + +void ClassLoaderDataGraph::inc_instance_classes(size_t count) { + Atomic::add(count, &_num_instance_classes); +} + +void ClassLoaderDataGraph::dec_instance_classes(size_t count) { + assert(count <= _num_instance_classes, "Sanity"); + Atomic::sub(count, &_num_instance_classes); +} + +void ClassLoaderDataGraph::inc_array_classes(size_t count) { + Atomic::add(count, &_num_array_classes); +} + +void ClassLoaderDataGraph::dec_array_classes(size_t count) { + assert(count <= _num_array_classes, "Sanity"); + Atomic::sub(count, &_num_array_classes); +}
--- a/src/hotspot/share/classfile/moduleEntry.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/classfile/moduleEntry.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -103,7 +103,11 @@ void set_shared_protection_domain(ClassLoaderData *loader_data, Handle pd); ClassLoaderData* loader_data() const { return _loader_data; } - void set_loader_data(ClassLoaderData* l) { _loader_data = l; } + + void set_loader_data(ClassLoaderData* cld) { + assert(!cld->is_anonymous(), "Unexpected anonymous class loader data"); + _loader_data = cld; + } Symbol* version() const { return _version; } void set_version(Symbol* version);
--- a/src/hotspot/share/code/compiledIC.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/code/compiledIC.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -417,7 +417,7 @@ bool static_bound = info.is_optimized() || (info.cached_metadata() == NULL); #ifdef ASSERT CodeBlob* cb = CodeCache::find_blob_unsafe(info.entry()); - assert (cb->is_compiled(), "must be compiled!"); + assert (cb != NULL && cb->is_compiled(), "must be compiled!"); #endif /* ASSERT */ // This is MT safe if we come from a clean-cache and go through a
--- a/src/hotspot/share/code/nmethod.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/code/nmethod.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -955,6 +955,7 @@ CompiledIC *ic = CompiledIC_at(&iter); // Ok, to lookup references to zombies here CodeBlob *cb = CodeCache::find_blob_unsafe(ic->ic_destination()); + assert(cb != NULL, "destination not in CodeBlob?"); nmethod* nm = cb->as_nmethod_or_null(); if( nm != NULL ) { // Verify that inline caches pointing to both zombie and not_entrant methods are clean @@ -967,6 +968,7 @@ case relocInfo::static_call_type: { CompiledStaticCall *csc = compiledStaticCall_at(iter.reloc()); CodeBlob *cb = CodeCache::find_blob_unsafe(csc->destination()); + assert(cb != NULL, "destination not in CodeBlob?"); nmethod* nm = cb->as_nmethod_or_null(); if( nm != NULL ) { // Verify that inline caches pointing to both zombie and not_entrant methods are clean @@ -2732,7 +2734,7 @@ virtual void verify_resolve_call(address dest) const { CodeBlob* db = CodeCache::find_blob_unsafe(dest); - assert(!db->is_adapter_blob(), "must use stub!"); + assert(db != NULL && !db->is_adapter_blob(), "must use stub!"); } virtual bool is_call_to_interpreted(address dest) const {
--- a/src/hotspot/share/gc/shared/oopStorage.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/gc/shared/oopStorage.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -26,7 +26,9 @@ #include "gc/shared/oopStorage.inline.hpp" #include "gc/shared/oopStorageParState.inline.hpp" #include "logging/log.hpp" +#include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" #include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" #include "runtime/mutex.hpp" @@ -107,7 +109,7 @@ } // Blocks start with an array of BitsPerWord oop entries. That array -// is divided into conceptual BytesPerWord sections of BitsPerWord +// is divided into conceptual BytesPerWord sections of BitsPerByte // entries. Blocks are allocated aligned on section boundaries, for // the convenience of mapping from an entry to the containing block; // see block_for_ptr(). Aligning on section boundary rather than on @@ -130,7 +132,9 @@ _owner(owner), _memory(memory), _active_entry(), - _allocate_entry() + _allocate_entry(), + _deferred_updates_next(NULL), + _release_refcount(0) { STATIC_ASSERT(_data_pos == 0); STATIC_ASSERT(section_size * section_count == ARRAY_SIZE(_data)); @@ -143,6 +147,8 @@ #endif OopStorage::Block::~Block() { + assert(_release_refcount == 0, "deleting block while releasing"); + assert(_deferred_updates_next == NULL, "deleting block with deferred update"); // Clear fields used by block_for_ptr and entry validation, which // might help catch bugs. Volatile to prevent dead-store elimination. const_cast<uintx volatile&>(_allocated_bitmask) = 0; @@ -182,8 +188,24 @@ return bitmask_for_index(get_index(ptr)); } -uintx OopStorage::Block::cmpxchg_allocated_bitmask(uintx new_value, uintx compare_value) { - return Atomic::cmpxchg(new_value, &_allocated_bitmask, compare_value); +// A block is deletable if +// (1) It is empty. +// (2) There is not a release() operation currently operating on it. +// (3) It is not in the deferred updates list. +// The order of tests is important for proper interaction between release() +// and concurrent deletion. +bool OopStorage::Block::is_deletable() const { + return (OrderAccess::load_acquire(&_allocated_bitmask) == 0) && + (OrderAccess::load_acquire(&_release_refcount) == 0) && + (OrderAccess::load_acquire(&_deferred_updates_next) == NULL); +} + +OopStorage::Block* OopStorage::Block::deferred_updates_next() const { + return _deferred_updates_next; +} + +void OopStorage::Block::set_deferred_updates_next(Block* block) { + _deferred_updates_next = block; } bool OopStorage::Block::contains(const oop* ptr) const { @@ -203,7 +225,7 @@ assert(!is_full_bitmask(allocated), "attempt to allocate from full block"); unsigned index = count_trailing_zeros(~allocated); uintx new_value = allocated | bitmask_for_index(index); - uintx fetched = cmpxchg_allocated_bitmask(new_value, allocated); + uintx fetched = Atomic::cmpxchg(new_value, &_allocated_bitmask, allocated); if (fetched == allocated) { return get_pointer(index); // CAS succeeded; return entry for index. } @@ -261,20 +283,6 @@ return NULL; } -bool OopStorage::is_valid_block_locked_or_safepoint(const Block* check_block) const { - assert_locked_or_safepoint(_allocate_mutex); - // For now, simple linear search. Do something more clever if this - // is a performance bottleneck, particularly for allocation_status. - for (const Block* block = _active_list.chead(); - block != NULL; - block = _active_list.next(*block)) { - if (check_block == block) { - return true; - } - } - return false; -} - #ifdef ASSERT void OopStorage::assert_at_safepoint() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); @@ -291,39 +299,49 @@ // kept at the end of the _allocate_list, to make it easy for empty block // deletion to find them. // -// allocate(), release(), and delete_empty_blocks_concurrent() all lock the +// allocate(), and delete_empty_blocks_concurrent() lock the // _allocate_mutex while performing any list modifications. // // allocate() and release() update a block's _allocated_bitmask using CAS -// loops. This prevents loss of updates even though release() may perform -// some updates without any locking. +// loops. This prevents loss of updates even though release() performs +// its updates without any locking. // // allocate() obtains the entry from the first block in the _allocate_list, // and updates that block's _allocated_bitmask to indicate the entry is in // use. If this makes the block full (all entries in use), the block is // removed from the _allocate_list so it won't be considered by future -// allocations until some entries in it are relased. +// allocations until some entries in it are released. // -// release() looks up the block for the entry without locking. Once the block -// has been determined, its _allocated_bitmask needs to be updated, and its -// position in the _allocate_list may need to be updated. There are two -// cases: +// release() is performed lock-free. release() first looks up the block for +// the entry, using address alignment to find the enclosing block (thereby +// avoiding iteration over the _active_list). Once the block has been +// determined, its _allocated_bitmask needs to be updated, and its position in +// the _allocate_list may need to be updated. There are two cases: // // (a) If the block is neither full nor would become empty with the release of // the entry, only its _allocated_bitmask needs to be updated. But if the CAS // update fails, the applicable case may change for the retry. // -// (b) Otherwise, the _allocate_list will also need to be modified. This -// requires locking the _allocate_mutex, and then attempting to CAS the -// _allocated_bitmask. If the CAS fails, the applicable case may change for -// the retry. If the CAS succeeds, then update the _allocate_list according -// to the the state changes. If the block changed from full to not full, then -// it needs to be added to the _allocate_list, for use in future allocations. -// If the block changed from not empty to empty, then it is moved to the end -// of the _allocate_list, for ease of empty block deletion processing. +// (b) Otherwise, the _allocate_list also needs to be modified. This requires +// locking the _allocate_mutex. To keep the release() operation lock-free, +// rather than updating the _allocate_list itself, it instead performs a +// lock-free push of the block onto the _deferred_updates list. Entries on +// that list are processed by allocate() and delete_empty_blocks_XXX(), while +// they already hold the necessary lock. That processing makes the block's +// list state consistent with its current _allocated_bitmask. The block is +// added to the _allocate_list if not already present and the bitmask is not +// full. The block is moved to the end of the _allocated_list if the bitmask +// is empty, for ease of empty block deletion processing. oop* OopStorage::allocate() { MutexLockerEx ml(_allocate_mutex, Mutex::_no_safepoint_check_flag); + // Do some deferred update processing every time we allocate. + // Continue processing deferred updates if _allocate_list is empty, + // in the hope that we'll get a block from that, rather than + // allocating a new block. + while (reduce_deferred_updates() && (_allocate_list.head() == NULL)) {} + + // Use the first block in _allocate_list for the allocation. Block* block = _allocate_list.head(); if (block == NULL) { // No available blocks; make a new one, and add to storage. @@ -331,7 +349,17 @@ MutexUnlockerEx mul(_allocate_mutex, Mutex::_no_safepoint_check_flag); block = Block::new_block(this); } - if (block != NULL) { + if (block == NULL) { + while (_allocate_list.head() == NULL) { + if (!reduce_deferred_updates()) { + // Failed to make new block, no other thread made a block + // available while the mutex was released, and didn't get + // one from a deferred update either, so return failure. + log_info(oopstorage, ref)("%s: failed allocation", name()); + return NULL; + } + } + } else { // Add new block to storage. log_info(oopstorage, blocks)("%s: new block " PTR_FORMAT, name(), p2i(block)); @@ -340,22 +368,14 @@ // to allocate from non-empty blocks, to allow empty blocks to // be deleted. _allocate_list.push_back(*block); - ++_empty_block_count; // Add to front of _active_list, and then record as the head // block, for concurrent iteration protocol. _active_list.push_front(*block); ++_block_count; // Ensure all setup of block is complete before making it visible. OrderAccess::release_store(&_active_head, block); - } else { - log_info(oopstorage, blocks)("%s: failed new block allocation", name()); } block = _allocate_list.head(); - if (block == NULL) { - // Failed to make new block, and no other thread made a block - // available while the mutex was released, so return failure. - return NULL; - } } // Allocate from first block. assert(block != NULL, "invariant"); @@ -363,7 +383,6 @@ if (block->is_empty()) { // Transitioning from empty to not empty. log_debug(oopstorage, blocks)("%s: block not empty " PTR_FORMAT, name(), p2i(block)); - --_empty_block_count; } oop* result = block->allocate(); assert(result != NULL, "allocation failed"); @@ -384,72 +403,115 @@ return Block::block_for_ptr(this, ptr); } -void OopStorage::release_from_block(Block& block, uintx releasing) { - assert(releasing != 0, "invariant"); - uintx allocated = block.allocated_bitmask(); - while (true) { - assert(releasing == (allocated & releasing), "invariant"); - uintx new_value = allocated ^ releasing; - // CAS new_value into block's allocated bitmask, retrying with - // updated allocated bitmask until the CAS succeeds. - uintx fetched; - if (!is_full_bitmask(allocated) && !is_empty_bitmask(new_value)) { - fetched = block.cmpxchg_allocated_bitmask(new_value, allocated); - if (fetched == allocated) return; - } else { - // Need special handling if transitioning from full to not full, - // or from not empty to empty. For those cases, must hold the - // _allocation_mutex when updating the allocated bitmask, to - // ensure the associated list manipulations will be consistent - // with the allocation bitmask that is visible to other threads - // in allocate() or deleting empty blocks. - MutexLockerEx ml(_allocate_mutex, Mutex::_no_safepoint_check_flag); - fetched = block.cmpxchg_allocated_bitmask(new_value, allocated); - if (fetched == allocated) { - // CAS succeeded; handle special cases, which might no longer apply. - if (is_full_bitmask(allocated)) { - // Transitioning from full to not-full; add to _allocate_list. - log_debug(oopstorage, blocks)("%s: block not full " PTR_FORMAT, name(), p2i(&block)); - _allocate_list.push_front(block); - assert(!block.is_full(), "invariant"); // Still not full. - } - if (is_empty_bitmask(new_value)) { - // Transitioning from not-empty to empty; move to end of - // _allocate_list, to make it a deletion candidate. - log_debug(oopstorage, blocks)("%s: block empty " PTR_FORMAT, name(), p2i(&block)); - _allocate_list.unlink(block); - _allocate_list.push_back(block); - ++_empty_block_count; - assert(block.is_empty(), "invariant"); // Still empty. - } - return; // Successful CAS and transitions handled. - } - } - // CAS failed; retry with latest value. - allocated = fetched; +static void log_release_transitions(uintx releasing, + uintx old_allocated, + const OopStorage* owner, + const void* block) { + ResourceMark rm; + Log(oopstorage, blocks) log; + LogStream ls(log.debug()); + if (is_full_bitmask(old_allocated)) { + ls.print_cr("%s: block not full " PTR_FORMAT, owner->name(), p2i(block)); + } + if (releasing == old_allocated) { + ls.print_cr("%s: block empty " PTR_FORMAT, owner->name(), p2i(block)); } } -#ifdef ASSERT -void OopStorage::check_release(const Block* block, const oop* ptr) const { - switch (allocation_status_validating_block(block, ptr)) { - case INVALID_ENTRY: - fatal("Releasing invalid entry: " PTR_FORMAT, p2i(ptr)); - break; +void OopStorage::Block::release_entries(uintx releasing, Block* volatile* deferred_list) { + assert(releasing != 0, "preconditon"); + // Prevent empty block deletion when transitioning to empty. + Atomic::inc(&_release_refcount); - case UNALLOCATED_ENTRY: - fatal("Releasing unallocated entry: " PTR_FORMAT, p2i(ptr)); - break; + // Atomically update allocated bitmask. + uintx old_allocated = _allocated_bitmask; + while (true) { + assert((releasing & ~old_allocated) == 0, "releasing unallocated entries"); + uintx new_value = old_allocated ^ releasing; + uintx fetched = Atomic::cmpxchg(new_value, &_allocated_bitmask, old_allocated); + if (fetched == old_allocated) break; // Successful update. + old_allocated = fetched; // Retry with updated bitmask. + } - case ALLOCATED_ENTRY: - assert(block->contains(ptr), "invariant"); - break; + // Now that the bitmask has been updated, if we have a state transition + // (updated bitmask is empty or old bitmask was full), atomically push + // this block onto the deferred updates list. Some future call to + // reduce_deferred_updates will make any needed changes related to this + // block and _allocate_list. This deferral avoids list updates and the + // associated locking here. + if ((releasing == old_allocated) || is_full_bitmask(old_allocated)) { + // Log transitions. Both transitions are possible in a single update. + if (log_is_enabled(Debug, oopstorage, blocks)) { + log_release_transitions(releasing, old_allocated, _owner, this); + } + // Attempt to claim responsibility for adding this block to the deferred + // list, by setting the link to non-NULL by self-looping. If this fails, + // then someone else has made such a claim and the deferred update has not + // yet been processed and will include our change, so we don't need to do + // anything further. + if (Atomic::replace_if_null(this, &_deferred_updates_next)) { + // Successfully claimed. Push, with self-loop for end-of-list. + Block* head = *deferred_list; + while (true) { + _deferred_updates_next = (head == NULL) ? this : head; + Block* fetched = Atomic::cmpxchg(this, deferred_list, head); + if (fetched == head) break; // Successful update. + head = fetched; // Retry with updated head. + } + log_debug(oopstorage, blocks)("%s: deferred update " PTR_FORMAT, + _owner->name(), p2i(this)); + } + } + // Release hold on empty block deletion. + Atomic::dec(&_release_refcount); +} - default: - ShouldNotReachHere(); +// Process one available deferred update. Returns true if one was processed. +bool OopStorage::reduce_deferred_updates() { + assert_locked_or_safepoint(_allocate_mutex); + // Atomically pop a block off the list, if any available. + // No ABA issue because this is only called by one thread at a time. + // The atomicity is wrto pushes by release(). + Block* block = OrderAccess::load_acquire(&_deferred_updates); + while (true) { + if (block == NULL) return false; + // Try atomic pop of block from list. + Block* tail = block->deferred_updates_next(); + if (block == tail) tail = NULL; // Handle self-loop end marker. + Block* fetched = Atomic::cmpxchg(tail, &_deferred_updates, block); + if (fetched == block) break; // Update successful. + block = fetched; // Retry with updated block. } + block->set_deferred_updates_next(NULL); // Clear tail after updating head. + // Ensure bitmask read after pop is complete, including clearing tail, for + // ordering with release(). Without this, we may be processing a stale + // bitmask state here while blocking a release() operation from recording + // the deferred update needed for its bitmask change. + OrderAccess::storeload(); + // Process popped block. + uintx allocated = block->allocated_bitmask(); + + // Make membership in list consistent with bitmask state. + if ((_allocate_list.ctail() != NULL) && + ((_allocate_list.ctail() == block) || + (_allocate_list.next(*block) != NULL))) { + // Block is in the allocate list. + assert(!is_full_bitmask(allocated), "invariant"); + } else if (!is_full_bitmask(allocated)) { + // Block is not in the allocate list, but now should be. + _allocate_list.push_front(*block); + } // Else block is full and not in list, which is correct. + + // Move empty block to end of list, for possible deletion. + if (is_empty_bitmask(allocated)) { + _allocate_list.unlink(*block); + _allocate_list.push_back(*block); + } + + log_debug(oopstorage, blocks)("%s: processed deferred update " PTR_FORMAT, + name(), p2i(block)); + return true; // Processed one pending update. } -#endif // ASSERT inline void check_release_entry(const oop* entry) { assert(entry != NULL, "Releasing NULL"); @@ -459,9 +521,9 @@ void OopStorage::release(const oop* ptr) { check_release_entry(ptr); Block* block = find_block_or_null(ptr); - check_release(block, ptr); + assert(block != NULL, "%s: invalid release " PTR_FORMAT, name(), p2i(ptr)); log_info(oopstorage, ref)("%s: released " PTR_FORMAT, name(), p2i(ptr)); - release_from_block(*block, block->bitmask_for_entry(ptr)); + block->release_entries(block->bitmask_for_entry(ptr), &_deferred_updates); Atomic::dec(&_allocation_count); } @@ -470,15 +532,15 @@ while (i < size) { check_release_entry(ptrs[i]); Block* block = find_block_or_null(ptrs[i]); - check_release(block, ptrs[i]); + assert(block != NULL, "%s: invalid release " PTR_FORMAT, name(), p2i(ptrs[i])); log_info(oopstorage, ref)("%s: released " PTR_FORMAT, name(), p2i(ptrs[i])); size_t count = 0; uintx releasing = 0; for ( ; i < size; ++i) { const oop* entry = ptrs[i]; + check_release_entry(entry); // If entry not in block, finish block and resume outer loop with entry. if (!block->contains(entry)) break; - check_release_entry(entry); // Add entry to releasing bitmap. log_info(oopstorage, ref)("%s: released " PTR_FORMAT, name(), p2i(entry)); uintx entry_bitmask = block->bitmask_for_entry(entry); @@ -488,7 +550,7 @@ ++count; } // Release the contiguous entries that are in block. - release_from_block(*block, releasing); + block->release_entries(releasing, &_deferred_updates); Atomic::sub(count, &_allocation_count); } } @@ -506,11 +568,11 @@ _active_list(&Block::get_active_entry), _allocate_list(&Block::get_allocate_entry), _active_head(NULL), + _deferred_updates(NULL), _allocate_mutex(allocate_mutex), _active_mutex(active_mutex), _allocation_count(0), _block_count(0), - _empty_block_count(0), _concurrent_iteration_active(false) { assert(_active_mutex->rank() < _allocate_mutex->rank(), @@ -529,6 +591,10 @@ OopStorage::~OopStorage() { Block* block; + while ((block = _deferred_updates) != NULL) { + _deferred_updates = block->deferred_updates_next(); + block->set_deferred_updates_next(NULL); + } while ((block = _allocate_list.head()) != NULL) { _allocate_list.unlink(*block); } @@ -539,43 +605,47 @@ FREE_C_HEAP_ARRAY(char, _name); } -void OopStorage::delete_empty_blocks_safepoint(size_t retain) { +void OopStorage::delete_empty_blocks_safepoint() { assert_at_safepoint(); + // Process any pending release updates, which may make more empty + // blocks available for deletion. + while (reduce_deferred_updates()) {} // Don't interfere with a concurrent iteration. if (_concurrent_iteration_active) return; - // Compute the number of blocks to remove, to minimize volatile accesses. - size_t empty_blocks = _empty_block_count; - if (retain < empty_blocks) { - size_t remove_count = empty_blocks - retain; - // Update volatile counters once. - _block_count -= remove_count; - _empty_block_count -= remove_count; - do { - const Block* block = _allocate_list.ctail(); - assert(block != NULL, "invariant"); - assert(block->is_empty(), "invariant"); - // Remove block from lists, and delete it. - _active_list.unlink(*block); - _allocate_list.unlink(*block); - delete_empty_block(*block); - } while (--remove_count > 0); - // Update _active_head, in case current value was in deleted set. - _active_head = _active_list.head(); + // Delete empty (and otherwise deletable) blocks from end of _allocate_list. + for (const Block* block = _allocate_list.ctail(); + (block != NULL) && block->is_deletable(); + block = _allocate_list.ctail()) { + _active_list.unlink(*block); + _allocate_list.unlink(*block); + delete_empty_block(*block); + --_block_count; } + // Update _active_head, in case current value was in deleted set. + _active_head = _active_list.head(); } -void OopStorage::delete_empty_blocks_concurrent(size_t retain) { +void OopStorage::delete_empty_blocks_concurrent() { MutexLockerEx ml(_allocate_mutex, Mutex::_no_safepoint_check_flag); // Other threads could be adding to the empty block count while we // release the mutex across the block deletions. Set an upper bound // on how many blocks we'll try to release, so other threads can't // cause an unbounded stay in this function. - if (_empty_block_count <= retain) return; - size_t limit = _empty_block_count - retain; - for (size_t i = 0; (i < limit) && (retain < _empty_block_count); ++i) { + size_t limit = _block_count; + + for (size_t i = 0; i < limit; ++i) { + // Additional updates might become available while we dropped the + // lock. But limit number processed to limit lock duration. + reduce_deferred_updates(); + const Block* block = _allocate_list.ctail(); - assert(block != NULL, "invariant"); - assert(block->is_empty(), "invariant"); + if ((block == NULL) || !block->is_deletable()) { + // No block to delete, so done. There could be more pending + // deferred updates that could give us more work to do; deal with + // that in some later call, to limit lock duration here. + return; + } + { MutexLockerEx aml(_active_mutex, Mutex::_no_safepoint_check_flag); // Don't interfere with a concurrent iteration. @@ -589,28 +659,31 @@ } // Remove block from _allocate_list and delete it. _allocate_list.unlink(*block); - --_empty_block_count; // Release mutex while deleting block. MutexUnlockerEx ul(_allocate_mutex, Mutex::_no_safepoint_check_flag); delete_empty_block(*block); } } -OopStorage::EntryStatus -OopStorage::allocation_status_validating_block(const Block* block, - const oop* ptr) const { - MutexLockerEx ml(_allocate_mutex, Mutex::_no_safepoint_check_flag); - if ((block == NULL) || !is_valid_block_locked_or_safepoint(block)) { - return INVALID_ENTRY; - } else if ((block->allocated_bitmask() & block->bitmask_for_entry(ptr)) != 0) { - return ALLOCATED_ENTRY; - } else { - return UNALLOCATED_ENTRY; +OopStorage::EntryStatus OopStorage::allocation_status(const oop* ptr) const { + const Block* block = find_block_or_null(ptr); + if (block != NULL) { + // Verify block is a real block. For now, simple linear search. + // Do something more clever if this is a performance bottleneck. + MutexLockerEx ml(_allocate_mutex, Mutex::_no_safepoint_check_flag); + for (const Block* check_block = _active_list.chead(); + check_block != NULL; + check_block = _active_list.next(*check_block)) { + if (check_block == block) { + if ((block->allocated_bitmask() & block->bitmask_for_entry(ptr)) != 0) { + return ALLOCATED_ENTRY; + } else { + return UNALLOCATED_ENTRY; + } + } + } } -} - -OopStorage::EntryStatus OopStorage::allocation_status(const oop* ptr) const { - return allocation_status_validating_block(find_block_or_null(ptr), ptr); + return INVALID_ENTRY; } size_t OopStorage::allocation_count() const { @@ -621,10 +694,6 @@ return _block_count; } -size_t OopStorage::empty_block_count() const { - return _empty_block_count; -} - size_t OopStorage::total_memory_usage() const { size_t total_size = sizeof(OopStorage); total_size += strlen(name()) + 1; @@ -690,17 +759,12 @@ void OopStorage::print_on(outputStream* st) const { size_t allocations = _allocation_count; size_t blocks = _block_count; - size_t empties = _empty_block_count; - // Comparison is being careful about racy accesses. - size_t used = (blocks < empties) ? 0 : (blocks - empties); double data_size = section_size * section_count; - double alloc_percentage = percent_of((double)allocations, used * data_size); + double alloc_percentage = percent_of((double)allocations, blocks * data_size); - st->print("%s: " SIZE_FORMAT " entries in " SIZE_FORMAT " blocks (%.F%%), " - SIZE_FORMAT " empties, " SIZE_FORMAT " bytes", - name(), allocations, used, alloc_percentage, - empties, total_memory_usage()); + st->print("%s: " SIZE_FORMAT " entries in " SIZE_FORMAT " blocks (%.F%%), " SIZE_FORMAT " bytes", + name(), allocations, blocks, alloc_percentage, total_memory_usage()); if (_concurrent_iteration_active) { st->print(", concurrent iteration active"); }
--- a/src/hotspot/share/gc/shared/oopStorage.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/gc/shared/oopStorage.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -84,10 +84,6 @@ // The number of blocks of entries. Useful for sizing parallel iteration. size_t block_count() const; - // The number of blocks with no allocated entries. Useful for sizing - // parallel iteration and scheduling block deletion. - size_t empty_block_count() const; - // Total number of blocks * memory allocation per block, plus // bookkeeping overhead, including this storage object. size_t total_memory_usage() const; @@ -107,14 +103,13 @@ // postcondition: *result == NULL. oop* allocate(); - // Deallocates ptr, after setting its value to NULL. Locks _allocate_mutex. + // Deallocates ptr. No locking. // precondition: ptr is a valid allocated entry. // precondition: *ptr == NULL. void release(const oop* ptr); // Releases all the ptrs. Possibly faster than individual calls to - // release(oop*). Best if ptrs is sorted by address. Locks - // _allocate_mutex. + // release(oop*). Best if ptrs is sorted by address. No locking. // precondition: All elements of ptrs are valid allocated entries. // precondition: *ptrs[i] == NULL, for i in [0,size). void release(const oop* const* ptrs, size_t size); @@ -160,8 +155,8 @@ // Block cleanup functions are for the exclusive use of the GC. // Both stop deleting if there is an in-progress concurrent iteration. // Concurrent deletion locks both the allocate_mutex and the active_mutex. - void delete_empty_blocks_safepoint(size_t retain = 1); - void delete_empty_blocks_concurrent(size_t retain = 1); + void delete_empty_blocks_safepoint(); + void delete_empty_blocks_concurrent(); // Debugging and logging support. const char* name() const; @@ -231,6 +226,7 @@ BlockList _active_list; BlockList _allocate_list; Block* volatile _active_head; + Block* volatile _deferred_updates; Mutex* _allocate_mutex; Mutex* _active_mutex; @@ -238,16 +234,12 @@ // Counts are volatile for racy unlocked accesses. volatile size_t _allocation_count; volatile size_t _block_count; - volatile size_t _empty_block_count; // mutable because this gets set even for const iteration. mutable bool _concurrent_iteration_active; Block* find_block_or_null(const oop* ptr) const; - bool is_valid_block_locked_or_safepoint(const Block* block) const; - EntryStatus allocation_status_validating_block(const Block* block, const oop* ptr) const; - void check_release(const Block* block, const oop* ptr) const NOT_DEBUG_RETURN; - void release_from_block(Block& block, uintx release_bitmask); void delete_empty_block(const Block& block); + bool reduce_deferred_updates(); static void assert_at_safepoint() NOT_DEBUG_RETURN;
--- a/src/hotspot/share/gc/shared/oopStorage.inline.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/gc/shared/oopStorage.inline.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -44,6 +44,8 @@ void* _memory; // Unaligned storage containing block. BlockEntry _active_entry; BlockEntry _allocate_entry; + Block* volatile _deferred_updates_next; + volatile uintx _release_refcount; Block(const OopStorage* owner, void* memory); ~Block(); @@ -75,7 +77,10 @@ bool is_full() const; bool is_empty() const; uintx allocated_bitmask() const; - uintx cmpxchg_allocated_bitmask(uintx new_value, uintx compare_value); + bool is_deletable() const; + + Block* deferred_updates_next() const; + void set_deferred_updates_next(Block* new_next); bool contains(const oop* ptr) const; @@ -86,6 +91,8 @@ static Block* new_block(const OopStorage* owner); static void delete_block(const Block& block); + void release_entries(uintx releasing, Block* volatile* deferred_list); + template<typename F> bool iterate(F f); template<typename F> bool iterate(F f) const; }; // class Block
--- a/src/hotspot/share/interpreter/bytecodeInterpreter.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/interpreter/bytecodeInterpreter.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -594,10 +594,6 @@ VERIFY_OOP(rcvr); } #endif -// #define HACK -#ifdef HACK - bool interesting = false; -#endif // HACK /* QQQ this should be a stack method so we don't know actual direction */ guarantee(istate->msg() == initialize || @@ -649,19 +645,6 @@ os::breakpoint(); } -#ifdef HACK - { - ResourceMark rm; - char *method_name = istate->method()->name_and_sig_as_C_string(); - if (strstr(method_name, "runThese$TestRunner.run()V") != NULL) { - tty->print_cr("entering: depth %d bci: %d", - (istate->_stack_base - istate->_stack), - istate->_bcp - istate->_method->code_base()); - interesting = true; - } - } -#endif // HACK - // Lock method if synchronized. if (METHOD->is_synchronized()) { // oop rcvr = locals[0].j.r; @@ -793,18 +776,6 @@ // resume os::breakpoint(); } -#ifdef HACK - { - ResourceMark rm; - char *method_name = istate->method()->name_and_sig_as_C_string(); - if (strstr(method_name, "runThese$TestRunner.run()V") != NULL) { - tty->print_cr("resume: depth %d bci: %d", - (istate->_stack_base - istate->_stack) , - istate->_bcp - istate->_method->code_base()); - interesting = true; - } - } -#endif // HACK // returned from a java call, continue executing. if (THREAD->pop_frame_pending() && !THREAD->pop_frame_in_process()) { goto handle_Pop_Frame;
--- a/src/hotspot/share/logging/logTag.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/logging/logTag.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -66,6 +66,7 @@ LOG_TAG(exceptions) \ LOG_TAG(exit) \ LOG_TAG(fingerprint) \ + LOG_TAG(free) \ LOG_TAG(freelist) \ LOG_TAG(gc) \ LOG_TAG(handshake) \ @@ -85,6 +86,7 @@ LOG_TAG(load) /* Trace all classes loaded */ \ LOG_TAG(loader) \ LOG_TAG(logging) \ + LOG_TAG(malloc) \ LOG_TAG(mark) \ LOG_TAG(marking) \ LOG_TAG(membername) \
--- a/src/hotspot/share/memory/allocation.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/memory/allocation.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -210,18 +210,6 @@ } #endif // ASSERT - -void trace_heap_malloc(size_t size, const char* name, void* p) { - // A lock is not needed here - tty uses a lock internally - tty->print_cr("Heap malloc " INTPTR_FORMAT " " SIZE_FORMAT " %s", p2i(p), size, name == NULL ? "" : name); -} - - -void trace_heap_free(void* p) { - // A lock is not needed here - tty uses a lock internally - tty->print_cr("Heap free " INTPTR_FORMAT, p2i(p)); -} - //-------------------------------------------------------------------------------------- // Non-product code
--- a/src/hotspot/share/memory/allocation.inline.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/memory/allocation.inline.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -33,9 +33,6 @@ // Explicit C-heap memory management -void trace_heap_malloc(size_t size, const char* name, void *p); -void trace_heap_free(void *p); - #ifndef PRODUCT // Increments unsigned long value for statistics (not atomic on MP). inline void inc_stat_counter(volatile julong* dest, julong add_value) { @@ -56,9 +53,6 @@ const NativeCallStack& stack, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) { char* p = (char*) os::malloc(size, flags, stack); - #ifdef ASSERT - if (PrintMallocFree) trace_heap_malloc(size, "AllocateHeap", p); - #endif if (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) { vm_exit_out_of_memory(size, OOM_MALLOC_ERROR, "AllocateHeap"); } @@ -73,9 +67,6 @@ ALWAYSINLINE char* ReallocateHeap(char *old, size_t size, MEMFLAGS flag, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) { char* p = (char*) os::realloc(old, size, flag, CURRENT_PC); - #ifdef ASSERT - if (PrintMallocFree) trace_heap_malloc(size, "ReallocateHeap", p); - #endif if (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) { vm_exit_out_of_memory(size, OOM_MALLOC_ERROR, "ReallocateHeap"); } @@ -83,20 +74,13 @@ } inline void FreeHeap(void* p) { - #ifdef ASSERT - if (PrintMallocFree) trace_heap_free(p); - #endif os::free(p); } template <MEMFLAGS F> void* CHeapObj<F>::operator new(size_t size, const NativeCallStack& stack) throw() { - void* p = (void*)AllocateHeap(size, F, stack); -#ifdef ASSERT - if (PrintMallocFree) trace_heap_malloc(size, "CHeapObj-new", p); -#endif - return p; + return (void*)AllocateHeap(size, F, stack); } template <MEMFLAGS F> void* CHeapObj<F>::operator new(size_t size) throw() { @@ -104,14 +88,9 @@ } template <MEMFLAGS F> void* CHeapObj<F>::operator new (size_t size, - const std::nothrow_t& nothrow_constant, const NativeCallStack& stack) throw() { - void* p = (void*)AllocateHeap(size, F, stack, - AllocFailStrategy::RETURN_NULL); -#ifdef ASSERT - if (PrintMallocFree) trace_heap_malloc(size, "CHeapObj-new", p); -#endif - return p; - } + const std::nothrow_t& nothrow_constant, const NativeCallStack& stack) throw() { + return (void*)AllocateHeap(size, F, stack, AllocFailStrategy::RETURN_NULL); +} template <MEMFLAGS F> void* CHeapObj<F>::operator new (size_t size, const std::nothrow_t& nothrow_constant) throw() {
--- a/src/hotspot/share/memory/arena.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/memory/arena.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -299,23 +299,11 @@ // dynamic memory type binding void* Arena::operator new(size_t size, MEMFLAGS flags) throw() { -#ifdef ASSERT - void* p = (void*)AllocateHeap(size, flags, CALLER_PC); - if (PrintMallocFree) trace_heap_malloc(size, "Arena-new", p); - return p; -#else return (void *) AllocateHeap(size, flags, CALLER_PC); -#endif } void* Arena::operator new(size_t size, const std::nothrow_t& nothrow_constant, MEMFLAGS flags) throw() { -#ifdef ASSERT - void* p = os::malloc(size, flags, CALLER_PC); - if (PrintMallocFree) trace_heap_malloc(size, "Arena-new", p); - return p; -#else - return os::malloc(size, flags, CALLER_PC); -#endif + return (void*)AllocateHeap(size, flags, CALLER_PC, AllocFailStrategy::RETURN_NULL); } void Arena::operator delete(void* p) {
--- a/src/hotspot/share/oops/instanceKlass.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/oops/instanceKlass.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -124,8 +124,6 @@ #endif // ndef DTRACE_ENABLED -volatile int InstanceKlass::_total_instanceKlass_count = 0; - static inline bool is_class_loader(const Symbol* class_name, const ClassFileParser& parser) { assert(class_name != NULL, "invariant"); @@ -193,8 +191,6 @@ // Add all classes to our internal class loader list here, // including classes in the bootstrap (NULL) class loader. loader_data->add_class(ik, publicize); - Atomic::inc(&_total_instanceKlass_count); - return ik; } @@ -2241,9 +2237,6 @@ // class can't be referenced anymore). if (_array_name != NULL) _array_name->decrement_refcount(); if (_source_debug_extension != NULL) FREE_C_HEAP_ARRAY(char, _source_debug_extension); - - assert(_total_instanceKlass_count >= 1, "Sanity check"); - Atomic::dec(&_total_instanceKlass_count); } void InstanceKlass::set_source_debug_extension(const char* array, int length) {
--- a/src/hotspot/share/oops/instanceKlass.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/oops/instanceKlass.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -135,10 +135,7 @@ initialization_error // error happened during initialization }; - static int number_of_instance_classes() { return _total_instanceKlass_count; } - private: - static volatile int _total_instanceKlass_count; static InstanceKlass* allocate_instance_klass(const ClassFileParser& parser, TRAPS); protected:
--- a/src/hotspot/share/opto/escape.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/opto/escape.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -3226,7 +3226,7 @@ n->Opcode() == Op_EncodeISOArray) { // get the memory projection n = n->find_out_with(Op_SCMemProj); - assert(n->Opcode() == Op_SCMemProj, "memory projection required"); + assert(n != NULL && n->Opcode() == Op_SCMemProj, "memory projection required"); } else { assert(n->is_Mem(), "memory node required."); Node *addr = n->in(MemNode::Address); @@ -3250,7 +3250,7 @@ } else if (n->is_LoadStore()) { // get the memory projection n = n->find_out_with(Op_SCMemProj); - assert(n->Opcode() == Op_SCMemProj, "memory projection required"); + assert(n != NULL && n->Opcode() == Op_SCMemProj, "memory projection required"); } } // push user on appropriate worklist
--- a/src/hotspot/share/opto/parse2.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/opto/parse2.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2264,7 +2264,7 @@ ciMethodData* methodData = method()->method_data(); if (!methodData->is_mature()) break; ciProfileData* data = methodData->bci_to_data(bci()); - assert( data->is_JumpData(), "" ); + assert(data != NULL && data->is_JumpData(), "need JumpData for taken branch"); int taken = ((ciJumpData*)data)->taken(); taken = method()->scale_count(taken); target_block->set_count(taken);
--- a/src/hotspot/share/opto/parseHelper.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/opto/parseHelper.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -459,7 +459,7 @@ ciMethodData* md = method()->method_data(); assert(md != NULL, "expected valid ciMethodData"); ciProfileData* data = md->bci_to_data(cur_bci); - assert(data->is_JumpData(), "need JumpData for taken branch"); + assert(data != NULL && data->is_JumpData(), "need JumpData for taken branch"); increment_md_counter_at(md, data, JumpData::taken_offset()); } @@ -470,6 +470,7 @@ ciMethodData* md = method()->method_data(); if (osr_site) { ciProfileData* data = md->bci_to_data(cur_bci); + assert(data != NULL && data->is_JumpData(), "need JumpData for taken branch"); int limit = (CompileThreshold * (OnStackReplacePercentage - InterpreterProfilePercentage)) / 100; test_for_osr_md_counter_at(md, data, JumpData::taken_offset(), limit); @@ -495,7 +496,7 @@ ciMethodData* md = method()->method_data(); assert(md != NULL, "expected valid ciMethodData"); ciProfileData* data = md->bci_to_data(bci()); - assert(data->is_BranchData(), "need BranchData for not taken branch"); + assert(data != NULL && data->is_BranchData(), "need BranchData for not taken branch"); increment_md_counter_at(md, data, BranchData::not_taken_offset()); } @@ -526,7 +527,7 @@ ciMethodData* md = method()->method_data(); assert(md != NULL, "expected valid ciMethodData"); ciProfileData* data = md->bci_to_data(bci()); - assert(data->is_CounterData(), "need CounterData for not taken branch"); + assert(data != NULL && data->is_CounterData(), "need CounterData for not taken branch"); increment_md_counter_at(md, data, CounterData::count_offset()); } @@ -537,7 +538,7 @@ ciMethodData* md = method()->method_data(); assert(md != NULL, "expected valid ciMethodData"); ciProfileData* data = md->bci_to_data(bci()); - assert(data->is_ReceiverTypeData(), "need ReceiverTypeData here"); + assert(data != NULL && data->is_ReceiverTypeData(), "need ReceiverTypeData here"); // Skip if we aren't tracking receivers if (TypeProfileWidth < 1) { @@ -568,7 +569,7 @@ ciMethodData* md = method()->method_data(); assert(md != NULL, "expected valid ciMethodData"); ciProfileData* data = md->bci_to_data(bci()); - assert(data->is_RetData(), "need RetData for ret"); + assert(data != NULL && data->is_RetData(), "need RetData for ret"); ciRetData* ret_data = (ciRetData*)data->as_RetData(); // Look for the target_bci is already in the table @@ -601,7 +602,7 @@ ciMethodData* md = method()->method_data(); assert(md != NULL, "expected valid ciMethodData"); ciProfileData* data = md->bci_to_data(bci()); - assert(data->is_BitData(), "need BitData for checkcast"); + assert(data != NULL && data->is_BitData(), "need BitData for checkcast"); set_md_flag_at(md, data, BitData::null_seen_byte_constant()); } @@ -613,7 +614,7 @@ assert(md != NULL, "expected valid ciMethodData"); ciProfileData* data = md->bci_to_data(bci()); - assert(data->is_MultiBranchData(), "need MultiBranchData for switch case"); + assert(data != NULL && data->is_MultiBranchData(), "need MultiBranchData for switch case"); if (table_index >= 0) { increment_md_counter_at(md, data, MultiBranchData::case_count_offset(table_index)); } else {
--- a/src/hotspot/share/prims/jvmtiExport.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/prims/jvmtiExport.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -597,12 +597,10 @@ } void JvmtiExport::enter_early_start_phase() { - JvmtiManageCapabilities::recompute_always_capabilities(); set_early_vmstart_recorded(true); } void JvmtiExport::enter_start_phase() { - JvmtiManageCapabilities::recompute_always_capabilities(); JvmtiEnvBase::set_phase(JVMTI_PHASE_START); }
--- a/src/hotspot/share/prims/jvmtiGetLoadedClasses.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/prims/jvmtiGetLoadedClasses.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,9 @@ #include "prims/jvmtiGetLoadedClasses.hpp" #include "runtime/thread.hpp" #include "utilities/stack.inline.hpp" +#if INCLUDE_ALL_GCS +#include "gc/g1/g1SATBCardTableModRefBS.hpp" +#endif // The closure for GetLoadedClasses @@ -38,6 +41,20 @@ JvmtiEnv* _env; Thread* _cur_thread; +// Tell the GC to keep this klass alive +static void ensure_klass_alive(oop o) { + // A klass that was previously considered dead can be looked up in the + // CLD/SD, and its _java_mirror or _class_loader can be stored in a root + // or a reachable object making it alive again. The SATB part of G1 needs + // to get notified about this potential resurrection, otherwise the marking + // might not find the object. +#if INCLUDE_ALL_GCS + if (UseG1GC && o != NULL) { + G1SATBCardTableModRefBS::enqueue(o); + } +#endif +} + public: LoadedClassesClosure(Thread* thread, JvmtiEnv* env) : _cur_thread(thread), _env(env) { assert(_cur_thread == Thread::current(), "must be current thread"); @@ -46,6 +63,7 @@ void do_klass(Klass* k) { // Collect all jclasses _classStack.push((jclass) _env->jni_reference(Handle(_cur_thread, k->java_mirror()))); + ensure_klass_alive(k->java_mirror()); } int extract(jclass* result_list) {
--- a/src/hotspot/share/prims/jvmtiManageCapabilities.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/prims/jvmtiManageCapabilities.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -57,9 +57,6 @@ void JvmtiManageCapabilities::initialize() { always_capabilities = init_always_capabilities(); - if (JvmtiEnv::get_phase() != JVMTI_PHASE_ONLOAD) { - recompute_always_capabilities(); - } onload_capabilities = init_onload_capabilities(); always_solo_capabilities = init_always_solo_capabilities(); onload_solo_capabilities = init_onload_solo_capabilities(); @@ -68,19 +65,6 @@ memset(&acquired_capabilities, 0, sizeof(acquired_capabilities)); } -// if the capability sets are initialized in the onload phase then -// it happens before class data sharing (CDS) is initialized. If it -// turns out that CDS gets disabled then we must adjust the always -// capabilities. To ensure a consistent view of the capabililties -// anything we add here should already be in the onload set. -void JvmtiManageCapabilities::recompute_always_capabilities() { - if (!UseSharedSpaces) { - jvmtiCapabilities jc = always_capabilities; - jc.can_generate_all_class_hook_events = 1; - always_capabilities = jc; - } -} - // corresponding init functions jvmtiCapabilities JvmtiManageCapabilities::init_always_capabilities() { @@ -94,6 +78,7 @@ jc.can_get_synthetic_attribute = 1; jc.can_get_monitor_info = 1; jc.can_get_constant_pool = 1; + jc.can_generate_all_class_hook_events = 1; jc.can_generate_monitor_events = 1; jc.can_generate_garbage_collection_events = 1; jc.can_generate_compiled_method_load_events = 1; @@ -126,7 +111,6 @@ jc.can_get_source_debug_extension = 1; jc.can_access_local_variables = 1; jc.can_maintain_original_method_order = 1; - jc.can_generate_all_class_hook_events = 1; jc.can_generate_single_step_events = 1; jc.can_generate_exception_events = 1; jc.can_generate_frame_pop_events = 1;
--- a/src/hotspot/share/prims/jvmtiManageCapabilities.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/prims/jvmtiManageCapabilities.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -64,9 +64,6 @@ public: static void initialize(); - // may have to adjust always capabilities when VM initialization has completed - static void recompute_always_capabilities(); - // queries and actions static void get_potential_capabilities(const jvmtiCapabilities *current, const jvmtiCapabilities *prohibited,
--- a/src/hotspot/share/runtime/arguments.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/runtime/arguments.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -514,7 +514,9 @@ { "DeferThrSuspendLoopCount", JDK_Version::jdk(10), JDK_Version::jdk(11), JDK_Version::jdk(12) }, { "DeferPollingPageLoopCount", JDK_Version::jdk(10), JDK_Version::jdk(11), JDK_Version::jdk(12) }, { "IgnoreUnverifiableClassesDuringDump", JDK_Version::jdk(10), JDK_Version::undefined(), JDK_Version::undefined() }, - { "CheckEndorsedAndExtDirs", JDK_Version::jdk(10), JDK_Version::undefined(), JDK_Version::undefined() }, + { "CheckEndorsedAndExtDirs", JDK_Version::jdk(10), JDK_Version::undefined(), JDK_Version::undefined() }, + { "CompilerThreadHintNoPreempt", JDK_Version::jdk(11), JDK_Version::jdk(12), JDK_Version::jdk(13) }, + { "VMThreadHintNoPreempt", JDK_Version::jdk(11), JDK_Version::jdk(12), JDK_Version::jdk(13) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: { "DefaultMaxRAMFraction", JDK_Version::jdk(8), JDK_Version::undefined(), JDK_Version::undefined() }, @@ -527,6 +529,8 @@ { "ConvertYieldToSleep", JDK_Version::jdk(9), JDK_Version::jdk(10), JDK_Version::jdk(11) }, { "MinSleepInterval", JDK_Version::jdk(9), JDK_Version::jdk(10), JDK_Version::jdk(11) }, { "CheckAssertionStatusDirectives",JDK_Version::undefined(), JDK_Version::jdk(11), JDK_Version::jdk(12) }, + { "PrintMallocFree", JDK_Version::undefined(), JDK_Version::jdk(11), JDK_Version::jdk(12) }, + { "PrintMalloc", JDK_Version::undefined(), JDK_Version::jdk(11), JDK_Version::jdk(12) }, { "PermSize", JDK_Version::undefined(), JDK_Version::jdk(8), JDK_Version::undefined() }, { "MaxPermSize", JDK_Version::undefined(), JDK_Version::jdk(8), JDK_Version::undefined() }, { "SharedReadWriteSize", JDK_Version::undefined(), JDK_Version::jdk(10), JDK_Version::undefined() },
--- a/src/hotspot/share/runtime/compilationPolicy.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/runtime/compilationPolicy.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "classfile/classLoaderData.inline.hpp" #include "code/compiledIC.hpp" #include "code/nmethod.hpp" #include "code/scopeDesc.hpp" @@ -312,10 +313,10 @@ // and hence GC's will not be going on, all Java mutators are suspended // at this point and hence SystemDictionary_lock is also not needed. assert(SafepointSynchronize::is_at_safepoint(), "can only be executed at a safepoint"); - int nclasses = InstanceKlass::number_of_instance_classes(); - int classes_per_tick = nclasses * (CounterDecayMinIntervalLength * 1e-3 / + size_t nclasses = ClassLoaderDataGraph::num_instance_classes(); + size_t classes_per_tick = nclasses * (CounterDecayMinIntervalLength * 1e-3 / CounterHalfLifeTime); - for (int i = 0; i < classes_per_tick; i++) { + for (size_t i = 0; i < classes_per_tick; i++) { InstanceKlass* k = ClassLoaderDataGraph::try_get_next_class(); if (k != NULL) { k->methods_do(do_method);
--- a/src/hotspot/share/runtime/globals.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/runtime/globals.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -893,18 +893,12 @@ develop(bool, TraceJavaAssertions, false, \ "Trace java language assertions") \ \ - notproduct(bool, PrintMallocFree, false, \ - "Trace calls to C heap malloc/free allocation") \ - \ notproduct(bool, VerifyCodeCache, false, \ "Verify code cache on memory allocation/deallocation") \ \ develop(bool, UseMallocOnly, false, \ "Use only malloc/free for allocation (no resource area/arena)") \ \ - develop(bool, PrintMalloc, false, \ - "Print all malloc/free calls") \ - \ develop(bool, PrintMallocStatistics, false, \ "Print malloc/free statistics") \ \ @@ -3545,7 +3539,7 @@ "(-1 means no change)") \ range(-1, 127) \ \ - product(bool, CompilerThreadHintNoPreempt, true, \ + product(bool, CompilerThreadHintNoPreempt, false, \ "(Solaris only) Give compiler threads an extra quanta") \ \ product(bool, VMThreadHintNoPreempt, false, \
--- a/src/hotspot/share/runtime/memprofiler.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/runtime/memprofiler.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "classfile/classLoaderData.inline.hpp" #include "classfile/systemDictionary.hpp" #include "code/codeCache.hpp" #include "gc/shared/collectedHeap.inline.hpp" @@ -116,10 +117,10 @@ } // Print trace line in log - fprintf(_log_fp, "%6.1f,%5d,%5d," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",", + fprintf(_log_fp, "%6.1f,%5d," SIZE_FORMAT_W(5) "," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",", os::elapsedTime(), jtiwh.length(), - InstanceKlass::number_of_instance_classes(), + ClassLoaderDataGraph::num_instance_classes(), Universe::heap()->used() / K, Universe::heap()->capacity() / K); }
--- a/src/hotspot/share/runtime/mutexLocker.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/runtime/mutexLocker.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -253,10 +253,10 @@ // of some places which hold other locks while releasing a handle, including // the Patching_lock, which is of "special" rank. As a temporary workaround, // lower the JNI oopstorage lock ranks to make them super-special. - def(JNIGlobalAlloc_lock , PaddedMutex , special-1, true, Monitor::_safepoint_check_never); - def(JNIGlobalActive_lock , PaddedMutex , special-2, true, Monitor::_safepoint_check_never); - def(JNIWeakAlloc_lock , PaddedMutex , special-1, true, Monitor::_safepoint_check_never); - def(JNIWeakActive_lock , PaddedMutex , special-2, true, Monitor::_safepoint_check_never); + def(JNIGlobalAlloc_lock , PaddedMutex , nonleaf, true, Monitor::_safepoint_check_never); + def(JNIGlobalActive_lock , PaddedMutex , nonleaf-1, true, Monitor::_safepoint_check_never); + def(JNIWeakAlloc_lock , PaddedMutex , nonleaf, true, Monitor::_safepoint_check_never); + def(JNIWeakActive_lock , PaddedMutex , nonleaf-1, true, Monitor::_safepoint_check_never); def(JNICritical_lock , PaddedMonitor, nonleaf, true, Monitor::_safepoint_check_always); // used for JNI critical regions def(AdapterHandlerLibrary_lock , PaddedMutex , nonleaf, true, Monitor::_safepoint_check_always);
--- a/src/hotspot/share/runtime/os.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/runtime/os.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -33,6 +33,7 @@ #include "code/icBuffer.hpp" #include "code/vtableStubs.hpp" #include "gc/shared/vmGCOperations.hpp" +#include "logging/log.hpp" #include "interpreter/interpreter.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" @@ -610,9 +611,12 @@ static void verify_memory(void* ptr) { GuardedMemory guarded(ptr); if (!guarded.verify_guards()) { - tty->print_cr("## nof_mallocs = " UINT64_FORMAT ", nof_frees = " UINT64_FORMAT, os::num_mallocs, os::num_frees); - tty->print_cr("## memory stomp:"); - guarded.print_on(tty); + LogTarget(Warning, malloc, free) lt; + ResourceMark rm; + LogStream ls(lt); + ls.print_cr("## nof_mallocs = " UINT64_FORMAT ", nof_frees = " UINT64_FORMAT, os::num_mallocs, os::num_frees); + ls.print_cr("## memory stomp:"); + guarded.print_on(&ls); fatal("memory stomping error"); } } @@ -684,13 +688,10 @@ ptr = guarded.get_user_ptr(); #endif if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) { - tty->print_cr("os::malloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, p2i(ptr)); + log_warning(malloc, free)("os::malloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, p2i(ptr)); breakpoint(); } debug_only(if (paranoid) verify_memory(ptr)); - if (PrintMalloc && tty != NULL) { - tty->print_cr("os::malloc " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, p2i(ptr)); - } // we do not track guard memory return MemTracker::record_malloc((address)ptr, size, memflags, stack, level); @@ -727,7 +728,7 @@ return os::malloc(size, memflags, stack); } if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) { - tty->print_cr("os::realloc caught " PTR_FORMAT, p2i(memblock)); + log_warning(malloc, free)("os::realloc caught " PTR_FORMAT, p2i(memblock)); breakpoint(); } // NMT support @@ -735,18 +736,15 @@ verify_memory(membase); // always move the block void* ptr = os::malloc(size, memflags, stack); - if (PrintMalloc && tty != NULL) { - tty->print_cr("os::realloc " SIZE_FORMAT " bytes, " PTR_FORMAT " --> " PTR_FORMAT, size, p2i(memblock), p2i(ptr)); - } // Copy to new memory if malloc didn't fail - if ( ptr != NULL ) { + if (ptr != NULL ) { GuardedMemory guarded(MemTracker::malloc_base(memblock)); // Guard's user data contains NMT header size_t memblock_size = guarded.get_user_size() - MemTracker::malloc_header_size(memblock); memcpy(ptr, memblock, MIN2(size, memblock_size)); if (paranoid) verify_memory(MemTracker::malloc_base(ptr)); if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) { - tty->print_cr("os::realloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, p2i(ptr)); + log_warning(malloc, free)("os::realloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, p2i(ptr)); breakpoint(); } os::free(memblock); @@ -761,7 +759,7 @@ #ifdef ASSERT if (memblock == NULL) return; if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) { - if (tty != NULL) tty->print_cr("os::free caught " PTR_FORMAT, p2i(memblock)); + log_warning(malloc, free)("os::free caught " PTR_FORMAT, p2i(memblock)); breakpoint(); } void* membase = MemTracker::record_free(memblock); @@ -771,9 +769,6 @@ size_t size = guarded.get_user_size(); inc_stat_counter(&free_bytes, size); membase = guarded.release_for_freeing(); - if (PrintMalloc && tty != NULL) { - fprintf(stderr, "os::free " SIZE_FORMAT " bytes --> " PTR_FORMAT "\n", size, (uintptr_t)membase); - } ::free(membase); #else void* membase = MemTracker::record_free(memblock); @@ -1754,7 +1749,7 @@ bool os::uncommit_memory(char* addr, size_t bytes) { bool res; if (MemTracker::tracking_level() > NMT_minimal) { - Tracker tkr = MemTracker::get_virtual_memory_uncommit_tracker(); + Tracker tkr(Tracker::uncommit); res = pd_uncommit_memory(addr, bytes); if (res) { tkr.record((address)addr, bytes); @@ -1768,7 +1763,7 @@ bool os::release_memory(char* addr, size_t bytes) { bool res; if (MemTracker::tracking_level() > NMT_minimal) { - Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + Tracker tkr(Tracker::release); res = pd_release_memory(addr, bytes); if (res) { tkr.record((address)addr, bytes); @@ -1805,7 +1800,7 @@ bool os::unmap_memory(char *addr, size_t bytes) { bool result; if (MemTracker::tracking_level() > NMT_minimal) { - Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + Tracker tkr(Tracker::release); result = pd_unmap_memory(addr, bytes); if (result) { tkr.record((address)addr, bytes);
--- a/src/hotspot/share/services/memBaseline.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/services/memBaseline.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "classfile/classLoaderData.inline.hpp" #include "memory/allocation.hpp" #include "runtime/safepoint.hpp" #include "runtime/thread.inline.hpp" @@ -180,7 +181,8 @@ bool MemBaseline::baseline(bool summaryOnly) { reset(); - _class_count = InstanceKlass::number_of_instance_classes(); + _instance_class_count = ClassLoaderDataGraph::num_instance_classes(); + _array_class_count = ClassLoaderDataGraph::num_array_classes(); if (!baseline_summary()) { return false;
--- a/src/hotspot/share/services/memBaseline.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/services/memBaseline.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,7 +67,8 @@ VirtualMemorySnapshot _virtual_memory_snapshot; MetaspaceSnapshot _metaspace_snapshot; - size_t _class_count; + size_t _instance_class_count; + size_t _array_class_count; // Allocation sites information // Malloc allocation sites @@ -89,7 +90,7 @@ // create a memory baseline MemBaseline(): _baseline_type(Not_baselined), - _class_count(0) { + _instance_class_count(0), _array_class_count(0) { } bool baseline(bool summaryOnly = true); @@ -160,7 +161,17 @@ size_t class_count() const { assert(baseline_type() != Not_baselined, "Not yet baselined"); - return _class_count; + return _instance_class_count + _array_class_count; + } + + size_t instance_class_count() const { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + return _instance_class_count; + } + + size_t array_class_count() const { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + return _array_class_count; } size_t thread_count() const { @@ -172,7 +183,8 @@ void reset() { _baseline_type = Not_baselined; // _malloc_memory_snapshot and _virtual_memory_snapshot are copied over. - _class_count = 0; + _instance_class_count = 0; + _array_class_count = 0; _malloc_sites.clear(); _virtual_memory_sites.clear();
--- a/src/hotspot/share/services/memReporter.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/services/memReporter.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -145,7 +145,10 @@ if (flag == mtClass) { // report class count - out->print_cr("%27s (classes #" SIZE_FORMAT ")", " ", _class_count); + out->print_cr("%27s (classes #" SIZE_FORMAT ")", + " ", (_instance_class_count + _array_class_count)); + out->print_cr("%27s ( instance classes #" SIZE_FORMAT ", array classes #" SIZE_FORMAT ")", + " ", _instance_class_count, _array_class_count); } else if (flag == mtThread) { // report thread count out->print_cr("%27s (thread #" SIZE_FORMAT ")", " ", _malloc_snapshot->thread_count()); @@ -459,6 +462,17 @@ out->print(" %+d", (int)(_current_baseline.class_count() - _early_baseline.class_count())); } out->print_cr(")"); + + out->print("%27s ( instance classes #" SIZE_FORMAT, " ", _current_baseline.instance_class_count()); + if (_current_baseline.instance_class_count() != _early_baseline.instance_class_count()) { + out->print(" %+d", (int)(_current_baseline.instance_class_count() - _early_baseline.instance_class_count())); + } + out->print(", array classes #" SIZE_FORMAT, _current_baseline.array_class_count()); + if (_current_baseline.array_class_count() != _early_baseline.array_class_count()) { + out->print(" %+d", (int)(_current_baseline.array_class_count() - _early_baseline.array_class_count())); + } + out->print_cr(")"); + } else if (flag == mtThread) { // report thread count out->print("%27s (thread #" SIZE_FORMAT "", " ", _current_baseline.thread_count());
--- a/src/hotspot/share/services/memReporter.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/services/memReporter.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,7 +94,8 @@ private: MallocMemorySnapshot* _malloc_snapshot; VirtualMemorySnapshot* _vm_snapshot; - size_t _class_count; + size_t _instance_class_count; + size_t _array_class_count; public: // This constructor is for normal reporting from a recent baseline. @@ -102,7 +103,8 @@ size_t scale = K) : MemReporterBase(output, scale), _malloc_snapshot(baseline.malloc_memory_snapshot()), _vm_snapshot(baseline.virtual_memory_snapshot()), - _class_count(baseline.class_count()) { } + _instance_class_count(baseline.instance_class_count()), + _array_class_count(baseline.array_class_count()) { } // Generate summary report
--- a/src/hotspot/share/services/memTracker.hpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/services/memTracker.hpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,8 +36,14 @@ class Tracker : public StackObj { public: - Tracker() { } - void record(address addr, size_t size) { } + enum TrackerType { + uncommit, + release + }; + Tracker(enum TrackerType type) : _type(type) { } + void record(address addr, size_t size); + private: + enum TrackerType _type; }; class MemTracker : AllStatic { @@ -63,8 +69,6 @@ static inline void record_virtual_memory_reserve_and_commit(void* addr, size_t size, const NativeCallStack& stack, MEMFLAGS flag = mtNone) { } static inline void record_virtual_memory_commit(void* addr, size_t size, const NativeCallStack& stack) { } - static inline Tracker get_virtual_memory_uncommit_tracker() { return Tracker(); } - static inline Tracker get_virtual_memory_release_tracker() { return Tracker(); } static inline void record_virtual_memory_type(void* addr, MEMFLAGS flag) { } static inline void record_thread_stack(void* addr, size_t size) { } static inline void release_thread_stack(void* addr, size_t size) { } @@ -227,16 +231,6 @@ } } - static inline Tracker get_virtual_memory_uncommit_tracker() { - assert(tracking_level() >= NMT_summary, "Check by caller"); - return Tracker(Tracker::uncommit); - } - - static inline Tracker get_virtual_memory_release_tracker() { - assert(tracking_level() >= NMT_summary, "Check by caller"); - return Tracker(Tracker::release); - } - static inline void record_virtual_memory_type(void* addr, MEMFLAGS flag) { if (tracking_level() < NMT_summary) return; if (addr != NULL) {
--- a/src/hotspot/share/utilities/ostream.cpp Fri Feb 16 12:24:38 2018 -0800 +++ b/src/hotspot/share/utilities/ostream.cpp Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -947,18 +947,11 @@ delete classlist_file; } #endif - { - // we temporaly disable PrintMallocFree here - // as otherwise it'll lead to using of almost deleted - // tty or defaultStream::instance in logging facility - // of HeapFree(), see 6391258 - DEBUG_ONLY(FlagSetting fs(PrintMallocFree, false);) - if (tty != defaultStream::instance) { - delete tty; - } - if (defaultStream::instance != NULL) { - delete defaultStream::instance; - } + if (tty != defaultStream::instance) { + delete tty; + } + if (defaultStream::instance != NULL) { + delete defaultStream::instance; } tty = NULL; xtty = NULL;
--- a/src/java.base/share/classes/java/lang/Thread.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/java/lang/Thread.java Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -150,9 +150,6 @@ private Thread threadQ; private long eetop; - /* Whether or not to single_step this thread. */ - private boolean single_step; - /* Whether or not the thread is a daemon thread. */ private boolean daemon = false;
--- a/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java Fri Feb 16 13:49:07 2018 -0800 @@ -67,12 +67,12 @@ // VM is pushing arguments at us pullModeBSM = null; if (pullMode) { - bootstrapMethod = Adapters.pushMePullYou(bootstrapMethod, true); + bootstrapMethod = pushMePullYou(bootstrapMethod, true); } } else { // VM wants us to pull args from it pullModeBSM = pullMode ? bootstrapMethod : - Adapters.pushMePullYou(bootstrapMethod, false); + pushMePullYou(bootstrapMethod, false); bootstrapMethod = null; } try { @@ -237,9 +237,10 @@ // give up at first null and grab the rest in one big block if (i >= end) return i; Object[] temp = new Object[end - i]; - if (TRACE_METHOD_LINKAGE) - System.out.println("resolving more BSM arguments: "+ + if (TRACE_METHOD_LINKAGE) { + System.out.println("resolving more BSM arguments: " + Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end)); + } copyOutBootstrapArguments(caller, indexInfo, i, end, temp, 0, true, null); @@ -285,9 +286,10 @@ private void prefetchIntoCache(int i, int pfLimit) { if (pfLimit <= i) return; // corner case Object[] temp = new Object[pfLimit - i]; - if (TRACE_METHOD_LINKAGE) - System.out.println("prefetching BSM arguments: "+ + if (TRACE_METHOD_LINKAGE) { + System.out.println("prefetching BSM arguments: " + Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit)); + } copyOutBootstrapArguments(caller, indexInfo, i, pfLimit, temp, 0, false, NOT_PRESENT); @@ -301,7 +303,7 @@ } /*non-public*/ static final - class Adapters { + class PushAdapter { // skeleton for push-mode BSM which wraps a pull-mode BSM: static Object pushToBootstrapMethod(MethodHandle pullModeBSM, MethodHandles.Lookup lookup, String name, Object type, @@ -313,29 +315,75 @@ return pullModeBSM.invoke(lookup, bsci); } - // skeleton for pull-mode BSM which wraps a push-mode BSM: - static Object pullFromBootstrapMethod(MethodHandle pushModeBSM, - MethodHandles.Lookup lookup, BootstrapCallInfo<?> bsci) - throws Throwable { - int argc = bsci.size(); - Object arguments[] = new Object[3 + argc]; - arguments[0] = lookup; - arguments[1] = bsci.invocationName(); - arguments[2] = bsci.invocationType(); - bsci.copyConstants(0, argc, arguments, 3); - if (TRACE_METHOD_LINKAGE) - System.out.println("pulled arguments from VM for push-mode BSM"); - return pushModeBSM.invokeWithArguments(arguments); - } static final MethodHandle MH_pushToBootstrapMethod; - static final MethodHandle MH_pullFromBootstrapMethod; static { - final Class<?> THIS_CLASS = Adapters.class; + final Class<?> THIS_CLASS = PushAdapter.class; try { MH_pushToBootstrapMethod = IMPL_LOOKUP .findStatic(THIS_CLASS, "pushToBootstrapMethod", MethodType.methodType(Object.class, MethodHandle.class, Lookup.class, String.class, Object.class, Object[].class)); + } catch (Throwable ex) { + throw new InternalError(ex); + } + } + } + + /*non-public*/ static final + class PullAdapter { + // skeleton for pull-mode BSM which wraps a push-mode BSM: + static Object pullFromBootstrapMethod(MethodHandle pushModeBSM, + MethodHandles.Lookup lookup, + BootstrapCallInfo<?> bsci) + throws Throwable { + int argc = bsci.size(); + switch (argc) { + case 0: + return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType()); + case 1: + return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), + bsci.get(0)); + case 2: + return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), + bsci.get(0), bsci.get(1)); + case 3: + return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), + bsci.get(0), bsci.get(1), bsci.get(2)); + case 4: + return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), + bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3)); + case 5: + return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), + bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4)); + case 6: + return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(), + bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5)); + default: + final int NON_SPREAD_ARG_COUNT = 3; // (lookup, name, type) + final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT; + if (argc >= MAX_SAFE_SIZE) { + // to be on the safe side, use invokeWithArguments which handles jumbo lists + Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argc]; + newargv[0] = lookup; + newargv[1] = bsci.invocationName(); + newargv[2] = bsci.invocationType(); + bsci.copyConstants(0, argc, newargv, NON_SPREAD_ARG_COUNT); + return pushModeBSM.invokeWithArguments(newargv); + } + MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argc); + MethodHandle typedBSM = pushModeBSM.asType(invocationType); + MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); + Object[] argv = new Object[argc]; + bsci.copyConstants(0, argc, argv, 0); + return spreader.invokeExact(typedBSM, (Object) lookup, (Object) bsci.invocationName(), bsci.invocationType(), argv); + } + } + + static final MethodHandle MH_pullFromBootstrapMethod; + + static { + final Class<?> THIS_CLASS = PullAdapter.class; + try { MH_pullFromBootstrapMethod = IMPL_LOOKUP .findStatic(THIS_CLASS, "pullFromBootstrapMethod", MethodType.methodType(Object.class, MethodHandle.class, @@ -344,23 +392,25 @@ throw new InternalError(ex); } } + } - /** Given a push-mode BSM (taking one argument) convert it to a - * pull-mode BSM (taking N pre-resolved arguments). - * This method is used when, in fact, the JVM is passing up - * pre-resolved arguments, but the BSM is expecting lazy stuff. - * Or, when goToPushMode is true, do the reverse transform. - * (The two transforms are exactly inverse.) - */ - static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) { - if (TRACE_METHOD_LINKAGE) - System.out.println("converting BSM to "+(goToPushMode ? "push mode" : "pull mode")); - assert(isPullModeBSM(bsm) == goToPushMode); //there must be a change - if (goToPushMode) { - return Adapters.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true); - } else { - return Adapters.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false); - } + /** Given a push-mode BSM (taking one argument) convert it to a + * pull-mode BSM (taking N pre-resolved arguments). + * This method is used when, in fact, the JVM is passing up + * pre-resolved arguments, but the BSM is expecting lazy stuff. + * Or, when goToPushMode is true, do the reverse transform. + * (The two transforms are exactly inverse.) + */ + static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) { + if (TRACE_METHOD_LINKAGE) { + System.out.println("converting BSM of type " + bsm.type() + " to " + + (goToPushMode ? "push mode" : "pull mode")); + } + assert(isPullModeBSM(bsm) == goToPushMode); // there must be a change + if (goToPushMode) { + return PushAdapter.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true); + } else { + return PullAdapter.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false); } } }
--- a/src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java Fri Feb 16 13:49:07 2018 -0800 @@ -37,7 +37,7 @@ * unless the argument is specified to be unused or specified to accept a * {@code null} value. * - * @since 10 + * @since 11 */ public final class ConstantBootstraps { // implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
--- a/src/java.base/share/classes/java/lang/ref/Finalizer.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/java/lang/ref/Finalizer.java Fri Feb 16 13:49:07 2018 -0800 @@ -36,18 +36,18 @@ class */ private static ReferenceQueue<Object> queue = new ReferenceQueue<>(); + + /** Head of doubly linked list of Finalizers awaiting finalization. */ private static Finalizer unfinalized = null; + + /** Lock guarding access to unfinalized list. */ private static final Object lock = new Object(); - private Finalizer - next = null, - prev = null; + private Finalizer next, prev; - private boolean hasBeenFinalized() { - return (next == this); - } - - private void add() { + private Finalizer(Object finalizee) { + super(finalizee, queue); + // push onto unfinalized synchronized (lock) { if (unfinalized != null) { this.next = unfinalized; @@ -57,31 +57,6 @@ } } - private void remove() { - synchronized (lock) { - if (unfinalized == this) { - if (this.next != null) { - unfinalized = this.next; - } else { - unfinalized = this.prev; - } - } - if (this.next != null) { - this.next.prev = this.prev; - } - if (this.prev != null) { - this.prev.next = this.next; - } - this.next = this; /* Indicates that this has been finalized */ - this.prev = this; - } - } - - private Finalizer(Object finalizee) { - super(finalizee, queue); - add(); - } - static ReferenceQueue<Object> getQueue() { return queue; } @@ -91,11 +66,24 @@ new Finalizer(finalizee); } + private void deregisterAndRunFinalizer(JavaLangAccess jla) { + synchronized (lock) { + if (this.next == this) // already finalized + return; + // unlink from unfinalized + if (unfinalized == this) + unfinalized = this.next; + else + this.prev.next = this.next; + if (this.next != null) + this.next.prev = this.prev; + this.prev = null; + this.next = this; // mark as finalized + } + runFinalizer(jla); + } + private void runFinalizer(JavaLangAccess jla) { - synchronized (this) { - if (hasBeenFinalized()) return; - remove(); - } try { Object finalizee = this.get(); if (finalizee != null && !(finalizee instanceof java.lang.Enum)) { @@ -155,11 +143,8 @@ return; final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); running = true; - for (;;) { - Finalizer f = (Finalizer)queue.poll(); - if (f == null) break; - f.runFinalizer(jla); - } + for (Finalizer f; (f = (Finalizer)queue.poll()) != null; ) + f.deregisterAndRunFinalizer(jla); } }); } @@ -179,11 +164,15 @@ final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); running = true; for (;;) { + // "pollFirst" from unfinalized Finalizer f; synchronized (lock) { f = unfinalized; if (f == null) break; unfinalized = f.next; + if (unfinalized != null) + unfinalized.prev = null; + f.next = f; // mark as finalized } f.runFinalizer(jla); }}}); @@ -214,7 +203,7 @@ for (;;) { try { Finalizer f = (Finalizer)queue.remove(); - f.runFinalizer(jla); + f.deregisterAndRunFinalizer(jla); } catch (InterruptedException x) { // ignore and continue }
--- a/src/java.base/share/classes/java/util/Base64.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/java/util/Base64.java Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -692,7 +692,27 @@ int dp = 0; int bits = 0; int shiftto = 18; // pos of first byte of 4-byte atom + while (sp < sl) { + if (shiftto == 18 && sp + 4 < sl) { // fast path + int sl0 = sp + ((sl - sp) & ~0b11); + while (sp < sl0) { + int b1 = base64[src[sp++] & 0xff]; + int b2 = base64[src[sp++] & 0xff]; + int b3 = base64[src[sp++] & 0xff]; + int b4 = base64[src[sp++] & 0xff]; + if ((b1 | b2 | b3 | b4) < 0) { // non base64 byte + sp -= 4; + break; + } + int bits0 = b1 << 18 | b2 << 12 | b3 << 6 | b4; + dst[dp++] = (byte)(bits0 >> 16); + dst[dp++] = (byte)(bits0 >> 8); + dst[dp++] = (byte)(bits0); + } + if (sp >= sl) + break; + } int b = src[sp++] & 0xff; if ((b = base64[b]) < 0) { if (b == -2) { // padding byte '=' @@ -762,6 +782,7 @@ private final int linemax; private final boolean doPadding;// whether or not to pad private int linepos = 0; + private byte[] buf; EncOutputStream(OutputStream os, char[] base64, byte[] newline, int linemax, boolean doPadding) { @@ -770,6 +791,7 @@ this.newline = newline; this.linemax = linemax; this.doPadding = doPadding; + this.buf = new byte[linemax <= 0 ? 8124 : linemax]; } @Override @@ -786,6 +808,14 @@ } } + private void writeb4(char b1, char b2, char b3, char b4) throws IOException { + buf[0] = (byte)b1; + buf[1] = (byte)b2; + buf[2] = (byte)b3; + buf[3] = (byte)b4; + out.write(buf, 0, 4); + } + @Override public void write(byte[] b, int off, int len) throws IOException { if (closed) @@ -806,25 +836,34 @@ b2 = b[off++] & 0xff; len--; checkNewline(); - out.write(base64[b0 >> 2]); - out.write(base64[(b0 << 4) & 0x3f | (b1 >> 4)]); - out.write(base64[(b1 << 2) & 0x3f | (b2 >> 6)]); - out.write(base64[b2 & 0x3f]); + writeb4(base64[b0 >> 2], + base64[(b0 << 4) & 0x3f | (b1 >> 4)], + base64[(b1 << 2) & 0x3f | (b2 >> 6)], + base64[b2 & 0x3f]); linepos += 4; } int nBits24 = len / 3; leftover = len - (nBits24 * 3); - while (nBits24-- > 0) { + + while (nBits24 > 0) { checkNewline(); - int bits = (b[off++] & 0xff) << 16 | - (b[off++] & 0xff) << 8 | - (b[off++] & 0xff); - out.write(base64[(bits >>> 18) & 0x3f]); - out.write(base64[(bits >>> 12) & 0x3f]); - out.write(base64[(bits >>> 6) & 0x3f]); - out.write(base64[bits & 0x3f]); - linepos += 4; - } + int dl = linemax <= 0 ? buf.length : buf.length - linepos; + int sl = off + Math.min(nBits24, dl / 4) * 3; + int dp = 0; + for (int sp = off; sp < sl; ) { + int bits = (b[sp++] & 0xff) << 16 | + (b[sp++] & 0xff) << 8 | + (b[sp++] & 0xff); + buf[dp++] = (byte)base64[(bits >>> 18) & 0x3f]; + buf[dp++] = (byte)base64[(bits >>> 12) & 0x3f]; + buf[dp++] = (byte)base64[(bits >>> 6) & 0x3f]; + buf[dp++] = (byte)base64[bits & 0x3f]; + } + out.write(buf, 0, dp); + off = sl; + linepos += dp; + nBits24 -= dp / 4; + } if (leftover == 1) { b0 = b[off++] & 0xff; } else if (leftover == 2) { @@ -889,6 +928,52 @@ return read(sbBuf, 0, 1) == -1 ? -1 : sbBuf[0] & 0xff; } + private int eof(byte[] b, int off, int len, int oldOff) + throws IOException + { + eof = true; + if (nextin != 18) { + if (nextin == 12) + throw new IOException("Base64 stream has one un-decoded dangling byte."); + // treat ending xx/xxx without padding character legal. + // same logic as v == '=' below + b[off++] = (byte)(bits >> (16)); + if (nextin == 0) { // only one padding byte + if (len == 1) { // no enough output space + bits >>= 8; // shift to lowest byte + nextout = 0; + } else { + b[off++] = (byte) (bits >> 8); + } + } + } + return off == oldOff ? -1 : off - oldOff; + } + + private int padding(byte[] b, int off, int len, int oldOff) + throws IOException + { + // = shiftto==18 unnecessary padding + // x= shiftto==12 dangling x, invalid unit + // xx= shiftto==6 && missing last '=' + // xx=y or last is not '=' + if (nextin == 18 || nextin == 12 || + nextin == 6 && is.read() != '=') { + throw new IOException("Illegal base64 ending sequence:" + nextin); + } + b[off++] = (byte)(bits >> (16)); + if (nextin == 0) { // only one padding byte + if (len == 1) { // no enough output space + bits >>= 8; // shift to lowest byte + nextout = 0; + } else { + b[off++] = (byte) (bits >> 8); + } + } + eof = true; + return off - oldOff; + } + @Override public int read(byte[] b, int off, int len) throws IOException { if (closed) @@ -898,82 +983,46 @@ if (off < 0 || len < 0 || len > b.length - off) throw new IndexOutOfBoundsException(); int oldOff = off; - if (nextout >= 0) { // leftover output byte(s) in bits buf - do { - if (len == 0) - return off - oldOff; - b[off++] = (byte)(bits >> nextout); - len--; - nextout -= 8; - } while (nextout >= 0); - bits = 0; + while (nextout >= 0) { // leftover output byte(s) in bits buf + if (len == 0) + return off - oldOff; + b[off++] = (byte)(bits >> nextout); + len--; + nextout -= 8; } + bits = 0; while (len > 0) { int v = is.read(); if (v == -1) { - eof = true; - if (nextin != 18) { - if (nextin == 12) - throw new IOException("Base64 stream has one un-decoded dangling byte."); - // treat ending xx/xxx without padding character legal. - // same logic as v == '=' below - b[off++] = (byte)(bits >> (16)); - len--; - if (nextin == 0) { // only one padding byte - if (len == 0) { // no enough output space - bits >>= 8; // shift to lowest byte - nextout = 0; - } else { - b[off++] = (byte) (bits >> 8); - } - } + return eof(b, off, len, oldOff); + } + if ((v = base64[v]) < 0) { + if (v == -2) { // padding byte(s) + return padding(b, off, len, oldOff); } - if (off == oldOff) - return -1; - else - return off - oldOff; - } - if (v == '=') { // padding byte(s) - // = shiftto==18 unnecessary padding - // x= shiftto==12 dangling x, invalid unit - // xx= shiftto==6 && missing last '=' - // xx=y or last is not '=' - if (nextin == 18 || nextin == 12 || - nextin == 6 && is.read() != '=') { - throw new IOException("Illegal base64 ending sequence:" + nextin); + if (v == -1) { + if (!isMIME) + throw new IOException("Illegal base64 character " + + Integer.toString(v, 16)); + continue; // skip if for rfc2045 } - b[off++] = (byte)(bits >> (16)); - len--; - if (nextin == 0) { // only one padding byte - if (len == 0) { // no enough output space - bits >>= 8; // shift to lowest byte - nextout = 0; - } else { - b[off++] = (byte) (bits >> 8); - } - } - eof = true; - break; - } - if ((v = base64[v]) == -1) { - if (isMIME) // skip if for rfc2045 - continue; - else - throw new IOException("Illegal base64 character " + - Integer.toString(v, 16)); + // neve be here } bits |= (v << nextin); if (nextin == 0) { - nextin = 18; // clear for next - nextout = 16; - while (nextout >= 0) { - b[off++] = (byte)(bits >> nextout); - len--; - nextout -= 8; - if (len == 0 && nextout >= 0) { // don't clean "bits" - return off - oldOff; - } + nextin = 18; // clear for next in + b[off++] = (byte)(bits >> 16); + if (len == 1) { + nextout = 8; // 2 bytes left in bits + break; } + b[off++] = (byte)(bits >> 8); + if (len == 2) { + nextout = 0; // 1 byte left in bits + break; + } + b[off++] = (byte)bits; + len -= 3; bits = 0; } else { nextin -= 6;
--- a/src/java.base/share/classes/java/util/Collections.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/java/util/Collections.java Fri Feb 16 13:49:07 2018 -0800 @@ -3771,9 +3771,9 @@ * Ensure that we don't get an ArrayStoreException even if * s.toArray returns an array of something other than Object */ - Object[] dest = (CheckedEntry.class.isInstance( - source.getClass().getComponentType()) ? source : - new Object[source.length]); + Object[] dest = (source.getClass() == Object[].class) + ? source + : new Object[source.length]; for (int i = 0; i < source.length; i++) dest[i] = checkedEntry((Map.Entry<K,V>)source[i],
--- a/src/java.base/share/classes/java/util/regex/Pattern.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/java/util/regex/Pattern.java Fri Feb 16 13:49:07 2018 -0800 @@ -782,12 +782,9 @@ * arguments, they can also be passed as inline modifiers. * For example, the following statements have the same effect. * <pre> - * RegExp r1 = RegExp.compile("abc", Pattern.I|Pattern.M); - * RegExp r2 = RegExp.compile("(?im)abc", 0); + * Pattern p1 = Pattern.compile("abc", Pattern.CASE_INSENSITIVE|Pattern.MULTILINE); + * Pattern p2 = Pattern.compile("(?im)abc", 0); * </pre> - * - * The flags are duplicated so that the familiar Perl match flag - * names are available. */ /** @@ -2527,7 +2524,7 @@ throw error("\\k is not followed by '<' for named capturing group"); String name = groupname(read()); if (!namedGroups().containsKey(name)) - throw error("(named capturing group <"+ name+"> does not exit"); + throw error("named capturing group <" + name + "> does not exist"); if (create) { hasGroupRef = true; if (has(CASE_INSENSITIVE)) @@ -2922,13 +2919,11 @@ */ private String groupname(int ch) { StringBuilder sb = new StringBuilder(); - sb.append(Character.toChars(ch)); - while (ASCII.isLower(ch=read()) || ASCII.isUpper(ch) || - ASCII.isDigit(ch)) { - sb.append(Character.toChars(ch)); - } - if (sb.length() == 0) - throw error("named capturing group has 0 length name"); + if (!ASCII.isAlpha(ch)) + throw error("capturing group name does not start with a Latin letter"); + do { + sb.append((char) ch); + } while (ASCII.isAlnum(ch=read())); if (ch != '>') throw error("named capturing group is missing trailing '>'"); return sb.toString(); @@ -2974,7 +2969,7 @@ break; case '<': // (?<xxx) look behind ch = read(); - if (ASCII.isLower(ch) || ASCII.isUpper(ch)) { + if (ch != '=' && ch != '!') { // named captured group String name = groupname(ch); if (namedGroups().containsKey(name)) @@ -3005,14 +3000,12 @@ info.minLength) : new Behind(head, info.maxLength, info.minLength)); - } else if (ch == '!') { + } else { // if (ch == '!') head = tail = (hasSupplementary ? new NotBehindS(head, info.maxLength, info.minLength) : new NotBehind(head, info.maxLength, info.minLength)); - } else { - throw error("Unknown look-behind group"); } // clear all top-closure-nodes inside lookbehind if (saveTCNCount < topClosureNodes.size())
--- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties Fri Feb 16 13:49:07 2018 -0800 @@ -220,7 +220,7 @@ Error: The JavaFX launchApplication method has the wrong signature, it\n\ must be declared static and return a value of type void java.launcher.module.error1=\ - module {0} does not have a MainClass attribute, use -m <module>/<main-class> + module {0} does not have a ModuleMainClass attribute, use -m <module>/<main-class> java.launcher.module.error2=\ Error: Could not find or load main class {0} in module {1} java.launcher.module.error3=\
--- a/src/java.base/share/classes/sun/net/www/ParseUtil.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/sun/net/www/ParseUtil.java Fri Feb 16 13:49:07 2018 -0800 @@ -44,7 +44,9 @@ * @author Mike McCloskey */ -public class ParseUtil { +public final class ParseUtil { + + private ParseUtil() {} /** * Constructs an encoded version of the specified path string suitable @@ -80,10 +82,13 @@ int len = path.length(); for (int i = 0; i < len; i++) { char c = path.charAt(i); - if (c == '/' || c == '.' || - c >= 'a' && c <= 'z' || - c >= 'A' && c <= 'Z' || - c >= '0' && c <= '9') { + // Ordering in the following test is performance sensitive, + // and typically paths have most chars in the a-z range, then + // in the symbol range '&'-':' (includes '.', '/' and '0'-'9') + // and more rarely in the A-Z range. + if (c >= 'a' && c <= 'z' || + c >= '&' && c <= ':' || + c >= 'A' && c <= 'Z') { continue; } else if (c > 0x007F || match(c, L_ENCODED, H_ENCODED)) { return i; @@ -219,9 +224,17 @@ /** * Returns a canonical version of the specified string. */ - public String canonizeString(String file) { - int i = 0; - int lim = file.length(); + public static String canonizeString(String file) { + int len = file.length(); + if (len == 0 || (file.indexOf("./") == -1 && file.charAt(len - 1) != '.')) { + return file; + } else { + return doCanonize(file); + } + } + + private static String doCanonize(String file) { + int i, lim; // Remove embedded /../ while ((i = file.indexOf("/../")) >= 0) {
--- a/src/java.base/share/classes/sun/net/www/protocol/jar/Handler.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/sun/net/www/protocol/jar/Handler.java Fri Feb 16 13:49:07 2018 -0800 @@ -141,10 +141,9 @@ // 1. absolute (jar:) // 2. relative (i.e. url + foo/bar/baz.ext) // 3. anchor-only (i.e. url + #foo), which we already did (refOnly) - boolean absoluteSpec = false; - if (spec.length() >= 4) { - absoluteSpec = spec.substring(0, 4).equalsIgnoreCase("jar:"); - } + boolean absoluteSpec = spec.length() >= 4 + ? spec.regionMatches(true, 0, "jar:", 0, 4) + : false; spec = spec.substring(start, limit); if (absoluteSpec) { @@ -156,16 +155,14 @@ int bangSlash = indexOfBangSlash(file); String toBangSlash = file.substring(0, bangSlash); String afterBangSlash = file.substring(bangSlash); - sun.net.www.ParseUtil canonizer = new ParseUtil(); - afterBangSlash = canonizer.canonizeString(afterBangSlash); + afterBangSlash = ParseUtil.canonizeString(afterBangSlash); file = toBangSlash + afterBangSlash; } setURL(url, "jar", "", -1, file, ref); } private String parseAbsoluteSpec(String spec) { - URL url = null; - int index = -1; + int index; // check for !/ if ((index = indexOfBangSlash(spec)) == -1) { throw new NullPointerException("no !/ in spec"); @@ -173,7 +170,7 @@ // test the inner URL try { String innerSpec = spec.substring(0, index - 1); - url = new URL(innerSpec); + new URL(innerSpec); } catch (MalformedURLException e) { throw new NullPointerException("invalid url: " + spec + " (" + e + ")"); @@ -193,16 +190,16 @@ ": no !/"); } ctxFile = ctxFile.substring(0, bangSlash); - } - if (!ctxFile.endsWith("/") && (!spec.startsWith("/"))){ + } else { // chop up the last component int lastSlash = ctxFile.lastIndexOf('/'); if (lastSlash == -1) { throw new NullPointerException("malformed " + "context url:" + url); + } else if (lastSlash < ctxFile.length() - 1) { + ctxFile = ctxFile.substring(0, lastSlash + 1); } - ctxFile = ctxFile.substring(0, lastSlash + 1); } return (ctxFile + spec); }
--- a/src/java.base/share/classes/sun/util/cldr/CLDRCalendarDataProviderImpl.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/share/classes/sun/util/cldr/CLDRCalendarDataProviderImpl.java Fri Feb 16 13:49:07 2018 -0800 @@ -97,10 +97,11 @@ } private static Optional<Integer> retrieveInteger(String src, String region) { - return Arrays.stream(src.split(";")) - .filter(entry -> entry.contains(region)) - .map(entry -> entry.substring(0, entry.indexOf(":"))) - .findAny() - .map(Integer::parseInt); + int regionIndex = src.indexOf(region); + if (regionIndex >= 0) { + int start = src.lastIndexOf(';', regionIndex) + 1; + return Optional.of(Integer.parseInt(src, start, src.indexOf(':', start), 10)); + } + return Optional.empty(); } }
--- a/src/java.base/windows/classes/sun/nio/ch/PipeImpl.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.base/windows/classes/sun/nio/ch/PipeImpl.java Fri Feb 16 13:49:07 2018 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -110,7 +110,7 @@ ByteBuffer bb = ByteBuffer.allocate(NUM_SECRET_BYTES); // Loopback address - InetAddress lb = InetAddress.getByName("127.0.0.1"); + InetAddress lb = InetAddress.getLoopbackAddress(); assert(lb.isLoopbackAddress()); InetSocketAddress sa = null; for(;;) {
--- a/src/java.scripting/share/classes/javax/script/ScriptEngineManager.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/java.scripting/share/classes/javax/script/ScriptEngineManager.java Fri Feb 16 13:49:07 2018 -0800 @@ -77,7 +77,10 @@ private void init(final ClassLoader loader) { globalScope = new SimpleBindings(); - engineSpis = new TreeSet<ScriptEngineFactory>(Comparator.comparing(ScriptEngineFactory::getEngineName)); + engineSpis = new TreeSet<ScriptEngineFactory>(Comparator.comparing( + ScriptEngineFactory::getEngineName, + Comparator.nullsLast(Comparator.naturalOrder())) + ); nameAssociations = new HashMap<String, ScriptEngineFactory>(); extensionAssociations = new HashMap<String, ScriptEngineFactory>(); mimeTypeAssociations = new HashMap<String, ScriptEngineFactory>();
--- a/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/DataPatchProcessor.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/DataPatchProcessor.java Fri Feb 16 13:49:07 2018 -0800 @@ -143,7 +143,7 @@ int alignment = data.getAlignment(); byte[] value = new byte[size]; ByteBuffer buffer = ByteBuffer.wrap(value).order(ByteOrder.nativeOrder()); - DataSection.emit(buffer, data, p -> { + DataSection.emit(buffer, data, (p, c) -> { }); String targetSymbol = "data.M" + methodInfo.getCodeId() + "." + dataOffset; Symbol relocationSymbol = binaryContainer.getSymbol(targetSymbol);
--- a/src/jdk.internal.vm.compiler/.mx.graal/suite.py Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/.mx.graal/suite.py Fri Feb 16 13:49:07 2018 -0800 @@ -82,6 +82,24 @@ "javaCompliance" : "1.8", "workingSets" : "API,SDK", }, + "org.graalvm.collections" : { + "subDir" : "share/classes", + "sourceDirs" : ["src"], + "checkstyle" : "org.graalvm.word", + "javaCompliance" : "1.8", + "workingSets" : "API,SDK", + }, + "org.graalvm.collections.test" : { + "subDir" : "share/classes", + "sourceDirs" : ["src"], + "dependencies" : [ + "mx:JUNIT", + "org.graalvm.collections", + ], + "checkstyle" : "org.graalvm.word", + "javaCompliance" : "1.8", + "workingSets" : "API,SDK,Test", + }, # ------------- Graal ------------- @@ -190,6 +208,9 @@ "org.graalvm.util" : { "subDir" : "share/classes", "sourceDirs" : ["src"], + "dependencies" : [ + "org.graalvm.collections", + ], "checkstyle" : "org.graalvm.compiler.graph", "javaCompliance" : "1.8", "workingSets" : "API,Graal", @@ -201,6 +222,7 @@ "dependencies" : [ "mx:JUNIT", "org.graalvm.util", + "org.graalvm.compiler.core.test", ], "checkstyle" : "org.graalvm.compiler.graph", "javaCompliance" : "1.8", @@ -970,10 +992,11 @@ "workingSets" : "Graal,SPARC", }, - "org.graalvm.compiler.core.sparc.test" : { + "org.graalvm.compiler.hotspot.sparc.test" : { "subDir" : "share/classes", "sourceDirs" : ["src"], "dependencies" : [ + "org.graalvm.compiler.hotspot", "org.graalvm.compiler.lir.jtt", "JVMCI_HOTSPOT" ], @@ -1007,6 +1030,7 @@ "subDir" : "share/classes", "sourceDirs" : ["src"], "dependencies" : [ + "org.graalvm.collections", "org.graalvm.compiler.debug", "org.graalvm.word", ], @@ -1037,7 +1061,6 @@ "sourceDirs" : ["src"], "dependencies" : [ "org.graalvm.compiler.debug", - "org.graalvm.util", "mx:JUNIT", ], "checkstyle" : "org.graalvm.compiler.graph", @@ -1225,11 +1248,11 @@ "org.graalvm.compiler.asm.amd64.test", "org.graalvm.compiler.core.aarch64.test", "org.graalvm.compiler.core.amd64.test", - "org.graalvm.compiler.core.sparc.test", "org.graalvm.compiler.debug.test", "org.graalvm.compiler.hotspot.aarch64.test", "org.graalvm.compiler.hotspot.amd64.test", "org.graalvm.compiler.hotspot.lir.test", + "org.graalvm.compiler.hotspot.sparc.test", "org.graalvm.compiler.options.test", "org.graalvm.compiler.jtt", "org.graalvm.compiler.lir.jtt",
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections.test/src/org/graalvm/collections/test/EconomicMapImplTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections.test; + +import java.util.Arrays; +import java.util.Iterator; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.EconomicSet; +import org.graalvm.collections.Equivalence; +import org.graalvm.collections.UnmodifiableEconomicSet; +import org.junit.Assert; +import org.junit.Test; + +public class EconomicMapImplTest { + + @Test(expected = UnsupportedOperationException.class) + public void testRemoveNull() { + EconomicMap<Integer, Integer> map = EconomicMap.create(10); + map.removeKey(null); + } + + @Test + public void testInitFromHashSet() { + UnmodifiableEconomicSet<Integer> set = new UnmodifiableEconomicSet<Integer>() { + + @Override + public boolean contains(Integer element) { + return element == 0; + } + + @Override + public int size() { + return 1; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Iterator<Integer> iterator() { + return new Iterator<Integer>() { + + private boolean visited = false; + + @Override + public boolean hasNext() { + return !visited; + } + + @Override + public Integer next() { + if (visited) { + return null; + } else { + visited = true; + return 1; + } + } + }; + } + }; + + EconomicSet<Integer> newSet = EconomicSet.create(Equivalence.DEFAULT, set); + Assert.assertEquals(newSet.size(), 1); + } + + @Test + public void testCopyHash() { + EconomicSet<Integer> set = EconomicSet.create(Equivalence.IDENTITY); + set.addAll(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); + EconomicSet<Integer> newSet = EconomicSet.create(Equivalence.IDENTITY, set); + Assert.assertEquals(newSet.size(), 10); + newSet.remove(8); + newSet.remove(9); + Assert.assertEquals(newSet.size(), 8); + } + + @Test + public void testNewEquivalence() { + EconomicSet<Integer> set = EconomicSet.create(new Equivalence() { + @Override + public boolean equals(Object a, Object b) { + return false; + } + + @Override + public int hashCode(Object o) { + return 0; + } + }); + set.addAll(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); + Assert.assertTrue(set.add(new Integer(0))); + } + + @Test(expected = UnsupportedOperationException.class) + public void testMapPutNull() { + EconomicMap<Integer, Integer> map = EconomicMap.create(); + map.put(null, null); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections.test/src/org/graalvm/collections/test/EconomicMapLargeTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections.test; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Objects; +import java.util.Random; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.Equivalence; +import org.graalvm.collections.MapCursor; +import org.graalvm.collections.UnmodifiableMapCursor; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class EconomicMapLargeTest { + + @Parameter(value = 0) public EconomicMap<Object, Object> testMap; + @Parameter(value = 1) public EconomicMap<Object, Object> referenceMap; + @Parameter(value = 2) public String name; + + @Parameters(name = "{2}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[]{EconomicMap.create(Equivalence.DEFAULT), EconomicMap.create(Equivalence.DEFAULT), "EconomicMap"}, + new Object[]{EconomicMap.create(Equivalence.IDENTITY), EconomicMap.create(Equivalence.IDENTITY), "EconomicMap(IDENTITY)"}, + new Object[]{EconomicMap.create(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE), EconomicMap.create(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE), + "EconomicMap(IDENTITY_WITH_SYSTEM_HASHCODE)"}, + new Object[]{EconomicMap.create(Equivalence.DEFAULT), EconomicMap.wrapMap(new LinkedHashMap<>()), "EconomicMap<->wrapMap"}, + new Object[]{EconomicMap.wrapMap(new LinkedHashMap<>()), EconomicMap.wrapMap(new LinkedHashMap<>()), "wrapMap"}); + } + + private static int[] createRandomRange(Random random, int count) { + int[] result = new int[count]; + for (int i = 0; i < count; ++i) { + int range = random.nextInt(14); + if (range == 0 || range > 10) { + range = Integer.MAX_VALUE; + } else if (range == 10) { + range = 100; + } + result[i] = range; + } + return result; + } + + private static final class BadHashClass { + private int value; + + BadHashClass(int randomInt) { + this.value = randomInt; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object other) { + if (other instanceof BadHashClass) { + BadHashClass badHashClass = (BadHashClass) other; + return badHashClass.value == value; + } + return false; + } + } + + interface MapAction { + Object perform(EconomicMap<Object, Object> map, int randomInt); + } + + static final Object EXISTING_VALUE = new Object(); + + static final MapAction[] INCREASE_ACTIONS = new MapAction[]{ + (map, randomInt) -> map.put(randomInt, "value"), + (map, randomInt) -> map.get(randomInt) + }; + + static final MapAction[] ACTIONS = new MapAction[]{ + (map, randomInt) -> map.removeKey(randomInt), + (map, randomInt) -> map.put(randomInt, "value"), + (map, randomInt) -> map.put(randomInt, null), + (map, randomInt) -> map.put(EXISTING_VALUE, randomInt), + (map, randomInt) -> { + if (randomInt == 0) { + map.clear(); + } + return map.isEmpty(); + }, + (map, randomInt) -> map.containsKey(randomInt), + (map, randomInt) -> map.get(randomInt), + (map, randomInt) -> map.put(new BadHashClass(randomInt), "unique"), + (map, randomInt) -> { + if (randomInt == 0) { + map.replaceAll((key, value) -> Objects.toString(value) + "!"); + } + return map.isEmpty(); + } + + }; + + @Test + public void testVeryLarge() { + testMap.clear(); + referenceMap.clear(); + + Random random = new Random(0); + for (int i = 0; i < 200000; ++i) { + for (int j = 0; j < INCREASE_ACTIONS.length; ++j) { + int nextInt = random.nextInt(10000000); + MapAction action = INCREASE_ACTIONS[j]; + Object result = action.perform(testMap, nextInt); + Object referenceResult = action.perform(referenceMap, nextInt); + Assert.assertEquals(result, referenceResult); + } + } + } + + /** + * Tests a sequence of random operations on the map. + */ + @Test + public void testAddRemove() { + testMap.clear(); + referenceMap.clear(); + + for (int seed = 0; seed < 10; ++seed) { + Random random = new Random(seed); + int[] ranges = createRandomRange(random, ACTIONS.length); + int value = random.nextInt(10000); + for (int i = 0; i < value; ++i) { + for (int j = 0; j < ACTIONS.length; ++j) { + if (random.nextInt(ranges[j]) == 0) { + int nextInt = random.nextInt(100); + MapAction action = ACTIONS[j]; + Object result = action.perform(testMap, nextInt); + Object referenceResult = action.perform(referenceMap, nextInt); + Assert.assertEquals(result, referenceResult); + if (j % 100 == 0) { + checkEquality(testMap, referenceMap); + } + } + } + + if (random.nextInt(20) == 0) { + removeElement(random.nextInt(100), testMap, referenceMap); + } + } + } + } + + private static void removeElement(int index, EconomicMap<?, ?> map, EconomicMap<?, ?> referenceMap) { + Assert.assertEquals(referenceMap.size(), map.size()); + MapCursor<?, ?> cursor = map.getEntries(); + MapCursor<?, ?> referenceCursor = referenceMap.getEntries(); + int z = 0; + while (cursor.advance()) { + Assert.assertTrue(referenceCursor.advance()); + Assert.assertEquals(referenceCursor.getKey(), cursor.getKey()); + Assert.assertEquals(referenceCursor.getValue(), cursor.getValue()); + if (index == z) { + cursor.remove(); + referenceCursor.remove(); + } + ++z; + } + + Assert.assertFalse(referenceCursor.advance()); + } + + private static void checkEquality(EconomicMap<?, ?> map, EconomicMap<?, ?> referenceMap) { + Assert.assertEquals(referenceMap.size(), map.size()); + + // Check entries. + UnmodifiableMapCursor<?, ?> cursor = map.getEntries(); + UnmodifiableMapCursor<?, ?> referenceCursor = referenceMap.getEntries(); + while (cursor.advance()) { + Assert.assertTrue(referenceCursor.advance()); + Assert.assertEquals(referenceCursor.getKey(), cursor.getKey()); + Assert.assertEquals(referenceCursor.getValue(), cursor.getValue()); + } + + // Check keys. + Iterator<?> iterator = map.getKeys().iterator(); + Iterator<?> referenceIterator = referenceMap.getKeys().iterator(); + while (iterator.hasNext()) { + Assert.assertTrue(referenceIterator.hasNext()); + Assert.assertEquals(iterator.next(), referenceIterator.next()); + } + + // Check values. + iterator = map.getValues().iterator(); + referenceIterator = referenceMap.getValues().iterator(); + while (iterator.hasNext()) { + Assert.assertTrue(referenceIterator.hasNext()); + Assert.assertEquals(iterator.next(), referenceIterator.next()); + } + Assert.assertFalse(referenceIterator.hasNext()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections.test/src/org/graalvm/collections/test/EconomicMapTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections.test; + +import java.util.LinkedHashMap; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.UnmodifiableEconomicMap; +import org.junit.Assert; +import org.junit.Test; + +public class EconomicMapTest { + + @Test + public void testMapGetDefault() { + EconomicMap<Integer, Integer> map = EconomicMap.create(); + map.put(0, 1); + Assert.assertEquals(map.get(0, 2), Integer.valueOf(1)); + Assert.assertEquals(map.get(1, 2), Integer.valueOf(2)); + } + + @Test + public void testMapPutAll() { + EconomicMap<Integer, Integer> map = EconomicMap.create(); + EconomicMap<Integer, Integer> newMap = EconomicMap.wrapMap(new LinkedHashMap<>()); + newMap.put(1, 1); + newMap.put(2, 4); + map.putAll(newMap); + Assert.assertEquals(map.size(), 2); + + UnmodifiableEconomicMap<Integer, Integer> unmodifiableEconomicMap = EconomicMap.create(newMap); + + map.removeKey(1); + map.put(2, 2); + map.put(3, 9); + + map.putAll(unmodifiableEconomicMap); + Assert.assertEquals(map.size(), 3); + Assert.assertEquals(map.get(2), Integer.valueOf(4)); + } + + @Test + public void testToString() { + EconomicMap<Integer, Integer> map = EconomicMap.create(); + map.put(0, 0); + map.put(1, 1); + Assert.assertEquals(map.toString(), "map(size=2, {(0,0),(1,1)})"); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections.test/src/org/graalvm/collections/test/EconomicSetTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections.test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; + +import org.graalvm.collections.EconomicSet; +import org.graalvm.collections.Equivalence; +import org.junit.Assert; +import org.junit.Test; + +public class EconomicSetTest { + + @Test + public void testUtilities() { + EconomicSet<Integer> set = EconomicSet.create(0); + set.add(0); + Assert.assertTrue(set.add(1)); + Assert.assertEquals(set.size(), 2); + Assert.assertFalse(set.add(1)); + Assert.assertEquals(set.size(), 2); + set.remove(1); + Assert.assertEquals(set.size(), 1); + set.remove(2); + Assert.assertEquals(set.size(), 1); + Assert.assertTrue(set.add(1)); + set.clear(); + Assert.assertEquals(set.size(), 0); + } + + @Test + public void testAddAll() { + EconomicSet<Integer> set = EconomicSet.create(); + set.addAll(Arrays.asList(0, 1, 0)); + Assert.assertEquals(set.size(), 2); + + EconomicSet<Integer> newSet = EconomicSet.create(); + newSet.addAll(Arrays.asList(1, 2)); + Assert.assertEquals(newSet.size(), 2); + newSet.addAll(set); + Assert.assertEquals(newSet.size(), 3); + } + + @Test + public void testRemoveAll() { + EconomicSet<Integer> set = EconomicSet.create(); + set.addAll(Arrays.asList(0, 1)); + + set.removeAll(Arrays.asList(1, 2)); + Assert.assertEquals(set.size(), 1); + + set.removeAll(EconomicSet.create(set)); + Assert.assertEquals(set.size(), 0); + } + + @Test + public void testRetainAll() { + EconomicSet<Integer> set = EconomicSet.create(); + set.addAll(Arrays.asList(0, 1, 2)); + + EconomicSet<Integer> newSet = EconomicSet.create(); + newSet.addAll(Arrays.asList(2, 3)); + + set.retainAll(newSet); + Assert.assertEquals(set.size(), 1); + } + + @Test + public void testToArray() { + EconomicSet<Integer> set = EconomicSet.create(); + set.addAll(Arrays.asList(0, 1)); + Assert.assertArrayEquals(set.toArray(new Integer[2]), new Integer[]{0, 1}); + } + + @Test + public void testToString() { + EconomicSet<Integer> set = EconomicSet.create(); + set.addAll(Arrays.asList(0, 1)); + Assert.assertEquals(set.toString(), "set(size=2, {0,1})"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testToUnalignedArray() { + Assert.assertArrayEquals(EconomicSet.create().toArray(new Integer[2]), new Integer[0]); + } + + @Test + public void testSetRemoval() { + ArrayList<Integer> initialList = new ArrayList<>(); + ArrayList<Integer> removalList = new ArrayList<>(); + ArrayList<Integer> finalList = new ArrayList<>(); + EconomicSet<Integer> set = EconomicSet.create(Equivalence.IDENTITY); + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + set.add(6); + set.add(7); + set.add(8); + set.add(9); + Iterator<Integer> i1 = set.iterator(); + while (i1.hasNext()) { + initialList.add(i1.next()); + } + int size = 0; + Iterator<Integer> i2 = set.iterator(); + while (i2.hasNext()) { + Integer elem = i2.next(); + if (size++ < 8) { + i2.remove(); + } + removalList.add(elem); + } + Iterator<Integer> i3 = set.iterator(); + while (i3.hasNext()) { + finalList.add(i3.next()); + } + Assert.assertEquals(initialList, removalList); + Assert.assertEquals(1, finalList.size()); + Assert.assertEquals(new Integer(9), finalList.get(0)); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections.test/src/org/graalvm/collections/test/EquivalenceTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections.test; + +import org.graalvm.collections.Equivalence; +import org.junit.Assert; +import org.junit.Test; + +public class EquivalenceTest { + + private static final String TEST_STRING = "Graal"; + private static final String TEST_STRING2 = "Graal2"; + + @Test + public void testDEFAULT() { + Assert.assertTrue(Equivalence.DEFAULT.equals(TEST_STRING, new String(TEST_STRING))); + Assert.assertEquals(Equivalence.DEFAULT.hashCode(TEST_STRING), Equivalence.DEFAULT.hashCode(new String(TEST_STRING))); + Assert.assertFalse(Equivalence.DEFAULT.equals(TEST_STRING, TEST_STRING2)); + Assert.assertNotEquals(Equivalence.DEFAULT.hashCode(TEST_STRING), Equivalence.DEFAULT.hashCode(TEST_STRING2)); + } + + @Test + public void testIDENTITY() { + Assert.assertFalse(Equivalence.IDENTITY.equals(TEST_STRING, new String(TEST_STRING))); + Assert.assertEquals(Equivalence.IDENTITY.hashCode(TEST_STRING), Equivalence.IDENTITY.hashCode(new String(TEST_STRING))); + Assert.assertFalse(Equivalence.IDENTITY.equals(TEST_STRING, TEST_STRING2)); + Assert.assertNotEquals(Equivalence.IDENTITY.hashCode(TEST_STRING), Equivalence.IDENTITY.hashCode(TEST_STRING2)); + } + + @Test + public void testIDENTITYWITHSYSTEMHASHCODE() { + Assert.assertFalse(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE.equals(TEST_STRING, new String(TEST_STRING))); + Assert.assertNotEquals(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE.hashCode(TEST_STRING), Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE.hashCode(new String(TEST_STRING))); + Assert.assertFalse(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE.equals(TEST_STRING, TEST_STRING2)); + Assert.assertNotEquals(Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE.hashCode(TEST_STRING), Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE.hashCode(TEST_STRING2)); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections.test/src/org/graalvm/collections/test/PairTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections.test; + +import org.graalvm.collections.Pair; +import org.junit.Assert; +import org.junit.Test; + +public class PairTest { + + @Test + public void testCreate() { + Assert.assertEquals(Pair.create(null, null), Pair.empty()); + Assert.assertNotEquals(Pair.create(null, null), null); + Assert.assertEquals(Pair.createLeft(null), Pair.empty()); + Assert.assertEquals(Pair.createRight(null), Pair.empty()); + Assert.assertEquals(Pair.create(1, null), Pair.createLeft(1)); + Assert.assertEquals(Pair.create(null, 1), Pair.createRight(1)); + } + + @Test + public void testUtilities() { + Pair<Integer, Integer> pair = Pair.create(1, null); + Assert.assertEquals(pair.getLeft(), Integer.valueOf(1)); + Assert.assertEquals(pair.getRight(), null); + Assert.assertEquals(pair.toString(), "(1, null)"); + Assert.assertEquals(pair.hashCode(), Pair.createLeft(1).hashCode()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections/src/org/graalvm/collections/EconomicMap.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections; + +import java.util.Iterator; +import java.util.Map; +import java.util.function.BiFunction; + +/** + * Memory efficient map data structure. + * + * @since 1.0 + */ +public interface EconomicMap<K, V> extends UnmodifiableEconomicMap<K, V> { + + /** + * Associates {@code value} with {@code key} in this map. If the map previously contained a + * mapping for {@code key}, the old value is replaced by {@code value}. + * + * @return the previous value associated with {@code key}, or {@code null} if there was no + * mapping for {@code key}. + * @since 1.0 + */ + V put(K key, V value); + + /** + * Copies all of the mappings from {@code other} to this map. + * + * @since 1.0 + */ + default void putAll(EconomicMap<K, V> other) { + MapCursor<K, V> e = other.getEntries(); + while (e.advance()) { + put(e.getKey(), e.getValue()); + } + } + + /** + * Copies all of the mappings from {@code other} to this map. + * + * @since 1.0 + */ + default void putAll(UnmodifiableEconomicMap<? extends K, ? extends V> other) { + UnmodifiableMapCursor<? extends K, ? extends V> entry = other.getEntries(); + while (entry.advance()) { + put(entry.getKey(), entry.getValue()); + } + } + + /** + * Removes all of the mappings from this map. The map will be empty after this call returns. + * + * @since 1.0 + */ + void clear(); + + /** + * Removes the mapping for {@code key} from this map if it is present. The map will not contain + * a mapping for {@code key} once the call returns. + * + * @return the previous value associated with {@code key}, or {@code null} if there was no + * mapping for {@code key}. + * @since 1.0 + */ + V removeKey(K key); + + /** + * Returns a {@link MapCursor} view of the mappings contained in this map. + * + * @since 1.0 + */ + @Override + MapCursor<K, V> getEntries(); + + /** + * Replaces each entry's value with the result of invoking {@code function} on that entry until + * all entries have been processed or the function throws an exception. Exceptions thrown by the + * function are relayed to the caller. + * + * @since 1.0 + */ + void replaceAll(BiFunction<? super K, ? super V, ? extends V> function); + + /** + * Creates a new map that guarantees insertion order on the key set with the default + * {@link Equivalence#DEFAULT} comparison strategy for keys. + * + * @since 1.0 + */ + static <K, V> EconomicMap<K, V> create() { + return EconomicMap.create(Equivalence.DEFAULT); + } + + /** + * Creates a new map that guarantees insertion order on the key set with the default + * {@link Equivalence#DEFAULT} comparison strategy for keys and initializes with a specified + * capacity. + * + * @since 1.0 + */ + static <K, V> EconomicMap<K, V> create(int initialCapacity) { + return EconomicMap.create(Equivalence.DEFAULT, initialCapacity); + } + + /** + * Creates a new map that guarantees insertion order on the key set with the given comparison + * strategy for keys. + * + * @since 1.0 + */ + static <K, V> EconomicMap<K, V> create(Equivalence strategy) { + return EconomicMapImpl.create(strategy, false); + } + + /** + * Creates a new map that guarantees insertion order on the key set with the default + * {@link Equivalence#DEFAULT} comparison strategy for keys and copies all elements from the + * specified existing map. + * + * @since 1.0 + */ + static <K, V> EconomicMap<K, V> create(UnmodifiableEconomicMap<K, V> m) { + return EconomicMap.create(Equivalence.DEFAULT, m); + } + + /** + * Creates a new map that guarantees insertion order on the key set and copies all elements from + * the specified existing map. + * + * @since 1.0 + */ + static <K, V> EconomicMap<K, V> create(Equivalence strategy, UnmodifiableEconomicMap<K, V> m) { + return EconomicMapImpl.create(strategy, m, false); + } + + /** + * Creates a new map that guarantees insertion order on the key set and initializes with a + * specified capacity. + * + * @since 1.0 + */ + static <K, V> EconomicMap<K, V> create(Equivalence strategy, int initialCapacity) { + return EconomicMapImpl.create(strategy, initialCapacity, false); + } + + /** + * Wraps an existing {@link Map} as an {@link EconomicMap}. + * + * @since 1.0 + */ + static <K, V> EconomicMap<K, V> wrapMap(Map<K, V> map) { + return new EconomicMap<K, V>() { + + @Override + public V get(K key) { + V result = map.get(key); + return result; + } + + @Override + public V put(K key, V value) { + V result = map.put(key, value); + return result; + } + + @Override + public int size() { + int result = map.size(); + return result; + } + + @Override + public boolean containsKey(K key) { + return map.containsKey(key); + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public V removeKey(K key) { + V result = map.remove(key); + return result; + } + + @Override + public Iterable<V> getValues() { + return map.values(); + } + + @Override + public Iterable<K> getKeys() { + return map.keySet(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public MapCursor<K, V> getEntries() { + Iterator<java.util.Map.Entry<K, V>> iterator = map.entrySet().iterator(); + return new MapCursor<K, V>() { + + private Map.Entry<K, V> current; + + @Override + public boolean advance() { + boolean result = iterator.hasNext(); + if (result) { + current = iterator.next(); + } + + return result; + } + + @Override + public K getKey() { + return current.getKey(); + } + + @Override + public V getValue() { + return current.getValue(); + } + + @Override + public void remove() { + iterator.remove(); + } + }; + } + + @Override + public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { + map.replaceAll(function); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections/src/org/graalvm/collections/EconomicMapImpl.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,857 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections; + +import java.util.Iterator; +import java.util.Objects; +import java.util.function.BiFunction; + +/** + * Implementation of a map with a memory-efficient structure that always preserves insertion order + * when iterating over keys. Particularly efficient when number of entries is 0 or smaller equal + * {@link #INITIAL_CAPACITY} or smaller 256. + * + * The key/value pairs are kept in an expanding flat object array with keys at even indices and + * values at odd indices. If the map has smaller or equal to {@link #HASH_THRESHOLD} entries, there + * is no additional hash data structure and comparisons are done via linear checking of the + * key/value pairs. For the case where the equality check is particularly cheap (e.g., just an + * object identity comparison), this limit below which the map is without an actual hash table is + * higher and configured at {@link #HASH_THRESHOLD_IDENTITY_COMPARE}. + * + * When the hash table needs to be constructed, the field {@link #hashArray} becomes a new hash + * array where an entry of 0 means no hit and otherwise denotes the entry number in the + * {@link #entries} array. The hash array is interpreted as an actual byte array if the indices fit + * within 8 bit, or as an array of short values if the indices fit within 16 bit, or as an array of + * integer values in other cases. + * + * Hash collisions are handled by chaining a linked list of {@link CollisionLink} objects that take + * the place of the values in the {@link #entries} array. + * + * Removing entries will put {@code null} into the {@link #entries} array. If the occupation of the + * map falls below a specific threshold, the map will be compressed via the + * {@link #maybeCompress(int)} method. + */ +final class EconomicMapImpl<K, V> implements EconomicMap<K, V>, EconomicSet<K> { + + /** + * Initial number of key/value pair entries that is allocated in the first entries array. + */ + private static final int INITIAL_CAPACITY = 4; + + /** + * Maximum number of entries that are moved linearly forward if a key is removed. + */ + private static final int COMPRESS_IMMEDIATE_CAPACITY = 8; + + /** + * Minimum number of key/value pair entries added when the entries array is increased in size. + */ + private static final int MIN_CAPACITY_INCREASE = 8; + + /** + * Number of entries above which a hash table is created. + */ + private static final int HASH_THRESHOLD = 4; + + /** + * Number of entries above which a hash table is created when equality can be checked with + * object identity. + */ + private static final int HASH_THRESHOLD_IDENTITY_COMPARE = 8; + + /** + * Maximum number of entries allowed in the map. + */ + private static final int MAX_ELEMENT_COUNT = Integer.MAX_VALUE >> 1; + + /** + * Number of entries above which more than 1 byte is necessary for the hash index. + */ + private static final int LARGE_HASH_THRESHOLD = ((1 << Byte.SIZE) << 1); + + /** + * Number of entries above which more than 2 bytes are are necessary for the hash index. + */ + private static final int VERY_LARGE_HASH_THRESHOLD = (LARGE_HASH_THRESHOLD << Byte.SIZE); + + /** + * Total number of entries (actual entries plus deleted entries). + */ + private int totalEntries; + + /** + * Number of deleted entries. + */ + private int deletedEntries; + + /** + * Entries array with even indices storing keys and odd indices storing values. + */ + private Object[] entries; + + /** + * Hash array that is interpreted either as byte or short or int array depending on number of + * map entries. + */ + private byte[] hashArray; + + /** + * The strategy used for comparing keys or {@code null} for denoting special strategy + * {@link Equivalence#IDENTITY}. + */ + private final Equivalence strategy; + + /** + * Intercept method for debugging purposes. + */ + private static <K, V> EconomicMapImpl<K, V> intercept(EconomicMapImpl<K, V> map) { + return map; + } + + public static <K, V> EconomicMapImpl<K, V> create(Equivalence strategy, boolean isSet) { + return intercept(new EconomicMapImpl<>(strategy, isSet)); + } + + public static <K, V> EconomicMapImpl<K, V> create(Equivalence strategy, int initialCapacity, boolean isSet) { + return intercept(new EconomicMapImpl<>(strategy, initialCapacity, isSet)); + } + + public static <K, V> EconomicMapImpl<K, V> create(Equivalence strategy, UnmodifiableEconomicMap<K, V> other, boolean isSet) { + return intercept(new EconomicMapImpl<>(strategy, other, isSet)); + } + + public static <K, V> EconomicMapImpl<K, V> create(Equivalence strategy, UnmodifiableEconomicSet<K> other, boolean isSet) { + return intercept(new EconomicMapImpl<>(strategy, other, isSet)); + } + + private EconomicMapImpl(Equivalence strategy, boolean isSet) { + if (strategy == Equivalence.IDENTITY) { + this.strategy = null; + } else { + this.strategy = strategy; + } + this.isSet = isSet; + } + + private EconomicMapImpl(Equivalence strategy, int initialCapacity, boolean isSet) { + this(strategy, isSet); + init(initialCapacity); + } + + private EconomicMapImpl(Equivalence strategy, UnmodifiableEconomicMap<K, V> other, boolean isSet) { + this(strategy, isSet); + if (!initFrom(other)) { + init(other.size()); + putAll(other); + } + } + + private EconomicMapImpl(Equivalence strategy, UnmodifiableEconomicSet<K> other, boolean isSet) { + this(strategy, isSet); + if (!initFrom(other)) { + init(other.size()); + addAll(other); + } + } + + @SuppressWarnings("unchecked") + private boolean initFrom(Object o) { + if (o instanceof EconomicMapImpl) { + EconomicMapImpl<K, V> otherMap = (EconomicMapImpl<K, V>) o; + // We are only allowed to directly copy if the strategies of the two maps are the same. + if (strategy == otherMap.strategy) { + totalEntries = otherMap.totalEntries; + deletedEntries = otherMap.deletedEntries; + if (otherMap.entries != null) { + entries = otherMap.entries.clone(); + } + if (otherMap.hashArray != null) { + hashArray = otherMap.hashArray.clone(); + } + return true; + } + } + return false; + } + + private void init(int size) { + if (size > INITIAL_CAPACITY) { + entries = new Object[size << 1]; + } + } + + /** + * Links the collisions. Needs to be immutable class for allowing efficient shallow copy from + * other map on construction. + */ + private static final class CollisionLink { + + CollisionLink(Object value, int next) { + this.value = value; + this.next = next; + } + + final Object value; + + /** + * Index plus one of the next entry in the collision link chain. + */ + final int next; + } + + @SuppressWarnings("unchecked") + @Override + public V get(K key) { + Objects.requireNonNull(key); + + int index = find(key); + if (index != -1) { + return (V) getValue(index); + } + return null; + } + + private int find(K key) { + if (hasHashArray()) { + return findHash(key); + } else { + return findLinear(key); + } + } + + private int findLinear(K key) { + for (int i = 0; i < totalEntries; i++) { + Object entryKey = entries[i << 1]; + if (entryKey != null && compareKeys(key, entryKey)) { + return i; + } + } + return -1; + } + + private boolean compareKeys(Object key, Object entryKey) { + if (key == entryKey) { + return true; + } + if (strategy != null && strategy != Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE) { + if (strategy == Equivalence.DEFAULT) { + return key.equals(entryKey); + } else { + return strategy.equals(key, entryKey); + } + } + return false; + } + + private int findHash(K key) { + int index = getHashArray(getHashIndex(key)) - 1; + if (index != -1) { + Object entryKey = getKey(index); + if (compareKeys(key, entryKey)) { + return index; + } else { + Object entryValue = getRawValue(index); + if (entryValue instanceof CollisionLink) { + return findWithCollision(key, (CollisionLink) entryValue); + } + } + } + + return -1; + } + + private int findWithCollision(K key, CollisionLink initialEntryValue) { + int index; + Object entryKey; + CollisionLink entryValue = initialEntryValue; + while (true) { + CollisionLink collisionLink = entryValue; + index = collisionLink.next; + entryKey = getKey(index); + if (compareKeys(key, entryKey)) { + return index; + } else { + Object value = getRawValue(index); + if (value instanceof CollisionLink) { + entryValue = (CollisionLink) getRawValue(index); + } else { + return -1; + } + } + } + } + + private int getHashArray(int index) { + if (entries.length < LARGE_HASH_THRESHOLD) { + return (hashArray[index] & 0xFF); + } else if (entries.length < VERY_LARGE_HASH_THRESHOLD) { + int adjustedIndex = index << 1; + return (hashArray[adjustedIndex] & 0xFF) | ((hashArray[adjustedIndex + 1] & 0xFF) << 8); + } else { + int adjustedIndex = index << 2; + return (hashArray[adjustedIndex] & 0xFF) | ((hashArray[adjustedIndex + 1] & 0xFF) << 8) | ((hashArray[adjustedIndex + 2] & 0xFF) << 16) | ((hashArray[adjustedIndex + 3] & 0xFF) << 24); + } + } + + private void setHashArray(int index, int value) { + if (entries.length < LARGE_HASH_THRESHOLD) { + hashArray[index] = (byte) value; + } else if (entries.length < VERY_LARGE_HASH_THRESHOLD) { + int adjustedIndex = index << 1; + hashArray[adjustedIndex] = (byte) value; + hashArray[adjustedIndex + 1] = (byte) (value >> 8); + } else { + int adjustedIndex = index << 2; + hashArray[adjustedIndex] = (byte) value; + hashArray[adjustedIndex + 1] = (byte) (value >> 8); + hashArray[adjustedIndex + 2] = (byte) (value >> 16); + hashArray[adjustedIndex + 3] = (byte) (value >> 24); + } + } + + private int findAndRemoveHash(Object key) { + int hashIndex = getHashIndex(key); + int index = getHashArray(hashIndex) - 1; + if (index != -1) { + Object entryKey = getKey(index); + if (compareKeys(key, entryKey)) { + Object value = getRawValue(index); + int nextIndex = -1; + if (value instanceof CollisionLink) { + CollisionLink collisionLink = (CollisionLink) value; + nextIndex = collisionLink.next; + } + setHashArray(hashIndex, nextIndex + 1); + return index; + } else { + Object entryValue = getRawValue(index); + if (entryValue instanceof CollisionLink) { + return findAndRemoveWithCollision(key, (CollisionLink) entryValue, index); + } + } + } + + return -1; + } + + private int findAndRemoveWithCollision(Object key, CollisionLink initialEntryValue, int initialIndexValue) { + int index; + Object entryKey; + CollisionLink entryValue = initialEntryValue; + int lastIndex = initialIndexValue; + while (true) { + CollisionLink collisionLink = entryValue; + index = collisionLink.next; + entryKey = getKey(index); + if (compareKeys(key, entryKey)) { + Object value = getRawValue(index); + if (value instanceof CollisionLink) { + CollisionLink thisCollisionLink = (CollisionLink) value; + setRawValue(lastIndex, new CollisionLink(collisionLink.value, thisCollisionLink.next)); + } else { + setRawValue(lastIndex, collisionLink.value); + } + return index; + } else { + Object value = getRawValue(index); + if (value instanceof CollisionLink) { + entryValue = (CollisionLink) getRawValue(index); + lastIndex = index; + } else { + return -1; + } + } + } + } + + private int getHashIndex(Object key) { + int hash; + if (strategy != null && strategy != Equivalence.DEFAULT) { + if (strategy == Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE) { + hash = System.identityHashCode(key); + } else { + hash = strategy.hashCode(key); + } + } else { + hash = key.hashCode(); + } + hash = hash ^ (hash >>> 16); + return hash & (getHashTableSize() - 1); + } + + @SuppressWarnings("unchecked") + @Override + public V put(K key, V value) { + if (key == null) { + throw new UnsupportedOperationException("null not supported as key!"); + } + int index = find(key); + if (index != -1) { + Object oldValue = getValue(index); + setValue(index, value); + return (V) oldValue; + } + + int nextEntryIndex = totalEntries; + if (entries == null) { + entries = new Object[INITIAL_CAPACITY << 1]; + } else if (entries.length == nextEntryIndex << 1) { + grow(); + + assert entries.length > totalEntries << 1; + // Can change if grow is actually compressing. + nextEntryIndex = totalEntries; + } + + setKey(nextEntryIndex, key); + setValue(nextEntryIndex, value); + totalEntries++; + + if (hasHashArray()) { + // Rehash on collision if hash table is more than three quarters full. + boolean rehashOnCollision = (getHashTableSize() < (size() + (size() >> 1))); + putHashEntry(key, nextEntryIndex, rehashOnCollision); + } else if (totalEntries > getHashThreshold()) { + createHash(); + } + + return null; + } + + /** + * Number of entries above which a hash table should be constructed. + */ + private int getHashThreshold() { + if (strategy == null || strategy == Equivalence.IDENTITY_WITH_SYSTEM_HASHCODE) { + return HASH_THRESHOLD_IDENTITY_COMPARE; + } else { + return HASH_THRESHOLD; + } + } + + private void grow() { + int entriesLength = entries.length; + int newSize = (entriesLength >> 1) + Math.max(MIN_CAPACITY_INCREASE, entriesLength >> 2); + if (newSize > MAX_ELEMENT_COUNT) { + throw new UnsupportedOperationException("map grown too large!"); + } + Object[] newEntries = new Object[newSize << 1]; + System.arraycopy(entries, 0, newEntries, 0, entriesLength); + entries = newEntries; + if ((entriesLength < LARGE_HASH_THRESHOLD && newEntries.length >= LARGE_HASH_THRESHOLD) || + (entriesLength < VERY_LARGE_HASH_THRESHOLD && newEntries.length > VERY_LARGE_HASH_THRESHOLD)) { + // Rehash in order to change number of bits reserved for hash indices. + createHash(); + } + } + + /** + * Compresses the graph if there is a large number of deleted entries and returns the translated + * new next index. + */ + private int maybeCompress(int nextIndex) { + if (entries.length != INITIAL_CAPACITY << 1 && deletedEntries >= (totalEntries >> 1) + (totalEntries >> 2)) { + return compressLarge(nextIndex); + } + return nextIndex; + } + + /** + * Compresses the graph and returns the translated new next index. + */ + private int compressLarge(int nextIndex) { + int size = INITIAL_CAPACITY; + int remaining = totalEntries - deletedEntries; + + while (size <= remaining) { + size += Math.max(MIN_CAPACITY_INCREASE, size >> 1); + } + + Object[] newEntries = new Object[size << 1]; + int z = 0; + int newNextIndex = remaining; + for (int i = 0; i < totalEntries; ++i) { + Object key = getKey(i); + if (i == nextIndex) { + newNextIndex = z; + } + if (key != null) { + newEntries[z << 1] = key; + newEntries[(z << 1) + 1] = getValue(i); + z++; + } + } + + this.entries = newEntries; + totalEntries = z; + deletedEntries = 0; + if (z <= getHashThreshold()) { + this.hashArray = null; + } else { + createHash(); + } + return newNextIndex; + } + + private int getHashTableSize() { + if (entries.length < LARGE_HASH_THRESHOLD) { + return hashArray.length; + } else if (entries.length < VERY_LARGE_HASH_THRESHOLD) { + return hashArray.length >> 1; + } else { + return hashArray.length >> 2; + } + } + + private void createHash() { + int entryCount = size(); + + // Calculate smallest 2^n that is greater number of entries. + int size = getHashThreshold(); + while (size <= entryCount) { + size <<= 1; + } + + // Give extra size to avoid collisions. + size <<= 1; + + if (this.entries.length >= VERY_LARGE_HASH_THRESHOLD) { + // Every entry has 4 bytes. + size <<= 2; + } else if (this.entries.length >= LARGE_HASH_THRESHOLD) { + // Every entry has 2 bytes. + size <<= 1; + } else { + // Entries are very small => give extra size to further reduce collisions. + size <<= 1; + } + + hashArray = new byte[size]; + for (int i = 0; i < totalEntries; i++) { + Object entryKey = getKey(i); + if (entryKey != null) { + putHashEntry(entryKey, i, false); + } + } + } + + private void putHashEntry(Object key, int entryIndex, boolean rehashOnCollision) { + int hashIndex = getHashIndex(key); + int oldIndex = getHashArray(hashIndex) - 1; + if (oldIndex != -1 && rehashOnCollision) { + this.createHash(); + return; + } + setHashArray(hashIndex, entryIndex + 1); + Object value = getRawValue(entryIndex); + if (oldIndex != -1) { + assert entryIndex != oldIndex : "this cannot happend and would create an endless collision link cycle"; + if (value instanceof CollisionLink) { + CollisionLink collisionLink = (CollisionLink) value; + setRawValue(entryIndex, new CollisionLink(collisionLink.value, oldIndex)); + } else { + setRawValue(entryIndex, new CollisionLink(getRawValue(entryIndex), oldIndex)); + } + } else { + if (value instanceof CollisionLink) { + CollisionLink collisionLink = (CollisionLink) value; + setRawValue(entryIndex, collisionLink.value); + } + } + } + + @Override + public int size() { + return totalEntries - deletedEntries; + } + + @Override + public boolean containsKey(K key) { + return find(key) != -1; + } + + @Override + public void clear() { + entries = null; + hashArray = null; + totalEntries = deletedEntries = 0; + } + + private boolean hasHashArray() { + return hashArray != null; + } + + @SuppressWarnings("unchecked") + @Override + public V removeKey(K key) { + if (key == null) { + throw new UnsupportedOperationException("null not supported as key!"); + } + int index; + if (hasHashArray()) { + index = this.findAndRemoveHash(key); + } else { + index = this.findLinear(key); + } + + if (index != -1) { + Object value = getValue(index); + remove(index); + return (V) value; + } + return null; + } + + /** + * Removes the element at the specific index and returns the index of the next element. This can + * be a different value if graph compression was triggered. + */ + private int remove(int indexToRemove) { + int index = indexToRemove; + int entriesAfterIndex = totalEntries - index - 1; + int result = index + 1; + + // Without hash array, compress immediately. + if (entriesAfterIndex <= COMPRESS_IMMEDIATE_CAPACITY && !hasHashArray()) { + while (index < totalEntries - 1) { + setKey(index, getKey(index + 1)); + setRawValue(index, getRawValue(index + 1)); + index++; + } + result--; + } + + setKey(index, null); + setRawValue(index, null); + if (index == totalEntries - 1) { + // Make sure last element is always non-null. + totalEntries--; + while (index > 0 && getKey(index - 1) == null) { + totalEntries--; + deletedEntries--; + index--; + } + } else { + deletedEntries++; + result = maybeCompress(result); + } + + return result; + } + + private abstract class SparseMapIterator<E> implements Iterator<E> { + + protected int current; + + @Override + public boolean hasNext() { + return current < totalEntries; + } + + @Override + public void remove() { + if (hasHashArray()) { + EconomicMapImpl.this.findAndRemoveHash(getKey(current - 1)); + } + current = EconomicMapImpl.this.remove(current - 1); + } + } + + @Override + public Iterable<V> getValues() { + return new Iterable<V>() { + @Override + public Iterator<V> iterator() { + return new SparseMapIterator<V>() { + @SuppressWarnings("unchecked") + @Override + public V next() { + Object result; + while (true) { + result = getValue(current); + if (result == null && getKey(current) == null) { + // values can be null, double-check if key is also null + current++; + } else { + current++; + break; + } + } + return (V) result; + } + }; + } + }; + } + + @Override + public Iterable<K> getKeys() { + return this; + } + + @Override + public boolean isEmpty() { + return this.size() == 0; + } + + @Override + public MapCursor<K, V> getEntries() { + return new MapCursor<K, V>() { + int current = -1; + + @Override + public boolean advance() { + current++; + if (current >= totalEntries) { + return false; + } else { + while (EconomicMapImpl.this.getKey(current) == null) { + // Skip over null entries + current++; + } + return true; + } + } + + @SuppressWarnings("unchecked") + @Override + public K getKey() { + return (K) EconomicMapImpl.this.getKey(current); + } + + @SuppressWarnings("unchecked") + @Override + public V getValue() { + return (V) EconomicMapImpl.this.getValue(current); + } + + @Override + public void remove() { + if (hasHashArray()) { + EconomicMapImpl.this.findAndRemoveHash(EconomicMapImpl.this.getKey(current)); + } + current = EconomicMapImpl.this.remove(current) - 1; + } + }; + } + + @SuppressWarnings("unchecked") + @Override + public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { + for (int i = 0; i < totalEntries; i++) { + Object entryKey = getKey(i); + if (entryKey != null) { + Object newValue = function.apply((K) entryKey, (V) getValue(i)); + setValue(i, newValue); + } + } + } + + private Object getKey(int index) { + return entries[index << 1]; + } + + private void setKey(int index, Object newValue) { + entries[index << 1] = newValue; + } + + private void setValue(int index, Object newValue) { + Object oldValue = getRawValue(index); + if (oldValue instanceof CollisionLink) { + CollisionLink collisionLink = (CollisionLink) oldValue; + setRawValue(index, new CollisionLink(newValue, collisionLink.next)); + } else { + setRawValue(index, newValue); + } + } + + private void setRawValue(int index, Object newValue) { + entries[(index << 1) + 1] = newValue; + } + + private Object getRawValue(int index) { + return entries[(index << 1) + 1]; + } + + private Object getValue(int index) { + Object object = getRawValue(index); + if (object instanceof CollisionLink) { + return ((CollisionLink) object).value; + } + return object; + } + + private final boolean isSet; + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(isSet ? "set(size=" : "map(size=").append(size()).append(", {"); + String sep = ""; + MapCursor<K, V> cursor = getEntries(); + while (cursor.advance()) { + builder.append(sep); + if (isSet) { + builder.append(cursor.getKey()); + } else { + builder.append("(").append(cursor.getKey()).append(",").append(cursor.getValue()).append(")"); + } + sep = ","; + } + builder.append("})"); + return builder.toString(); + } + + @Override + public Iterator<K> iterator() { + return new SparseMapIterator<K>() { + @SuppressWarnings("unchecked") + @Override + public K next() { + Object result; + while ((result = getKey(current++)) == null) { + // skip null entries + } + return (K) result; + } + }; + } + + @Override + public boolean contains(K element) { + return containsKey(element); + } + + @SuppressWarnings("unchecked") + @Override + public boolean add(K element) { + return put(element, (V) element) == null; + } + + @Override + public void remove(K element) { + removeKey(element); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections/src/org/graalvm/collections/EconomicSet.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections; + +import java.util.Iterator; + +/** + * Memory efficient set data structure. + * + * @since 1.0 + */ +public interface EconomicSet<E> extends UnmodifiableEconomicSet<E> { + + /** + * Adds {@code element} to this set if it is not already present. + * + * @return {@code true} if this set did not already contain {@code element}. + * @since 1.0 + */ + boolean add(E element); + + /** + * Removes {@code element} from this set if it is present. This set will not contain + * {@code element} once the call returns. + * + * @since 1.0 + */ + void remove(E element); + + /** + * Removes all of the elements from this set. The set will be empty after this call returns. + * + * @since 1.0 + */ + void clear(); + + /** + * Adds all of the elements in {@code other} to this set if they're not already present. + * + * @since 1.0 + */ + default void addAll(EconomicSet<E> other) { + addAll(other.iterator()); + } + + /** + * Adds all of the elements in {@code values} to this set if they're not already present. + * + * @since 1.0 + */ + default void addAll(Iterable<E> values) { + addAll(values.iterator()); + } + + /** + * Adds all of the elements enumerated by {@code iterator} to this set if they're not already + * present. + * + * @since 1.0 + */ + default void addAll(Iterator<E> iterator) { + while (iterator.hasNext()) { + add(iterator.next()); + } + } + + /** + * Removes from this set all of its elements that are contained in {@code other}. + * + * @since 1.0 + */ + default void removeAll(EconomicSet<E> other) { + removeAll(other.iterator()); + } + + /** + * Removes from this set all of its elements that are contained in {@code values}. + * + * @since 1.0 + */ + default void removeAll(Iterable<E> values) { + removeAll(values.iterator()); + } + + /** + * Removes from this set all of its elements that are enumerated by {@code iterator}. + * + * @since 1.0 + */ + default void removeAll(Iterator<E> iterator) { + while (iterator.hasNext()) { + remove(iterator.next()); + } + } + + /** + * Removes from this set all of its elements that are not contained in {@code other}. + * + * @since 1.0 + */ + default void retainAll(EconomicSet<E> other) { + Iterator<E> iterator = iterator(); + while (iterator.hasNext()) { + E key = iterator.next(); + if (!other.contains(key)) { + iterator.remove(); + } + } + } + + /** + * Creates a new set guaranteeing insertion order when iterating over its elements with the + * default {@link Equivalence#DEFAULT} comparison strategy. + * + * @since 1.0 + */ + static <E> EconomicSet<E> create() { + return EconomicSet.create(Equivalence.DEFAULT); + } + + /** + * Creates a new set guaranteeing insertion order when iterating over its elements. + * + * @since 1.0 + */ + static <E> EconomicSet<E> create(Equivalence strategy) { + return EconomicMapImpl.create(strategy, true); + } + + /** + * Creates a new set guaranteeing insertion order when iterating over its elements with the + * default {@link Equivalence#DEFAULT} comparison strategy and inserts all elements of the + * specified collection. + * + * @since 1.0 + */ + static <E> EconomicSet<E> create(int initialCapacity) { + return EconomicSet.create(Equivalence.DEFAULT, initialCapacity); + } + + /** + * Creates a new set guaranteeing insertion order when iterating over its elements with the + * default {@link Equivalence#DEFAULT} comparison strategy and inserts all elements of the + * specified collection. + * + * @since 1.0 + */ + static <E> EconomicSet<E> create(UnmodifiableEconomicSet<E> c) { + return EconomicSet.create(Equivalence.DEFAULT, c); + } + + /** + * Creates a new set guaranteeing insertion order when iterating over its elements and + * initializes with the given capacity. + * + * @since 1.0 + */ + static <E> EconomicSet<E> create(Equivalence strategy, int initialCapacity) { + return EconomicMapImpl.create(strategy, initialCapacity, true); + } + + /** + * Creates a new set guaranteeing insertion order when iterating over its elements and inserts + * all elements of the specified collection. + * + * @since 1.0 + */ + static <E> EconomicSet<E> create(Equivalence strategy, UnmodifiableEconomicSet<E> c) { + return EconomicMapImpl.create(strategy, c, true); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections/src/org/graalvm/collections/Equivalence.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections; + +/** + * Strategy for comparing two objects. Default predefined strategies are {@link #DEFAULT}, + * {@link #IDENTITY}, and {@link #IDENTITY_WITH_SYSTEM_HASHCODE}. + * + * @since 1.0 + */ +public abstract class Equivalence { + + /** + * Default equivalence calling {@link #equals(Object)} to check equality and {@link #hashCode()} + * for obtaining hash values. Do not change the logic of this class as it may be inlined in + * other places. + * + * @since 1.0 + */ + public static final Equivalence DEFAULT = new Equivalence() { + + @Override + public boolean equals(Object a, Object b) { + return a.equals(b); + } + + @Override + public int hashCode(Object o) { + return o.hashCode(); + } + }; + + /** + * Identity equivalence using {@code ==} to check equality and {@link #hashCode()} for obtaining + * hash values. Do not change the logic of this class as it may be inlined in other places. + * + * @since 1.0 + */ + public static final Equivalence IDENTITY = new Equivalence() { + + @Override + public boolean equals(Object a, Object b) { + return a == b; + } + + @Override + public int hashCode(Object o) { + return o.hashCode(); + } + }; + + /** + * Identity equivalence using {@code ==} to check equality and + * {@link System#identityHashCode(Object)} for obtaining hash values. Do not change the logic of + * this class as it may be inlined in other places. + * + * @since 1.0 + */ + public static final Equivalence IDENTITY_WITH_SYSTEM_HASHCODE = new Equivalence() { + + @Override + public boolean equals(Object a, Object b) { + return a == b; + } + + @Override + public int hashCode(Object o) { + return System.identityHashCode(o); + } + }; + + /** + * Subclass for creating custom equivalence definitions. + * + * @since 1.0 + */ + protected Equivalence() { + } + + /** + * Returns {@code true} if the non-{@code null} arguments are equal to each other and + * {@code false} otherwise. + * + * @since 1.0 + */ + public abstract boolean equals(Object a, Object b); + + /** + * Returns the hash code of a non-{@code null} argument {@code o}. + * + * @since 1.0 + */ + public abstract int hashCode(Object o); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections/src/org/graalvm/collections/MapCursor.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections; + +/** + * Cursor to iterate over a mutable map. + * + * @since 1.0 + */ +public interface MapCursor<K, V> extends UnmodifiableMapCursor<K, V> { + /** + * Remove the current entry from the map. May only be called once. After calling + * {@link #remove()}, it is no longer valid to call {@link #getKey()} or {@link #getValue()} on + * the current entry. + * + * @since 1.0 + */ + void remove(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections/src/org/graalvm/collections/Pair.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections; + +import java.util.Objects; + +/** + * Utility class representing a pair of values. + * + * @since 1.0 + */ +public final class Pair<L, R> { + + private static final Pair<Object, Object> EMPTY = new Pair<>(null, null); + + private final L left; + private final R right; + + /** + * Returns an empty pair. + * + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static <L, R> Pair<L, R> empty() { + return (Pair<L, R>) EMPTY; + } + + /** + * Constructs a pair with its left value being {@code left}, or returns an empty pair if + * {@code left} is null. + * + * @return the constructed pair or an empty pair if {@code left} is null. + * @since 1.0 + */ + public static <L, R> Pair<L, R> createLeft(L left) { + if (left == null) { + return empty(); + } else { + return new Pair<>(left, null); + } + } + + /** + * Constructs a pair with its right value being {@code right}, or returns an empty pair if + * {@code right} is null. + * + * @return the constructed pair or an empty pair if {@code right} is null. + * @since 1.0 + */ + public static <L, R> Pair<L, R> createRight(R right) { + if (right == null) { + return empty(); + } else { + return new Pair<>(null, right); + } + } + + /** + * Constructs a pair with its left value being {@code left}, and its right value being + * {@code right}, or returns an empty pair if both inputs are null. + * + * @return the constructed pair or an empty pair if both inputs are null. + * @since 1.0 + */ + public static <L, R> Pair<L, R> create(L left, R right) { + if (right == null && left == null) { + return empty(); + } else { + return new Pair<>(left, right); + } + } + + private Pair(L left, R right) { + this.left = left; + this.right = right; + } + + /** + * Returns the left value of this pair. + * + * @since 1.0 + */ + public L getLeft() { + return left; + } + + /** + * Returns the right value of this pair. + * + * @since 1.0 + */ + public R getRight() { + return right; + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + @Override + public int hashCode() { + return Objects.hashCode(left) + 31 * Objects.hashCode(right); + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj instanceof Pair) { + Pair<L, R> pair = (Pair<L, R>) obj; + return Objects.equals(left, pair.left) && Objects.equals(right, pair.right); + } + + return false; + } + + /** + * {@inheritDoc} + * + * @since 1.0 + */ + @Override + public String toString() { + return String.format("(%s, %s)", left, right); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections/src/org/graalvm/collections/UnmodifiableEconomicMap.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections; + +/** + * Unmodifiable memory efficient map data structure. + * + * @since 1.0 + */ +public interface UnmodifiableEconomicMap<K, V> { + + /** + * Returns the value to which {@code key} is mapped, or {@code null} if this map contains no + * mapping for {@code key}. + * + * @since 1.0 + */ + V get(K key); + + /** + * Returns the value to which {@code key} is mapped, or {@code defaultValue} if this map + * contains no mapping for {@code key}. + * + * @since 1.0 + */ + default V get(K key, V defaultValue) { + V v = get(key); + if (v == null) { + return defaultValue; + } + return v; + } + + /** + * Returns {@code true} if this map contains a mapping for {@code key}. + * + * @since 1.0 + */ + boolean containsKey(K key); + + /** + * Returns the number of key-value mappings in this map. + * + * @since 1.0 + */ + int size(); + + /** + * Returns {@code true} if this map contains no key-value mappings. + * + * @since 1.0 + */ + boolean isEmpty(); + + /** + * Returns a {@link Iterable} view of the values contained in this map. + * + * @since 1.0 + */ + Iterable<V> getValues(); + + /** + * Returns a {@link Iterable} view of the keys contained in this map. + * + * @since 1.0 + */ + Iterable<K> getKeys(); + + /** + * Returns a {@link UnmodifiableMapCursor} view of the mappings contained in this map. + * + * @since 1.0 + */ + UnmodifiableMapCursor<K, V> getEntries(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections/src/org/graalvm/collections/UnmodifiableEconomicSet.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections; + +/** + * Unmodifiable memory efficient set data structure. + * + * @since 1.0 + */ +public interface UnmodifiableEconomicSet<E> extends Iterable<E> { + + /** + * Returns {@code true} if this set contains a mapping for the {@code element}. + * + * @since 1.0 + */ + boolean contains(E element); + + /** + * Returns the number of elements in this set. + * + * @since 1.0 + */ + int size(); + + /** + * Returns {@code true} if this set contains no elements. + * + * @since 1.0 + */ + boolean isEmpty(); + + /** + * Stores all of the elements in this set into {@code target}. An + * {@link UnsupportedOperationException} will be thrown if the length of {@code target} does not + * match the size of this set. + * + * @return an array containing all the elements in this set. + * @throws UnsupportedOperationException if the length of {@code target} does not equal the size + * of this set. + * @since 1.0 + */ + default E[] toArray(E[] target) { + if (target.length != size()) { + throw new UnsupportedOperationException("Length of target array must equal the size of the set."); + } + + int index = 0; + for (E element : this) { + target[index++] = element; + } + + return target; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections/src/org/graalvm/collections/UnmodifiableMapCursor.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, 2017, 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 org.graalvm.collections; + +/** + * Cursor to iterate over a map without changing its contents. + * + * @since 1.0 + */ +public interface UnmodifiableMapCursor<K, V> { + /** + * Advances to the next entry. + * + * @return {@code true} if a next entry exists, {@code false} if there is no next entry. + * @since 1.0 + */ + boolean advance(); + + /** + * The key of the current entry. + * + * @since 1.0 + */ + K getKey(); + + /** + * The value of the current entry. + * + * @since 1.0 + */ + V getValue(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.collections/src/org/graalvm/collections/package-info.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, 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. + */ +/** + * The Graal-SDK collections package contains memory efficient data structures. + * + * @see org.graalvm.collections.EconomicMap + * @see org.graalvm.collections.EconomicSet + * + * @since 1.0 + */ +package org.graalvm.collections;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ProbabilityDirectiveTest.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives.test/src/org/graalvm/compiler/api/directives/test/ProbabilityDirectiveTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -50,6 +50,21 @@ test("branchProbabilitySnippet", 5); } + public static int branchProbabilitySnippet2(int arg) { + if (!GraalDirectives.injectBranchProbability(0.125, arg <= 0)) { + GraalDirectives.controlFlowAnchor(); // prevent removal of the if + return 2; + } else { + GraalDirectives.controlFlowAnchor(); // prevent removal of the if + return 1; + } + } + + @Test + public void testBranchProbability2() { + test("branchProbabilitySnippet2", 5); + } + @Override protected boolean checkLowTierGraph(StructuredGraph graph) { NodeIterable<IfNode> ifNodes = graph.getNodes(IfNode.TYPE);
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives/src/org/graalvm/compiler/api/directives/GraalDirectives.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.directives/src/org/graalvm/compiler/api/directives/GraalDirectives.java Fri Feb 16 13:49:07 2018 -0800 @@ -50,6 +50,13 @@ } /** + * Directive for the compiler to fall back to the bytecode interpreter at this point, invalidate + * the compiled code, record a speculation and reprofile the method. + */ + public static void deoptimizeAndInvalidateWithSpeculation() { + } + + /** * Returns a boolean value indicating whether the method is executed in Graal-compiled code. */ public static boolean inCompiledCode() {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/MethodSubstitution.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/MethodSubstitution.java Fri Feb 16 13:49:07 2018 -0800 @@ -26,15 +26,52 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.Array; import jdk.vm.ci.meta.Signature; /** * Denotes a method whose body is used by a compiler as the substitute (or intrinsification) of - * another method. The exact method used to do the substitution is compiler dependent but every + * another method. The exact mechanism used to do the substitution is compiler dependent but every * compiler should require substitute methods to be annotated with {@link MethodSubstitution}. In * addition, a compiler is recommended to implement {@link MethodSubstitutionRegistry} to advertise * the mechanism by which it supports registration of method substitutes. + * + * A compiler may support partial intrinsification where only a part of a method is implemented by + * the compiler. The unsupported path is expressed by a call to either the original or substitute + * method from within the substitute method. Such as call is a <i>partial intrinsic exit</i>. + * + * For example, here's a HotSpot specific intrinsic for {@link Array#newInstance(Class, int)} that + * only handles the case where the VM representation of the array class to be instantiated already + * exists: + * + * <pre> + * @MethodSubstitution + * public static Object newInstance(Class<?> componentType, int length) { + * if (componentType == null || loadKlassFromObject(componentType, arrayKlassOffset(INJECTED_VMCONFIG), CLASS_ARRAY_KLASS_LOCATION).isNull()) { + * // Array class not yet created - exit the intrinsic and call the original method + * return newInstance(componentType, length); + * } + * return DynamicNewArrayNode.newArray(GraalDirectives.guardingNonNull(componentType), length, JavaKind.Object); + * } + * </pre> + * + * Here's the same intrinsification where the exit is expressed as a call to the original method: + * + * <pre> + * @MethodSubstitution + * public static Object newInstance(Class<?> componentType, int length) { + * if (componentType == null || loadKlassFromObject(componentType, arrayKlassOffset(INJECTED_VMCONFIG), CLASS_ARRAY_KLASS_LOCATION).isNull()) { + * // Array class not yet created - exit the intrinsic and call the original method + * return java.lang.reflect.newInstance(componentType, length); + * } + * return DynamicNewArrayNode.newArray(GraalDirectives.guardingNonNull(componentType), length, JavaKind.Object); + * } + * </pre> + * + * A condition for a partial intrinsic exit is that it is uses the unmodified parameters of the + * substitute as arguments to the partial intrinsic exit call. There must also be no side effecting + * instruction between the start of the substitute method and the partial intrinsic exit. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/SnippetReflectionProvider.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.replacements/src/org/graalvm/compiler/api/replacements/SnippetReflectionProvider.java Fri Feb 16 13:49:07 2018 -0800 @@ -90,4 +90,12 @@ * if this provider cannot provide a value of the requested type */ <T> T getInjectedNodeIntrinsicParameter(Class<T> type); + + /** + * Get the original Java class corresponding to a {@link ResolvedJavaType}. + * + * @param type the type for which the original Java class is requested + * @return the original Java class corresponding to the {@code type} parameter + */ + Class<?> originalClass(ResolvedJavaType type); }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.runtime/src/org/graalvm/compiler/api/runtime/GraalRuntime.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.api.runtime/src/org/graalvm/compiler/api/runtime/GraalRuntime.java Fri Feb 16 13:49:07 2018 -0800 @@ -22,9 +22,19 @@ */ package org.graalvm.compiler.api.runtime; +import jdk.vm.ci.common.JVMCIError; + public interface GraalRuntime { String getName(); <T> T getCapability(Class<T> clazz); + + default <T> T getRequiredCapability(Class<T> clazz) { + T ret = getCapability(clazz); + if (ret == null) { + throw new JVMCIError("The VM does not expose the required Graal capability %s.", clazz.getName()); + } + return ret; + } }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java Fri Feb 16 13:49:07 2018 -0800 @@ -208,7 +208,6 @@ } private static class VexOpcode { - private static final int VEX_OPCODE_NONE = 0x0; private static final int VEX_OPCODE_0F = 0x1; private static final int VEX_OPCODE_0F_38 = 0x2; private static final int VEX_OPCODE_0F_3A = 0x3; @@ -861,9 +860,26 @@ break; } + int opc = 0; + if (isSimd) { + switch (prefix2) { + case P_0F: + opc = VexOpcode.VEX_OPCODE_0F; + break; + case P_0F38: + opc = VexOpcode.VEX_OPCODE_0F_38; + break; + case P_0F3A: + opc = VexOpcode.VEX_OPCODE_0F_3A; + break; + default: + isSimd = false; + break; + } + } + if (isSimd) { int pre; - int opc; boolean rexVexW = (size == QWORD) ? true : false; AMD64InstructionAttr attributes = new AMD64InstructionAttr(AvxVectorLen.AVX_128bit, rexVexW, /* legacyMode */ false, /* noMaskReg */ false, /* usesVl */ false, asm.target); int curPrefix = size.sizePrefix | prefix1; @@ -881,20 +897,6 @@ pre = VexSimdPrefix.VEX_SIMD_NONE; break; } - switch (prefix2) { - case P_0F: - opc = VexOpcode.VEX_OPCODE_0F; - break; - case P_0F38: - opc = VexOpcode.VEX_OPCODE_0F_38; - break; - case P_0F3A: - opc = VexOpcode.VEX_OPCODE_0F_3A; - break; - default: - opc = VexOpcode.VEX_OPCODE_NONE; - break; - } int encode; if (noNds) { encode = asm.simdPrefixAndEncode(dst, Register.None, src, pre, opc, attributes); @@ -938,9 +940,26 @@ break; } + int opc = 0; + if (isSimd) { + switch (prefix2) { + case P_0F: + opc = VexOpcode.VEX_OPCODE_0F; + break; + case P_0F38: + opc = VexOpcode.VEX_OPCODE_0F_38; + break; + case P_0F3A: + opc = VexOpcode.VEX_OPCODE_0F_3A; + break; + default: + isSimd = false; + break; + } + } + if (isSimd) { int pre; - int opc; boolean rexVexW = (size == QWORD) ? true : false; AMD64InstructionAttr attributes = new AMD64InstructionAttr(AvxVectorLen.AVX_128bit, rexVexW, /* legacyMode */ false, /* noMaskReg */ false, /* usesVl */ false, asm.target); int curPrefix = size.sizePrefix | prefix1; @@ -958,20 +977,6 @@ pre = VexSimdPrefix.VEX_SIMD_NONE; break; } - switch (prefix2) { - case P_0F: - opc = VexOpcode.VEX_OPCODE_0F; - break; - case P_0F38: - opc = VexOpcode.VEX_OPCODE_0F_38; - break; - case P_0F3A: - opc = VexOpcode.VEX_OPCODE_0F_3A; - break; - default: - opc = VexOpcode.VEX_OPCODE_NONE; - break; - } if (noNds) { asm.simdPrefix(dst, Register.None, src, pre, opc, attributes); } else { @@ -1055,8 +1060,7 @@ opc = VexOpcode.VEX_OPCODE_0F_3A; break; default: - opc = VexOpcode.VEX_OPCODE_NONE; - break; + throw GraalError.shouldNotReachHere("invalid VEX instruction prefix"); } int encode; encode = asm.simdPrefixAndEncode(dst, nds, src, pre, opc, attributes); @@ -1096,8 +1100,7 @@ opc = VexOpcode.VEX_OPCODE_0F_3A; break; default: - opc = VexOpcode.VEX_OPCODE_NONE; - break; + throw GraalError.shouldNotReachHere("invalid VEX instruction prefix"); } asm.simdPrefix(dst, nds, src, pre, opc, attributes); asm.emitByte(op); @@ -1163,9 +1166,26 @@ break; } + int opc = 0; + if (isSimd) { + switch (prefix2) { + case P_0F: + opc = VexOpcode.VEX_OPCODE_0F; + break; + case P_0F38: + opc = VexOpcode.VEX_OPCODE_0F_38; + break; + case P_0F3A: + opc = VexOpcode.VEX_OPCODE_0F_3A; + break; + default: + isSimd = false; + break; + } + } + if (isSimd) { int pre; - int opc; boolean rexVexW = (size == QWORD) ? true : false; AMD64InstructionAttr attributes = new AMD64InstructionAttr(AvxVectorLen.AVX_128bit, rexVexW, /* legacyMode */ false, /* noMaskReg */ false, /* usesVl */ false, asm.target); int curPrefix = size.sizePrefix | prefix1; @@ -1183,20 +1203,6 @@ pre = VexSimdPrefix.VEX_SIMD_NONE; break; } - switch (prefix2) { - case P_0F: - opc = VexOpcode.VEX_OPCODE_0F; - break; - case P_0F38: - opc = VexOpcode.VEX_OPCODE_0F_38; - break; - case P_0F3A: - opc = VexOpcode.VEX_OPCODE_0F_3A; - break; - default: - opc = VexOpcode.VEX_OPCODE_NONE; - break; - } int encode; if (noNds) { encode = asm.simdPrefixAndEncode(src, Register.None, dst, pre, opc, attributes); @@ -1222,9 +1228,26 @@ break; } + int opc = 0; + if (isSimd) { + switch (prefix2) { + case P_0F: + opc = VexOpcode.VEX_OPCODE_0F; + break; + case P_0F38: + opc = VexOpcode.VEX_OPCODE_0F_38; + break; + case P_0F3A: + opc = VexOpcode.VEX_OPCODE_0F_3A; + break; + default: + isSimd = false; + break; + } + } + if (isSimd) { int pre; - int opc; boolean rexVexW = (size == QWORD) ? true : false; AMD64InstructionAttr attributes = new AMD64InstructionAttr(AvxVectorLen.AVX_128bit, rexVexW, /* legacyMode */ false, /* noMaskReg */ false, /* usesVl */ false, asm.target); int curPrefix = size.sizePrefix | prefix1; @@ -1242,20 +1265,6 @@ pre = VexSimdPrefix.VEX_SIMD_NONE; break; } - switch (prefix2) { - case P_0F: - opc = VexOpcode.VEX_OPCODE_0F; - break; - case P_0F38: - opc = VexOpcode.VEX_OPCODE_0F_38; - break; - case P_0F3A: - opc = VexOpcode.VEX_OPCODE_0F_3A; - break; - default: - opc = VexOpcode.VEX_OPCODE_NONE; - break; - } asm.simdPrefix(src, Register.None, dst, pre, opc, attributes); asm.emitByte(op); asm.emitOperandHelper(src, dst, 0); @@ -1390,9 +1399,26 @@ break; } + int opc = 0; + if (isSimd) { + switch (prefix2) { + case P_0F: + opc = VexOpcode.VEX_OPCODE_0F; + break; + case P_0F38: + opc = VexOpcode.VEX_OPCODE_0F_38; + break; + case P_0F3A: + opc = VexOpcode.VEX_OPCODE_0F_3A; + break; + default: + isSimd = false; + break; + } + } + if (isSimd) { int pre; - int opc; AMD64InstructionAttr attributes = new AMD64InstructionAttr(AvxVectorLen.AVX_128bit, /* rexVexW */ false, /* legacyMode */ false, /* noMaskReg */ false, /* usesVl */ false, asm.target); int curPrefix = size.sizePrefix | prefix1; switch (curPrefix) { @@ -1409,20 +1435,6 @@ pre = VexSimdPrefix.VEX_SIMD_NONE; break; } - switch (prefix2) { - case P_0F: - opc = VexOpcode.VEX_OPCODE_0F; - break; - case P_0F38: - opc = VexOpcode.VEX_OPCODE_0F_38; - break; - case P_0F3A: - opc = VexOpcode.VEX_OPCODE_0F_3A; - break; - default: - opc = VexOpcode.VEX_OPCODE_NONE; - break; - } int encode; if (noNds) { encode = asm.simdPrefixAndEncode(dst, Register.None, src, pre, opc, attributes); @@ -1453,9 +1465,26 @@ break; } + int opc = 0; + if (isSimd) { + switch (prefix2) { + case P_0F: + opc = VexOpcode.VEX_OPCODE_0F; + break; + case P_0F38: + opc = VexOpcode.VEX_OPCODE_0F_38; + break; + case P_0F3A: + opc = VexOpcode.VEX_OPCODE_0F_3A; + break; + default: + isSimd = false; + break; + } + } + if (isSimd) { int pre; - int opc; AMD64InstructionAttr attributes = new AMD64InstructionAttr(AvxVectorLen.AVX_128bit, /* rexVexW */ false, /* legacyMode */ false, /* noMaskReg */ false, /* usesVl */ false, asm.target); int curPrefix = size.sizePrefix | prefix1; switch (curPrefix) { @@ -1472,21 +1501,6 @@ pre = VexSimdPrefix.VEX_SIMD_NONE; break; } - switch (prefix2) { - case P_0F: - opc = VexOpcode.VEX_OPCODE_0F; - break; - case P_0F38: - opc = VexOpcode.VEX_OPCODE_0F_38; - break; - case P_0F3A: - opc = VexOpcode.VEX_OPCODE_0F_3A; - break; - default: - opc = VexOpcode.VEX_OPCODE_NONE; - break; - } - if (noNds) { asm.simdPrefix(dst, Register.None, src, pre, opc, attributes); } else {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.code/src/org/graalvm/compiler/code/CompilationResult.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.code/src/org/graalvm/compiler/code/CompilationResult.java Fri Feb 16 13:49:07 2018 -0800 @@ -33,9 +33,9 @@ import java.util.List; import java.util.Objects; +import org.graalvm.collections.EconomicSet; import org.graalvm.compiler.core.common.CompilationIdentifier; import org.graalvm.compiler.graph.NodeSourcePosition; -import org.graalvm.util.EconomicSet; import jdk.vm.ci.code.DebugInfo; import jdk.vm.ci.code.StackSlot;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.code/src/org/graalvm/compiler/code/DataSection.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.code/src/org/graalvm/compiler/code/DataSection.java Fri Feb 16 13:49:07 2018 -0800 @@ -40,7 +40,7 @@ public interface Patches { - void registerPatch(VMConstant c); + void registerPatch(int position, VMConstant c); } public abstract static class Data {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java Fri Feb 16 13:49:07 2018 -0800 @@ -27,7 +27,6 @@ import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.type.AbstractPointerStamp; import org.graalvm.compiler.core.common.type.IntegerStamp; -import org.graalvm.compiler.core.common.type.PrimitiveStamp; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; @@ -107,7 +106,9 @@ ret.setBase(add.getX()); ret.setIndex(considerNegation(graph, add.getY(), isBaseNegated)); return true; - } else if (ret.getBase() == null && ret.getIndex() instanceof AddNode) { + } + + if (ret.getBase() == null && ret.getIndex() instanceof AddNode) { AddNode add = (AddNode) ret.getIndex(); ret.setBase(considerNegation(graph, add.getX(), isIndexNegated)); ret.setIndex(add.getY()); @@ -188,7 +189,7 @@ return improveConstDisp(address, node, c, null, shift, negateExtractedDisplacement); } else { if (node.stamp(NodeView.DEFAULT) instanceof IntegerStamp) { - assert PrimitiveStamp.getBits(node.stamp(NodeView.DEFAULT)) == ADDRESS_BITS; + assert IntegerStamp.getBits(node.stamp(NodeView.DEFAULT)) == ADDRESS_BITS; /* * we can't swallow zero-extends because of multiple reasons:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64CompressAddressLowering.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017, 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. + */ + +package org.graalvm.compiler.core.amd64; + +import jdk.vm.ci.code.Register; +import org.graalvm.compiler.asm.amd64.AMD64Address; +import org.graalvm.compiler.core.common.LIRKind; +import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.debug.CounterKey; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.graph.NodeClass; +import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.CompressionNode; +import org.graalvm.compiler.nodes.NodeView; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.calc.FloatingNode; +import org.graalvm.compiler.nodes.spi.LIRLowerable; +import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; + +import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0; +import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0; + +public abstract class AMD64CompressAddressLowering extends AMD64AddressLowering { + private static final CounterKey counterFoldedUncompressDuringAddressLowering = DebugContext.counter("FoldedUncompressDuringAddressLowering"); + + @Override + protected final boolean improve(StructuredGraph graph, DebugContext debug, AMD64AddressNode addr, boolean isBaseNegated, boolean isIndexNegated) { + if (super.improve(graph, debug, addr, isBaseNegated, isIndexNegated)) { + return true; + } + + if (!isBaseNegated && !isIndexNegated && addr.getScale() == AMD64Address.Scale.Times1) { + ValueNode base = addr.getBase(); + ValueNode index = addr.getIndex(); + + if (tryToImproveUncompression(addr, index, base) || tryToImproveUncompression(addr, base, index)) { + counterFoldedUncompressDuringAddressLowering.increment(debug); + return true; + } + } + + return false; + } + + private boolean tryToImproveUncompression(AMD64AddressNode addr, ValueNode value, ValueNode other) { + if (value instanceof CompressionNode) { + CompressionNode compression = (CompressionNode) value; + if (compression.getOp() == CompressionNode.CompressionOp.Uncompress && improveUncompression(addr, compression, other)) { + return true; + } + } + + return false; + } + + protected abstract boolean improveUncompression(AMD64AddressNode addr, CompressionNode compression, ValueNode other); + + @NodeInfo(cycles = CYCLES_0, size = SIZE_0) + public static class HeapBaseNode extends FloatingNode implements LIRLowerable { + + public static final NodeClass<HeapBaseNode> TYPE = NodeClass.create(HeapBaseNode.class); + + private final Register heapBaseRegister; + + public HeapBaseNode(Register heapBaseRegister) { + super(TYPE, StampFactory.pointer()); + this.heapBaseRegister = heapBaseRegister; + } + + @Override + public void generate(NodeLIRBuilderTool generator) { + LIRKind kind = generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT)); + generator.setResult(this, heapBaseRegister.asValue(kind)); + } + } +}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64MoveFactoryBase.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64MoveFactoryBase.java Fri Feb 16 13:49:07 2018 -0800 @@ -26,14 +26,14 @@ import static org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize.QWORD; import static org.graalvm.compiler.asm.amd64.AMD64Assembler.OperandSize.WORD; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.Equivalence; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.lir.VirtualStackSlot; import org.graalvm.compiler.lir.amd64.AMD64LIRInstruction; import org.graalvm.compiler.lir.amd64.AMD64Move.AMD64PushPopStackMove; import org.graalvm.compiler.lir.framemap.FrameMapBuilder; import org.graalvm.compiler.lir.gen.LIRGeneratorTool.MoveFactory; -import org.graalvm.util.Equivalence; -import org.graalvm.util.EconomicMap; import jdk.vm.ci.amd64.AMD64Kind; import jdk.vm.ci.code.Architecture;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java Fri Feb 16 13:49:07 2018 -0800 @@ -44,6 +44,7 @@ import org.graalvm.compiler.asm.amd64.AMD64Assembler.SSEOp; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.NumUtil; +import org.graalvm.compiler.core.common.calc.CanonicalCondition; import org.graalvm.compiler.core.common.calc.Condition; import org.graalvm.compiler.core.gen.NodeLIRBuilder; import org.graalvm.compiler.core.gen.NodeMatchRules; @@ -128,7 +129,7 @@ } protected ComplexMatchResult emitCompareBranchMemory(IfNode ifNode, CompareNode compare, ValueNode value, LIRLowerableAccess access) { - Condition cond = compare.condition(); + Condition cond = compare.condition().asCondition(); AMD64Kind kind = getMemoryKind(access); boolean matchedAsConstant = false; // For assertion checking @@ -303,7 +304,7 @@ @MatchRule("(If (FloatEquals=compare value ValueCompareAndSwap=cas))") @MatchRule("(If (IntegerEquals=compare value ValueCompareAndSwap=cas))") public ComplexMatchResult ifCompareValueCas(IfNode root, CompareNode compare, ValueNode value, ValueCompareAndSwapNode cas) { - assert compare.condition() == Condition.EQ; + assert compare.condition() == CanonicalCondition.EQ; if (value == cas.getExpectedValue() && cas.usages().count() == 1) { return builder -> { LIRKind kind = getLirKind(cas); @@ -326,7 +327,7 @@ @MatchRule("(If (IntegerEquals=compare value LogicCompareAndSwap=cas))") public ComplexMatchResult ifCompareLogicCas(IfNode root, CompareNode compare, ValueNode value, LogicCompareAndSwapNode cas) { JavaConstant constant = value.asJavaConstant(); - assert compare.condition() == Condition.EQ; + assert compare.condition() == CanonicalCondition.EQ; if (constant != null && cas.usages().count() == 1) { long constantValue = constant.asLong(); boolean successIsTrue;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/GraalOptions.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/GraalOptions.java Fri Feb 16 13:49:07 2018 -0800 @@ -233,6 +233,9 @@ public static final OptionKey<Boolean> OptScheduleOutOfLoops = new OptionKey<>(true); @Option(help = "", type = OptionType.Debug) + public static final OptionKey<Boolean> GuardPriorities = new OptionKey<>(true); + + @Option(help = "", type = OptionType.Debug) public static final OptionKey<Boolean> OptEliminateGuards = new OptionKey<>(true); @Option(help = "", type = OptionType.Debug) @@ -271,4 +274,7 @@ @Option(help = "Enable experimental Trace Register Allocation.", type = OptionType.Debug) public static final OptionKey<Boolean> TraceRA = new OptionKey<>(false); + @Option(help = "How to trace inlining decisions, one of: None, Linear, Tree", type = OptionType.Debug) + public static final OptionKey<TraceInliningMode> TraceInlining = new OptionKey<>(TraceInliningMode.None); + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/TraceInliningMode.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.core.common; + +public enum TraceInliningMode { + None(false), + Linear(true), + Tree(true); + + private final boolean tracing; + + TraceInliningMode(boolean tracing) { + this.tracing = tracing; + } + + public boolean isTracing() { + return tracing; + } +}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/RegisterAllocationConfig.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/alloc/RegisterAllocationConfig.java Fri Feb 16 13:49:07 2018 -0800 @@ -22,9 +22,9 @@ */ package org.graalvm.compiler.core.common.alloc; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.Equivalence; import org.graalvm.compiler.core.common.GraalOptions; -import org.graalvm.util.EconomicMap; -import org.graalvm.util.Equivalence; import jdk.vm.ci.code.Register; import jdk.vm.ci.code.Register.RegisterCategory;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/CanonicalCondition.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009, 2015, 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. + */ +package org.graalvm.compiler.core.common.calc; + +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.ConstantReflectionProvider; +import jdk.vm.ci.meta.PrimitiveConstant; + +public enum CanonicalCondition { + EQ(Condition.EQ), + LT(Condition.LT), + BT(Condition.BT); + + private final Condition condition; + + CanonicalCondition(Condition condition) { + assert condition.isCanonical(); + this.condition = condition; + } + + public Condition asCondition() { + return condition; + } + + public boolean foldCondition(Constant lt, Constant rt, ConstantReflectionProvider constantReflection, boolean unorderedIsTrue) { + return asCondition().foldCondition(lt, rt, constantReflection, unorderedIsTrue); + } + + public boolean foldCondition(PrimitiveConstant lp, PrimitiveConstant rp, boolean unorderedIsTrue) { + return asCondition().foldCondition(lp, rp, unorderedIsTrue); + } +}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/Condition.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/calc/Condition.java Fri Feb 16 13:49:07 2018 -0800 @@ -115,6 +115,55 @@ throw new IllegalArgumentException(this.toString()); } + public static final class CanonicalizedCondition { + private final CanonicalCondition canonicalCondition; + private final boolean mirror; + private final boolean negate; + + private CanonicalizedCondition(CanonicalCondition canonicalCondition, boolean mirror, boolean negate) { + this.canonicalCondition = canonicalCondition; + this.mirror = mirror; + this.negate = negate; + } + + public CanonicalCondition getCanonicalCondition() { + return canonicalCondition; + } + + public boolean mustMirror() { + return mirror; + } + + public boolean mustNegate() { + return negate; + } + } + + public CanonicalizedCondition canonicalize() { + CanonicalCondition canonicalCondition; + switch (this) { + case EQ: + case NE: + canonicalCondition = CanonicalCondition.EQ; + break; + case LT: + case LE: + case GT: + case GE: + canonicalCondition = CanonicalCondition.LT; + break; + case BT: + case BE: + case AT: + case AE: + canonicalCondition = CanonicalCondition.BT; + break; + default: + throw new IllegalArgumentException(this.toString()); + } + return new CanonicalizedCondition(canonicalCondition, canonicalMirror(), canonicalNegate()); + } + /** * Given a condition and its negation, this method returns true for one of the two and false for * the other one. This can be used to keep comparisons in a canonical form. @@ -151,7 +200,7 @@ * Returns true if the condition needs to be mirrored to get to a canonical condition. The * result of the mirroring operation might still need to be negated to achieve a canonical form. */ - public boolean canonicalMirror() { + private boolean canonicalMirror() { switch (this) { case EQ: return false; @@ -181,7 +230,7 @@ * Returns true if the condition needs to be negated to get to a canonical condition. The result * of the negation might still need to be mirrored to achieve a canonical form. */ - public boolean canonicalNegate() { + private boolean canonicalNegate() { switch (this) { case EQ: return false;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/spi/ArrayOffsetProvider.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.core.common.spi; + +import jdk.vm.ci.meta.JavaKind; + +public interface ArrayOffsetProvider { + + int arrayBaseOffset(JavaKind elementKind); + + int arrayScalingFactor(JavaKind elementKind); +}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/spi/CodeGenProviders.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/spi/CodeGenProviders.java Fri Feb 16 13:49:07 2018 -0800 @@ -40,4 +40,5 @@ ConstantReflectionProvider getConstantReflection(); + ArrayOffsetProvider getArrayOffsetProvider(); }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/ArithmeticOpTable.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/ArithmeticOpTable.java Fri Feb 16 13:49:07 2018 -0800 @@ -28,6 +28,9 @@ import java.util.Objects; import java.util.function.Function; +import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.JavaKind; + import org.graalvm.compiler.core.common.calc.FloatConvert; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Add; import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.And; @@ -51,9 +54,6 @@ import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Sqrt; import org.graalvm.util.CollectionsUtil; -import jdk.vm.ci.meta.Constant; -import jdk.vm.ci.meta.JavaKind; - /** * Information about arithmetic operations. */
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/FrequencyEncoder.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/FrequencyEncoder.java Fri Feb 16 13:49:07 2018 -0800 @@ -25,8 +25,8 @@ import java.util.ArrayList; import java.util.List; -import org.graalvm.util.EconomicMap; -import org.graalvm.util.Equivalence; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.Equivalence; /** * Creates an array of T objects order by the occurrence frequency of each object. The most
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.match.processor/src/org/graalvm/compiler/core/match/processor/MatchProcessor.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.match.processor/src/org/graalvm/compiler/core/match/processor/MatchProcessor.java Fri Feb 16 13:49:07 2018 -0800 @@ -58,6 +58,9 @@ import javax.tools.JavaFileObject; import javax.tools.StandardLocation; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.EconomicSet; +import org.graalvm.collections.Equivalence; import org.graalvm.compiler.core.gen.NodeMatchRules; import org.graalvm.compiler.core.match.ComplexMatchResult; import org.graalvm.compiler.core.match.MatchRule; @@ -70,9 +73,6 @@ import org.graalvm.compiler.graph.Position; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.serviceprovider.ServiceProvider; -import org.graalvm.util.Equivalence; -import org.graalvm.util.EconomicMap; -import org.graalvm.util.EconomicSet; /** * Processes classes annotated with {@link MatchRule}. A {@link MatchStatementSet} service is
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc.test/src/org/graalvm/compiler/core/sparc/test/SPARCAllocatorTest.java Fri Feb 16 12:24:38 2018 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2013, 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. - */ -package org.graalvm.compiler.core.sparc.test; - -import static org.graalvm.compiler.core.common.GraalOptions.RegisterPressure; -import static org.graalvm.compiler.core.common.GraalOptions.TraceRA; -import static org.junit.Assume.assumeTrue; - -import org.junit.Before; -import org.junit.Test; - -import org.graalvm.compiler.core.test.backend.AllocatorTest; - -import jdk.vm.ci.sparc.SPARC; - -public class SPARCAllocatorTest extends AllocatorTest { - - @Before - public void checkSPARC() { - assumeTrue("skipping SPARC specific test", getTarget().arch instanceof SPARC); - assumeTrue("RegisterPressure is set -> skip", RegisterPressure.getValue(getInitialOptions()) == null); - assumeTrue("TraceRA is set -> skip", !TraceRA.getValue(getInitialOptions())); - } - - @Test - public void test1() { - testAllocation("test1snippet", 2, 0, 0); - } - - public static long test1snippet(long x) { - return x + 41; - } - - @Test - public void test2() { - testAllocation("test2snippet", 2, 0, 0); - } - - public static long test2snippet(long x) { - return x * 41; - } - - @Test - public void test3() { - testAllocation("test3snippet", 4, 0, 0); - } - - public static long test3snippet(long x) { - return x / 41 + x % 41; - } - -}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCNodeMatchRules.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCNodeMatchRules.java Fri Feb 16 13:49:07 2018 -0800 @@ -29,6 +29,7 @@ import static jdk.vm.ci.sparc.SPARCKind.XWORD; import org.graalvm.compiler.core.common.LIRKind; +import org.graalvm.compiler.core.common.calc.CanonicalCondition; import org.graalvm.compiler.core.common.calc.Condition; import org.graalvm.compiler.core.gen.NodeMatchRules; import org.graalvm.compiler.core.match.ComplexMatchResult; @@ -147,7 +148,7 @@ @MatchRule("(If (IntegerEquals=compare value LogicCompareAndSwap=cas))") public ComplexMatchResult ifCompareLogicCas(IfNode root, CompareNode compare, ValueNode value, LogicCompareAndSwapNode cas) { JavaConstant constant = value.asJavaConstant(); - assert compare.condition() == Condition.EQ; + assert compare.condition() == CanonicalCondition.EQ; if (constant != null && cas.usages().count() == 1) { long constantValue = constant.asLong(); boolean successIsTrue;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest11.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTest11.java Fri Feb 16 13:49:07 2018 -0800 @@ -123,19 +123,19 @@ public static int test6Snippet(int a) { if ((a & 8) != 0) { - GraalDirectives.deoptimizeAndInvalidate(); + GraalDirectives.deoptimize(); } if ((a & 15) != 15) { - GraalDirectives.deoptimizeAndInvalidate(); + GraalDirectives.deoptimize(); } return 0; } public static int reference6Snippet(int a) { if ((a & 8) != 0) { - GraalDirectives.deoptimizeAndInvalidate(); + GraalDirectives.deoptimize(); } - GraalDirectives.deoptimizeAndInvalidate(); + GraalDirectives.deoptimize(); return 0; }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ConditionalEliminationTestBase.java Fri Feb 16 13:49:07 2018 -0800 @@ -74,7 +74,6 @@ new IterativeConditionalEliminationPhase(canonicalizer, true).apply(graph, context); canonicalizer.apply(graph, context); canonicalizer.apply(graph, context); - new ConvertDeoptimizeToGuardPhase().apply(graph, context); } catch (Throwable t) { debug.handle(t); } @@ -86,7 +85,6 @@ } canonicalizer.apply(referenceGraph, context); canonicalizer.apply(referenceGraph, context); - new ConvertDeoptimizeToGuardPhase().apply(graph, context); } catch (Throwable t) { debug.handle(t); }
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -25,14 +25,12 @@ import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; -import org.graalvm.compiler.nodes.NodeView; -import org.junit.Test; - import org.graalvm.compiler.api.directives.GraalDirectives; import org.graalvm.compiler.graph.NodeClass; import org.graalvm.compiler.loop.InductionVariable; import org.graalvm.compiler.loop.LoopsData; import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodes.NodeView; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.calc.FloatingNode; @@ -42,6 +40,7 @@ import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration; import org.graalvm.compiler.nodes.spi.LIRLowerable; import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; +import org.junit.Test; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -117,6 +116,21 @@ test("incrementSnippet", 0, 256, 3); } + @Test + public void increment4() { + test("incrementSnippet", -10, Integer.MAX_VALUE, 1); + } + + @Test + public void increment5() { + test("incrementSnippet", 256, 256, 1); + } + + @Test + public void increment6() { + test("incrementSnippet", 257, 256, 1); + } + public static Result incrementEqSnippet(int start, int limit, int step) { int i; int inc = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive @@ -144,6 +158,21 @@ test("incrementEqSnippet", 0, 256, 3); } + @Test + public void incrementEq4() { + test("incrementEqSnippet", -10, 0, Integer.MAX_VALUE); + } + + @Test + public void incrementEq5() { + test("incrementEqSnippet", 256, 256, 1); + } + + @Test + public void incrementEq6() { + test("incrementEqSnippet", 257, 256, 1); + } + public static Result decrementSnippet(int start, int limit, int step) { int i; int dec = ((step - 1) & 0xFFFF) + 1; // make sure this value is always strictly positive @@ -198,6 +227,11 @@ test("decrementEqSnippet", 256, 0, 3); } + @Test + public void decrementEq4() { + test("decrementEqSnippet", -10, 0, Integer.MAX_VALUE); + } + public static Result twoVariablesSnippet() { Result ret = new Result(); int j = 0;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DumpPathTest.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/DumpPathTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -27,10 +27,10 @@ import java.nio.file.Files; import java.nio.file.Path; +import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.debug.DebugOptions; import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionValues; -import org.graalvm.util.EconomicMap; import org.junit.Test; /**
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraalCompilerTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -561,7 +561,7 @@ * @return a scheduled textual dump of {@code graph} . */ protected static String getScheduledGraphString(StructuredGraph graph) { - SchedulePhase schedule = new SchedulePhase(SchedulingStrategy.EARLIEST); + SchedulePhase schedule = new SchedulePhase(SchedulingStrategy.EARLIEST_WITH_GUARD_ORDER); schedule.apply(graph); ScheduleResult scheduleResult = graph.getLastSchedule();
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraphResetDebugTest.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraphResetDebugTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -22,14 +22,14 @@ */ package org.graalvm.compiler.core.test; +import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.DebugContext.Scope; import org.graalvm.compiler.debug.DebugOptions; -import org.graalvm.compiler.debug.DebugContext.Scope; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionValues; -import org.graalvm.util.EconomicMap; import org.junit.Assert; import org.junit.Test;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraphScheduleTest.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GraphScheduleTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -24,20 +24,29 @@ import java.util.List; -import org.junit.Assert; - import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeMap; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult; import org.graalvm.compiler.nodes.cfg.Block; import org.graalvm.compiler.phases.schedule.SchedulePhase; +import org.junit.Assert; + +import jdk.vm.ci.meta.SpeculationLog; public class GraphScheduleTest extends GraalCompilerTest { protected void assertOrderedAfterSchedule(StructuredGraph graph, Node a, Node b) { - SchedulePhase ibp = new SchedulePhase(SchedulePhase.SchedulingStrategy.LATEST); + assertOrderedAfterSchedule(graph, SchedulePhase.SchedulingStrategy.LATEST, a, b); + } + + protected void assertOrderedAfterSchedule(StructuredGraph graph, SchedulePhase.SchedulingStrategy strategy, Node a, Node b) { + SchedulePhase ibp = new SchedulePhase(strategy); ibp.apply(graph); + assertOrderedAfterLastSchedule(graph, a, b); + } + + protected void assertOrderedAfterLastSchedule(StructuredGraph graph, Node a, Node b) { assertOrderedAfterSchedule(graph.getLastSchedule(), a, b); } @@ -48,7 +57,7 @@ if (bBlock == aBlock) { List<Node> instructions = ibp.nodesFor(bBlock); - Assert.assertTrue(instructions.indexOf(b) > instructions.indexOf(a)); + Assert.assertTrue(a + " should be before " + b, instructions.indexOf(b) > instructions.indexOf(a)); } else { Block block = bBlock; while (block != null) { @@ -60,4 +69,9 @@ Assert.fail("block of A doesn't dominate the block of B"); } } + + @Override + protected SpeculationLog getSpeculationLog() { + return getCodeCache().createSpeculationLog(); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/GuardPrioritiesTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017, 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. + */ +package org.graalvm.compiler.core.test; + +import static org.graalvm.compiler.graph.test.matchers.NodeIterableCount.hasCount; +import static org.graalvm.compiler.graph.test.matchers.NodeIterableIsEmpty.isNotEmpty; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeThat; +import static org.junit.Assume.assumeTrue; + +import java.util.Iterator; + +import org.graalvm.compiler.api.directives.GraalDirectives; +import org.graalvm.compiler.core.common.GraalOptions; +import org.graalvm.compiler.graph.iterators.NodeIterable; +import org.graalvm.compiler.nodes.GuardNode; +import org.graalvm.compiler.nodes.ParameterNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.calc.IntegerLowerThanNode; +import org.graalvm.compiler.nodes.calc.IsNullNode; +import org.graalvm.compiler.nodes.spi.LoweringTool; +import org.graalvm.compiler.phases.common.CanonicalizerPhase; +import org.graalvm.compiler.phases.common.ConvertDeoptimizeToGuardPhase; +import org.graalvm.compiler.phases.common.FloatingReadPhase; +import org.graalvm.compiler.phases.common.LoweringPhase; +import org.graalvm.compiler.phases.schedule.SchedulePhase; +import org.graalvm.compiler.phases.tiers.HighTierContext; +import org.junit.Test; + +public class GuardPrioritiesTest extends GraphScheduleTest { + private int[] array; + private int size; + + public void growing(int e) { + if (size >= array.length) { + // grow + GraalDirectives.deoptimizeAndInvalidateWithSpeculation(); + } + array[size++] = e; + } + + @Test + public void growingTest() { + assumeTrue("GuardPriorities must be turned one", GraalOptions.GuardPriorities.getValue(getInitialOptions())); + StructuredGraph graph = prepareGraph("growing"); + + NodeIterable<GuardNode> guards = graph.getNodes(GuardNode.TYPE).filter(n -> n.inputs().filter(i -> i instanceof IntegerLowerThanNode).isNotEmpty()); + assertThat(guards, isNotEmpty()); + assumeThat(guards, hasCount(2)); + + Iterator<GuardNode> iterator = guards.iterator(); + GuardNode g1 = iterator.next(); + GuardNode g2 = iterator.next(); + assertTrue("There should be one guard with speculation, the other one without", g1.getSpeculation().isNull() ^ g2.getSpeculation().isNull()); + GuardNode withSpeculation = g1.getSpeculation().isNull() ? g2 : g1; + GuardNode withoutSpeculation = g1.getSpeculation().isNull() ? g1 : g2; + + assertOrderedAfterSchedule(graph, SchedulePhase.SchedulingStrategy.EARLIEST_WITH_GUARD_ORDER, withSpeculation, withoutSpeculation); + } + + private StructuredGraph prepareGraph(String method) { + StructuredGraph graph = parseEager(method, StructuredGraph.AllowAssumptions.YES); + HighTierContext highTierContext = getDefaultHighTierContext(); + CanonicalizerPhase canonicalizer = new CanonicalizerPhase(); + new ConvertDeoptimizeToGuardPhase().apply(graph, highTierContext); + new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highTierContext); + new FloatingReadPhase().apply(graph); + return graph; + } + + public int unknownCondition(Integer c, Object o, int[] a, Integer i) { + if (o != null) { + GraalDirectives.deoptimizeAndInvalidate(); + } + if (i > 5560) { + GraalDirectives.deoptimizeAndInvalidate(); + } + if (c >= 10) { + GraalDirectives.deoptimizeAndInvalidateWithSpeculation(); + } + return array[8] + a[i]; + } + + @Test + public void unknownTest() { + assumeTrue("GuardPriorities must be turned one", GraalOptions.GuardPriorities.getValue(getInitialOptions())); + StructuredGraph graph = prepareGraph("unknownCondition"); + + new SchedulePhase(SchedulePhase.SchedulingStrategy.EARLIEST_WITH_GUARD_ORDER).apply(graph); + for (GuardNode g1 : graph.getNodes(GuardNode.TYPE)) { + for (GuardNode g2 : graph.getNodes(GuardNode.TYPE)) { + if (g1.getSpeculation().isNull() ^ g2.getSpeculation().isNull()) { + GuardNode withSpeculation = g1.getSpeculation().isNull() ? g2 : g1; + GuardNode withoutSpeculation = g1.getSpeculation().isNull() ? g1 : g2; + + if (withoutSpeculation.isNegated() && withoutSpeculation.getCondition() instanceof IsNullNode) { + IsNullNode isNullNode = (IsNullNode) withoutSpeculation.getCondition(); + if (isNullNode.getValue() instanceof ParameterNode && ((ParameterNode) isNullNode.getValue()).index() == 1) { + // this is the null check before the speculative guard, it's the only + // one that should be above + assertOrderedAfterLastSchedule(graph, withoutSpeculation, withSpeculation); + continue; + } + } + + assertOrderedAfterLastSchedule(graph, withSpeculation, withoutSpeculation); + } + } + } + } +}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LongNodeChainTest.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/LongNodeChainTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -44,7 +44,7 @@ public static final int N = 10000; - private static final SchedulingStrategy[] Strategies = new SchedulingStrategy[]{SchedulingStrategy.EARLIEST}; + private static final SchedulingStrategy[] Strategies = new SchedulingStrategy[]{SchedulingStrategy.EARLIEST_WITH_GUARD_ORDER}; @Test public void testLongAddChain() {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ReflectionOptionDescriptors.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ReflectionOptionDescriptors.java Fri Feb 16 13:49:07 2018 -0800 @@ -31,13 +31,13 @@ import java.util.Map; import java.util.Properties; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.MapCursor; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionDescriptor; import org.graalvm.compiler.options.OptionDescriptors; import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionValues; -import org.graalvm.util.EconomicMap; -import org.graalvm.util.MapCursor; /** * An implementation of {@link OptionDescriptor} that uses reflection to create descriptors from a
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SchedulingTest2.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/SchedulingTest2.java Fri Feb 16 13:49:07 2018 -0800 @@ -70,7 +70,7 @@ returnNode.replaceAtPredecessor(beginNode); beginNode.setNext(returnNode); debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph"); - SchedulePhase schedulePhase = new SchedulePhase(SchedulingStrategy.EARLIEST); + SchedulePhase schedulePhase = new SchedulePhase(SchedulingStrategy.EARLIEST_WITH_GUARD_ORDER); schedulePhase.apply(graph); ScheduleResult schedule = graph.getLastSchedule(); BlockMap<List<Node>> blockToNodesMap = schedule.getBlockToNodesMap();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/TrivialInliningExplosionTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017, 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. + */ +package org.graalvm.compiler.core.test; + +import org.graalvm.compiler.core.common.CompilationIdentifier; +import org.graalvm.compiler.core.common.GraalOptions; +import org.graalvm.compiler.java.BytecodeParserOptions; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.options.OptionValues; +import org.junit.Assert; +import org.junit.Test; + +import jdk.vm.ci.meta.ResolvedJavaMethod; + +/** + * Tests that the defaults for {@link GraalOptions#TrivialInliningSize} and + * {@link BytecodeParserOptions#InlineDuringParsingMaxDepth} prevent explosive graph growth for code + * with small recursive methods. + */ +public class TrivialInliningExplosionTest extends GraalCompilerTest { + + public static void trivial() { + trivial(); + trivial(); + trivial(); + } + + public static void main() { + trivial(); + trivial(); + trivial(); + trivial(); + trivial(); + trivial(); + trivial(); + trivial(); + trivial(); + } + + private int afterParseSize; + + @Override + protected StructuredGraph parseForCompile(ResolvedJavaMethod method, CompilationIdentifier compilationId, OptionValues options) { + final StructuredGraph graph = super.parseForCompile(method, compilationId, options); + this.afterParseSize = graph.getNodeCount(); + return graph; + } + + @Test + public void test() { + ResolvedJavaMethod methodm0 = getResolvedJavaMethod("trivial"); + Assert.assertTrue(methodm0.getCodeSize() <= GraalOptions.TrivialInliningSize.getValue(getInitialOptions())); + test("main"); + int afterCompileSize = lastCompiledGraph.getNodeCount(); + + // The values of afterParseSize and afterCompileSize when this + // test was written were 849 and 848 respectively. + Assert.assertTrue(afterParseSize < 2000); + Assert.assertTrue(afterCompileSize < 2000); + + } +}
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/NestedLoopEffectsPhaseComplexityTest.java Fri Feb 16 13:49:07 2018 -0800 @@ -24,6 +24,7 @@ import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Optional; +import org.graalvm.collections.EconomicSet; import org.graalvm.compiler.core.test.GraalCompilerTest; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.TTY; @@ -43,7 +44,6 @@ import org.graalvm.compiler.phases.tiers.PhaseContext; import org.graalvm.compiler.virtual.phases.ea.EarlyReadEliminationPhase; import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase; -import org.graalvm.util.EconomicSet; import org.junit.Test; import jdk.vm.ci.meta.ResolvedJavaMethod;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompiler.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/GraalCompiler.java Fri Feb 16 13:49:07 2018 -0800 @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.List; +import org.graalvm.collections.EconomicSet; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.core.LIRGenerationPhase.LIRGenerationContext; import org.graalvm.compiler.core.common.GraalOptions; @@ -64,7 +65,6 @@ import org.graalvm.compiler.phases.tiers.Suites; import org.graalvm.compiler.phases.tiers.TargetProvider; import org.graalvm.compiler.phases.util.Providers; -import org.graalvm.util.EconomicSet; import jdk.vm.ci.code.RegisterConfig; import jdk.vm.ci.code.TargetDescription;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/DebugInfoBuilder.java Fri Feb 16 13:49:07 2018 -0800 @@ -26,6 +26,8 @@ import java.util.Arrays; import java.util.Queue; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.Equivalence; import org.graalvm.compiler.debug.CounterKey; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.GraalError; @@ -42,8 +44,6 @@ import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; import org.graalvm.compiler.virtual.nodes.MaterializedObjectState; import org.graalvm.compiler.virtual.nodes.VirtualObjectState; -import org.graalvm.util.EconomicMap; -import org.graalvm.util.Equivalence; import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.VirtualObject;
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java Fri Feb 16 13:49:07 2018 -0800 @@ -33,6 +33,8 @@ import java.util.Collection; import java.util.List; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.UnmodifiableMapCursor; import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.calc.Condition; import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; @@ -100,8 +102,6 @@ import org.graalvm.compiler.nodes.spi.NodeValueMap; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; import org.graalvm.compiler.options.OptionValues; -import org.graalvm.util.EconomicMap; -import org.graalvm.util.UnmodifiableMapCursor; import jdk.vm.ci.code.CallingConvention; import jdk.vm.ci.code.StackSlot; @@ -538,7 +538,8 @@ public void emitCompareBranch(CompareNode compare, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { PlatformKind kind = gen.getLIRKind(compare.getX().stamp(NodeView.DEFAULT)).getPlatformKind(); - gen.emitCompareBranch(kind, operand(compare.getX()), operand(compare.getY()), compare.condition(), compare.unorderedIsTrue(), trueSuccessor, falseSuccessor, trueSuccessorProbability); + gen.emitCompareBranch(kind, operand(compare.getX()), operand(compare.getY()), compare.condition().asCondition(), compare.unorderedIsTrue(), trueSuccessor, falseSuccessor, + trueSuccessorProbability); } public void emitIntegerTestBranch(IntegerTestNode test, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { @@ -566,7 +567,7 @@ } else if (node instanceof CompareNode) { CompareNode compare = (CompareNode) node; PlatformKind kind = gen.getLIRKind(compare.getX().stamp(NodeView.DEFAULT)).getPlatformKind(); - return gen.emitConditionalMove(kind, operand(compare.getX()), operand(compare.getY()), compare.condition(), compare.unorderedIsTrue(), trueValue, falseValue); + return gen.emitConditionalMove(kind, operand(compare.getX()), operand(compare.getY()), compare.condition().asCondition(), compare.unorderedIsTrue(), trueValue, falseValue); } else if (node instanceof LogicConstantNode) { return gen.emitMove(((LogicConstantNode) node).getValue() ? trueValue : falseValue); } else if (node instanceof IntegerTestNode) {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchContext.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchContext.java Fri Feb 16 13:49:07 2018 -0800 @@ -28,6 +28,8 @@ import java.util.Arrays; import java.util.List; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.Equivalence; import org.graalvm.compiler.core.gen.NodeLIRBuilder; import org.graalvm.compiler.core.match.MatchPattern.Result; import org.graalvm.compiler.debug.DebugContext; @@ -35,8 +37,6 @@ import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; -import org.graalvm.util.EconomicMap; -import org.graalvm.util.Equivalence; /** * Container for state captured during a match.
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchRuleRegistry.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/match/MatchRuleRegistry.java Fri Feb 16 13:49:07 2018 -0800 @@ -27,6 +27,9 @@ import java.util.ArrayList; import java.util.List; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.Equivalence; +import org.graalvm.collections.MapCursor; import org.graalvm.compiler.core.gen.NodeMatchRules; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.GraalError; @@ -36,9 +39,6 @@ import org.graalvm.compiler.graph.Position; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.serviceprovider.GraalServices; -import org.graalvm.util.EconomicMap; -import org.graalvm.util.Equivalence; -import org.graalvm.util.MapCursor; public class MatchRuleRegistry {
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/GraphChangeMonitoringPhase.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/phases/GraphChangeMonitoringPhase.java Fri Feb 16 13:49:07 2018 -0800 @@ -22,6 +22,8 @@ */ package org.graalvm.compiler.core.phases; +import org.graalvm.collections.EconomicSet; +import org.graalvm.collections.Equivalence; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.graph.Graph.NodeEvent; import org.graalvm.compiler.graph.Graph.NodeEventScope; @@ -32,8 +34,6 @@ import org.graalvm.compiler.phases.PhaseSuite; import org.graalvm.compiler.phases.common.util.HashSetNodeEventListener; import org.graalvm.compiler.phases.tiers.PhaseContext; -import org.graalvm.util.EconomicSet; -import org.graalvm.util.Equivalence; /** * A utility phase for detecting when a phase would change the graph and reporting extra information
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java Fri Feb 16 12:24:38 2018 -0800 +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java Fri Feb 16 13:49:07 2018 -0800 @@ -24,6 +24,7 @@ import java.util.ArrayList; +import org.graalvm.collections.EconomicSet; import org.graalvm.compiler.asm.Assembler; import org.graalvm.compiler.code.CompilationResult; import org.graalvm.compiler.core.common.CompilationIdentifier; @@ -44,7 +45,6 @@ import org.graalvm.compiler.phases.tiers.SuitesProvider; import org.graalvm.compiler.phases.tiers.TargetProvider; import org.graalvm.compiler.phases.util.Providers; -import org.graalvm.util.EconomicSet; import jdk.vm.ci.code.BailoutException; import jdk.vm.ci.code.CodeCacheProvider; @@ -204,31 +204,48 @@ } try (DebugContext.Scope s2 = debug.scope("CodeInstall", debugContext); DebugContext.Activation a = debug.activate()) { - for (CodeInstallationTask task : tasks) { - task.preProcess(compilationResult); + preCodeInstallationTasks(tasks, compilationResult); + + InstalledCode installedCode; + try { + CompiledCode compiledCode = createCompiledCode(method, compilationRequest, compilationResult); + installedCode = getProviders().getCodeCache().installCode(method, compiledCode, predefinedInstalledCode, speculationLog, isDefault); + } catch (Throwable t) { + failCodeInstallationTasks(tasks, t); + throw t; } - CompiledCode compiledCode = createCompiledCode(method, compilationRequest, compilationResult); - InstalledCode installedCode = getProviders().getCodeCache().installCode(method, compiledCode, predefinedInstalledCode, speculationLog, isDefault); + postCodeInstallationTasks(tasks, installedCode); - // Run post-code installation tasks. - try { - for (CodeInstallationTask task : tasks) { - task.postProcess(installedCode); - } - for (CodeInstallationTask task : tasks) { - task.releaseInstallation(installedCode); - } - } catch (Throwable t) { - installedCode.invalidate(); - throw t; - } return installedCode; } catch (Throwable e) { throw debug.handle(e);