diff --git a/.hgtags b/.hgtags index cb3ba5ed43e..1cb41f0429d 100644 --- a/.hgtags +++ b/.hgtags @@ -269,3 +269,7 @@ c5495e25c7258ab5f96a1ae14610887d76d2be63 jdk9-b18 d9ce05f36ffec3e5e8af62a92455c1c66a63c320 jdk9-b24 13a5c76976fe48e55c9727c25fae2d2ce7c05da0 jdk9-b25 cd6f4557e7fea5799ff3762ed7a80a743e75d5fd jdk9-b26 +d06a6d3c66c08293b2a9650f3cc01fd55c620e65 jdk9-b27 +f4269e8f454eb77763ecee228a88ae102a9aef6e jdk9-b28 +c36c0092693707a8255561433647e8c3cd724ccd jdk9-b29 +b2287cac7813c70ed7f679d9a46fe774bd4005f8 jdk9-b30 diff --git a/.hgtags-top-repo b/.hgtags-top-repo index d0fc930890e..0f468dba94d 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -269,3 +269,7 @@ ee4fd72b2ec3d92497f37163352f294aa695c6fb jdk9-b20 1d4a293fbec19dc2d5790bbb2c7dd0ed8f265484 jdk9-b24 aefd8899a8d6615fb34ba99b2e38996a7145baa8 jdk9-b25 d3ec8d048e6c3c46b6e0ee011cc551ad386dfba5 jdk9-b26 +ba5645f2735b41ed085d07ba20fa7b322afff318 jdk9-b27 +ea2f7981236f3812436958748ab3d26e80a35130 jdk9-b28 +9e6581aeda388a23fbee021fc33e6aa152a60657 jdk9-b29 +36e9bc875325813ac9c44ac0c617a463091fa9f5 jdk9-b30 diff --git a/Makefile b/Makefile index 1e574940964..d385e0f3299 100644 --- a/Makefile +++ b/Makefile @@ -108,12 +108,23 @@ else $(shell $(MKDIR) -p $(SJAVAC_SERVER_DIR) && $(RM) -rf $(SJAVAC_SERVER_DIR)/*) endif + # Split out the targets requiring sequential execution. Run these targets separately + # from the rest so that the rest may still enjoy full parallel execution. + SEQUENTIAL_TARGETS := $(filter dist-clean clean% reconfigure, $(MAIN_TARGETS)) + PARALLEL_TARGETS := $(filter-out $(SEQUENTIAL_TARGETS), $(MAIN_TARGETS)) + main-wrapper: - @$(if $(findstring clean, $(MAIN_TARGETS)), , $(call AtMakeStart)) - (cd $(root_dir)/make && $(BUILD_LOG_WRAPPER) $(MAKE) -f Main.gmk SPEC=$(SPEC) -j $(JOBS) \ - $(VERBOSE) VERBOSE=$(VERBOSE) LOG_LEVEL=$(LOG_LEVEL) $(MAIN_TARGETS) \ - $(if $(filter true, $(OUTPUT_SYNC_SUPPORTED)), -O$(OUTPUT_SYNC))) - @$(if $(findstring clean, $(MAIN_TARGETS)), , $(call AtMakeEnd)) + ifneq ($(SEQUENTIAL_TARGETS), ) + (cd $(root_dir)/make && $(MAKE) -f Main.gmk SPEC=$(SPEC) -j 1 \ + $(VERBOSE) VERBOSE=$(VERBOSE) LOG_LEVEL=$(LOG_LEVEL) $(SEQUENTIAL_TARGETS)) + endif + ifneq ($(PARALLEL_TARGETS), ) + @$(call AtMakeStart) + (cd $(root_dir)/make && $(BUILD_LOG_WRAPPER) $(MAKE) -f Main.gmk SPEC=$(SPEC) -j $(JOBS) \ + $(VERBOSE) VERBOSE=$(VERBOSE) LOG_LEVEL=$(LOG_LEVEL) $(PARALLEL_TARGETS) \ + $(if $(filter true, $(OUTPUT_SYNC_SUPPORTED)), -O$(OUTPUT_SYNC))) + @$(call AtMakeEnd) + endif .PHONY: main-wrapper @@ -136,8 +147,9 @@ help: $(info . make docs # Create all docs) $(info . make docs-javadoc # Create just javadocs, depends on less than full docs) $(info . make profiles # Create complete j2re compact profile images) - $(info . make bootcycle-images # Build images twice, second time with newly build JDK) + $(info . make bootcycle-images # Build images twice, second time with newly built JDK) $(info . make install # Install the generated images locally) + $(info . make reconfigure # Rerun configure with the same arguments as last time) $(info . make clean # Remove all files generated by make, but not those) $(info . # generated by configure) $(info . make dist-clean # Remove all files, including configuration) diff --git a/common/autoconf/basics.m4 b/common/autoconf/basics.m4 index 79d54a231ee..1a8cb100d94 100644 --- a/common/autoconf/basics.m4 +++ b/common/autoconf/basics.m4 @@ -377,7 +377,6 @@ AC_DEFUN_ONCE([BASIC_SETUP_FUNDAMENTAL_TOOLS], BASIC_REQUIRE_PROGS(CMP, cmp) BASIC_REQUIRE_PROGS(COMM, comm) BASIC_REQUIRE_PROGS(CP, cp) - BASIC_REQUIRE_PROGS(CPIO, cpio) BASIC_REQUIRE_PROGS(CUT, cut) BASIC_REQUIRE_PROGS(DATE, date) BASIC_REQUIRE_PROGS(DIFF, [gdiff diff]) @@ -427,6 +426,7 @@ AC_DEFUN_ONCE([BASIC_SETUP_FUNDAMENTAL_TOOLS], BASIC_PATH_PROGS(READLINK, [greadlink readlink]) BASIC_PATH_PROGS(DF, df) BASIC_PATH_PROGS(SETFILE, SetFile) + BASIC_PATH_PROGS(CPIO, [cpio bsdcpio]) ]) # Setup basic configuration paths, and platform-specific stuff related to PATHs. @@ -849,7 +849,12 @@ AC_DEFUN([BASIC_CHECK_FIND_DELETE], if test -f $DELETEDIR/TestIfFindSupportsDelete; then # No, it does not. rm $DELETEDIR/TestIfFindSupportsDelete - FIND_DELETE="-exec rm \{\} \+" + if test "x$OPENJDK_TARGET_OS" = "xaix"; then + # AIX 'find' is buggy if called with '-exec {} \+' and an empty file list + FIND_DELETE="-print | xargs rm" + else + FIND_DELETE="-exec rm \{\} \+" + fi AC_MSG_RESULT([no]) else AC_MSG_RESULT([yes]) @@ -954,7 +959,7 @@ AC_DEFUN([BASIC_CHECK_DIR_ON_LOCAL_DISK], # not be the case in cygwin in certain conditions. AC_DEFUN_ONCE([BASIC_CHECK_SRC_PERMS], [ - if test x"$OPENJDK_BUILD_OS" = xwindows; then + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then file_to_test="$SRC_ROOT/LICENSE" if test `$STAT -c '%a' "$file_to_test"` -lt 400; then AC_MSG_ERROR([Bad file permissions on src files. This is usually caused by cloning the repositories with a non cygwin hg in a directory not created in cygwin.]) diff --git a/common/autoconf/basics_windows.m4 b/common/autoconf/basics_windows.m4 index 0d43553fd95..1bcb6151486 100644 --- a/common/autoconf/basics_windows.m4 +++ b/common/autoconf/basics_windows.m4 @@ -266,6 +266,14 @@ AC_DEFUN([BASIC_FIXUP_EXECUTABLE_MSYS], BASIC_WINDOWS_REWRITE_AS_UNIX_PATH(new_path) new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + BASIC_WINDOWS_REWRITE_AS_UNIX_PATH(new_path) + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. diff --git a/common/autoconf/boot-jdk.m4 b/common/autoconf/boot-jdk.m4 index 872e33fb609..fbfa15c7b06 100644 --- a/common/autoconf/boot-jdk.m4 +++ b/common/autoconf/boot-jdk.m4 @@ -370,18 +370,27 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS], # Maximum amount of heap memory. # Maximum stack size. + JVM_MAX_HEAP=`expr $MEMORY_SIZE / 2` if test "x$BUILD_NUM_BITS" = x32; then - JVM_MAX_HEAP=1100M + if test "$JVM_MAX_HEAP" -gt "1100"; then + JVM_MAX_HEAP=1100 + elif test "$JVM_MAX_HEAP" -lt "512"; then + JVM_MAX_HEAP=512 + fi STACK_SIZE=768 else # Running Javac on a JVM on a 64-bit machine, takes more space since 64-bit # pointers are used. Apparently, we need to increase the heap and stack # space for the jvm. More specifically, when running javac to build huge # jdk batch - JVM_MAX_HEAP=1600M + if test "$JVM_MAX_HEAP" -gt "1600"; then + JVM_MAX_HEAP=1600 + elif test "$JVM_MAX_HEAP" -lt "512"; then + JVM_MAX_HEAP=512 + fi STACK_SIZE=1536 fi - ADD_JVM_ARG_IF_OK([-Xmx$JVM_MAX_HEAP],boot_jdk_jvmargs_big,[$JAVA]) + ADD_JVM_ARG_IF_OK([-Xmx${JVM_MAX_HEAP}M],boot_jdk_jvmargs_big,[$JAVA]) ADD_JVM_ARG_IF_OK([-XX:ThreadStackSize=$STACK_SIZE],boot_jdk_jvmargs_big,[$JAVA]) AC_MSG_RESULT([$boot_jdk_jvmargs_big]) diff --git a/common/autoconf/build-performance.m4 b/common/autoconf/build-performance.m4 index 7d4b8abb8da..8425d744990 100644 --- a/common/autoconf/build-performance.m4 +++ b/common/autoconf/build-performance.m4 @@ -131,8 +131,8 @@ AC_DEFUN_ONCE([BPERF_SETUP_BUILD_JOBS], if test "x$with_jobs" = x; then # Number of jobs was not specified, calculate. AC_MSG_CHECKING([for appropriate number of jobs to run in parallel]) - # Approximate memory in GB, rounding up a bit. - memory_gb=`expr $MEMORY_SIZE / 1100` + # Approximate memory in GB. + memory_gb=`expr $MEMORY_SIZE / 1024` # Pick the lowest of memory in gb and number of cores. if test "$memory_gb" -lt "$NUM_CORES"; then JOBS="$memory_gb" @@ -291,16 +291,11 @@ AC_DEFUN_ONCE([BPERF_SETUP_SMART_JAVAC], AC_MSG_ERROR([Could not execute server java: $SJAVAC_SERVER_JAVA]) fi else - SJAVAC_SERVER_JAVA="" - # Hotspot specific options. - ADD_JVM_ARG_IF_OK([-verbosegc],SJAVAC_SERVER_JAVA,[$JAVA]) - # JRockit specific options. - ADD_JVM_ARG_IF_OK([-Xverbose:gc],SJAVAC_SERVER_JAVA,[$JAVA]) - SJAVAC_SERVER_JAVA="$JAVA $SJAVAC_SERVER_JAVA" + SJAVAC_SERVER_JAVA="$JAVA" fi AC_SUBST(SJAVAC_SERVER_JAVA) - if test "$MEMORY_SIZE" -gt "2500"; then + if test "$MEMORY_SIZE" -gt "3000"; then ADD_JVM_ARG_IF_OK([-d64],SJAVAC_SERVER_JAVA,[$SJAVAC_SERVER_JAVA]) if test "$JVM_ARG_OK" = true; then JVM_64BIT=true @@ -308,34 +303,33 @@ AC_DEFUN_ONCE([BPERF_SETUP_SMART_JAVAC], fi fi + MX_VALUE=`expr $MEMORY_SIZE / 2` if test "$JVM_64BIT" = true; then - if test "$MEMORY_SIZE" -gt "17000"; then - ADD_JVM_ARG_IF_OK([-Xms10G -Xmx10G],SJAVAC_SERVER_JAVA,[$SJAVAC_SERVER_JAVA]) + # Set ms lower than mx since more than one instance of the server might + # get launched at the same time before they figure out which instance won. + MS_VALUE=512 + if test "$MX_VALUE" -gt "2048"; then + MX_VALUE=2048 fi - if test "$MEMORY_SIZE" -gt "10000" && test "$JVM_ARG_OK" = false; then - ADD_JVM_ARG_IF_OK([-Xms6G -Xmx6G],SJAVAC_SERVER_JAVA,[$SJAVAC_SERVER_JAVA]) - fi - if test "$MEMORY_SIZE" -gt "5000" && test "$JVM_ARG_OK" = false; then - ADD_JVM_ARG_IF_OK([-Xms1G -Xmx3G],SJAVAC_SERVER_JAVA,[$SJAVAC_SERVER_JAVA]) - fi - if test "$MEMORY_SIZE" -gt "3800" && test "$JVM_ARG_OK" = false; then - ADD_JVM_ARG_IF_OK([-Xms1G -Xmx2500M],SJAVAC_SERVER_JAVA,[$SJAVAC_SERVER_JAVA]) + else + MS_VALUE=256 + if test "$MX_VALUE" -gt "1500"; then + MX_VALUE=1500 fi fi - if test "$MEMORY_SIZE" -gt "2500" && test "$JVM_ARG_OK" = false; then - ADD_JVM_ARG_IF_OK([-Xms1000M -Xmx1500M],SJAVAC_SERVER_JAVA,[$SJAVAC_SERVER_JAVA]) - fi - if test "$MEMORY_SIZE" -gt "1000" && test "$JVM_ARG_OK" = false; then - ADD_JVM_ARG_IF_OK([-Xms400M -Xmx1100M],SJAVAC_SERVER_JAVA,[$SJAVAC_SERVER_JAVA]) - fi - if test "$JVM_ARG_OK" = false; then - ADD_JVM_ARG_IF_OK([-Xms256M -Xmx512M],SJAVAC_SERVER_JAVA,[$SJAVAC_SERVER_JAVA]) + if test "$MX_VALUE" -lt "512"; then + MX_VALUE=512 fi + ADD_JVM_ARG_IF_OK([-Xms${MS_VALUE}M -Xmx${MX_VALUE}M],SJAVAC_SERVER_JAVA,[$SJAVAC_SERVER_JAVA]) - AC_MSG_CHECKING([whether to use sjavac]) AC_ARG_ENABLE([sjavac], [AS_HELP_STRING([--enable-sjavac], [use sjavac to do fast incremental compiles @<:@disabled@:>@])], [ENABLE_SJAVAC="${enableval}"], [ENABLE_SJAVAC='no']) + if test "x$JVM_ARG_OK" = "xfalse"; then + AC_MSG_WARN([Could not set -Xms${MS_VALUE}M -Xmx${MX_VALUE}M, disabling sjavac]) + ENABLE_SJAVAC=no; + fi + AC_MSG_CHECKING([whether to use sjavac]) AC_MSG_RESULT([$ENABLE_SJAVAC]) AC_SUBST(ENABLE_SJAVAC) diff --git a/common/autoconf/configure.ac b/common/autoconf/configure.ac index ec0d7490d59..481d328a1c4 100644 --- a/common/autoconf/configure.ac +++ b/common/autoconf/configure.ac @@ -54,6 +54,7 @@ m4_include([toolchain_windows.m4]) AC_DEFUN_ONCE([CUSTOM_EARLY_HOOK]) AC_DEFUN_ONCE([CUSTOM_LATE_HOOK]) +AC_DEFUN_ONCE([CUSTOM_SUMMARY_AND_WARNINGS_HOOK]) # This line needs to be here, verbatim, after all includes and the dummy hook # definitions. It is replaced with custom functionality when building @@ -142,7 +143,6 @@ JDKOPT_SETUP_JDK_VERSION_NUMBERS ############################################################################### BOOTJDK_SETUP_BOOT_JDK -BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS ############################################################################### # @@ -233,6 +233,9 @@ BPERF_SETUP_BUILD_CORES BPERF_SETUP_BUILD_MEMORY BPERF_SETUP_BUILD_JOBS +# Setup arguments for the boot jdk (after cores and memory have been setup) +BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS + # Setup smart javac (after cores and memory have been setup) BPERF_SETUP_SMART_JAVAC @@ -271,3 +274,4 @@ $CHMOD +x $OUTPUT_ROOT/compare.sh # Finally output some useful information to the user HELP_PRINT_SUMMARY_AND_WARNINGS +CUSTOM_SUMMARY_AND_WARNINGS_HOOK diff --git a/common/autoconf/flags.m4 b/common/autoconf/flags.m4 index 6f69f0c53a2..7a62d93ea7f 100644 --- a/common/autoconf/flags.m4 +++ b/common/autoconf/flags.m4 @@ -342,17 +342,15 @@ AC_DEFUN_ONCE([FLAGS_SETUP_COMPILER_FLAGS_FOR_OPTIMIZATION], # no adjustment ;; fastdebug ) - # Add compile time bounds checks. - CFLAGS_DEBUG_OPTIONS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1" - CXXFLAGS_DEBUG_OPTIONS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1" + # no adjustment ;; slowdebug ) - # Add runtime bounds checks and symbol info. - CFLAGS_DEBUG_OPTIONS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-all --param ssp-buffer-size=1" - CXXFLAGS_DEBUG_OPTIONS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-all --param ssp-buffer-size=1" + # Add runtime stack smashing and undefined behavior checks + CFLAGS_DEBUG_OPTIONS="-fstack-protector-all --param ssp-buffer-size=1" + CXXFLAGS_DEBUG_OPTIONS="-fstack-protector-all --param ssp-buffer-size=1" if test "x$HAS_CFLAG_DETECT_UNDEFINED_BEHAVIOR" = "xtrue"; then CFLAGS_DEBUG_OPTIONS="$CFLAGS_DEBUG_OPTIONS $CFLAG_DETECT_UNDEFINED_BEHAVIOR_FLAG" - CXXFLAGS_DEBUG_OPTIONS="$CXXFLAGS_DEBUG_OPTIONS $CFLAG_DETECT_UNDEFINED_BEHAVIOR_FLAG" + CXXFLAGS_DEBUG_OPTIONS="$CXXFLAGS_DEBUG_OPTIONS $CFLAG_DETECT_UNDEFINED_BEHAVIsOR_FLAG" fi ;; esac @@ -900,7 +898,7 @@ AC_DEFUN_ONCE([FLAGS_SETUP_COMPILER_FLAGS_MISC], case "${TOOLCHAIN_TYPE}" in microsoft) - CFLAGS_WARNINGS_ARE_ERRORS="/WX" + CFLAGS_WARNINGS_ARE_ERRORS="-WX" ;; solstudio) CFLAGS_WARNINGS_ARE_ERRORS="-errtags -errwarn=%all" diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh index 5b044dc241d..8ceccb58d54 100644 --- a/common/autoconf/generated-configure.sh +++ b/common/autoconf/generated-configure.sh @@ -634,6 +634,10 @@ USE_PRECOMPILED_HEADER SJAVAC_SERVER_DIR ENABLE_SJAVAC SJAVAC_SERVER_JAVA +JAVA_TOOL_FLAGS_SMALL +JAVA_FLAGS_SMALL +JAVA_FLAGS_BIG +JAVA_FLAGS JOBS MEMORY_SIZE NUM_CORES @@ -749,6 +753,7 @@ MCS GNM NM STRIP +MSBUILD DUMPBIN RC MT @@ -805,10 +810,6 @@ JAXWS_TOPDIR JAXP_TOPDIR CORBA_TOPDIR LANGTOOLS_TOPDIR -JAVA_TOOL_FLAGS_SMALL -JAVA_FLAGS_SMALL -JAVA_FLAGS_BIG -JAVA_FLAGS JAVAC_FLAGS BOOT_JDK_SOURCETARGET JARSIGNER @@ -920,12 +921,14 @@ OPENJDK_TARGET_CPU_ENDIAN OPENJDK_TARGET_CPU_BITS OPENJDK_TARGET_CPU_ARCH OPENJDK_TARGET_CPU +OPENJDK_TARGET_OS_ENV OPENJDK_TARGET_OS_API OPENJDK_TARGET_OS OPENJDK_BUILD_CPU_ENDIAN OPENJDK_BUILD_CPU_BITS OPENJDK_BUILD_CPU_ARCH OPENJDK_BUILD_CPU +OPENJDK_BUILD_OS_ENV OPENJDK_BUILD_OS_API OPENJDK_BUILD_OS OPENJDK_BUILD_AUTOCONF_NAME @@ -942,6 +945,7 @@ build_os build_vendor build_cpu build +CPIO SETFILE DF READLINK @@ -980,7 +984,6 @@ DIRNAME DIFF DATE CUT -CPIO CP COMM CMP @@ -1062,7 +1065,6 @@ with_update_version with_user_release_suffix with_build_number with_boot_jdk -with_boot_jdk_jvmargs with_add_source_root with_override_source_root with_adds_and_overrides @@ -1087,6 +1089,7 @@ with_cups_include with_freetype with_freetype_include with_freetype_lib +with_freetype_src enable_freetype_bundling with_alsa with_alsa_include @@ -1104,6 +1107,7 @@ with_dxsdk_include with_num_cores with_memory_size with_jobs +with_boot_jdk_jvmargs with_sjavac_server_java enable_sjavac enable_precompiled_headers @@ -1120,7 +1124,6 @@ CHMOD CMP COMM CP -CPIO CUT DATE DIFF @@ -1159,6 +1162,7 @@ CYGPATH READLINK DF SETFILE +CPIO UNZIP ZIP LDD @@ -1902,10 +1906,6 @@ Optional Packages: number is not set.[username_builddateb00] --with-build-number Set build number value for build [b00] --with-boot-jdk path to Boot JDK (used to bootstrap build) [probed] - --with-boot-jdk-jvmargs specify JVM arguments to be passed to all java - invocations of boot JDK, overriding the default - values, e.g --with-boot-jdk-jvmargs="-Xmx8G - -enableassertions" --with-add-source-root for each and every source directory, look in this additional source root for the same directory; if it exists and have files in it, include it in the build @@ -1944,6 +1944,9 @@ Optional Packages: headers under PATH/include) --with-freetype-include specify directory for the freetype include files --with-freetype-lib specify directory for the freetype library + --with-freetype-src specify directory with freetype sources to + automatically build the library (experimental, + Windows-only) --with-alsa specify prefix directory for the alsa package (expecting the libraries under PATH/lib and the headers under PATH/include) @@ -1977,6 +1980,10 @@ Optional Packages: --with-memory-size=1024 [probed] --with-jobs number of parallel jobs to let make run [calculated based on cores and memory] + --with-boot-jdk-jvmargs specify JVM arguments to be passed to all java + invocations of boot JDK, overriding the default + values, e.g --with-boot-jdk-jvmargs="-Xmx8G + -enableassertions" --with-sjavac-server-java use this java binary for running the sjavac background server [Boot JDK java] @@ -1990,7 +1997,6 @@ Some influential environment variables: CMP Override default value for CMP COMM Override default value for COMM CP Override default value for CP - CPIO Override default value for CPIO CUT Override default value for CUT DATE Override default value for DATE DIFF Override default value for DIFF @@ -2029,6 +2035,7 @@ Some influential environment variables: READLINK Override default value for READLINK DF Override default value for DF SETFILE Override default value for SETFILE + CPIO Override default value for CPIO UNZIP Override default value for UNZIP ZIP Override default value for ZIP LDD Override default value for LDD @@ -3880,20 +3887,18 @@ cygwin_help() { HELP_MSG="You might be able to fix this by running '$PKGHANDLER_COMMAND'." ;; freetype) - if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then - HELP_MSG="To install freetype, run: -wget \"http://gnuwin32.sourceforge.net/downlinks/freetype.php\" -O /tmp/freetype-setup.exe -chmod +x /tmp/freetype-setup.exe -/tmp/freetype-setup.exe -Follow GUI prompts, and install to default directory \"C:\Program Files (x86)\GnuWin32\". -After installation, locate lib/libfreetype.dll.a and make a copy with the name freetype.dll." - else - HELP_MSG="You need to build a 64-bit version of freetype. -This is not readily available. -You can find source code and build instructions on -http://www.freetype.org/ -If you put the resulting build in \"C:\Program Files\GnuWin32\", it will be found automatically." - fi + HELP_MSG=" +The freetype library can now be build during the configure process. +Download the freetype sources and unpack them into an arbitrary directory: + +wget http://download.savannah.gnu.org/releases/freetype/freetype-2.5.3.tar.gz +tar -xzf freetype-2.5.3.tar.gz + +Then run configure with '--with-freetype-src='. This will +automatically build the freetype library into '/lib64' for 64-bit +builds or into '/lib32' for 32-bit builds. +Afterwards you can always use '--with-freetype-include=/include' +and '--with-freetype-lib=/lib32|64' for other builds." ;; esac } @@ -4058,6 +4063,8 @@ pkgadd_help() { + + # # Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. @@ -4313,13 +4320,14 @@ TOOLCHAIN_DESCRIPTION_xlc="IBM XL C/C++" + # This line needs to be here, verbatim, after all includes and the dummy hook # definitions. It is replaced with custom functionality when building # custom sources. #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1407143049 +DATE_WHEN_GENERATED=1410377275 ############################################################################### # @@ -5721,199 +5729,6 @@ $as_echo "$tool_specified" >&6; } - # Publish this variable in the help. - - - if test "x$CPIO" = x; then - # The variable is not set by user, try to locate tool using the code snippet - for ac_prog in cpio -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CPIO+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $CPIO in - [\\/]* | ?:[\\/]*) - ac_cv_path_CPIO="$CPIO" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_CPIO="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -CPIO=$ac_cv_path_CPIO -if test -n "$CPIO"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPIO" >&5 -$as_echo "$CPIO" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CPIO" && break -done - - else - # The variable is set, but is it from the command line or the environment? - - # Try to remove the string !CPIO! from our list. - try_remove_var=${CONFIGURE_OVERRIDDEN_VARIABLES//!CPIO!/} - if test "x$try_remove_var" = "x$CONFIGURE_OVERRIDDEN_VARIABLES"; then - # If it failed, the variable was not from the command line. Ignore it, - # but warn the user (except for BASH, which is always set by the calling BASH). - if test "xCPIO" != xBASH; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Ignoring value of CPIO from the environment. Use command line variables instead." >&5 -$as_echo "$as_me: WARNING: Ignoring value of CPIO from the environment. Use command line variables instead." >&2;} - fi - # Try to locate tool using the code snippet - for ac_prog in cpio -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CPIO+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $CPIO in - [\\/]* | ?:[\\/]*) - ac_cv_path_CPIO="$CPIO" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_CPIO="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -CPIO=$ac_cv_path_CPIO -if test -n "$CPIO"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPIO" >&5 -$as_echo "$CPIO" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CPIO" && break -done - - else - # If it succeeded, then it was overridden by the user. We will use it - # for the tool. - - # First remove it from the list of overridden variables, so we can test - # for unknown variables in the end. - CONFIGURE_OVERRIDDEN_VARIABLES="$try_remove_var" - - # Check if the provided tool contains a complete path. - tool_specified="$CPIO" - tool_basename="${tool_specified##*/}" - if test "x$tool_basename" = "x$tool_specified"; then - # A command without a complete path is provided, search $PATH. - { $as_echo "$as_me:${as_lineno-$LINENO}: Will search for user supplied tool CPIO=$tool_basename" >&5 -$as_echo "$as_me: Will search for user supplied tool CPIO=$tool_basename" >&6;} - # Extract the first word of "$tool_basename", so it can be a program name with args. -set dummy $tool_basename; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CPIO+:} false; then : - $as_echo_n "(cached) " >&6 -else - case $CPIO in - [\\/]* | ?:[\\/]*) - ac_cv_path_CPIO="$CPIO" # Let the user override the test with a path. - ;; - *) - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_path_CPIO="$as_dir/$ac_word$ac_exec_ext" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - - ;; -esac -fi -CPIO=$ac_cv_path_CPIO -if test -n "$CPIO"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPIO" >&5 -$as_echo "$CPIO" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - if test "x$CPIO" = x; then - as_fn_error $? "User supplied tool $tool_basename could not be found" "$LINENO" 5 - fi - else - # Otherwise we believe it is a complete path. Use it as it is. - { $as_echo "$as_me:${as_lineno-$LINENO}: Will use user supplied tool CPIO=$tool_specified" >&5 -$as_echo "$as_me: Will use user supplied tool CPIO=$tool_specified" >&6;} - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CPIO" >&5 -$as_echo_n "checking for CPIO... " >&6; } - if test ! -x "$tool_specified"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 -$as_echo "not found" >&6; } - as_fn_error $? "User supplied tool CPIO=$tool_specified does not exist or is not executable" "$LINENO" 5 - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tool_specified" >&5 -$as_echo "$tool_specified" >&6; } - fi - fi - fi - - - - if test "x$CPIO" = x; then - as_fn_error $? "Could not find required tool for CPIO" "$LINENO" 5 - fi - - - - - # Publish this variable in the help. @@ -13386,6 +13201,192 @@ $as_echo "$tool_specified" >&6; } + # Publish this variable in the help. + + + if test "x$CPIO" = x; then + # The variable is not set by user, try to locate tool using the code snippet + for ac_prog in cpio bsdcpio +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_CPIO+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $CPIO in + [\\/]* | ?:[\\/]*) + ac_cv_path_CPIO="$CPIO" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_CPIO="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +CPIO=$ac_cv_path_CPIO +if test -n "$CPIO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPIO" >&5 +$as_echo "$CPIO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CPIO" && break +done + + else + # The variable is set, but is it from the command line or the environment? + + # Try to remove the string !CPIO! from our list. + try_remove_var=${CONFIGURE_OVERRIDDEN_VARIABLES//!CPIO!/} + if test "x$try_remove_var" = "x$CONFIGURE_OVERRIDDEN_VARIABLES"; then + # If it failed, the variable was not from the command line. Ignore it, + # but warn the user (except for BASH, which is always set by the calling BASH). + if test "xCPIO" != xBASH; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Ignoring value of CPIO from the environment. Use command line variables instead." >&5 +$as_echo "$as_me: WARNING: Ignoring value of CPIO from the environment. Use command line variables instead." >&2;} + fi + # Try to locate tool using the code snippet + for ac_prog in cpio bsdcpio +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_CPIO+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $CPIO in + [\\/]* | ?:[\\/]*) + ac_cv_path_CPIO="$CPIO" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_CPIO="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +CPIO=$ac_cv_path_CPIO +if test -n "$CPIO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPIO" >&5 +$as_echo "$CPIO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CPIO" && break +done + + else + # If it succeeded, then it was overridden by the user. We will use it + # for the tool. + + # First remove it from the list of overridden variables, so we can test + # for unknown variables in the end. + CONFIGURE_OVERRIDDEN_VARIABLES="$try_remove_var" + + # Check if the provided tool contains a complete path. + tool_specified="$CPIO" + tool_basename="${tool_specified##*/}" + if test "x$tool_basename" = "x$tool_specified"; then + # A command without a complete path is provided, search $PATH. + { $as_echo "$as_me:${as_lineno-$LINENO}: Will search for user supplied tool CPIO=$tool_basename" >&5 +$as_echo "$as_me: Will search for user supplied tool CPIO=$tool_basename" >&6;} + # Extract the first word of "$tool_basename", so it can be a program name with args. +set dummy $tool_basename; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_CPIO+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $CPIO in + [\\/]* | ?:[\\/]*) + ac_cv_path_CPIO="$CPIO" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_CPIO="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +CPIO=$ac_cv_path_CPIO +if test -n "$CPIO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPIO" >&5 +$as_echo "$CPIO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "x$CPIO" = x; then + as_fn_error $? "User supplied tool $tool_basename could not be found" "$LINENO" 5 + fi + else + # Otherwise we believe it is a complete path. Use it as it is. + { $as_echo "$as_me:${as_lineno-$LINENO}: Will use user supplied tool CPIO=$tool_specified" >&5 +$as_echo "$as_me: Will use user supplied tool CPIO=$tool_specified" >&6;} + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CPIO" >&5 +$as_echo_n "checking for CPIO... " >&6; } + if test ! -x "$tool_specified"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + as_fn_error $? "User supplied tool CPIO=$tool_specified does not exist or is not executable" "$LINENO" 5 + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $tool_specified" >&5 +$as_echo "$tool_specified" >&6; } + fi + fi + fi + + + + # Now we can determine OpenJDK build and target platforms. This is required to # have early on. # Make sure we can run config.sub. @@ -13644,6 +13645,7 @@ test -n "$target_alias" && + { $as_echo "$as_me:${as_lineno-$LINENO}: checking openjdk-build os-cpu" >&5 $as_echo_n "checking openjdk-build os-cpu... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OPENJDK_BUILD_OS-$OPENJDK_BUILD_CPU" >&5 @@ -13775,6 +13777,7 @@ $as_echo "$OPENJDK_BUILD_OS-$OPENJDK_BUILD_CPU" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking openjdk-target os-cpu" >&5 $as_echo_n "checking openjdk-target os-cpu... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OPENJDK_TARGET_OS-$OPENJDK_TARGET_CPU" >&5 @@ -15611,6 +15614,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -15967,6 +15987,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -16320,6 +16357,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -16678,6 +16732,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -17030,6 +17101,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -17202,7 +17290,12 @@ $as_echo_n "checking if find supports -delete... " >&6; } if test -f $DELETEDIR/TestIfFindSupportsDelete; then # No, it does not. rm $DELETEDIR/TestIfFindSupportsDelete - FIND_DELETE="-exec rm \{\} \+" + if test "x$OPENJDK_TARGET_OS" = "xaix"; then + # AIX 'find' is buggy if called with '-exec {} \+' and an empty file list + FIND_DELETE="-print | xargs rm" + else + FIND_DELETE="-exec rm \{\} \+" + fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else @@ -26233,197 +26326,6 @@ $as_echo "$tool_specified" >&6; } - ############################################################################## - # - # Specify jvm options for anything that is run with the Boot JDK. - # Not all JVM:s accept the same arguments on the command line. - # - -# Check whether --with-boot-jdk-jvmargs was given. -if test "${with_boot_jdk_jvmargs+set}" = set; then : - withval=$with_boot_jdk_jvmargs; -fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking flags for boot jdk java command " >&5 -$as_echo_n "checking flags for boot jdk java command ... " >&6; } - - # Disable special log output when a debug build is used as Boot JDK... - - $ECHO "Check if jvm arg is ok: -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput" >&5 - $ECHO "Command: $JAVA -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput -version" >&5 - OUTPUT=`$JAVA -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - boot_jdk_jvmargs="$boot_jdk_jvmargs -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - - # Apply user provided options. - - $ECHO "Check if jvm arg is ok: $with_boot_jdk_jvmargs" >&5 - $ECHO "Command: $JAVA $with_boot_jdk_jvmargs -version" >&5 - OUTPUT=`$JAVA $with_boot_jdk_jvmargs -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - boot_jdk_jvmargs="$boot_jdk_jvmargs $with_boot_jdk_jvmargs" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $boot_jdk_jvmargs" >&5 -$as_echo "$boot_jdk_jvmargs" >&6; } - - # For now, general JAVA_FLAGS are the same as the boot jdk jvmargs - JAVA_FLAGS=$boot_jdk_jvmargs - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking flags for boot jdk java command for big workloads" >&5 -$as_echo_n "checking flags for boot jdk java command for big workloads... " >&6; } - - # Starting amount of heap memory. - - $ECHO "Check if jvm arg is ok: -Xms64M" >&5 - $ECHO "Command: $JAVA -Xms64M -version" >&5 - OUTPUT=`$JAVA -Xms64M -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - boot_jdk_jvmargs_big="$boot_jdk_jvmargs_big -Xms64M" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - - # Maximum amount of heap memory. - # Maximum stack size. - if test "x$BUILD_NUM_BITS" = x32; then - JVM_MAX_HEAP=1100M - STACK_SIZE=768 - else - # Running Javac on a JVM on a 64-bit machine, takes more space since 64-bit - # pointers are used. Apparently, we need to increase the heap and stack - # space for the jvm. More specifically, when running javac to build huge - # jdk batch - JVM_MAX_HEAP=1600M - STACK_SIZE=1536 - fi - - $ECHO "Check if jvm arg is ok: -Xmx$JVM_MAX_HEAP" >&5 - $ECHO "Command: $JAVA -Xmx$JVM_MAX_HEAP -version" >&5 - OUTPUT=`$JAVA -Xmx$JVM_MAX_HEAP -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - boot_jdk_jvmargs_big="$boot_jdk_jvmargs_big -Xmx$JVM_MAX_HEAP" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - - $ECHO "Check if jvm arg is ok: -XX:ThreadStackSize=$STACK_SIZE" >&5 - $ECHO "Command: $JAVA -XX:ThreadStackSize=$STACK_SIZE -version" >&5 - OUTPUT=`$JAVA -XX:ThreadStackSize=$STACK_SIZE -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - boot_jdk_jvmargs_big="$boot_jdk_jvmargs_big -XX:ThreadStackSize=$STACK_SIZE" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $boot_jdk_jvmargs_big" >&5 -$as_echo "$boot_jdk_jvmargs_big" >&6; } - - JAVA_FLAGS_BIG=$boot_jdk_jvmargs_big - - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking flags for boot jdk java command for small workloads" >&5 -$as_echo_n "checking flags for boot jdk java command for small workloads... " >&6; } - - # Use serial gc for small short lived tools if possible - - $ECHO "Check if jvm arg is ok: -XX:+UseSerialGC" >&5 - $ECHO "Command: $JAVA -XX:+UseSerialGC -version" >&5 - OUTPUT=`$JAVA -XX:+UseSerialGC -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - boot_jdk_jvmargs_small="$boot_jdk_jvmargs_small -XX:+UseSerialGC" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - - $ECHO "Check if jvm arg is ok: -Xms32M" >&5 - $ECHO "Command: $JAVA -Xms32M -version" >&5 - OUTPUT=`$JAVA -Xms32M -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - boot_jdk_jvmargs_small="$boot_jdk_jvmargs_small -Xms32M" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - - $ECHO "Check if jvm arg is ok: -Xmx512M" >&5 - $ECHO "Command: $JAVA -Xmx512M -version" >&5 - OUTPUT=`$JAVA -Xmx512M -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - boot_jdk_jvmargs_small="$boot_jdk_jvmargs_small -Xmx512M" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $boot_jdk_jvmargs_small" >&5 -$as_echo "$boot_jdk_jvmargs_small" >&6; } - - JAVA_FLAGS_SMALL=$boot_jdk_jvmargs_small - - - JAVA_TOOL_FLAGS_SMALL="" - for f in $JAVA_FLAGS_SMALL; do - JAVA_TOOL_FLAGS_SMALL="$JAVA_TOOL_FLAGS_SMALL -J$f" - done - - - ############################################################################### # # Configure the sources to use. We can add or override individual directories. @@ -26985,6 +26887,10 @@ $as_echo "no" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS100BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS100BASE using $METHOD" >&6;} VS_ENV_CMD="$VS100BASE/$VCVARSFILE" + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be one of 'v100', 'v110' or 'v120' for VS 2010, 2012 or VS2013 + # TODO: improve detection for other versions of VS + PLATFORM_TOOLSET="v100" else { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS100BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS100BASE using $METHOD" >&6;} @@ -27026,6 +26932,10 @@ $as_echo "$as_me: Please point to the VC/bin directory within the Visual Studio { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS100BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS100BASE using $METHOD" >&6;} VS_ENV_CMD="$VS100BASE/$VCVARSFILE" + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be one of 'v100', 'v110' or 'v120' for VS 2010, 2012 or VS2013 + # TODO: improve detection for other versions of VS + PLATFORM_TOOLSET="v100" else { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS100BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS100BASE using $METHOD" >&6;} @@ -27056,6 +26966,10 @@ $as_echo "$as_me: Warning: $VCVARSFILE is missing, this is probably Visual Studi { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS100BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS100BASE using $METHOD" >&6;} VS_ENV_CMD="$VS100BASE/$VCVARSFILE" + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be one of 'v100', 'v110' or 'v120' for VS 2010, 2012 or VS2013 + # TODO: improve detection for other versions of VS + PLATFORM_TOOLSET="v100" else { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS100BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS100BASE using $METHOD" >&6;} @@ -27085,6 +26999,10 @@ $as_echo "$as_me: Warning: $VCVARSFILE is missing, this is probably Visual Studi { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS100BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS100BASE using $METHOD" >&6;} VS_ENV_CMD="$VS100BASE/$VCVARSFILE" + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be one of 'v100', 'v110' or 'v120' for VS 2010, 2012 or VS2013 + # TODO: improve detection for other versions of VS + PLATFORM_TOOLSET="v100" else { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS100BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS100BASE using $METHOD" >&6;} @@ -27113,6 +27031,10 @@ $as_echo "$as_me: Warning: $VCVARSFILE is missing, this is probably Visual Studi { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS100BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS100BASE using $METHOD" >&6;} VS_ENV_CMD="$VS100BASE/$VCVARSFILE" + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be one of 'v100', 'v110' or 'v120' for VS 2010, 2012 or VS2013 + # TODO: improve detection for other versions of VS + PLATFORM_TOOLSET="v100" else { $as_echo "$as_me:${as_lineno-$LINENO}: Found Visual Studio installation at $VS100BASE using $METHOD" >&5 $as_echo "$as_me: Found Visual Studio installation at $VS100BASE using $METHOD" >&6;} @@ -27155,6 +27077,10 @@ $as_echo "$as_me: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" else VS_ENV_ARGS="/x64" fi + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be 'Windows7.1SDK' for Windows7.1SDK + # TODO: improve detection for other versions of SDK + PLATFORM_TOOLSET="Windows7.1SDK" else { $as_echo "$as_me:${as_lineno-$LINENO}: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" >&5 $as_echo "$as_me: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" >&6;} @@ -27197,6 +27123,10 @@ $as_echo "$as_me: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" else VS_ENV_ARGS="/x64" fi + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be 'Windows7.1SDK' for Windows7.1SDK + # TODO: improve detection for other versions of SDK + PLATFORM_TOOLSET="Windows7.1SDK" else { $as_echo "$as_me:${as_lineno-$LINENO}: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" >&5 $as_echo "$as_me: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" >&6;} @@ -27239,6 +27169,10 @@ $as_echo "$as_me: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" else VS_ENV_ARGS="/x64" fi + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be 'Windows7.1SDK' for Windows7.1SDK + # TODO: improve detection for other versions of SDK + PLATFORM_TOOLSET="Windows7.1SDK" else { $as_echo "$as_me:${as_lineno-$LINENO}: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" >&5 $as_echo "$as_me: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" >&6;} @@ -27280,6 +27214,10 @@ $as_echo "$as_me: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" else VS_ENV_ARGS="/x64" fi + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be 'Windows7.1SDK' for Windows7.1SDK + # TODO: improve detection for other versions of SDK + PLATFORM_TOOLSET="Windows7.1SDK" else { $as_echo "$as_me:${as_lineno-$LINENO}: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" >&5 $as_echo "$as_me: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" >&6;} @@ -27320,6 +27258,10 @@ $as_echo "$as_me: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" else VS_ENV_ARGS="/x64" fi + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be 'Windows7.1SDK' for Windows7.1SDK + # TODO: improve detection for other versions of SDK + PLATFORM_TOOLSET="Windows7.1SDK" else { $as_echo "$as_me:${as_lineno-$LINENO}: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" >&5 $as_echo "$as_me: Found Windows SDK installation at $WIN_SDK_BASE using $METHOD" >&6;} @@ -27493,6 +27435,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -28136,6 +28095,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -28576,6 +28552,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -29842,6 +29835,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -30282,6 +30292,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -31127,6 +31154,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -31525,6 +31569,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -31852,6 +31913,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -32330,6 +32408,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -32941,6 +33036,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -33460,6 +33572,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -33911,6 +34040,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -34235,6 +34381,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -34555,6 +34718,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -34857,6 +35037,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -34961,6 +35158,50 @@ $as_echo "$as_me: This might be caused by spaces in the path, which is not allow $as_echo "$as_me: Rewriting DUMPBIN to \"$new_complete\"" >&6;} fi + # We need to check for 'msbuild.exe' because at the place where we expect to + # find 'msbuild.exe' there's also a directory called 'msbuild' and configure + # won't find the 'msbuild.exe' executable in that case (and the + # 'ac_executable_extensions' is unusable due to performance reasons). + # Notice that we intentionally don't fix up the path to MSBUILD because we + # will call it in a DOS shell during freetype detection on Windows (see + # 'LIB_SETUP_FREETYPE' in "libraries.m4" + # Extract the first word of "msbuild.exe", so it can be a program name with args. +set dummy msbuild.exe; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MSBUILD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MSBUILD"; then + ac_cv_prog_MSBUILD="$MSBUILD" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MSBUILD="msbuild.exe" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MSBUILD=$ac_cv_prog_MSBUILD +if test -n "$MSBUILD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSBUILD" >&5 +$as_echo "$MSBUILD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + fi if test "x$OPENJDK_TARGET_OS" = xsolaris; then @@ -35311,6 +35552,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -35762,6 +36020,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -36213,6 +36488,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -36665,6 +36957,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -37228,6 +37537,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -37789,6 +38115,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -38359,6 +38702,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -38926,6 +39286,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -39402,6 +39779,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -39853,6 +40247,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -40304,6 +40715,23 @@ $as_echo "$as_me: Neither \"$new_path\" nor \"$new_path.exe/cmd\" can be found" new_path=`$WHICH "$new_path" 2> /dev/null` + # bat and cmd files are not always considered executable in MSYS causing which + # to not find them + if test "x$new_path" = x \ + && test "x`$ECHO \"$path\" | $GREP -i -e \"\\.bat$\" -e \"\\.cmd$\"`" != x \ + && test "x`$LS \"$path\" 2>/dev/null`" != x; then + new_path="$path" + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + fi if test "x$new_path" = x; then # It's still not found. Now this is an unrecoverable error. @@ -41991,17 +42419,15 @@ $as_echo "$ac_cv_c_bigendian" >&6; } # no adjustment ;; fastdebug ) - # Add compile time bounds checks. - CFLAGS_DEBUG_OPTIONS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1" - CXXFLAGS_DEBUG_OPTIONS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1" + # no adjustment ;; slowdebug ) - # Add runtime bounds checks and symbol info. - CFLAGS_DEBUG_OPTIONS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-all --param ssp-buffer-size=1" - CXXFLAGS_DEBUG_OPTIONS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-all --param ssp-buffer-size=1" + # Add runtime stack smashing and undefined behavior checks + CFLAGS_DEBUG_OPTIONS="-fstack-protector-all --param ssp-buffer-size=1" + CXXFLAGS_DEBUG_OPTIONS="-fstack-protector-all --param ssp-buffer-size=1" if test "x$HAS_CFLAG_DETECT_UNDEFINED_BEHAVIOR" = "xtrue"; then CFLAGS_DEBUG_OPTIONS="$CFLAGS_DEBUG_OPTIONS $CFLAG_DETECT_UNDEFINED_BEHAVIOR_FLAG" - CXXFLAGS_DEBUG_OPTIONS="$CXXFLAGS_DEBUG_OPTIONS $CFLAG_DETECT_UNDEFINED_BEHAVIOR_FLAG" + CXXFLAGS_DEBUG_OPTIONS="$CXXFLAGS_DEBUG_OPTIONS $CFLAG_DETECT_UNDEFINED_BEHAVIsOR_FLAG" fi ;; esac @@ -42629,7 +43055,7 @@ $as_echo "$supports" >&6; } case "${TOOLCHAIN_TYPE}" in microsoft) - CFLAGS_WARNINGS_ARE_ERRORS="/WX" + CFLAGS_WARNINGS_ARE_ERRORS="-WX" ;; solstudio) CFLAGS_WARNINGS_ARE_ERRORS="-errtags -errwarn=%all" @@ -43982,6 +44408,12 @@ if test "${with_freetype_lib+set}" = set; then : withval=$with_freetype_lib; fi + +# Check whether --with-freetype-src was given. +if test "${with_freetype_src+set}" = set; then : + withval=$with_freetype_src; +fi + # Check whether --enable-freetype-bundling was given. if test "${enable_freetype_bundling+set}" = set; then : enableval=$enable_freetype_bundling; @@ -43993,7 +44425,7 @@ fi FREETYPE_BUNDLE_LIB_PATH= if test "x$FREETYPE_NOT_NEEDED" = xyes; then - if test "x$with_freetype" != x || test "x$with_freetype_include" != x || test "x$with_freetype_lib" != x; then + if test "x$with_freetype" != x || test "x$with_freetype_include" != x || test "x$with_freetype_lib" != x || test "x$with_freetype_src" != x; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: freetype not used, so --with-freetype is ignored" >&5 $as_echo "$as_me: WARNING: freetype not used, so --with-freetype is ignored" >&2;} fi @@ -44006,6 +44438,429 @@ $as_echo "$as_me: WARNING: freetype not used, so --enable-freetype-bundling is i BUNDLE_FREETYPE="$enable_freetype_bundling" + if test "x$with_freetype_src" != x; then + if test "x$OPENJDK_TARGET_OS" = xwindows; then + # Try to build freetype if --with-freetype-src was given on Windows + + FREETYPE_SRC_PATH="$with_freetype_src" + BUILD_FREETYPE=yes + + # Check if the freetype sources are acessible.. + if ! test -d $FREETYPE_SRC_PATH; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-freetype-src specified, but can't find path \"$FREETYPE_SRC_PATH\" - ignoring --with-freetype-src" >&5 +$as_echo "$as_me: WARNING: --with-freetype-src specified, but can't find path \"$FREETYPE_SRC_PATH\" - ignoring --with-freetype-src" >&2;} + BUILD_FREETYPE=no + fi + # ..and contain a vc2010 project file + vcxproj_path="$FREETYPE_SRC_PATH/builds/windows/vc2010/freetype.vcxproj" + if test "x$BUILD_FREETYPE" = xyes && ! test -s $vcxproj_path; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Can't find project file $vcxproj_path (you may try a newer freetype version) - ignoring --with-freetype-src" >&5 +$as_echo "$as_me: WARNING: Can't find project file $vcxproj_path (you may try a newer freetype version) - ignoring --with-freetype-src" >&2;} + BUILD_FREETYPE=no + fi + # Now check if configure found a version of 'msbuild.exe' + if test "x$BUILD_FREETYPE" = xyes && test "x$MSBUILD" == x ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Can't find an msbuild.exe executable (you may try to install .NET 4.0) - ignoring --with-freetype-src" >&5 +$as_echo "$as_me: WARNING: Can't find an msbuild.exe executable (you may try to install .NET 4.0) - ignoring --with-freetype-src" >&2;} + BUILD_FREETYPE=no + fi + + # Ready to go.. + if test "x$BUILD_FREETYPE" = xyes; then + + # msbuild requires trailing slashes for output directories + freetype_lib_path="$FREETYPE_SRC_PATH/lib$OPENJDK_TARGET_CPU_BITS/" + freetype_lib_path_unix="$freetype_lib_path" + freetype_obj_path="$FREETYPE_SRC_PATH/obj$OPENJDK_TARGET_CPU_BITS/" + + unix_path="$vcxproj_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + windows_path=`$CYGPATH -m "$unix_path"` + vcxproj_path="$windows_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + windows_path=`cmd //c echo $unix_path` + vcxproj_path="$windows_path" + fi + + + unix_path="$freetype_lib_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + windows_path=`$CYGPATH -m "$unix_path"` + freetype_lib_path="$windows_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + windows_path=`cmd //c echo $unix_path` + freetype_lib_path="$windows_path" + fi + + + unix_path="$freetype_obj_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + windows_path=`$CYGPATH -m "$unix_path"` + freetype_obj_path="$windows_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + windows_path=`cmd //c echo $unix_path` + freetype_obj_path="$windows_path" + fi + + if test "x$OPENJDK_TARGET_CPU_BITS" = x64; then + freetype_platform=x64 + else + freetype_platform=win32 + fi + + # The original freetype project file is for VS 2010 (i.e. 'v100'), + # so we have to adapt the toolset if building with any other toolsed (i.e. SDK). + # Currently 'PLATFORM_TOOLSET' is set in 'TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT'/ + # 'TOOLCHAIN_CHECK_POSSIBLE_WIN_SDK_ROOT' in toolchain_windows.m4 + { $as_echo "$as_me:${as_lineno-$LINENO}: Trying to compile freetype sources with PlatformToolset=$PLATFORM_TOOLSET to $freetype_lib_path_unix ..." >&5 +$as_echo "$as_me: Trying to compile freetype sources with PlatformToolset=$PLATFORM_TOOLSET to $freetype_lib_path_unix ..." >&6;} + + # First we try to build the freetype.dll + $ECHO -e "@echo off\n"\ + "$MSBUILD $vcxproj_path "\ + "/p:PlatformToolset=$PLATFORM_TOOLSET "\ + "/p:Configuration=\"Release Multithreaded\" "\ + "/p:Platform=$freetype_platform "\ + "/p:ConfigurationType=DynamicLibrary "\ + "/p:TargetName=freetype "\ + "/p:OutDir=\"$freetype_lib_path\" "\ + "/p:IntDir=\"$freetype_obj_path\" > freetype.log" > freetype.bat + cmd /c freetype.bat + + if test -s "$freetype_lib_path_unix/freetype.dll"; then + # If that succeeds we also build freetype.lib + $ECHO -e "@echo off\n"\ + "$MSBUILD $vcxproj_path "\ + "/p:PlatformToolset=$PLATFORM_TOOLSET "\ + "/p:Configuration=\"Release Multithreaded\" "\ + "/p:Platform=$freetype_platform "\ + "/p:ConfigurationType=StaticLibrary "\ + "/p:TargetName=freetype "\ + "/p:OutDir=\"$freetype_lib_path\" "\ + "/p:IntDir=\"$freetype_obj_path\" >> freetype.log" > freetype.bat + cmd /c freetype.bat + + if test -s "$freetype_lib_path_unix/freetype.lib"; then + # Once we build both, lib and dll, set freetype lib and include path appropriately + POTENTIAL_FREETYPE_INCLUDE_PATH="$FREETYPE_SRC_PATH/include" + POTENTIAL_FREETYPE_LIB_PATH="$freetype_lib_path_unix" + { $as_echo "$as_me:${as_lineno-$LINENO}: Compiling freetype sources succeeded! (see freetype.log for build results)" >&5 +$as_echo "$as_me: Compiling freetype sources succeeded! (see freetype.log for build results)" >&6;} + else + BUILD_FREETYPE=no + fi + else + BUILD_FREETYPE=no + fi + fi + + if test "x$BUILD_FREETYPE" = xyes; then + # Okay, we built it. Check that it works. + + POTENTIAL_FREETYPE_INCLUDE_PATH="$POTENTIAL_FREETYPE_INCLUDE_PATH" + POTENTIAL_FREETYPE_LIB_PATH="$POTENTIAL_FREETYPE_LIB_PATH" + METHOD="--with-freetype-src" + + # First check if the files exists. + if test -s "$POTENTIAL_FREETYPE_INCLUDE_PATH/ft2build.h"; then + # We found an arbitrary include file. That's a good sign. + { $as_echo "$as_me:${as_lineno-$LINENO}: Found freetype include files at $POTENTIAL_FREETYPE_INCLUDE_PATH using $METHOD" >&5 +$as_echo "$as_me: Found freetype include files at $POTENTIAL_FREETYPE_INCLUDE_PATH using $METHOD" >&6;} + FOUND_FREETYPE=yes + + FREETYPE_LIB_NAME="${LIBRARY_PREFIX}freetype${SHARED_LIBRARY_SUFFIX}" + if ! test -s "$POTENTIAL_FREETYPE_LIB_PATH/$FREETYPE_LIB_NAME"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: Could not find $POTENTIAL_FREETYPE_LIB_PATH/$FREETYPE_LIB_NAME. Ignoring location." >&5 +$as_echo "$as_me: Could not find $POTENTIAL_FREETYPE_LIB_PATH/$FREETYPE_LIB_NAME. Ignoring location." >&6;} + FOUND_FREETYPE=no + else + if test "x$OPENJDK_TARGET_OS" = xwindows; then + # On Windows, we will need both .lib and .dll file. + if ! test -s "$POTENTIAL_FREETYPE_LIB_PATH/freetype.lib"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: Could not find $POTENTIAL_FREETYPE_LIB_PATH/freetype.lib. Ignoring location." >&5 +$as_echo "$as_me: Could not find $POTENTIAL_FREETYPE_LIB_PATH/freetype.lib. Ignoring location." >&6;} + FOUND_FREETYPE=no + fi + elif test "x$OPENJDK_TARGET_OS" = xsolaris \ + && test -s "$POTENTIAL_FREETYPE_LIB_PATH$OPENJDK_TARGET_CPU_ISADIR/$FREETYPE_LIB_NAME"; then + # Found lib in isa dir, use that instead. + POTENTIAL_FREETYPE_LIB_PATH="$POTENTIAL_FREETYPE_LIB_PATH$OPENJDK_TARGET_CPU_ISADIR" + fi + fi + fi + + if test "x$FOUND_FREETYPE" = xyes; then + + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + + # Input might be given as Windows format, start by converting to + # unix format. + path="$POTENTIAL_FREETYPE_INCLUDE_PATH" + new_path=`$CYGPATH -u "$path"` + + # Cygwin tries to hide some aspects of the Windows file system, such that binaries are + # named .exe but called without that suffix. Therefore, "foo" and "foo.exe" are considered + # the same file, most of the time (as in "test -f"). But not when running cygpath -s, then + # "foo.exe" is OK but "foo" is an error. + # + # This test is therefore slightly more accurate than "test -f" to check for file precense. + # It is also a way to make sure we got the proper file name for the real test later on. + test_shortpath=`$CYGPATH -s -m "$new_path" 2> /dev/null` + if test "x$test_shortpath" = x; then + { $as_echo "$as_me:${as_lineno-$LINENO}: The path of POTENTIAL_FREETYPE_INCLUDE_PATH, which resolves as \"$path\", is invalid." >&5 +$as_echo "$as_me: The path of POTENTIAL_FREETYPE_INCLUDE_PATH, which resolves as \"$path\", is invalid." >&6;} + as_fn_error $? "Cannot locate the the path of POTENTIAL_FREETYPE_INCLUDE_PATH" "$LINENO" 5 + fi + + # Call helper function which possibly converts this using DOS-style short mode. + # If so, the updated path is stored in $new_path. + + input_path="$new_path" + # Check if we need to convert this using DOS-style short mode. If the path + # contains just simple characters, use it. Otherwise (spaces, weird characters), + # take no chances and rewrite it. + # Note: m4 eats our [], so we need to use [ and ] instead. + has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-._/a-zA-Z0-9]` + if test "x$has_forbidden_chars" != x; then + # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) + shortmode_path=`$CYGPATH -s -m -a "$input_path"` + path_after_shortmode=`$CYGPATH -u "$shortmode_path"` + if test "x$path_after_shortmode" != "x$input_to_shortpath"; then + # Going to short mode and back again did indeed matter. Since short mode is + # case insensitive, let's make it lowercase to improve readability. + shortmode_path=`$ECHO "$shortmode_path" | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Now convert it back to Unix-stile (cygpath) + input_path=`$CYGPATH -u "$shortmode_path"` + new_path="$input_path" + fi + fi + + test_cygdrive_prefix=`$ECHO $input_path | $GREP ^/cygdrive/` + if test "x$test_cygdrive_prefix" = x; then + # As a simple fix, exclude /usr/bin since it's not a real path. + if test "x`$ECHO $new_path | $GREP ^/usr/bin/`" = x; then + # The path is in a Cygwin special directory (e.g. /home). We need this converted to + # a path prefixed by /cygdrive for fixpath to work. + new_path="$CYGWIN_ROOT_PATH$input_path" + fi + fi + + + if test "x$path" != "x$new_path"; then + POTENTIAL_FREETYPE_INCLUDE_PATH="$new_path" + { $as_echo "$as_me:${as_lineno-$LINENO}: Rewriting POTENTIAL_FREETYPE_INCLUDE_PATH to \"$new_path\"" >&5 +$as_echo "$as_me: Rewriting POTENTIAL_FREETYPE_INCLUDE_PATH to \"$new_path\"" >&6;} + fi + + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + + path="$POTENTIAL_FREETYPE_INCLUDE_PATH" + has_colon=`$ECHO $path | $GREP ^.:` + new_path="$path" + if test "x$has_colon" = x; then + # Not in mixed or Windows style, start by that. + new_path=`cmd //c echo $path` + fi + + + input_path="$new_path" + # Check if we need to convert this using DOS-style short mode. If the path + # contains just simple characters, use it. Otherwise (spaces, weird characters), + # take no chances and rewrite it. + # Note: m4 eats our [], so we need to use [ and ] instead. + has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-_/:a-zA-Z0-9]` + if test "x$has_forbidden_chars" != x; then + # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) + new_path=`cmd /c "for %A in (\"$input_path\") do @echo %~sA"|$TR \\\\\\\\ / | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + fi + + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + if test "x$path" != "x$new_path"; then + POTENTIAL_FREETYPE_INCLUDE_PATH="$new_path" + { $as_echo "$as_me:${as_lineno-$LINENO}: Rewriting POTENTIAL_FREETYPE_INCLUDE_PATH to \"$new_path\"" >&5 +$as_echo "$as_me: Rewriting POTENTIAL_FREETYPE_INCLUDE_PATH to \"$new_path\"" >&6;} + fi + + # Save the first 10 bytes of this path to the storage, so fixpath can work. + all_fixpath_prefixes=("${all_fixpath_prefixes[@]}" "${new_path:0:10}") + + else + # We're on a posix platform. Hooray! :) + path="$POTENTIAL_FREETYPE_INCLUDE_PATH" + has_space=`$ECHO "$path" | $GREP " "` + if test "x$has_space" != x; then + { $as_echo "$as_me:${as_lineno-$LINENO}: The path of POTENTIAL_FREETYPE_INCLUDE_PATH, which resolves as \"$path\", is invalid." >&5 +$as_echo "$as_me: The path of POTENTIAL_FREETYPE_INCLUDE_PATH, which resolves as \"$path\", is invalid." >&6;} + as_fn_error $? "Spaces are not allowed in this path." "$LINENO" 5 + fi + + # Use eval to expand a potential ~ + eval path="$path" + if test ! -f "$path" && test ! -d "$path"; then + as_fn_error $? "The path of POTENTIAL_FREETYPE_INCLUDE_PATH, which resolves as \"$path\", is not found." "$LINENO" 5 + fi + + POTENTIAL_FREETYPE_INCLUDE_PATH="`cd "$path"; $THEPWDCMD -L`" + fi + + + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + + # Input might be given as Windows format, start by converting to + # unix format. + path="$POTENTIAL_FREETYPE_LIB_PATH" + new_path=`$CYGPATH -u "$path"` + + # Cygwin tries to hide some aspects of the Windows file system, such that binaries are + # named .exe but called without that suffix. Therefore, "foo" and "foo.exe" are considered + # the same file, most of the time (as in "test -f"). But not when running cygpath -s, then + # "foo.exe" is OK but "foo" is an error. + # + # This test is therefore slightly more accurate than "test -f" to check for file precense. + # It is also a way to make sure we got the proper file name for the real test later on. + test_shortpath=`$CYGPATH -s -m "$new_path" 2> /dev/null` + if test "x$test_shortpath" = x; then + { $as_echo "$as_me:${as_lineno-$LINENO}: The path of POTENTIAL_FREETYPE_LIB_PATH, which resolves as \"$path\", is invalid." >&5 +$as_echo "$as_me: The path of POTENTIAL_FREETYPE_LIB_PATH, which resolves as \"$path\", is invalid." >&6;} + as_fn_error $? "Cannot locate the the path of POTENTIAL_FREETYPE_LIB_PATH" "$LINENO" 5 + fi + + # Call helper function which possibly converts this using DOS-style short mode. + # If so, the updated path is stored in $new_path. + + input_path="$new_path" + # Check if we need to convert this using DOS-style short mode. If the path + # contains just simple characters, use it. Otherwise (spaces, weird characters), + # take no chances and rewrite it. + # Note: m4 eats our [], so we need to use [ and ] instead. + has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-._/a-zA-Z0-9]` + if test "x$has_forbidden_chars" != x; then + # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) + shortmode_path=`$CYGPATH -s -m -a "$input_path"` + path_after_shortmode=`$CYGPATH -u "$shortmode_path"` + if test "x$path_after_shortmode" != "x$input_to_shortpath"; then + # Going to short mode and back again did indeed matter. Since short mode is + # case insensitive, let's make it lowercase to improve readability. + shortmode_path=`$ECHO "$shortmode_path" | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Now convert it back to Unix-stile (cygpath) + input_path=`$CYGPATH -u "$shortmode_path"` + new_path="$input_path" + fi + fi + + test_cygdrive_prefix=`$ECHO $input_path | $GREP ^/cygdrive/` + if test "x$test_cygdrive_prefix" = x; then + # As a simple fix, exclude /usr/bin since it's not a real path. + if test "x`$ECHO $new_path | $GREP ^/usr/bin/`" = x; then + # The path is in a Cygwin special directory (e.g. /home). We need this converted to + # a path prefixed by /cygdrive for fixpath to work. + new_path="$CYGWIN_ROOT_PATH$input_path" + fi + fi + + + if test "x$path" != "x$new_path"; then + POTENTIAL_FREETYPE_LIB_PATH="$new_path" + { $as_echo "$as_me:${as_lineno-$LINENO}: Rewriting POTENTIAL_FREETYPE_LIB_PATH to \"$new_path\"" >&5 +$as_echo "$as_me: Rewriting POTENTIAL_FREETYPE_LIB_PATH to \"$new_path\"" >&6;} + fi + + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + + path="$POTENTIAL_FREETYPE_LIB_PATH" + has_colon=`$ECHO $path | $GREP ^.:` + new_path="$path" + if test "x$has_colon" = x; then + # Not in mixed or Windows style, start by that. + new_path=`cmd //c echo $path` + fi + + + input_path="$new_path" + # Check if we need to convert this using DOS-style short mode. If the path + # contains just simple characters, use it. Otherwise (spaces, weird characters), + # take no chances and rewrite it. + # Note: m4 eats our [], so we need to use [ and ] instead. + has_forbidden_chars=`$ECHO "$input_path" | $GREP [^-_/:a-zA-Z0-9]` + if test "x$has_forbidden_chars" != x; then + # Now convert it to mixed DOS-style, short mode (no spaces, and / instead of \) + new_path=`cmd /c "for %A in (\"$input_path\") do @echo %~sA"|$TR \\\\\\\\ / | $TR 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + fi + + + windows_path="$new_path" + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then + unix_path=`$CYGPATH -u "$windows_path"` + new_path="$unix_path" + elif test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + unix_path=`$ECHO "$windows_path" | $SED -e 's,^\\(.\\):,/\\1,g' -e 's,\\\\,/,g'` + new_path="$unix_path" + fi + + if test "x$path" != "x$new_path"; then + POTENTIAL_FREETYPE_LIB_PATH="$new_path" + { $as_echo "$as_me:${as_lineno-$LINENO}: Rewriting POTENTIAL_FREETYPE_LIB_PATH to \"$new_path\"" >&5 +$as_echo "$as_me: Rewriting POTENTIAL_FREETYPE_LIB_PATH to \"$new_path\"" >&6;} + fi + + # Save the first 10 bytes of this path to the storage, so fixpath can work. + all_fixpath_prefixes=("${all_fixpath_prefixes[@]}" "${new_path:0:10}") + + else + # We're on a posix platform. Hooray! :) + path="$POTENTIAL_FREETYPE_LIB_PATH" + has_space=`$ECHO "$path" | $GREP " "` + if test "x$has_space" != x; then + { $as_echo "$as_me:${as_lineno-$LINENO}: The path of POTENTIAL_FREETYPE_LIB_PATH, which resolves as \"$path\", is invalid." >&5 +$as_echo "$as_me: The path of POTENTIAL_FREETYPE_LIB_PATH, which resolves as \"$path\", is invalid." >&6;} + as_fn_error $? "Spaces are not allowed in this path." "$LINENO" 5 + fi + + # Use eval to expand a potential ~ + eval path="$path" + if test ! -f "$path" && test ! -d "$path"; then + as_fn_error $? "The path of POTENTIAL_FREETYPE_LIB_PATH, which resolves as \"$path\", is not found." "$LINENO" 5 + fi + + POTENTIAL_FREETYPE_LIB_PATH="`cd "$path"; $THEPWDCMD -L`" + fi + + + FREETYPE_INCLUDE_PATH="$POTENTIAL_FREETYPE_INCLUDE_PATH" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for freetype includes" >&5 +$as_echo_n "checking for freetype includes... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FREETYPE_INCLUDE_PATH" >&5 +$as_echo "$FREETYPE_INCLUDE_PATH" >&6; } + FREETYPE_LIB_PATH="$POTENTIAL_FREETYPE_LIB_PATH" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for freetype libraries" >&5 +$as_echo_n "checking for freetype libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FREETYPE_LIB_PATH" >&5 +$as_echo "$FREETYPE_LIB_PATH" >&6; } + fi + + if test "x$FOUND_FREETYPE" != xyes; then + as_fn_error $? "Can not use the built freetype at location given by --with-freetype-src" "$LINENO" 5 + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: User specified --with-freetype-src but building freetype failed. (see freetype.log for build results)" >&5 +$as_echo "$as_me: User specified --with-freetype-src but building freetype failed. (see freetype.log for build results)" >&6;} + as_fn_error $? "Consider building freetype manually and using --with-freetype instead." "$LINENO" 5 + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-freetype-src is currently only supported on Windows - ignoring" >&5 +$as_echo "$as_me: WARNING: --with-freetype-src is currently only supported on Windows - ignoring" >&2;} + fi + fi + if test "x$with_freetype" != x || test "x$with_freetype_include" != x || test "x$with_freetype_lib" != x; then # User has specified settings @@ -48711,12 +49566,22 @@ $as_echo "$as_me: Found msvcr100.dll at $POSSIBLE_MSVCR_DLL using $METHOD" >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: checking found msvcr100.dll architecture" >&5 $as_echo_n "checking found msvcr100.dll architecture... " >&6; } MSVCR_DLL_FILETYPE=`$FILE -b "$POSSIBLE_MSVCR_DLL"` - if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then - CORRECT_MSVCR_ARCH=386 + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + # The MSYS 'file' command returns "PE32 executable for MS Windows (DLL) (GUI) Intel 80386 32-bit" + # on x32 and "PE32+ executable for MS Windows (DLL) (GUI) Mono/.Net assembly" on x64 systems. + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH="PE32 executable" + else + CORRECT_MSVCR_ARCH="PE32+ executable" + fi else - CORRECT_MSVCR_ARCH=x86-64 + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH=386 + else + CORRECT_MSVCR_ARCH=x86-64 + fi fi - if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP $CORRECT_MSVCR_ARCH 2>&1 > /dev/null; then + if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP "$CORRECT_MSVCR_ARCH" 2>&1 > /dev/null; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } MSVCR_DLL="$POSSIBLE_MSVCR_DLL" @@ -48767,12 +49632,22 @@ $as_echo "$as_me: Found msvcr100.dll at $POSSIBLE_MSVCR_DLL using $METHOD" >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: checking found msvcr100.dll architecture" >&5 $as_echo_n "checking found msvcr100.dll architecture... " >&6; } MSVCR_DLL_FILETYPE=`$FILE -b "$POSSIBLE_MSVCR_DLL"` - if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then - CORRECT_MSVCR_ARCH=386 + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + # The MSYS 'file' command returns "PE32 executable for MS Windows (DLL) (GUI) Intel 80386 32-bit" + # on x32 and "PE32+ executable for MS Windows (DLL) (GUI) Mono/.Net assembly" on x64 systems. + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH="PE32 executable" + else + CORRECT_MSVCR_ARCH="PE32+ executable" + fi else - CORRECT_MSVCR_ARCH=x86-64 + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH=386 + else + CORRECT_MSVCR_ARCH=x86-64 + fi fi - if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP $CORRECT_MSVCR_ARCH 2>&1 > /dev/null; then + if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP "$CORRECT_MSVCR_ARCH" 2>&1 > /dev/null; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } MSVCR_DLL="$POSSIBLE_MSVCR_DLL" @@ -48805,12 +49680,22 @@ $as_echo "$as_me: Found msvcr100.dll at $POSSIBLE_MSVCR_DLL using $METHOD" >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: checking found msvcr100.dll architecture" >&5 $as_echo_n "checking found msvcr100.dll architecture... " >&6; } MSVCR_DLL_FILETYPE=`$FILE -b "$POSSIBLE_MSVCR_DLL"` - if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then - CORRECT_MSVCR_ARCH=386 + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + # The MSYS 'file' command returns "PE32 executable for MS Windows (DLL) (GUI) Intel 80386 32-bit" + # on x32 and "PE32+ executable for MS Windows (DLL) (GUI) Mono/.Net assembly" on x64 systems. + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH="PE32 executable" + else + CORRECT_MSVCR_ARCH="PE32+ executable" + fi else - CORRECT_MSVCR_ARCH=x86-64 + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH=386 + else + CORRECT_MSVCR_ARCH=x86-64 + fi fi - if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP $CORRECT_MSVCR_ARCH 2>&1 > /dev/null; then + if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP "$CORRECT_MSVCR_ARCH" 2>&1 > /dev/null; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } MSVCR_DLL="$POSSIBLE_MSVCR_DLL" @@ -48853,12 +49738,22 @@ $as_echo "$as_me: Found msvcr100.dll at $POSSIBLE_MSVCR_DLL using $METHOD" >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: checking found msvcr100.dll architecture" >&5 $as_echo_n "checking found msvcr100.dll architecture... " >&6; } MSVCR_DLL_FILETYPE=`$FILE -b "$POSSIBLE_MSVCR_DLL"` - if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then - CORRECT_MSVCR_ARCH=386 + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + # The MSYS 'file' command returns "PE32 executable for MS Windows (DLL) (GUI) Intel 80386 32-bit" + # on x32 and "PE32+ executable for MS Windows (DLL) (GUI) Mono/.Net assembly" on x64 systems. + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH="PE32 executable" + else + CORRECT_MSVCR_ARCH="PE32+ executable" + fi else - CORRECT_MSVCR_ARCH=x86-64 + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH=386 + else + CORRECT_MSVCR_ARCH=x86-64 + fi fi - if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP $CORRECT_MSVCR_ARCH 2>&1 > /dev/null; then + if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP "$CORRECT_MSVCR_ARCH" 2>&1 > /dev/null; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } MSVCR_DLL="$POSSIBLE_MSVCR_DLL" @@ -48906,12 +49801,22 @@ $as_echo "$as_me: Found msvcr100.dll at $POSSIBLE_MSVCR_DLL using $METHOD" >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: checking found msvcr100.dll architecture" >&5 $as_echo_n "checking found msvcr100.dll architecture... " >&6; } MSVCR_DLL_FILETYPE=`$FILE -b "$POSSIBLE_MSVCR_DLL"` - if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then - CORRECT_MSVCR_ARCH=386 + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + # The MSYS 'file' command returns "PE32 executable for MS Windows (DLL) (GUI) Intel 80386 32-bit" + # on x32 and "PE32+ executable for MS Windows (DLL) (GUI) Mono/.Net assembly" on x64 systems. + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH="PE32 executable" + else + CORRECT_MSVCR_ARCH="PE32+ executable" + fi else - CORRECT_MSVCR_ARCH=x86-64 + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH=386 + else + CORRECT_MSVCR_ARCH=x86-64 + fi fi - if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP $CORRECT_MSVCR_ARCH 2>&1 > /dev/null; then + if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP "$CORRECT_MSVCR_ARCH" 2>&1 > /dev/null; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } MSVCR_DLL="$POSSIBLE_MSVCR_DLL" @@ -48955,12 +49860,22 @@ $as_echo "$as_me: Found msvcr100.dll at $POSSIBLE_MSVCR_DLL using $METHOD" >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: checking found msvcr100.dll architecture" >&5 $as_echo_n "checking found msvcr100.dll architecture... " >&6; } MSVCR_DLL_FILETYPE=`$FILE -b "$POSSIBLE_MSVCR_DLL"` - if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then - CORRECT_MSVCR_ARCH=386 + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + # The MSYS 'file' command returns "PE32 executable for MS Windows (DLL) (GUI) Intel 80386 32-bit" + # on x32 and "PE32+ executable for MS Windows (DLL) (GUI) Mono/.Net assembly" on x64 systems. + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH="PE32 executable" + else + CORRECT_MSVCR_ARCH="PE32+ executable" + fi else - CORRECT_MSVCR_ARCH=x86-64 + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH=386 + else + CORRECT_MSVCR_ARCH=x86-64 + fi fi - if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP $CORRECT_MSVCR_ARCH 2>&1 > /dev/null; then + if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP "$CORRECT_MSVCR_ARCH" 2>&1 > /dev/null; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } MSVCR_DLL="$POSSIBLE_MSVCR_DLL" @@ -49300,8 +50215,8 @@ fi # Number of jobs was not specified, calculate. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for appropriate number of jobs to run in parallel" >&5 $as_echo_n "checking for appropriate number of jobs to run in parallel... " >&6; } - # Approximate memory in GB, rounding up a bit. - memory_gb=`expr $MEMORY_SIZE / 1100` + # Approximate memory in GB. + memory_gb=`expr $MEMORY_SIZE / 1024` # Pick the lowest of memory in gb and number of cores. if test "$memory_gb" -lt "$NUM_CORES"; then JOBS="$memory_gb" @@ -49327,6 +50242,208 @@ $as_echo "$JOBS" >&6; } +# Setup arguments for the boot jdk (after cores and memory have been setup) + + ############################################################################## + # + # Specify jvm options for anything that is run with the Boot JDK. + # Not all JVM:s accept the same arguments on the command line. + # + +# Check whether --with-boot-jdk-jvmargs was given. +if test "${with_boot_jdk_jvmargs+set}" = set; then : + withval=$with_boot_jdk_jvmargs; +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking flags for boot jdk java command " >&5 +$as_echo_n "checking flags for boot jdk java command ... " >&6; } + + # Disable special log output when a debug build is used as Boot JDK... + + $ECHO "Check if jvm arg is ok: -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput" >&5 + $ECHO "Command: $JAVA -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput -version" >&5 + OUTPUT=`$JAVA -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput -version 2>&1` + FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` + FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` + if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then + boot_jdk_jvmargs="$boot_jdk_jvmargs -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput" + JVM_ARG_OK=true + else + $ECHO "Arg failed:" >&5 + $ECHO "$OUTPUT" >&5 + JVM_ARG_OK=false + fi + + + # Apply user provided options. + + $ECHO "Check if jvm arg is ok: $with_boot_jdk_jvmargs" >&5 + $ECHO "Command: $JAVA $with_boot_jdk_jvmargs -version" >&5 + OUTPUT=`$JAVA $with_boot_jdk_jvmargs -version 2>&1` + FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` + FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` + if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then + boot_jdk_jvmargs="$boot_jdk_jvmargs $with_boot_jdk_jvmargs" + JVM_ARG_OK=true + else + $ECHO "Arg failed:" >&5 + $ECHO "$OUTPUT" >&5 + JVM_ARG_OK=false + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $boot_jdk_jvmargs" >&5 +$as_echo "$boot_jdk_jvmargs" >&6; } + + # For now, general JAVA_FLAGS are the same as the boot jdk jvmargs + JAVA_FLAGS=$boot_jdk_jvmargs + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking flags for boot jdk java command for big workloads" >&5 +$as_echo_n "checking flags for boot jdk java command for big workloads... " >&6; } + + # Starting amount of heap memory. + + $ECHO "Check if jvm arg is ok: -Xms64M" >&5 + $ECHO "Command: $JAVA -Xms64M -version" >&5 + OUTPUT=`$JAVA -Xms64M -version 2>&1` + FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` + FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` + if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then + boot_jdk_jvmargs_big="$boot_jdk_jvmargs_big -Xms64M" + JVM_ARG_OK=true + else + $ECHO "Arg failed:" >&5 + $ECHO "$OUTPUT" >&5 + JVM_ARG_OK=false + fi + + + # Maximum amount of heap memory. + # Maximum stack size. + JVM_MAX_HEAP=`expr $MEMORY_SIZE / 2` + if test "x$BUILD_NUM_BITS" = x32; then + if test "$JVM_MAX_HEAP" -gt "1100"; then + JVM_MAX_HEAP=1100 + elif test "$JVM_MAX_HEAP" -lt "512"; then + JVM_MAX_HEAP=512 + fi + STACK_SIZE=768 + else + # Running Javac on a JVM on a 64-bit machine, takes more space since 64-bit + # pointers are used. Apparently, we need to increase the heap and stack + # space for the jvm. More specifically, when running javac to build huge + # jdk batch + if test "$JVM_MAX_HEAP" -gt "1600"; then + JVM_MAX_HEAP=1600 + elif test "$JVM_MAX_HEAP" -lt "512"; then + JVM_MAX_HEAP=512 + fi + STACK_SIZE=1536 + fi + + $ECHO "Check if jvm arg is ok: -Xmx${JVM_MAX_HEAP}M" >&5 + $ECHO "Command: $JAVA -Xmx${JVM_MAX_HEAP}M -version" >&5 + OUTPUT=`$JAVA -Xmx${JVM_MAX_HEAP}M -version 2>&1` + FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` + FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` + if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then + boot_jdk_jvmargs_big="$boot_jdk_jvmargs_big -Xmx${JVM_MAX_HEAP}M" + JVM_ARG_OK=true + else + $ECHO "Arg failed:" >&5 + $ECHO "$OUTPUT" >&5 + JVM_ARG_OK=false + fi + + + $ECHO "Check if jvm arg is ok: -XX:ThreadStackSize=$STACK_SIZE" >&5 + $ECHO "Command: $JAVA -XX:ThreadStackSize=$STACK_SIZE -version" >&5 + OUTPUT=`$JAVA -XX:ThreadStackSize=$STACK_SIZE -version 2>&1` + FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` + FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` + if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then + boot_jdk_jvmargs_big="$boot_jdk_jvmargs_big -XX:ThreadStackSize=$STACK_SIZE" + JVM_ARG_OK=true + else + $ECHO "Arg failed:" >&5 + $ECHO "$OUTPUT" >&5 + JVM_ARG_OK=false + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $boot_jdk_jvmargs_big" >&5 +$as_echo "$boot_jdk_jvmargs_big" >&6; } + + JAVA_FLAGS_BIG=$boot_jdk_jvmargs_big + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking flags for boot jdk java command for small workloads" >&5 +$as_echo_n "checking flags for boot jdk java command for small workloads... " >&6; } + + # Use serial gc for small short lived tools if possible + + $ECHO "Check if jvm arg is ok: -XX:+UseSerialGC" >&5 + $ECHO "Command: $JAVA -XX:+UseSerialGC -version" >&5 + OUTPUT=`$JAVA -XX:+UseSerialGC -version 2>&1` + FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` + FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` + if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then + boot_jdk_jvmargs_small="$boot_jdk_jvmargs_small -XX:+UseSerialGC" + JVM_ARG_OK=true + else + $ECHO "Arg failed:" >&5 + $ECHO "$OUTPUT" >&5 + JVM_ARG_OK=false + fi + + + $ECHO "Check if jvm arg is ok: -Xms32M" >&5 + $ECHO "Command: $JAVA -Xms32M -version" >&5 + OUTPUT=`$JAVA -Xms32M -version 2>&1` + FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` + FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` + if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then + boot_jdk_jvmargs_small="$boot_jdk_jvmargs_small -Xms32M" + JVM_ARG_OK=true + else + $ECHO "Arg failed:" >&5 + $ECHO "$OUTPUT" >&5 + JVM_ARG_OK=false + fi + + + $ECHO "Check if jvm arg is ok: -Xmx512M" >&5 + $ECHO "Command: $JAVA -Xmx512M -version" >&5 + OUTPUT=`$JAVA -Xmx512M -version 2>&1` + FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` + FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` + if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then + boot_jdk_jvmargs_small="$boot_jdk_jvmargs_small -Xmx512M" + JVM_ARG_OK=true + else + $ECHO "Arg failed:" >&5 + $ECHO "$OUTPUT" >&5 + JVM_ARG_OK=false + fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $boot_jdk_jvmargs_small" >&5 +$as_echo "$boot_jdk_jvmargs_small" >&6; } + + JAVA_FLAGS_SMALL=$boot_jdk_jvmargs_small + + + JAVA_TOOL_FLAGS_SMALL="" + for f in $JAVA_FLAGS_SMALL; do + JAVA_TOOL_FLAGS_SMALL="$JAVA_TOOL_FLAGS_SMALL -J$f" + done + + + # Setup smart javac (after cores and memory have been setup) @@ -49343,44 +50460,11 @@ fi as_fn_error $? "Could not execute server java: $SJAVAC_SERVER_JAVA" "$LINENO" 5 fi else - SJAVAC_SERVER_JAVA="" - # Hotspot specific options. - - $ECHO "Check if jvm arg is ok: -verbosegc" >&5 - $ECHO "Command: $JAVA -verbosegc -version" >&5 - OUTPUT=`$JAVA -verbosegc -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - SJAVAC_SERVER_JAVA="$SJAVAC_SERVER_JAVA -verbosegc" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - # JRockit specific options. - - $ECHO "Check if jvm arg is ok: -Xverbose:gc" >&5 - $ECHO "Command: $JAVA -Xverbose:gc -version" >&5 - OUTPUT=`$JAVA -Xverbose:gc -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - SJAVAC_SERVER_JAVA="$SJAVAC_SERVER_JAVA -Xverbose:gc" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - SJAVAC_SERVER_JAVA="$JAVA $SJAVAC_SERVER_JAVA" + SJAVAC_SERVER_JAVA="$JAVA" fi - if test "$MEMORY_SIZE" -gt "2500"; then + if test "$MEMORY_SIZE" -gt "3000"; then $ECHO "Check if jvm arg is ok: -d64" >&5 $ECHO "Command: $SJAVAC_SERVER_JAVA -d64 -version" >&5 @@ -49402,85 +50486,31 @@ fi fi fi + MX_VALUE=`expr $MEMORY_SIZE / 2` if test "$JVM_64BIT" = true; then - if test "$MEMORY_SIZE" -gt "17000"; then - - $ECHO "Check if jvm arg is ok: -Xms10G -Xmx10G" >&5 - $ECHO "Command: $SJAVAC_SERVER_JAVA -Xms10G -Xmx10G -version" >&5 - OUTPUT=`$SJAVAC_SERVER_JAVA -Xms10G -Xmx10G -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - SJAVAC_SERVER_JAVA="$SJAVAC_SERVER_JAVA -Xms10G -Xmx10G" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - + # Set ms lower than mx since more than one instance of the server might + # get launched at the same time before they figure out which instance won. + MS_VALUE=512 + if test "$MX_VALUE" -gt "2048"; then + MX_VALUE=2048 fi - if test "$MEMORY_SIZE" -gt "10000" && test "$JVM_ARG_OK" = false; then - - $ECHO "Check if jvm arg is ok: -Xms6G -Xmx6G" >&5 - $ECHO "Command: $SJAVAC_SERVER_JAVA -Xms6G -Xmx6G -version" >&5 - OUTPUT=`$SJAVAC_SERVER_JAVA -Xms6G -Xmx6G -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - SJAVAC_SERVER_JAVA="$SJAVAC_SERVER_JAVA -Xms6G -Xmx6G" - JVM_ARG_OK=true else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - fi - if test "$MEMORY_SIZE" -gt "5000" && test "$JVM_ARG_OK" = false; then - - $ECHO "Check if jvm arg is ok: -Xms1G -Xmx3G" >&5 - $ECHO "Command: $SJAVAC_SERVER_JAVA -Xms1G -Xmx3G -version" >&5 - OUTPUT=`$SJAVAC_SERVER_JAVA -Xms1G -Xmx3G -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - SJAVAC_SERVER_JAVA="$SJAVAC_SERVER_JAVA -Xms1G -Xmx3G" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - fi - if test "$MEMORY_SIZE" -gt "3800" && test "$JVM_ARG_OK" = false; then - - $ECHO "Check if jvm arg is ok: -Xms1G -Xmx2500M" >&5 - $ECHO "Command: $SJAVAC_SERVER_JAVA -Xms1G -Xmx2500M -version" >&5 - OUTPUT=`$SJAVAC_SERVER_JAVA -Xms1G -Xmx2500M -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - SJAVAC_SERVER_JAVA="$SJAVAC_SERVER_JAVA -Xms1G -Xmx2500M" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - + MS_VALUE=256 + if test "$MX_VALUE" -gt "1500"; then + MX_VALUE=1500 fi fi - if test "$MEMORY_SIZE" -gt "2500" && test "$JVM_ARG_OK" = false; then + if test "$MX_VALUE" -lt "512"; then + MX_VALUE=512 + fi - $ECHO "Check if jvm arg is ok: -Xms1000M -Xmx1500M" >&5 - $ECHO "Command: $SJAVAC_SERVER_JAVA -Xms1000M -Xmx1500M -version" >&5 - OUTPUT=`$SJAVAC_SERVER_JAVA -Xms1000M -Xmx1500M -version 2>&1` + $ECHO "Check if jvm arg is ok: -Xms${MS_VALUE}M -Xmx${MX_VALUE}M" >&5 + $ECHO "Command: $SJAVAC_SERVER_JAVA -Xms${MS_VALUE}M -Xmx${MX_VALUE}M -version" >&5 + OUTPUT=`$SJAVAC_SERVER_JAVA -Xms${MS_VALUE}M -Xmx${MX_VALUE}M -version 2>&1` FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - SJAVAC_SERVER_JAVA="$SJAVAC_SERVER_JAVA -Xms1000M -Xmx1500M" + SJAVAC_SERVER_JAVA="$SJAVAC_SERVER_JAVA -Xms${MS_VALUE}M -Xmx${MX_VALUE}M" JVM_ARG_OK=true else $ECHO "Arg failed:" >&5 @@ -49488,44 +50518,7 @@ fi JVM_ARG_OK=false fi - fi - if test "$MEMORY_SIZE" -gt "1000" && test "$JVM_ARG_OK" = false; then - $ECHO "Check if jvm arg is ok: -Xms400M -Xmx1100M" >&5 - $ECHO "Command: $SJAVAC_SERVER_JAVA -Xms400M -Xmx1100M -version" >&5 - OUTPUT=`$SJAVAC_SERVER_JAVA -Xms400M -Xmx1100M -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - SJAVAC_SERVER_JAVA="$SJAVAC_SERVER_JAVA -Xms400M -Xmx1100M" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - fi - if test "$JVM_ARG_OK" = false; then - - $ECHO "Check if jvm arg is ok: -Xms256M -Xmx512M" >&5 - $ECHO "Command: $SJAVAC_SERVER_JAVA -Xms256M -Xmx512M -version" >&5 - OUTPUT=`$SJAVAC_SERVER_JAVA -Xms256M -Xmx512M -version 2>&1` - FOUND_WARN=`$ECHO "$OUTPUT" | grep -i warn` - FOUND_VERSION=`$ECHO $OUTPUT | grep " version \""` - if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then - SJAVAC_SERVER_JAVA="$SJAVAC_SERVER_JAVA -Xms256M -Xmx512M" - JVM_ARG_OK=true - else - $ECHO "Arg failed:" >&5 - $ECHO "$OUTPUT" >&5 - JVM_ARG_OK=false - fi - - fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use sjavac" >&5 -$as_echo_n "checking whether to use sjavac... " >&6; } # Check whether --enable-sjavac was given. if test "${enable_sjavac+set}" = set; then : enableval=$enable_sjavac; ENABLE_SJAVAC="${enableval}" @@ -49533,6 +50526,13 @@ else ENABLE_SJAVAC='no' fi + if test "x$JVM_ARG_OK" = "xfalse"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not set -Xms${MS_VALUE}M -Xmx${MX_VALUE}M, disabling sjavac" >&5 +$as_echo "$as_me: WARNING: Could not set -Xms${MS_VALUE}M -Xmx${MX_VALUE}M, disabling sjavac" >&2;} + ENABLE_SJAVAC=no; + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use sjavac" >&5 +$as_echo_n "checking whether to use sjavac... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLE_SJAVAC" >&5 $as_echo "$ENABLE_SJAVAC" >&6; } @@ -49903,7 +50903,7 @@ $as_echo "no, disabling ccaching of precompiled headers" >&6; } # Check for some common pitfalls - if test x"$OPENJDK_BUILD_OS" = xwindows; then + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.cygwin"; then file_to_test="$SRC_ROOT/LICENSE" if test `$STAT -c '%a' "$file_to_test"` -lt 400; then as_fn_error $? "Bad file permissions on src files. This is usually caused by cloning the repositories with a non cygwin hg in a directory not created in cygwin." "$LINENO" 5 @@ -51347,3 +52347,4 @@ $CHMOD +x $OUTPUT_ROOT/compare.sh printf "\n" fi + diff --git a/common/autoconf/help.m4 b/common/autoconf/help.m4 index 9ae7874fd6b..e1e792c3aa1 100644 --- a/common/autoconf/help.m4 +++ b/common/autoconf/help.m4 @@ -75,20 +75,18 @@ cygwin_help() { HELP_MSG="You might be able to fix this by running '$PKGHANDLER_COMMAND'." ;; freetype) - if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then - HELP_MSG="To install freetype, run: -wget \"http://gnuwin32.sourceforge.net/downlinks/freetype.php\" -O /tmp/freetype-setup.exe -chmod +x /tmp/freetype-setup.exe -/tmp/freetype-setup.exe -Follow GUI prompts, and install to default directory \"C:\Program Files (x86)\GnuWin32\". -After installation, locate lib/libfreetype.dll.a and make a copy with the name freetype.dll." - else - HELP_MSG="You need to build a 64-bit version of freetype. -This is not readily available. -You can find source code and build instructions on -http://www.freetype.org/ -If you put the resulting build in \"C:\Program Files\GnuWin32\", it will be found automatically." - fi + HELP_MSG=" +The freetype library can now be build during the configure process. +Download the freetype sources and unpack them into an arbitrary directory: + +wget http://download.savannah.gnu.org/releases/freetype/freetype-2.5.3.tar.gz +tar -xzf freetype-2.5.3.tar.gz + +Then run configure with '--with-freetype-src='. This will +automatically build the freetype library into '/lib64' for 64-bit +builds or into '/lib32' for 32-bit builds. +Afterwards you can always use '--with-freetype-include=/include' +and '--with-freetype-lib=/lib[32|64]' for other builds." ;; esac } diff --git a/common/autoconf/libraries.m4 b/common/autoconf/libraries.m4 index 3a689ef62ee..5f9eabf250b 100644 --- a/common/autoconf/libraries.m4 +++ b/common/autoconf/libraries.m4 @@ -247,12 +247,95 @@ AC_DEFUN_ONCE([LIB_SETUP_CUPS], ]) +AC_DEFUN([LIB_BUILD_FREETYPE], +[ + FREETYPE_SRC_PATH="$1" + BUILD_FREETYPE=yes + + # Check if the freetype sources are acessible.. + if ! test -d $FREETYPE_SRC_PATH; then + AC_MSG_WARN([--with-freetype-src specified, but can't find path "$FREETYPE_SRC_PATH" - ignoring --with-freetype-src]) + BUILD_FREETYPE=no + fi + # ..and contain a vc2010 project file + vcxproj_path="$FREETYPE_SRC_PATH/builds/windows/vc2010/freetype.vcxproj" + if test "x$BUILD_FREETYPE" = xyes && ! test -s $vcxproj_path; then + AC_MSG_WARN([Can't find project file $vcxproj_path (you may try a newer freetype version) - ignoring --with-freetype-src]) + BUILD_FREETYPE=no + fi + # Now check if configure found a version of 'msbuild.exe' + if test "x$BUILD_FREETYPE" = xyes && test "x$MSBUILD" == x ; then + AC_MSG_WARN([Can't find an msbuild.exe executable (you may try to install .NET 4.0) - ignoring --with-freetype-src]) + BUILD_FREETYPE=no + fi + + # Ready to go.. + if test "x$BUILD_FREETYPE" = xyes; then + + # msbuild requires trailing slashes for output directories + freetype_lib_path="$FREETYPE_SRC_PATH/lib$OPENJDK_TARGET_CPU_BITS/" + freetype_lib_path_unix="$freetype_lib_path" + freetype_obj_path="$FREETYPE_SRC_PATH/obj$OPENJDK_TARGET_CPU_BITS/" + BASIC_WINDOWS_REWRITE_AS_WINDOWS_MIXED_PATH(vcxproj_path) + BASIC_WINDOWS_REWRITE_AS_WINDOWS_MIXED_PATH(freetype_lib_path) + BASIC_WINDOWS_REWRITE_AS_WINDOWS_MIXED_PATH(freetype_obj_path) + if test "x$OPENJDK_TARGET_CPU_BITS" = x64; then + freetype_platform=x64 + else + freetype_platform=win32 + fi + + # The original freetype project file is for VS 2010 (i.e. 'v100'), + # so we have to adapt the toolset if building with any other toolsed (i.e. SDK). + # Currently 'PLATFORM_TOOLSET' is set in 'TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT'/ + # 'TOOLCHAIN_CHECK_POSSIBLE_WIN_SDK_ROOT' in toolchain_windows.m4 + AC_MSG_NOTICE([Trying to compile freetype sources with PlatformToolset=$PLATFORM_TOOLSET to $freetype_lib_path_unix ...]) + + # First we try to build the freetype.dll + $ECHO -e "@echo off\n"\ + "$MSBUILD $vcxproj_path "\ + "/p:PlatformToolset=$PLATFORM_TOOLSET "\ + "/p:Configuration=\"Release Multithreaded\" "\ + "/p:Platform=$freetype_platform "\ + "/p:ConfigurationType=DynamicLibrary "\ + "/p:TargetName=freetype "\ + "/p:OutDir=\"$freetype_lib_path\" "\ + "/p:IntDir=\"$freetype_obj_path\" > freetype.log" > freetype.bat + cmd /c freetype.bat + + if test -s "$freetype_lib_path_unix/freetype.dll"; then + # If that succeeds we also build freetype.lib + $ECHO -e "@echo off\n"\ + "$MSBUILD $vcxproj_path "\ + "/p:PlatformToolset=$PLATFORM_TOOLSET "\ + "/p:Configuration=\"Release Multithreaded\" "\ + "/p:Platform=$freetype_platform "\ + "/p:ConfigurationType=StaticLibrary "\ + "/p:TargetName=freetype "\ + "/p:OutDir=\"$freetype_lib_path\" "\ + "/p:IntDir=\"$freetype_obj_path\" >> freetype.log" > freetype.bat + cmd /c freetype.bat + + if test -s "$freetype_lib_path_unix/freetype.lib"; then + # Once we build both, lib and dll, set freetype lib and include path appropriately + POTENTIAL_FREETYPE_INCLUDE_PATH="$FREETYPE_SRC_PATH/include" + POTENTIAL_FREETYPE_LIB_PATH="$freetype_lib_path_unix" + AC_MSG_NOTICE([Compiling freetype sources succeeded! (see freetype.log for build results)]) + else + BUILD_FREETYPE=no + fi + else + BUILD_FREETYPE=no + fi + fi +]) + AC_DEFUN([LIB_CHECK_POTENTIAL_FREETYPE], [ POTENTIAL_FREETYPE_INCLUDE_PATH="$1" POTENTIAL_FREETYPE_LIB_PATH="$2" METHOD="$3" - + # First check if the files exists. if test -s "$POTENTIAL_FREETYPE_INCLUDE_PATH/ft2build.h"; then # We found an arbitrary include file. That's a good sign. @@ -305,6 +388,8 @@ AC_DEFUN_ONCE([LIB_SETUP_FREETYPE], [specify directory for the freetype include files])]) AC_ARG_WITH(freetype-lib, [AS_HELP_STRING([--with-freetype-lib], [specify directory for the freetype library])]) + AC_ARG_WITH(freetype-src, [AS_HELP_STRING([--with-freetype-src], + [specify directory with freetype sources to automatically build the library (experimental, Windows-only)])]) AC_ARG_ENABLE(freetype-bundling, [AS_HELP_STRING([--disable-freetype-bundling], [disable bundling of the freetype library with the build result @<:@enabled on Windows or when using --with-freetype, disabled otherwise@:>@])]) @@ -313,7 +398,7 @@ AC_DEFUN_ONCE([LIB_SETUP_FREETYPE], FREETYPE_BUNDLE_LIB_PATH= if test "x$FREETYPE_NOT_NEEDED" = xyes; then - if test "x$with_freetype" != x || test "x$with_freetype_include" != x || test "x$with_freetype_lib" != x; then + if test "x$with_freetype" != x || test "x$with_freetype_include" != x || test "x$with_freetype_lib" != x || test "x$with_freetype_src" != x; then AC_MSG_WARN([freetype not used, so --with-freetype is ignored]) fi if test "x$enable_freetype_bundling" != x; then @@ -324,6 +409,25 @@ AC_DEFUN_ONCE([LIB_SETUP_FREETYPE], BUNDLE_FREETYPE="$enable_freetype_bundling" + if test "x$with_freetype_src" != x; then + if test "x$OPENJDK_TARGET_OS" = xwindows; then + # Try to build freetype if --with-freetype-src was given on Windows + LIB_BUILD_FREETYPE([$with_freetype_src]) + if test "x$BUILD_FREETYPE" = xyes; then + # Okay, we built it. Check that it works. + LIB_CHECK_POTENTIAL_FREETYPE($POTENTIAL_FREETYPE_INCLUDE_PATH, $POTENTIAL_FREETYPE_LIB_PATH, [--with-freetype-src]) + if test "x$FOUND_FREETYPE" != xyes; then + AC_MSG_ERROR([Can not use the built freetype at location given by --with-freetype-src]) + fi + else + AC_MSG_NOTICE([User specified --with-freetype-src but building freetype failed. (see freetype.log for build results)]) + AC_MSG_ERROR([Consider building freetype manually and using --with-freetype instead.]) + fi + else + AC_MSG_WARN([--with-freetype-src is currently only supported on Windows - ignoring]) + fi + fi + if test "x$with_freetype" != x || test "x$with_freetype_include" != x || test "x$with_freetype_lib" != x; then # User has specified settings @@ -331,12 +435,12 @@ AC_DEFUN_ONCE([LIB_SETUP_FREETYPE], # If not specified, default is to bundle freetype BUNDLE_FREETYPE=yes fi - + if test "x$with_freetype" != x; then POTENTIAL_FREETYPE_INCLUDE_PATH="$with_freetype/include" POTENTIAL_FREETYPE_LIB_PATH="$with_freetype/lib" fi - + # Allow --with-freetype-lib and --with-freetype-include to override if test "x$with_freetype_include" != x; then POTENTIAL_FREETYPE_INCLUDE_PATH="$with_freetype_include" @@ -468,7 +572,7 @@ AC_DEFUN_ONCE([LIB_SETUP_FREETYPE], FREETYPE_CFLAGS="-I$FREETYPE_INCLUDE_PATH" fi fi - + if test "x$FREETYPE_LIBS" = x; then BASIC_FIXUP_PATH(FREETYPE_LIB_PATH) if test "x$OPENJDK_TARGET_OS" = xwindows; then @@ -484,7 +588,7 @@ AC_DEFUN_ONCE([LIB_SETUP_FREETYPE], PREV_CXXCFLAGS="$CXXFLAGS" PREV_LIBS="$LIBS" PREV_CXX="$CXX" - CXXFLAGS="$CXXFLAGS $FREETYPE_CFLAGS" + CXXFLAGS="$CXXFLAGS $FREETYPE_CFLAGS" LIBS="$LIBS $FREETYPE_LIBS" CXX="$FIXPATH $CXX" AC_LINK_IFELSE([AC_LANG_SOURCE([[ @@ -502,9 +606,9 @@ AC_DEFUN_ONCE([LIB_SETUP_FREETYPE], AC_MSG_RESULT([no]) AC_MSG_NOTICE([Could not compile and link with freetype. This might be a 32/64-bit mismatch.]) AC_MSG_NOTICE([Using FREETYPE_CFLAGS=$FREETYPE_CFLAGS and FREETYPE_LIBS=$FREETYPE_LIBS]) - + HELP_MSG_MISSING_DEPENDENCY([freetype]) - + AC_MSG_ERROR([Can not continue without freetype. $HELP_MSG]) ] ) diff --git a/common/autoconf/platform.m4 b/common/autoconf/platform.m4 index ead832d5050..4eacb397cbe 100644 --- a/common/autoconf/platform.m4 +++ b/common/autoconf/platform.m4 @@ -173,6 +173,7 @@ AC_DEFUN([PLATFORM_EXTRACT_TARGET_AND_BUILD], OPENJDK_BUILD_CPU_ENDIAN="$VAR_CPU_ENDIAN" AC_SUBST(OPENJDK_BUILD_OS) AC_SUBST(OPENJDK_BUILD_OS_API) + AC_SUBST(OPENJDK_BUILD_OS_ENV) AC_SUBST(OPENJDK_BUILD_CPU) AC_SUBST(OPENJDK_BUILD_CPU_ARCH) AC_SUBST(OPENJDK_BUILD_CPU_BITS) @@ -194,6 +195,7 @@ AC_DEFUN([PLATFORM_EXTRACT_TARGET_AND_BUILD], OPENJDK_TARGET_CPU_ENDIAN="$VAR_CPU_ENDIAN" AC_SUBST(OPENJDK_TARGET_OS) AC_SUBST(OPENJDK_TARGET_OS_API) + AC_SUBST(OPENJDK_TARGET_OS_ENV) AC_SUBST(OPENJDK_TARGET_CPU) AC_SUBST(OPENJDK_TARGET_CPU_ARCH) AC_SUBST(OPENJDK_TARGET_CPU_BITS) diff --git a/common/autoconf/spec.gmk.in b/common/autoconf/spec.gmk.in index 315c10615e4..e85c830783c 100644 --- a/common/autoconf/spec.gmk.in +++ b/common/autoconf/spec.gmk.in @@ -106,6 +106,7 @@ OPENJDK_TARGET_OS_EXPORT_DIR:=@OPENJDK_TARGET_OS_EXPORT_DIR@ # When not cross-compiling, it is the same as the target. OPENJDK_BUILD_OS:=@OPENJDK_BUILD_OS@ OPENJDK_BUILD_OS_API:=@OPENJDK_BUILD_OS_API@ +OPENJDK_BUILD_OS_ENV:=@OPENJDK_BUILD_OS_ENV@ OPENJDK_BUILD_CPU:=@OPENJDK_BUILD_CPU@ OPENJDK_BUILD_CPU_ARCH:=@OPENJDK_BUILD_CPU_ARCH@ diff --git a/common/autoconf/toolchain.m4 b/common/autoconf/toolchain.m4 index f380bf446ad..db0a253a207 100644 --- a/common/autoconf/toolchain.m4 +++ b/common/autoconf/toolchain.m4 @@ -557,6 +557,14 @@ AC_DEFUN_ONCE([TOOLCHAIN_DETECT_TOOLCHAIN_EXTRA], BASIC_FIXUP_EXECUTABLE(RC) AC_CHECK_PROG([DUMPBIN], [dumpbin], [dumpbin],,,) BASIC_FIXUP_EXECUTABLE(DUMPBIN) + # We need to check for 'msbuild.exe' because at the place where we expect to + # find 'msbuild.exe' there's also a directory called 'msbuild' and configure + # won't find the 'msbuild.exe' executable in that case (and the + # 'ac_executable_extensions' is unusable due to performance reasons). + # Notice that we intentionally don't fix up the path to MSBUILD because we + # will call it in a DOS shell during freetype detection on Windows (see + # 'LIB_SETUP_FREETYPE' in "libraries.m4" + AC_CHECK_PROG([MSBUILD], [msbuild.exe], [msbuild.exe],,,) fi if test "x$OPENJDK_TARGET_OS" = xsolaris; then diff --git a/common/autoconf/toolchain_windows.m4 b/common/autoconf/toolchain_windows.m4 index b4888a46ac5..380f7dd4054 100644 --- a/common/autoconf/toolchain_windows.m4 +++ b/common/autoconf/toolchain_windows.m4 @@ -33,6 +33,10 @@ AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT], if test -f "$VS100BASE/$VCVARSFILE"; then AC_MSG_NOTICE([Found Visual Studio installation at $VS100BASE using $METHOD]) VS_ENV_CMD="$VS100BASE/$VCVARSFILE" + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be one of 'v100', 'v110' or 'v120' for VS 2010, 2012 or VS2013 + # TODO: improve detection for other versions of VS + PLATFORM_TOOLSET="v100" else AC_MSG_NOTICE([Found Visual Studio installation at $VS100BASE using $METHOD]) AC_MSG_NOTICE([Warning: $VCVARSFILE is missing, this is probably Visual Studio Express. Ignoring]) @@ -61,6 +65,10 @@ AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_WIN_SDK_ROOT], else VS_ENV_ARGS="/x64" fi + # PLATFORM_TOOLSET is used during the compilation of the freetype sources (see + # 'LIB_BUILD_FREETYPE' in libraries.m4) and must be 'Windows7.1SDK' for Windows7.1SDK + # TODO: improve detection for other versions of SDK + PLATFORM_TOOLSET="Windows7.1SDK" else AC_MSG_NOTICE([Found Windows SDK installation at $WIN_SDK_BASE using $METHOD]) AC_MSG_NOTICE([Warning: Installation is broken, SetEnv.Cmd is missing. Ignoring]) @@ -244,12 +252,22 @@ AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_MSVCR_DLL], # Need to check if the found msvcr is correct architecture AC_MSG_CHECKING([found msvcr100.dll architecture]) MSVCR_DLL_FILETYPE=`$FILE -b "$POSSIBLE_MSVCR_DLL"` - if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then - CORRECT_MSVCR_ARCH=386 + if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.msys"; then + # The MSYS 'file' command returns "PE32 executable for MS Windows (DLL) (GUI) Intel 80386 32-bit" + # on x32 and "PE32+ executable for MS Windows (DLL) (GUI) Mono/.Net assembly" on x64 systems. + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH="PE32 executable" + else + CORRECT_MSVCR_ARCH="PE32+ executable" + fi else - CORRECT_MSVCR_ARCH=x86-64 + if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then + CORRECT_MSVCR_ARCH=386 + else + CORRECT_MSVCR_ARCH=x86-64 + fi fi - if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP $CORRECT_MSVCR_ARCH 2>&1 > /dev/null; then + if $ECHO "$MSVCR_DLL_FILETYPE" | $GREP "$CORRECT_MSVCR_ARCH" 2>&1 > /dev/null; then AC_MSG_RESULT([ok]) MSVCR_DLL="$POSSIBLE_MSVCR_DLL" AC_MSG_CHECKING([for msvcr100.dll]) diff --git a/common/bin/hgforest.sh b/common/bin/hgforest.sh index e7fbd1fe13f..5bf586bc13c 100644 --- a/common/bin/hgforest.sh +++ b/common/bin/hgforest.sh @@ -77,6 +77,11 @@ do shift done +# debug mode +if [ "${HGFOREST_DEBUG:-false}" = "true" ] ; then + global_opts="${global_opts} --debug" +fi + # silence standard output? if [ ${qflag} = "true" ] ; then global_opts="${global_opts} -q" @@ -89,14 +94,26 @@ if [ ${vflag} = "true" ] ; then fi # Make sure we have a command. -if [ $# -lt 1 -o -z "${1:-}" ] ; then - echo "ERROR: No command to hg supplied!" - usage +if [ ${#} -lt 1 -o -z "${1:-}" ] ; then + echo "ERROR: No command to hg supplied!" > ${status_output} + usage > ${status_output} fi -command="$1"; shift +# grab command +command="${1}"; shift + +if [ ${vflag} = "true" ] ; then + echo "# Mercurial command: ${command}" > ${status_output} +fi + + +# capture command options and arguments (if any) command_args="${@:-}" +if [ ${vflag} = "true" ] ; then + echo "# Mercurial command arguments: ${command_args}" > ${status_output} +fi + # Clean out the temporary directory that stores the pid files. tmp=/tmp/forest.$$ rm -f -r ${tmp} @@ -104,7 +121,8 @@ mkdir -p ${tmp} if [ "${HGFOREST_DEBUG:-false}" = "true" ] ; then - echo "DEBUG: temp files are in: ${tmp}" + # ignores redirection. + echo "DEBUG: temp files are in: ${tmp}" >&2 fi # Check if we can use fifos for monitoring sub-process completion. @@ -377,21 +395,33 @@ else fi fi done + + if [ ${have_fifos} = "true" ]; then + # done with the fifo + exec 3>&- + fi fi # Wait for all subprocesses to complete wait # Terminate with exit 0 only if all subprocesses were successful +# Terminate with highest exit code of subprocesses ec=0 if [ -d ${tmp} ]; then rcfiles="`(ls -a ${tmp}/*.pid.rc 2> /dev/null) || echo ''`" for rc in ${rcfiles} ; do exit_code=`cat ${rc} | tr -d ' \n\r'` if [ "${exit_code}" != "0" ] ; then + if [ ${exit_code} -gt 1 ]; then + # mercurial exit codes greater than "1" signal errors. repo="`echo ${rc} | sed -e 's@^'${tmp}'@@' -e 's@/*\([^/]*\)\.pid\.rc$@\1@' -e 's@_@/@g'`" echo "WARNING: ${repo} exited abnormally (${exit_code})" > ${status_output} - ec=1 + fi + if [ ${exit_code} -gt ${ec} ]; then + # assume that larger exit codes are more significant + ec=${exit_code} + fi fi done fi diff --git a/corba/.hgignore b/corba/.hgignore index e05f09317c5..5e6b22f24ee 100644 --- a/corba/.hgignore +++ b/corba/.hgignore @@ -1,5 +1,6 @@ ^build/ ^dist/ +^webrev /nbproject/private/ ^.hgtip .DS_Store diff --git a/corba/.hgtags b/corba/.hgtags index d7401bcb630..210911039ff 100644 --- a/corba/.hgtags +++ b/corba/.hgtags @@ -269,3 +269,7 @@ ddc07abf4307855c0dc904cc5c96cc764023a930 jdk9-b22 8a44142bb7fc8118f70f91a1b97c12dfc50563ee jdk9-b24 da08cca6b97f41b7081a3e176dcb400af6e4bb26 jdk9-b25 6c777df597bbf5abba3488d44c401edfe73c74af jdk9-b26 +7e06bf1dcb0907b80ddf59315426ce9ce775e56d jdk9-b27 +a00b04ef067e39f50b9a0fea6f1904e35d632a73 jdk9-b28 +163a9cd806fd09970baf1f5f42b92a3cfe7ee945 jdk9-b29 +98967ae6ae53ebf15615e07cd5a6b1ae04dfd84c jdk9-b30 diff --git a/corba/make/GensrcCorba.gmk b/corba/make/GensrcCorba.gmk index 8b814238272..1951cd43e37 100644 --- a/corba/make/GensrcCorba.gmk +++ b/corba/make/GensrcCorba.gmk @@ -50,8 +50,9 @@ $(eval $(call SetupJavaCompilation,BUILD_IDLJ, \ INCLUDES := com/sun/tools/corba/se/idl, \ EXCLUDE_FILES := ResourceBundleUtil.java)) +# Force the language to english for predictable source code generation. TOOL_IDLJ_CMD := $(JAVA) -cp $(CORBA_OUTPUTDIR)/idlj_classes \ - com.sun.tools.corba.se.idl.toJavaPortable.Compile + -Duser.language=en com.sun.tools.corba.se.idl.toJavaPortable.Compile ################################################################################ diff --git a/corba/src/java.corba/share/classes/org/omg/CORBA/FloatSeqHelper.java b/corba/src/java.corba/share/classes/org/omg/CORBA/FloatSeqHelper.java index ad02ae3b56a..8cf9684c585 100644 --- a/corba/src/java.corba/share/classes/org/omg/CORBA/FloatSeqHelper.java +++ b/corba/src/java.corba/share/classes/org/omg/CORBA/FloatSeqHelper.java @@ -38,11 +38,11 @@ package org.omg.CORBA; * OMG specifications : * */ diff --git a/get_source.sh b/get_source.sh index 5e874d1cb51..72049bdec8a 100644 --- a/get_source.sh +++ b/get_source.sh @@ -67,7 +67,7 @@ if [ "x$hgwhere" = "x" ]; then error "Could not locate Mercurial command" fi -hgversion="`hg --version 2> /dev/null | sed -n -e 's@^Mercurial Distributed SCM (version \([^+]*\).*)\$@\1@p'`" +hgversion="`LANGUAGE=en hg --version 2> /dev/null | sed -n -e 's@^Mercurial Distributed SCM (version \([^+]*\).*)\$@\1@p'`" if [ "x${hgversion}" = "x" ] ; then error "Could not determine Mercurial version of $hgwhere" fi diff --git a/hotspot/.hgignore b/hotspot/.hgignore index c652de177bf..e9ce602582b 100644 --- a/hotspot/.hgignore +++ b/hotspot/.hgignore @@ -1,5 +1,6 @@ ^build/ ^dist/ +^webrev /nbproject/private/ ^src/share/tools/hsdis/build/ ^src/share/tools/IdealGraphVisualizer/[a-zA-Z0-9]*/build/ diff --git a/hotspot/.hgtags b/hotspot/.hgtags index 2dbb04816d9..a7b7e9b5c01 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -429,3 +429,7 @@ dd472cdacc32e3afc7c5bfa7ef16ea0e0befb7fa jdk9-b23 dde2d03b0ea46a27650839e3a1d212c7c1f7b4c8 jdk9-b24 6de94e8693240cec8aae11f6b42f43433456a733 jdk9-b25 48b95a073d752d6891cc0d1d2836b321ecf3ce0c jdk9-b26 +f95347244306affc32ce3056f27ceff7b2100810 jdk9-b27 +657294869d7ff063e055f5492cab7ce5612ca851 jdk9-b28 +deb29e92f68ace2808a36ecfa18c7d61dcb645bb jdk9-b29 +5c722dffbc0f34eb8d903dca7b261e52248fa17e jdk9-b30 diff --git a/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c b/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c index 0d3960f7ae7..5df5f1f1359 100644 --- a/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c +++ b/hotspot/agent/src/os/linux/LinuxDebuggerLocal.c @@ -64,7 +64,10 @@ static jmethodID listAdd_ID = 0; #define THROW_NEW_DEBUGGER_EXCEPTION(str) { throw_new_debugger_exception(env, str); return;} void throw_new_debugger_exception(JNIEnv* env, const char* errMsg) { - (*env)->ThrowNew(env, (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"), errMsg); + jclass clazz; + clazz = (*env)->FindClass(env, "sun/jvm/hotspot/debugger/DebuggerException"); + CHECK_EXCEPTION; + (*env)->ThrowNew(env, clazz, errMsg); } struct ps_prochandle* get_proc_handle(JNIEnv* env, jobject this_obj) { @@ -149,11 +152,14 @@ static void fillThreadsAndLoadObjects(JNIEnv* env, jobject this_obj, struct ps_p const char* name; jobject loadObject; jobject loadObjectList; + jstring str; base = get_lib_base(ph, i); name = get_lib_name(ph, i); - loadObject = (*env)->CallObjectMethod(env, this_obj, createLoadObject_ID, - (*env)->NewStringUTF(env, name), (jlong)0, (jlong)base); + + str = (*env)->NewStringUTF(env, name); + CHECK_EXCEPTION; + loadObject = (*env)->CallObjectMethod(env, this_obj, createLoadObject_ID, str, (jlong)0, (jlong)base); CHECK_EXCEPTION; loadObjectList = (*env)->GetObjectField(env, this_obj, loadObjectList_ID); CHECK_EXCEPTION; @@ -298,13 +304,18 @@ JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_l JNIEXPORT jobject JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_lookupByAddress0 (JNIEnv *env, jobject this_obj, jlong addr) { uintptr_t offset; + jobject obj; + jstring str; const char* sym = NULL; struct ps_prochandle* ph = get_proc_handle(env, this_obj); sym = symbol_for_pc(ph, (uintptr_t) addr, &offset); if (sym == NULL) return 0; - return (*env)->CallObjectMethod(env, this_obj, createClosestSymbol_ID, - (*env)->NewStringUTF(env, sym), (jlong)offset); + str = (*env)->NewStringUTF(env, sym); + CHECK_EXCEPTION_(NULL); + obj = (*env)->CallObjectMethod(env, this_obj, createClosestSymbol_ID, str, (jlong)offset); + CHECK_EXCEPTION_(NULL); + return obj; } /* diff --git a/hotspot/agent/src/os/solaris/proc/saproc.cpp b/hotspot/agent/src/os/solaris/proc/saproc.cpp index 5a910d3a357..d727740ee34 100644 --- a/hotspot/agent/src/os/solaris/proc/saproc.cpp +++ b/hotspot/agent/src/os/solaris/proc/saproc.cpp @@ -90,7 +90,9 @@ struct DebuggerWith2Objects : DebuggerWithObject { */ static void throwNewDebuggerException(JNIEnv* env, const char* errMsg) { - env->ThrowNew(env->FindClass("sun/jvm/hotspot/debugger/DebuggerException"), errMsg); + jclass clazz = env->FindClass("sun/jvm/hotspot/debugger/DebuggerException"); + CHECK_EXCEPTION; + env->ThrowNew(clazz, errMsg); } // JNI ids for some fields, methods @@ -314,7 +316,7 @@ static void * pathmap_dlopen(const char * name, int mode) { handle = dlopen(name, mode); } if (_libsaproc_debug) { - printf("libsaproc DEBUG: pathmap_dlopen %s return 0x%x\n", name, handle); + printf("libsaproc DEBUG: pathmap_dlopen %s return 0x%lx\n", name, (unsigned long) handle); } return handle; } @@ -661,30 +663,30 @@ init_classsharing_workaround(void *cd, const prmap_t* pmap, const char* obj_name // read FileMapHeader size_t n = read(fd, pheader, sizeof(struct FileMapHeader)); if (n != sizeof(struct FileMapHeader)) { - free(pheader); - close(fd); char errMsg[ERR_MSG_SIZE]; sprintf(errMsg, "unable to read shared archive file map header from %s", classes_jsa); + close(fd); + free(pheader); THROW_NEW_DEBUGGER_EXCEPTION_(errMsg, 1); } // check file magic if (pheader->_magic != 0xf00baba2) { - free(pheader); - close(fd); char errMsg[ERR_MSG_SIZE]; sprintf(errMsg, "%s has bad shared archive magic 0x%x, expecting 0xf00baba2", classes_jsa, pheader->_magic); + close(fd); + free(pheader); THROW_NEW_DEBUGGER_EXCEPTION_(errMsg, 1); } // check version if (pheader->_version != CURRENT_ARCHIVE_VERSION) { - free(pheader); - close(fd); char errMsg[ERR_MSG_SIZE]; sprintf(errMsg, "%s has wrong shared archive version %d, expecting %d", classes_jsa, pheader->_version, CURRENT_ARCHIVE_VERSION); + close(fd); + free(pheader); THROW_NEW_DEBUGGER_EXCEPTION_(errMsg, 1); } @@ -962,6 +964,7 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLoca CHECK_EXCEPTION_(0); jboolean isCopy; jlong* ptr = env->GetLongArrayElements(res, &isCopy); + CHECK_EXCEPTION_(NULL); for (int i = 0; i < NPRGREG; i++) { ptr[i] = (jlong) (uintptr_t) gregs[i]; } @@ -1253,6 +1256,7 @@ JNIEXPORT jstring JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_d (JNIEnv *env, jobject this_object, jstring name) { jboolean isCopy; const char* ptr = env->GetStringUTFChars(name, &isCopy); + CHECK_EXCEPTION_(NULL); char buf[2*SYMBOL_BUF_SIZE + 1]; jstring res = 0; if (cplus_demangle((char*) ptr, buf, sizeof(buf)) != DEMANGLE_ESPACE) { @@ -1439,7 +1443,9 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_proc_ProcDebuggerLocal_init "createClosestSymbol", "(Ljava/lang/String;J)Lsun/jvm/hotspot/debugger/cdbg/ClosestSymbol;"); CHECK_EXCEPTION; - listAdd_ID = env->GetMethodID(env->FindClass("java/util/List"), "add", "(Ljava/lang/Object;)Z"); + jclass list_clazz = env->FindClass("java/util/List"); + CHECK_EXCEPTION; + listAdd_ID = env->GetMethodID(list_clazz, "add", "(Ljava/lang/Object;)Z"); CHECK_EXCEPTION; // part of the class sharing workaround diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java index eea5e4b7271..e177468e53d 100644 --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java @@ -43,10 +43,10 @@ import sun.jvm.hotspot.types.TypeDataBase; // Mirror class for G1CollectedHeap. public class G1CollectedHeap extends SharedHeap { - // HeapRegionSeq _seq; - static private long hrsFieldOffset; - // MemRegion _g1_committed; - static private long g1CommittedFieldOffset; + // HeapRegionManager _hrm; + static private long hrmFieldOffset; + // MemRegion _g1_reserved; + static private long g1ReservedFieldOffset; // size_t _summary_bytes_used; static private CIntegerField summaryBytesUsedField; // G1MonitoringSupport* _g1mm; @@ -67,8 +67,7 @@ public class G1CollectedHeap extends SharedHeap { static private synchronized void initialize(TypeDataBase db) { Type type = db.lookupType("G1CollectedHeap"); - hrsFieldOffset = type.getField("_hrs").getOffset(); - g1CommittedFieldOffset = type.getField("_g1_committed").getOffset(); + hrmFieldOffset = type.getField("_hrm").getOffset(); summaryBytesUsedField = type.getCIntegerField("_summary_bytes_used"); g1mmField = type.getAddressField("_g1mm"); oldSetFieldOffset = type.getField("_old_set").getOffset(); @@ -76,9 +75,7 @@ public class G1CollectedHeap extends SharedHeap { } public long capacity() { - Address g1CommittedAddr = addr.addOffsetTo(g1CommittedFieldOffset); - MemRegion g1Committed = new MemRegion(g1CommittedAddr); - return g1Committed.byteSize(); + return hrm().capacity(); } public long used() { @@ -86,13 +83,13 @@ public class G1CollectedHeap extends SharedHeap { } public long n_regions() { - return hrs().length(); + return hrm().length(); } - private HeapRegionSeq hrs() { - Address hrsAddr = addr.addOffsetTo(hrsFieldOffset); - return (HeapRegionSeq) VMObjectFactory.newObject(HeapRegionSeq.class, - hrsAddr); + private HeapRegionManager hrm() { + Address hrmAddr = addr.addOffsetTo(hrmFieldOffset); + return (HeapRegionManager) VMObjectFactory.newObject(HeapRegionManager.class, + hrmAddr); } public G1MonitoringSupport g1mm() { @@ -113,7 +110,7 @@ public class G1CollectedHeap extends SharedHeap { } private Iterator heapRegionIterator() { - return hrs().heapRegionIterator(); + return hrm().heapRegionIterator(); } public void heapRegionIterate(SpaceClosure scl) { diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1HeapRegionTable.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1HeapRegionTable.java index cfbea40435c..e8bbaaa0553 100644 --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1HeapRegionTable.java +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1HeapRegionTable.java @@ -93,19 +93,35 @@ public class G1HeapRegionTable extends VMObject { private class HeapRegionIterator implements Iterator { private long index; private long length; + private HeapRegion next; + + public HeapRegion positionToNext() { + HeapRegion result = next; + while (index < length && at(index) == null) { + index++; + } + if (index < length) { + next = at(index); + index++; // restart search at next element + } else { + next = null; + } + return result; + } @Override - public boolean hasNext() { return index < length; } + public boolean hasNext() { return next != null; } @Override - public HeapRegion next() { return at(index++); } + public HeapRegion next() { return positionToNext(); } @Override - public void remove() { /* not supported */ } + public void remove() { /* not supported */ } - HeapRegionIterator(long committedLength) { + HeapRegionIterator(long totalLength) { index = 0; - length = committedLength; + length = totalLength; + positionToNext(); } } diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSeq.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionManager.java similarity index 81% rename from hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSeq.java rename to hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionManager.java index 190bf56a17f..2a2a26f3947 100644 --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSeq.java +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,13 +37,13 @@ import sun.jvm.hotspot.types.CIntegerField; import sun.jvm.hotspot.types.Type; import sun.jvm.hotspot.types.TypeDataBase; -// Mirror class for HeapRegionSeq. It essentially encapsulates the G1HeapRegionTable. +// Mirror class for HeapRegionManager. -public class HeapRegionSeq extends VMObject { +public class HeapRegionManager extends VMObject { // G1HeapRegionTable _regions static private long regionsFieldOffset; // uint _committed_length - static private CIntegerField committedLengthField; + static private CIntegerField numCommittedField; static { VM.registerVMInitializedObserver(new Observer() { @@ -54,10 +54,10 @@ public class HeapRegionSeq extends VMObject { } static private synchronized void initialize(TypeDataBase db) { - Type type = db.lookupType("HeapRegionSeq"); + Type type = db.lookupType("HeapRegionManager"); regionsFieldOffset = type.getField("_regions").getOffset(); - committedLengthField = type.getCIntegerField("_committed_length"); + numCommittedField = type.getCIntegerField("_num_committed"); } private G1HeapRegionTable regions() { @@ -66,19 +66,23 @@ public class HeapRegionSeq extends VMObject { regionsAddr); } + public long capacity() { + return length() * HeapRegion.grainBytes(); + } + public long length() { return regions().length(); } public long committedLength() { - return committedLengthField.getValue(addr); + return numCommittedField.getValue(addr); } public Iterator heapRegionIterator() { - return regions().heapRegionIterator(committedLength()); + return regions().heapRegionIterator(length()); } - public HeapRegionSeq(Address addr) { + public HeapRegionManager(Address addr) { super(addr); } } diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java index ba9422ca8ee..a411534ea3a 100644 --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,7 +49,6 @@ public class ArrayKlass extends Klass { higherDimension = new MetadataField(type.getAddressField("_higher_dimension"), 0); lowerDimension = new MetadataField(type.getAddressField("_lower_dimension"), 0); vtableLen = new CIntField(type.getCIntegerField("_vtable_len"), 0); - componentMirror = new OopField(type.getOopField("_component_mirror"), 0); javaLangCloneableName = null; javaLangObjectName = null; javaIoSerializableName = null; @@ -63,7 +62,6 @@ public class ArrayKlass extends Klass { private static MetadataField higherDimension; private static MetadataField lowerDimension; private static CIntField vtableLen; - private static OopField componentMirror; public Klass getJavaSuper() { SystemDictionary sysDict = VM.getVM().getSystemDictionary(); @@ -74,7 +72,6 @@ public class ArrayKlass extends Klass { public Klass getHigherDimension() { return (Klass) higherDimension.getValue(this); } public Klass getLowerDimension() { return (Klass) lowerDimension.getValue(this); } public long getVtableLen() { return vtableLen.getValue(this); } - public Oop getComponentMirror() { return componentMirror.getValue(this); } // constant class names - javaLangCloneable, javaIoSerializable, javaLangObject // Initialized lazily to avoid initialization ordering dependencies between ArrayKlass and SymbolTable @@ -144,6 +141,5 @@ public class ArrayKlass extends Klass { visitor.doMetadata(higherDimension, true); visitor.doMetadata(lowerDimension, true); visitor.doCInt(vtableLen, true); - visitor.doOop(componentMirror, true); } } diff --git a/hotspot/make/Makefile b/hotspot/make/Makefile index bde309fc467..4ef0e45a9ba 100644 --- a/hotspot/make/Makefile +++ b/hotspot/make/Makefile @@ -337,7 +337,7 @@ export_optimized: export_product_jdk:: $(MAKE) BUILD_FLAVOR=$(@:export_%_jdk=%) ALT_EXPORT_PATH=$(JDK_IMAGE_DIR) generic_export export_optimized_jdk:: - $(MAKE) BUILD_FLAVOR=$(@:export_%_jdk=%) ALT_EXPORT_PATH=$(JDK_IMAGE_DIR) generic_export + $(MAKE) BUILD_FLAVOR=$(@:export_%_jdk=%) ALT_EXPORT_PATH=$(JDK_IMAGE_DIR)/$(@:export_%_jdk=%) generic_export export_fastdebug_jdk:: $(MAKE) BUILD_FLAVOR=$(@:export_%_jdk=%) ALT_EXPORT_PATH=$(JDK_IMAGE_DIR)/$(@:export_%_jdk=%) generic_export export_debug_jdk:: @@ -721,6 +721,19 @@ copy_debug_jdk:: ($(CD) $(JDK_IMAGE_DIR)/debug && $(TAR) -xf -) ; \ fi +copy_optimized_jdk:: + $(RM) -r $(JDK_IMAGE_DIR)/optimized + $(MKDIR) -p $(JDK_IMAGE_DIR)/optimized + if [ -d $(JDK_IMPORT_PATH)/optimized ] ; then \ + ($(CD) $(JDK_IMPORT_PATH)/optimized && \ + $(TAR) -cf - $(JDK_DIRS)) | \ + ($(CD) $(JDK_IMAGE_DIR)/optimized && $(TAR) -xf -) ; \ + else \ + ($(CD) $(JDK_IMPORT_PATH) && \ + $(TAR) -cf - $(JDK_DIRS)) | \ + ($(CD) $(JDK_IMAGE_DIR)/optimized && $(TAR) -xf -) ; \ + fi + # # Check target # diff --git a/hotspot/make/aix/makefiles/mapfile-vers-debug b/hotspot/make/aix/makefiles/mapfile-vers-debug index 0a9d3e981c9..613e26bd880 100644 --- a/hotspot/make/aix/makefiles/mapfile-vers-debug +++ b/hotspot/make/aix/makefiles/mapfile-vers-debug @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2014, 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 @@ -125,7 +125,6 @@ SUNWprivate_1.1 { JVM_GetClassSignature; JVM_GetClassSigners; JVM_GetClassTypeAnnotations; - JVM_GetComponentType; JVM_GetDeclaredClasses; JVM_GetDeclaringClass; JVM_GetEnclosingMethodInfo; diff --git a/hotspot/make/aix/makefiles/mapfile-vers-product b/hotspot/make/aix/makefiles/mapfile-vers-product index f748a10c8e2..0b8a040c74a 100644 --- a/hotspot/make/aix/makefiles/mapfile-vers-product +++ b/hotspot/make/aix/makefiles/mapfile-vers-product @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2014, 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 @@ -125,7 +125,6 @@ SUNWprivate_1.1 { JVM_GetClassSignature; JVM_GetClassSigners; JVM_GetClassTypeAnnotations; - JVM_GetComponentType; JVM_GetDeclaredClasses; JVM_GetDeclaringClass; JVM_GetEnclosingMethodInfo; diff --git a/hotspot/make/bsd/makefiles/gcc.make b/hotspot/make/bsd/makefiles/gcc.make index 54d319bb1eb..1877245e435 100644 --- a/hotspot/make/bsd/makefiles/gcc.make +++ b/hotspot/make/bsd/makefiles/gcc.make @@ -325,6 +325,10 @@ ifeq ($(USE_CLANG), true) else ifeq ($(shell expr $(CC_VER_MAJOR) = 5 \& $(CC_VER_MINOR) = 1), 1) OPT_CFLAGS/loopTransform.o += $(OPT_CFLAGS/NOOPT) OPT_CFLAGS/unsafe.o += -O1 + # Clang 6.0 + else ifeq ($(shell expr $(CC_VER_MAJOR) = 6 \& $(CC_VER_MINOR) = 0), 1) + OPT_CFLAGS/loopTransform.o += $(OPT_CFLAGS/NOOPT) + OPT_CFLAGS/unsafe.o += -O1 else $(error "Update compiler workarounds for Clang $(CC_VER_MAJOR).$(CC_VER_MINOR)") endif @@ -508,13 +512,9 @@ endif ifeq ($(USE_CLANG),) # Enable bounds checking. - # _FORTIFY_SOURCE appears in GCC 4.0+ ifeq "$(shell expr \( $(CC_VER_MAJOR) \> 3 \) )" "1" - # compile time size bounds checks - FASTDEBUG_CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 - - # and runtime size bounds checks and paranoid stack smashing checks. - DEBUG_CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-all --param ssp-buffer-size=1 + # stack smashing checks. + DEBUG_CFLAGS += -fstack-protector-all --param ssp-buffer-size=1 endif endif diff --git a/hotspot/make/bsd/makefiles/mapfile-vers-darwin-debug b/hotspot/make/bsd/makefiles/mapfile-vers-darwin-debug index db8c276811a..7f7f00036fe 100644 --- a/hotspot/make/bsd/makefiles/mapfile-vers-darwin-debug +++ b/hotspot/make/bsd/makefiles/mapfile-vers-darwin-debug @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2014, 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 @@ -123,7 +123,6 @@ _JVM_GetClassSignature _JVM_GetClassSigners _JVM_GetClassTypeAnnotations - _JVM_GetComponentType _JVM_GetDeclaredClasses _JVM_GetDeclaringClass _JVM_GetEnclosingMethodInfo diff --git a/hotspot/make/bsd/makefiles/mapfile-vers-darwin-product b/hotspot/make/bsd/makefiles/mapfile-vers-darwin-product index db8c276811a..7f7f00036fe 100644 --- a/hotspot/make/bsd/makefiles/mapfile-vers-darwin-product +++ b/hotspot/make/bsd/makefiles/mapfile-vers-darwin-product @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2014, 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 @@ -123,7 +123,6 @@ _JVM_GetClassSignature _JVM_GetClassSigners _JVM_GetClassTypeAnnotations - _JVM_GetComponentType _JVM_GetDeclaredClasses _JVM_GetDeclaringClass _JVM_GetEnclosingMethodInfo diff --git a/hotspot/make/bsd/makefiles/mapfile-vers-debug b/hotspot/make/bsd/makefiles/mapfile-vers-debug index df433ad55ea..f7d88125ab5 100644 --- a/hotspot/make/bsd/makefiles/mapfile-vers-debug +++ b/hotspot/make/bsd/makefiles/mapfile-vers-debug @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2014, 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 @@ -125,7 +125,6 @@ SUNWprivate_1.1 { JVM_GetClassSignature; JVM_GetClassSigners; JVM_GetClassTypeAnnotations; - JVM_GetComponentType; JVM_GetDeclaredClasses; JVM_GetDeclaringClass; JVM_GetEnclosingMethodInfo; diff --git a/hotspot/make/bsd/makefiles/mapfile-vers-product b/hotspot/make/bsd/makefiles/mapfile-vers-product index df433ad55ea..f7d88125ab5 100644 --- a/hotspot/make/bsd/makefiles/mapfile-vers-product +++ b/hotspot/make/bsd/makefiles/mapfile-vers-product @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2014, 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 @@ -125,7 +125,6 @@ SUNWprivate_1.1 { JVM_GetClassSignature; JVM_GetClassSigners; JVM_GetClassTypeAnnotations; - JVM_GetComponentType; JVM_GetDeclaredClasses; JVM_GetDeclaringClass; JVM_GetEnclosingMethodInfo; diff --git a/hotspot/make/excludeSrc.make b/hotspot/make/excludeSrc.make index 00db453d5f8..c28f3b9745e 100644 --- a/hotspot/make/excludeSrc.make +++ b/hotspot/make/excludeSrc.make @@ -70,7 +70,8 @@ ifeq ($(INCLUDE_CDS), false) CXXFLAGS += -DINCLUDE_CDS=0 CFLAGS += -DINCLUDE_CDS=0 - Src_Files_EXCLUDE += filemap.cpp metaspaceShared.cpp + Src_Files_EXCLUDE += filemap.cpp metaspaceShared*.cpp sharedPathsMiscInfo.cpp \ + systemDictionaryShared.cpp classLoaderExt.cpp sharedClassUtil.cpp endif ifeq ($(INCLUDE_ALL_GCS), false) @@ -119,8 +120,8 @@ ifeq ($(INCLUDE_NMT), false) CFLAGS += -DINCLUDE_NMT=0 Src_Files_EXCLUDE += \ - memBaseline.cpp memPtr.cpp memRecorder.cpp memReporter.cpp memSnapshot.cpp memTrackWorker.cpp \ - memTracker.cpp nmtDCmd.cpp + memBaseline.cpp memReporter.cpp mallocTracker.cpp virtualMemoryTracker.cpp nmtCommon.cpp \ + memTracker.cpp nmtDCmd.cpp mallocSiteTable.cpp endif -include $(HS_ALT_MAKE)/excludeSrc.make diff --git a/hotspot/make/jprt.gmk b/hotspot/make/jprt.gmk index aded08153ca..41db0685c93 100644 --- a/hotspot/make/jprt.gmk +++ b/hotspot/make/jprt.gmk @@ -42,6 +42,9 @@ jprt_build_debugEmb: jprt_build_fastdebugEmb: $(MAKE) JAVASE_EMBEDDED=true MINIMIZE_RAM_USAGE=true jprt_build_fastdebug +jprt_build_optimizedEmb: + $(MAKE) JAVASE_EMBEDDED=true MINIMIZE_RAM_USAGE=true jprt_build_optimized + jprt_build_productOpen: $(MAKE) OPENJDK=true jprt_build_product @@ -51,6 +54,9 @@ jprt_build_debugOpen: jprt_build_fastdebugOpen: $(MAKE) OPENJDK=true jprt_build_fastdebug +jprt_build_optimizedOpen: + $(MAKE) OPENJDK=true jprt_build_optimized + jprt_build_product: all_product copy_product_jdk export_product_jdk ( $(CD) $(JDK_IMAGE_DIR) && \ $(ZIPEXE) $(ZIPFLAGS) -r $(JPRT_ARCHIVE_BUNDLE) . ) @@ -63,5 +69,9 @@ jprt_build_debug: all_debug copy_debug_jdk export_debug_jdk ( $(CD) $(JDK_IMAGE_DIR)/debug && \ $(ZIPEXE) $(ZIPFLAGS) -r $(JPRT_ARCHIVE_BUNDLE) . ) -.PHONY: jprt_build_product jprt_build_fastdebug jprt_build_debug +jprt_build_optimized: all_optimized copy_optimized_jdk export_optimized_jdk + ( $(CD) $(JDK_IMAGE_DIR)/optimized && \ + $(ZIPEXE) $(ZIPFLAGS) -r $(JPRT_ARCHIVE_BUNDLE) . ) + +.PHONY: jprt_build_product jprt_build_fastdebug jprt_build_debug jprt_build_optimized diff --git a/hotspot/make/jprt.properties b/hotspot/make/jprt.properties index a6ff452c2ce..1614a5b9bf0 100644 --- a/hotspot/make/jprt.properties +++ b/hotspot/make/jprt.properties @@ -93,13 +93,13 @@ jprt.my.windows.x64=${jprt.my.windows.x64.${jprt.tools.default.release}} # Standard list of jprt build targets for this source tree jprt.build.targets.standard= \ - ${jprt.my.solaris.sparcv9}-{product|fastdebug|optimized}, \ + ${jprt.my.solaris.sparcv9}-{product|fastdebug}, \ ${jprt.my.solaris.x64}-{product|fastdebug}, \ ${jprt.my.linux.i586}-{product|fastdebug}, \ - ${jprt.my.linux.x64}-{product|fastdebug|optimized}, \ + ${jprt.my.linux.x64}-{product|fastdebug}, \ ${jprt.my.macosx.x64}-{product|fastdebug}, \ ${jprt.my.windows.i586}-{product|fastdebug}, \ - ${jprt.my.windows.x64}-{product|fastdebug|optimized}, \ + ${jprt.my.windows.x64}-{product|fastdebug}, \ ${jprt.my.linux.armvh}-{product|fastdebug} jprt.build.targets.open= \ @@ -356,14 +356,15 @@ jprt.make.rule.test.targets.standard.internalvmtests = \ ${jprt.my.windows.x64}-fastdebug-c2-internalvmtests jprt.make.rule.test.targets.standard.reg.group = \ - ${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-GROUP, \ - ${jprt.my.solaris.x64}-{product|fastdebug}-c2-GROUP, \ - ${jprt.my.linux.i586}-{product|fastdebug}-c2-GROUP, \ - ${jprt.my.linux.x64}-{product|fastdebug}-c2-GROUP, \ - ${jprt.my.windows.i586}-{product|fastdebug}-c2-GROUP, \ - ${jprt.my.windows.x64}-{product|fastdebug}-c2-GROUP, \ - ${jprt.my.linux.i586}-{product|fastdebug}-c1-GROUP, \ - ${jprt.my.windows.i586}-{product|fastdebug}-c1-GROUP + ${jprt.my.solaris.sparcv9}-fastdebug-c2-GROUP, \ + ${jprt.my.solaris.x64}-fastdebug-c2-GROUP, \ + ${jprt.my.linux.i586}-fastdebug-c2-GROUP, \ + ${jprt.my.linux.x64}-fastdebug-c2-GROUP, \ + ${jprt.my.macosx.x64}-fastdebug-c2-GROUP, \ + ${jprt.my.windows.i586}-fastdebug-c2-GROUP, \ + ${jprt.my.windows.x64}-fastdebug-c2-GROUP, \ + ${jprt.my.linux.i586}-fastdebug-c1-GROUP, \ + ${jprt.my.windows.i586}-fastdebug-c1-GROUP jprt.make.rule.test.targets.standard = \ ${jprt.make.rule.test.targets.standard.client}, \ @@ -373,6 +374,7 @@ jprt.make.rule.test.targets.standard = \ ${jprt.make.rule.test.targets.standard.reg.group:GROUP=hotspot_compiler}, \ ${jprt.make.rule.test.targets.standard.reg.group:GROUP=hotspot_gc}, \ ${jprt.make.rule.test.targets.standard.reg.group:GROUP=hotspot_runtime}, \ + ${jprt.make.rule.test.targets.standard.reg.group:GROUP=hotspot_runtime_closed}, \ ${jprt.make.rule.test.targets.standard.reg.group:GROUP=hotspot_serviceability} jprt.make.rule.test.targets.embedded = \ diff --git a/hotspot/make/linux/makefiles/gcc.make b/hotspot/make/linux/makefiles/gcc.make index a20e7a1c18b..2447c8d3dd6 100644 --- a/hotspot/make/linux/makefiles/gcc.make +++ b/hotspot/make/linux/makefiles/gcc.make @@ -365,16 +365,13 @@ endif ifeq ($(USE_CLANG),) # Enable bounds checking. - # _FORTIFY_SOURCE appears in GCC 4.0+ ifeq "$(shell expr \( $(CC_VER_MAJOR) \> 3 \) )" "1" - # compile time size bounds checks - FASTDEBUG_CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 - - # and runtime size bounds checks and paranoid stack smashing checks. - DEBUG_CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-all --param ssp-buffer-size=1 + # stack smashing checks. + DEBUG_CFLAGS += -fstack-protector-all --param ssp-buffer-size=1 endif endif + # If we are building HEADLESS, pass on to VM # so it can set the java.awt.headless property ifdef HEADLESS diff --git a/hotspot/make/linux/makefiles/mapfile-vers-debug b/hotspot/make/linux/makefiles/mapfile-vers-debug index df433ad55ea..f7d88125ab5 100644 --- a/hotspot/make/linux/makefiles/mapfile-vers-debug +++ b/hotspot/make/linux/makefiles/mapfile-vers-debug @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2014, 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 @@ -125,7 +125,6 @@ SUNWprivate_1.1 { JVM_GetClassSignature; JVM_GetClassSigners; JVM_GetClassTypeAnnotations; - JVM_GetComponentType; JVM_GetDeclaredClasses; JVM_GetDeclaringClass; JVM_GetEnclosingMethodInfo; diff --git a/hotspot/make/linux/makefiles/mapfile-vers-product b/hotspot/make/linux/makefiles/mapfile-vers-product index df433ad55ea..f7d88125ab5 100644 --- a/hotspot/make/linux/makefiles/mapfile-vers-product +++ b/hotspot/make/linux/makefiles/mapfile-vers-product @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2014, 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 @@ -125,7 +125,6 @@ SUNWprivate_1.1 { JVM_GetClassSignature; JVM_GetClassSigners; JVM_GetClassTypeAnnotations; - JVM_GetComponentType; JVM_GetDeclaredClasses; JVM_GetDeclaringClass; JVM_GetEnclosingMethodInfo; diff --git a/hotspot/make/solaris/makefiles/dtrace.make b/hotspot/make/solaris/makefiles/dtrace.make index 5af625b54db..ad2275ce5fb 100644 --- a/hotspot/make/solaris/makefiles/dtrace.make +++ b/hotspot/make/solaris/makefiles/dtrace.make @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -58,6 +58,8 @@ DTRACE_SRCDIR = $(GAMMADIR)/src/os/$(Platform_os_family)/dtrace DTRACE_COMMON_SRCDIR = $(GAMMADIR)/src/os/posix/dtrace DTRACE = dtrace DTRACE.o = $(DTRACE).o +DTRACE_JHELPER = dtrace_jhelper +DTRACE_JHELPER.o = $(DTRACE_JHELPER).o # to remove '-g' option which causes link problems # also '-z nodefs' is used as workaround @@ -255,7 +257,10 @@ ifeq ($(ENABLE_FULL_DEBUG_SYMBOLS),1) endif $(DTRACE).d: $(DTRACE_COMMON_SRCDIR)/hotspot.d $(DTRACE_COMMON_SRCDIR)/hotspot_jni.d \ - $(DTRACE_COMMON_SRCDIR)/hs_private.d $(DTRACE_SRCDIR)/jhelper.d + $(DTRACE_COMMON_SRCDIR)/hs_private.d + $(QUIETLY) cat $^ > $@ + +$(DTRACE_JHELPER).d: $(DTRACE_SRCDIR)/jhelper.d $(QUIETLY) cat $^ > $@ DTraced_Files = ciEnv.o \ @@ -280,7 +285,7 @@ DTraced_Files = ciEnv.o \ vmGCOperations.o \ # Dtrace is available, so we build $(DTRACE.o) -$(DTRACE.o): $(DTRACE).d $(JVMOFFS).h $(JVMOFFS)Index.h $(DTraced_Files) +$(DTRACE.o): $(DTRACE).d $(DTraced_Files) @echo Compiling $(DTRACE).d $(QUIETLY) $(DTRACE_PROG) $(DTRACE_OPTS) -C -I. -G -xlazyload -o $@ -s $(DTRACE).d \ @@ -344,6 +349,11 @@ $(DtraceOutDir)/hs_private.h: $(DTRACE_COMMON_SRCDIR)/hs_private.d | $(DtraceOut dtrace_gen_headers: $(DtraceOutDir)/hotspot.h $(DtraceOutDir)/hotspot_jni.h $(DtraceOutDir)/hs_private.h +# The jhelper.d and hotspot probes are separated into two different SUNW_dof sections. +# Now the jhelper.d is built without the -Xlazyload flag. +$(DTRACE_JHELPER.o) : $(DTRACE_JHELPER).d $(JVMOFFS).h $(JVMOFFS)Index.h + @echo Compiling $(DTRACE_JHELPER).d + $(QUIETLY) $(DTRACE_PROG) $(DTRACE_OPTS) -C -I. -G -o $@ -s $(DTRACE_JHELPER).d .PHONY: dtraceCheck @@ -372,7 +382,7 @@ endif # ifneq ("$(patchDtraceFound)", "") ifneq ("${DTRACE_PROG}", "") ifeq ("${HOTSPOT_DISABLE_DTRACE_PROBES}", "") -DTRACE_OBJS = $(DTRACE.o) $(JVMOFFS.o) +DTRACE_OBJS = $(DTRACE.o) $(JVMOFFS.o) $(DTRACE_JHELPER.o) CFLAGS += $(DTRACE_INCL) -DDTRACE_ENABLED MAPFILE_DTRACE_OPT = $(MAPFILE_DTRACE) diff --git a/hotspot/make/solaris/makefiles/gcc.make b/hotspot/make/solaris/makefiles/gcc.make index 9ea05411166..03ba4458c12 100644 --- a/hotspot/make/solaris/makefiles/gcc.make +++ b/hotspot/make/solaris/makefiles/gcc.make @@ -240,11 +240,7 @@ ifeq ($(DEBUG_CFLAGS/$(BUILDARCH)),) endif # Enable bounds checking. -# _FORTIFY_SOURCE appears in GCC 4.0+ ifeq "$(shell expr \( $(CC_VER_MAJOR) \> 3 \) )" "1" - # compile time size bounds checks - FASTDEBUG_CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 - - # and runtime size bounds checks and paranoid stack smashing checks. - DEBUG_CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fstack-protector-all --param ssp-buffer-size=1 + # stack smashing checks. + DEBUG_CFLAGS += -fstack-protector-all --param ssp-buffer-size=1 endif diff --git a/hotspot/make/solaris/makefiles/mapfile-vers b/hotspot/make/solaris/makefiles/mapfile-vers index d2a3d5c6e2a..9bf34ff8305 100644 --- a/hotspot/make/solaris/makefiles/mapfile-vers +++ b/hotspot/make/solaris/makefiles/mapfile-vers @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2014, 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 @@ -124,7 +124,6 @@ SUNWprivate_1.1 { JVM_GetClassNameUTF; JVM_GetClassSignature; JVM_GetClassSigners; - JVM_GetComponentType; JVM_GetClassTypeAnnotations; JVM_GetDeclaredClasses; JVM_GetDeclaringClass; diff --git a/hotspot/make/solaris/makefiles/sparcWorks.make b/hotspot/make/solaris/makefiles/sparcWorks.make index cff57a29682..30d0c2ec691 100644 --- a/hotspot/make/solaris/makefiles/sparcWorks.make +++ b/hotspot/make/solaris/makefiles/sparcWorks.make @@ -1,5 +1,5 @@ # -# Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1998, 2014, 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 @@ -53,9 +53,9 @@ ifeq ($(JRE_RELEASE_VER),1.6.0) VALIDATED_COMPILER_REVS := 5.8 VALIDATED_CC_COMPILER_REVS := 5.8 else - # Validated compiler for JDK7 is SS12 update 1 + patches (5.10) - VALIDATED_COMPILER_REVS := 5.10 - VALIDATED_CC_COMPILER_REVS := 5.10 + # Validated compiler for JDK9 is SS12.3 (5.12) + VALIDATED_COMPILER_REVS := 5.12 + VALIDATED_CC_COMPILER_REVS := 5.12 endif # Warning messages about not using the above validated versions diff --git a/hotspot/make/windows/makefiles/vm.make b/hotspot/make/windows/makefiles/vm.make index d8040f89ed8..f22175a7da9 100644 --- a/hotspot/make/windows/makefiles/vm.make +++ b/hotspot/make/windows/makefiles/vm.make @@ -34,6 +34,9 @@ ALTSRC=$(WorkSpace)\src\closed CXX_FLAGS=$(CXX_FLAGS) /D "PRODUCT" !else CXX_FLAGS=$(CXX_FLAGS) /D "ASSERT" +!if "$(BUILDARCH)" == "amd64" +CXX_FLAGS=$(CXX_FLAGS) /homeparams +!endif !endif !if "$(Variant)" == "compiler1" diff --git a/hotspot/src/cpu/ppc/vm/assembler_ppc.hpp b/hotspot/src/cpu/ppc/vm/assembler_ppc.hpp index 5a1c0f1d9c8..0c8cf435aea 100644 --- a/hotspot/src/cpu/ppc/vm/assembler_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/assembler_ppc.hpp @@ -298,6 +298,7 @@ class Assembler : public AbstractAssembler { LWZ_OPCODE = (32u << OPCODE_SHIFT), LWZX_OPCODE = (31u << OPCODE_SHIFT | 23u << 1), LWZU_OPCODE = (33u << OPCODE_SHIFT), + LWBRX_OPCODE = (31u << OPCODE_SHIFT | 534 << 1), LHA_OPCODE = (42u << OPCODE_SHIFT), LHAX_OPCODE = (31u << OPCODE_SHIFT | 343u << 1), @@ -306,6 +307,7 @@ class Assembler : public AbstractAssembler { LHZ_OPCODE = (40u << OPCODE_SHIFT), LHZX_OPCODE = (31u << OPCODE_SHIFT | 279u << 1), LHZU_OPCODE = (41u << OPCODE_SHIFT), + LHBRX_OPCODE = (31u << OPCODE_SHIFT | 790 << 1), LBZ_OPCODE = (34u << OPCODE_SHIFT), LBZX_OPCODE = (31u << OPCODE_SHIFT | 87u << 1), @@ -1364,11 +1366,17 @@ class Assembler : public AbstractAssembler { inline void lwax( Register d, Register s1, Register s2); inline void lwa( Register d, int si16, Register s1); + // 4 bytes reversed + inline void lwbrx( Register d, Register s1, Register s2); + // 2 bytes inline void lhzx( Register d, Register s1, Register s2); inline void lhz( Register d, int si16, Register s1); inline void lhzu( Register d, int si16, Register s1); + // 2 bytes reversed + inline void lhbrx( Register d, Register s1, Register s2); + // 2 bytes inline void lhax( Register d, Register s1, Register s2); inline void lha( Register d, int si16, Register s1); @@ -1858,10 +1866,12 @@ class Assembler : public AbstractAssembler { inline void lwz( Register d, int si16); inline void lwax( Register d, Register s2); inline void lwa( Register d, int si16); + inline void lwbrx(Register d, Register s2); inline void lhzx( Register d, Register s2); inline void lhz( Register d, int si16); inline void lhax( Register d, Register s2); inline void lha( Register d, int si16); + inline void lhbrx(Register d, Register s2); inline void lbzx( Register d, Register s2); inline void lbz( Register d, int si16); inline void ldx( Register d, Register s2); diff --git a/hotspot/src/cpu/ppc/vm/assembler_ppc.inline.hpp b/hotspot/src/cpu/ppc/vm/assembler_ppc.inline.hpp index 3b7eb5f55e5..87db4bff89d 100644 --- a/hotspot/src/cpu/ppc/vm/assembler_ppc.inline.hpp +++ b/hotspot/src/cpu/ppc/vm/assembler_ppc.inline.hpp @@ -263,10 +263,14 @@ inline void Assembler::lwzu( Register d, int si16, Register s1) { assert(d != inline void Assembler::lwax( Register d, Register s1, Register s2) { emit_int32(LWAX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} inline void Assembler::lwa( Register d, int si16, Register s1) { emit_int32(LWA_OPCODE | rt(d) | ds(si16) | ra0mem(s1));} +inline void Assembler::lwbrx( Register d, Register s1, Register s2) { emit_int32(LWBRX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} + inline void Assembler::lhzx( Register d, Register s1, Register s2) { emit_int32(LHZX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} inline void Assembler::lhz( Register d, int si16, Register s1) { emit_int32(LHZ_OPCODE | rt(d) | d1(si16) | ra0mem(s1));} inline void Assembler::lhzu( Register d, int si16, Register s1) { assert(d != s1, "according to ibm manual"); emit_int32(LHZU_OPCODE | rt(d) | d1(si16) | rta0mem(s1));} +inline void Assembler::lhbrx( Register d, Register s1, Register s2) { emit_int32(LHBRX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} + inline void Assembler::lhax( Register d, Register s1, Register s2) { emit_int32(LHAX_OPCODE | rt(d) | ra0mem(s1) | rb(s2));} inline void Assembler::lha( Register d, int si16, Register s1) { emit_int32(LHA_OPCODE | rt(d) | d1(si16) | ra0mem(s1));} inline void Assembler::lhau( Register d, int si16, Register s1) { assert(d != s1, "according to ibm manual"); emit_int32(LHAU_OPCODE | rt(d) | d1(si16) | rta0mem(s1));} @@ -736,10 +740,12 @@ inline void Assembler::lwzx( Register d, Register s2) { emit_int32( LWZX_OPCODE inline void Assembler::lwz( Register d, int si16 ) { emit_int32( LWZ_OPCODE | rt(d) | d1(si16));} inline void Assembler::lwax( Register d, Register s2) { emit_int32( LWAX_OPCODE | rt(d) | rb(s2));} inline void Assembler::lwa( Register d, int si16 ) { emit_int32( LWA_OPCODE | rt(d) | ds(si16));} +inline void Assembler::lwbrx(Register d, Register s2) { emit_int32( LWBRX_OPCODE| rt(d) | rb(s2));} inline void Assembler::lhzx( Register d, Register s2) { emit_int32( LHZX_OPCODE | rt(d) | rb(s2));} inline void Assembler::lhz( Register d, int si16 ) { emit_int32( LHZ_OPCODE | rt(d) | d1(si16));} inline void Assembler::lhax( Register d, Register s2) { emit_int32( LHAX_OPCODE | rt(d) | rb(s2));} inline void Assembler::lha( Register d, int si16 ) { emit_int32( LHA_OPCODE | rt(d) | d1(si16));} +inline void Assembler::lhbrx(Register d, Register s2) { emit_int32( LHBRX_OPCODE| rt(d) | rb(s2));} inline void Assembler::lbzx( Register d, Register s2) { emit_int32( LBZX_OPCODE | rt(d) | rb(s2));} inline void Assembler::lbz( Register d, int si16 ) { emit_int32( LBZ_OPCODE | rt(d) | d1(si16));} inline void Assembler::ld( Register d, int si16 ) { emit_int32( LD_OPCODE | rt(d) | ds(si16));} diff --git a/hotspot/src/cpu/ppc/vm/cppInterpreterGenerator_ppc.hpp b/hotspot/src/cpu/ppc/vm/cppInterpreterGenerator_ppc.hpp index 6e43183ea3f..94cbe0ac24a 100644 --- a/hotspot/src/cpu/ppc/vm/cppInterpreterGenerator_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/cppInterpreterGenerator_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright 2012, 2013 SAP AG. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,8 +26,9 @@ #ifndef CPU_PPC_VM_CPPINTERPRETERGENERATOR_PPC_HPP #define CPU_PPC_VM_CPPINTERPRETERGENERATOR_PPC_HPP - address generate_normal_entry(void); - address generate_native_entry(void); + address generate_normal_entry(bool synchronized); + address generate_native_entry(bool synchronized); + address generate_math_entry(AbstractInterpreter::MethodKind kind) { return NULL; } void lock_method(void); void unlock_method(void); diff --git a/hotspot/src/cpu/ppc/vm/cppInterpreter_ppc.cpp b/hotspot/src/cpu/ppc/vm/cppInterpreter_ppc.cpp index 8a9534e260d..3ff878b9328 100644 --- a/hotspot/src/cpu/ppc/vm/cppInterpreter_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/cppInterpreter_ppc.cpp @@ -938,8 +938,9 @@ void CppInterpreterGenerator::generate_counter_incr(Label& overflow) { // Interpreter stub for calling a native method. (C++ interpreter) // This sets up a somewhat different looking stack for calling the native method // than the typical interpreter frame setup. +// The synchronized parameter is ignored. // -address CppInterpreterGenerator::generate_native_entry(void) { +address CppInterpreterGenerator::generate_native_entry(bool synchronized) { if (native_entry != NULL) return native_entry; address entry = __ pc(); @@ -1729,7 +1730,8 @@ void CppInterpreterGenerator::generate_more_monitors() { __ std(R0, BasicObjectLock::obj_offset_in_bytes(), stack_base); // Mark lock as unused } -address CppInterpreterGenerator::generate_normal_entry(void) { +// The synchronized parameter is ignored +address CppInterpreterGenerator::generate_normal_entry(bool synchronized) { if (interpreter_frame_manager != NULL) return interpreter_frame_manager; address entry = __ pc(); @@ -2789,38 +2791,6 @@ address CppInterpreterGenerator::generate_normal_entry(void) { return interpreter_frame_manager; } -// Generate code for various sorts of method entries -// -address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) { - address entry_point = NULL; - - switch (kind) { - case Interpreter::zerolocals : break; - case Interpreter::zerolocals_synchronized : break; - case Interpreter::native : // Fall thru - case Interpreter::native_synchronized : entry_point = ((CppInterpreterGenerator*)this)->generate_native_entry(); break; - case Interpreter::empty : break; - case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break; - case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break; - // These are special interpreter intrinsics which we don't support so far. - case Interpreter::java_lang_math_sin : break; - case Interpreter::java_lang_math_cos : break; - case Interpreter::java_lang_math_tan : break; - case Interpreter::java_lang_math_abs : break; - case Interpreter::java_lang_math_log : break; - case Interpreter::java_lang_math_log10 : break; - case Interpreter::java_lang_math_sqrt : break; - case Interpreter::java_lang_math_pow : break; - case Interpreter::java_lang_math_exp : break; - case Interpreter::java_lang_ref_reference_get: entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break; - default : ShouldNotReachHere(); break; - } - - if (entry_point) { - return entry_point; - } - return ((InterpreterGenerator*)this)->generate_normal_entry(); -} InterpreterGenerator::InterpreterGenerator(StubQueue* code) : CppInterpreterGenerator(code) { diff --git a/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp b/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp index 98c34b15a33..a47f9f56593 100644 --- a/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp +++ b/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp @@ -119,9 +119,15 @@ void InterpreterMacroAssembler::check_and_handle_popframe(Register scratch_reg) // Call the Interpreter::remove_activation_preserving_args_entry() // func to get the address of the same-named entrypoint in the // generated interpreter code. +#if defined(ABI_ELFv2) + call_c(CAST_FROM_FN_PTR(address, + Interpreter::remove_activation_preserving_args_entry), + relocInfo::none); +#else call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, Interpreter::remove_activation_preserving_args_entry), relocInfo::none); +#endif // Jump to Interpreter::_remove_activation_preserving_args_entry. mtctr(R3_RET); @@ -331,29 +337,40 @@ void InterpreterMacroAssembler::empty_expression_stack() { void InterpreterMacroAssembler::get_2_byte_integer_at_bcp(int bcp_offset, Register Rdst, signedOrNot is_signed) { +#if defined(VM_LITTLE_ENDIAN) + if (bcp_offset) { + load_const_optimized(Rdst, bcp_offset); + lhbrx(Rdst, R14_bcp, Rdst); + } else { + lhbrx(Rdst, R14_bcp); + } + if (is_signed == Signed) { + extsh(Rdst, Rdst); + } +#else // Read Java big endian format. if (is_signed == Signed) { lha(Rdst, bcp_offset, R14_bcp); } else { lhz(Rdst, bcp_offset, R14_bcp); } -#if 0 - assert(Rtmp != Rdst, "need separate temp register"); - Register Rfirst = Rtmp; - lbz(Rfirst, bcp_offset, R14_bcp); // first byte - lbz(Rdst, bcp_offset+1, R14_bcp); // second byte - - // Rdst = ((Rfirst<<8) & 0xFF00) | (Rdst &~ 0xFF00) - rldimi(/*RA=*/Rdst, /*RS=*/Rfirst, /*sh=*/8, /*mb=*/48); - if (is_signed == Signed) { - extsh(Rdst, Rdst); - } #endif } void InterpreterMacroAssembler::get_4_byte_integer_at_bcp(int bcp_offset, Register Rdst, signedOrNot is_signed) { +#if defined(VM_LITTLE_ENDIAN) + if (bcp_offset) { + load_const_optimized(Rdst, bcp_offset); + lwbrx(Rdst, R14_bcp, Rdst); + } else { + lwbrx(Rdst, R14_bcp); + } + if (is_signed == Signed) { + extsw(Rdst, Rdst); + } +#else // Read Java big endian format. if (bcp_offset & 3) { // Offset unaligned? load_const_optimized(Rdst, bcp_offset); @@ -369,18 +386,26 @@ void InterpreterMacroAssembler::get_4_byte_integer_at_bcp(int bcp_offset lwz(Rdst, bcp_offset, R14_bcp); } } +#endif } + // Load the constant pool cache index from the bytecode stream. // // Kills / writes: // - Rdst, Rscratch void InterpreterMacroAssembler::get_cache_index_at_bcp(Register Rdst, int bcp_offset, size_t index_size) { assert(bcp_offset > 0, "bcp is still pointing to start of bytecode"); + // Cache index is always in the native format, courtesy of Rewriter. if (index_size == sizeof(u2)) { - get_2_byte_integer_at_bcp(bcp_offset, Rdst, Unsigned); + lhz(Rdst, bcp_offset, R14_bcp); } else if (index_size == sizeof(u4)) { - get_4_byte_integer_at_bcp(bcp_offset, Rdst, Signed); + if (bcp_offset & 3) { + load_const_optimized(Rdst, bcp_offset); + lwax(Rdst, R14_bcp, Rdst); + } else { + lwa(Rdst, bcp_offset, R14_bcp); + } assert(ConstantPool::decode_invokedynamic_index(~123) == 123, "else change next line"); nand(Rdst, Rdst, Rdst); // convert to plain index } else if (index_size == sizeof(u1)) { @@ -397,6 +422,29 @@ void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, int b add(cache, R27_constPoolCache, cache); } +// Load 4-byte signed or unsigned integer in Java format (that is, big-endian format) +// from (Rsrc)+offset. +void InterpreterMacroAssembler::get_u4(Register Rdst, Register Rsrc, int offset, + signedOrNot is_signed) { +#if defined(VM_LITTLE_ENDIAN) + if (offset) { + load_const_optimized(Rdst, offset); + lwbrx(Rdst, Rdst, Rsrc); + } else { + lwbrx(Rdst, Rsrc); + } + if (is_signed == Signed) { + extsw(Rdst, Rdst); + } +#else + if (is_signed == Signed) { + lwa(Rdst, offset, Rsrc); + } else { + lwz(Rdst, offset, Rsrc); + } +#endif +} + // Load object from cpool->resolved_references(index). void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result, Register index) { assert_different_registers(result, index); diff --git a/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.hpp b/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.hpp index b4c95a6458c..37c743228e9 100644 --- a/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.hpp +++ b/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.hpp @@ -130,6 +130,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void get_cache_and_index_at_bcp(Register cache, int bcp_offset, size_t index_size = sizeof(u2)); + void get_u4(Register Rdst, Register Rsrc, int offset, signedOrNot is_signed); // common code diff --git a/hotspot/src/cpu/ppc/vm/interpreterGenerator_ppc.hpp b/hotspot/src/cpu/ppc/vm/interpreterGenerator_ppc.hpp index 74f6001e766..d4f53f0accc 100644 --- a/hotspot/src/cpu/ppc/vm/interpreterGenerator_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/interpreterGenerator_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright 2012, 2013 SAP AG. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -31,7 +31,12 @@ private: address generate_abstract_entry(void); - address generate_accessor_entry(void); + address generate_jump_to_normal_entry(void); + address generate_accessor_entry(void) { return generate_jump_to_normal_entry(); } + address generate_empty_entry(void) { return generate_jump_to_normal_entry(); } address generate_Reference_get_entry(void); + // Not supported + address generate_CRC32_update_entry() { return NULL; } + address generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind) { return NULL; } #endif // CPU_PPC_VM_INTERPRETERGENERATOR_PPC_HPP diff --git a/hotspot/src/cpu/ppc/vm/interpreter_ppc.cpp b/hotspot/src/cpu/ppc/vm/interpreter_ppc.cpp index 96814188f03..d64da8ea17a 100644 --- a/hotspot/src/cpu/ppc/vm/interpreter_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/interpreter_ppc.cpp @@ -428,6 +428,19 @@ address AbstractInterpreterGenerator::generate_result_handler_for(BasicType type return entry; } + +// Call an accessor method (assuming it is resolved, otherwise drop into +// vanilla (slow path) entry. +address InterpreterGenerator::generate_jump_to_normal_entry(void) { + address entry = __ pc(); + address normal_entry = Interpreter::entry_for_kind(Interpreter::zerolocals); + assert(normal_entry != NULL, "should already be generated."); + __ branch_to_entry(normal_entry, R11_scratch1); + __ flush(); + + return entry; +} + // Abstract method entry. // address InterpreterGenerator::generate_abstract_entry(void) { @@ -485,203 +498,6 @@ address InterpreterGenerator::generate_abstract_entry(void) { return entry; } -// Call an accessor method (assuming it is resolved, otherwise drop into -// vanilla (slow path) entry. -address InterpreterGenerator::generate_accessor_entry(void) { - if (!UseFastAccessorMethods && (!FLAG_IS_ERGO(UseFastAccessorMethods))) { - return NULL; - } - - Label Lslow_path, Lacquire; - - const Register - Rclass_or_obj = R3_ARG1, - Rconst_method = R4_ARG2, - Rcodes = Rconst_method, - Rcpool_cache = R5_ARG3, - Rscratch = R11_scratch1, - Rjvmti_mode = Rscratch, - Roffset = R12_scratch2, - Rflags = R6_ARG4, - Rbtable = R7_ARG5; - - static address branch_table[number_of_states]; - - address entry = __ pc(); - - // Check for safepoint: - // Ditch this, real man don't need safepoint checks. - - // Also check for JVMTI mode - // Check for null obj, take slow path if so. - __ ld(Rclass_or_obj, Interpreter::stackElementSize, CC_INTERP_ONLY(R17_tos) NOT_CC_INTERP(R15_esp)); - __ lwz(Rjvmti_mode, thread_(interp_only_mode)); - __ cmpdi(CCR1, Rclass_or_obj, 0); - __ cmpwi(CCR0, Rjvmti_mode, 0); - __ crorc(/*CCR0 eq*/2, /*CCR1 eq*/4+2, /*CCR0 eq*/2); - __ beq(CCR0, Lslow_path); // this==null or jvmti_mode!=0 - - // Do 2 things in parallel: - // 1. Load the index out of the first instruction word, which looks like this: - // <0x2a><0xb4>. - // 2. Load constant pool cache base. - __ ld(Rconst_method, in_bytes(Method::const_offset()), R19_method); - __ ld(Rcpool_cache, in_bytes(ConstMethod::constants_offset()), Rconst_method); - - __ lhz(Rcodes, in_bytes(ConstMethod::codes_offset()) + 2, Rconst_method); // Lower half of 32 bit field. - __ ld(Rcpool_cache, ConstantPool::cache_offset_in_bytes(), Rcpool_cache); - - // Get the const pool entry by means of . - const int codes_shift = exact_log2(in_words(ConstantPoolCacheEntry::size()) * BytesPerWord); - __ slwi(Rscratch, Rcodes, codes_shift); // (codes&0xFFFF)<print_cr("accessor_entry: branch_table[%d] = 0x%llx (opcode 0x%llx)", i, branch_table[i], *((unsigned int*)branch_table[i])); - } -#endif - - __ bind(Lslow_path); - __ branch_to_entry(Interpreter::entry_for_kind(Interpreter::zerolocals), Rscratch); - __ flush(); - - return entry; -} - // Interpreter intrinsic for WeakReference.get(). // 1. Don't push a full blown frame and go on dispatching, but fetch the value // into R8 and return quickly @@ -713,7 +529,6 @@ address InterpreterGenerator::generate_Reference_get_entry(void) { // and so we don't need to call the G1 pre-barrier. Thus we can use the // regular method entry code to generate the NPE. // - // This code is based on generate_accessor_enty. address entry = __ pc(); @@ -768,7 +583,7 @@ address InterpreterGenerator::generate_Reference_get_entry(void) { return entry; } else { - return generate_accessor_entry(); + return generate_jump_to_normal_entry(); } } diff --git a/hotspot/src/cpu/ppc/vm/ppc.ad b/hotspot/src/cpu/ppc/vm/ppc.ad index 18870359ad9..ab2d535bbc9 100644 --- a/hotspot/src/cpu/ppc/vm/ppc.ad +++ b/hotspot/src/cpu/ppc/vm/ppc.ad @@ -1283,8 +1283,6 @@ int Compile::ConstantTable::calculate_table_base_offset() const { bool MachConstantBaseNode::requires_postalloc_expand() const { return true; } void MachConstantBaseNode::postalloc_expand(GrowableArray *nodes, PhaseRegAlloc *ra_) { - Compile *C = ra_->C; - iRegPdstOper *op_dst = new iRegPdstOper(); MachNode *m1 = new loadToc_hiNode(); MachNode *m2 = new loadToc_loNode(); @@ -2229,7 +2227,7 @@ const bool Matcher::isSimpleConstant64(jlong value) { } /* TODO: PPC port // Make a new machine dependent decode node (with its operands). -MachTypeNode *Matcher::make_decode_node(Compile *C) { +MachTypeNode *Matcher::make_decode_node() { assert(Universe::narrow_oop_base() == NULL && Universe::narrow_oop_shift() == 0, "This method is only implemented for unscaled cOops mode so far"); MachTypeNode *decode = new decodeN_unscaledNode(); @@ -2593,7 +2591,7 @@ typedef struct { MachNode *_last; } loadConLNodesTuple; -loadConLNodesTuple loadConLNodesTuple_create(Compile *C, PhaseRegAlloc *ra_, Node *toc, immLOper *immSrc, +loadConLNodesTuple loadConLNodesTuple_create(PhaseRegAlloc *ra_, Node *toc, immLOper *immSrc, OptoReg::Name reg_second, OptoReg::Name reg_first) { loadConLNodesTuple nodes; @@ -2669,7 +2667,7 @@ encode %{ enc_class postalloc_expand_load_long_constant(iRegLdst dst, immL src, iRegLdst toc) %{ // Create new nodes. loadConLNodesTuple loadConLNodes = - loadConLNodesTuple_create(C, ra_, n_toc, op_src, + loadConLNodesTuple_create(ra_, n_toc, op_src, ra_->get_reg_second(this), ra_->get_reg_first(this)); // Push new nodes. @@ -3391,7 +3389,7 @@ encode %{ immLOper *op_repl = new immLOper((jlong)replicate_immF(op_src->constantF())); loadConLNodesTuple loadConLNodes = - loadConLNodesTuple_create(C, ra_, n_toc, op_repl, + loadConLNodesTuple_create(ra_, n_toc, op_repl, ra_->get_reg_second(this), ra_->get_reg_first(this)); // Push new nodes. @@ -3611,7 +3609,7 @@ encode %{ // Create the nodes for loading the IC from the TOC. loadConLNodesTuple loadConLNodes_IC = - loadConLNodesTuple_create(C, ra_, n_toc, new immLOper((jlong)Universe::non_oop_word()), + loadConLNodesTuple_create(ra_, n_toc, new immLOper((jlong)Universe::non_oop_word()), OptoReg::Name(R19_H_num), OptoReg::Name(R19_num)); // Create the call node. @@ -3765,7 +3763,7 @@ encode %{ #if defined(ABI_ELFv2) jlong entry_address = (jlong) this->entry_point(); assert(entry_address, "need address here"); - loadConLNodes_Entry = loadConLNodesTuple_create(C, ra_, n_toc, new immLOper(entry_address), + loadConLNodes_Entry = loadConLNodesTuple_create(ra_, n_toc, new immLOper(entry_address), OptoReg::Name(R12_H_num), OptoReg::Name(R12_num)); #else // Get the struct that describes the function we are about to call. @@ -3777,13 +3775,13 @@ encode %{ loadConLNodesTuple loadConLNodes_Toc; // Create nodes and operands for loading the entry point. - loadConLNodes_Entry = loadConLNodesTuple_create(C, ra_, n_toc, new immLOper(entry_address), + loadConLNodes_Entry = loadConLNodesTuple_create(ra_, n_toc, new immLOper(entry_address), OptoReg::Name(R12_H_num), OptoReg::Name(R12_num)); // Create nodes and operands for loading the env pointer. if (fd->env() != NULL) { - loadConLNodes_Env = loadConLNodesTuple_create(C, ra_, n_toc, new immLOper((jlong) fd->env()), + loadConLNodes_Env = loadConLNodesTuple_create(ra_, n_toc, new immLOper((jlong) fd->env()), OptoReg::Name(R11_H_num), OptoReg::Name(R11_num)); } else { loadConLNodes_Env._large_hi = NULL; @@ -3796,7 +3794,7 @@ encode %{ } // Create nodes and operands for loading the Toc point. - loadConLNodes_Toc = loadConLNodesTuple_create(C, ra_, n_toc, new immLOper((jlong) fd->toc()), + loadConLNodes_Toc = loadConLNodesTuple_create(ra_, n_toc, new immLOper((jlong) fd->toc()), OptoReg::Name(R2_H_num), OptoReg::Name(R2_num)); #endif // ABI_ELFv2 // mtctr node diff --git a/hotspot/src/cpu/ppc/vm/templateInterpreterGenerator_ppc.hpp b/hotspot/src/cpu/ppc/vm/templateInterpreterGenerator_ppc.hpp index 051ed26e728..ea12f04124c 100644 --- a/hotspot/src/cpu/ppc/vm/templateInterpreterGenerator_ppc.hpp +++ b/hotspot/src/cpu/ppc/vm/templateInterpreterGenerator_ppc.hpp @@ -30,7 +30,6 @@ address generate_normal_entry(bool synchronized); address generate_native_entry(bool synchronized); address generate_math_entry(AbstractInterpreter::MethodKind kind); - address generate_empty_entry(void); void lock_method(Register Rflags, Register Rscratch1, Register Rscratch2, bool flags_preloaded=false); void unlock_method(bool check_exceptions = true); diff --git a/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp b/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp index b787fecc7d5..c53d73ec890 100644 --- a/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp @@ -176,8 +176,12 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, const Register size = R12_scratch2; __ get_cache_and_index_at_bcp(cache, 1, index_size); - // Big Endian (get least significant byte of 64 bit value): + // Get least significant byte of 64 bit value: +#if defined(VM_LITTLE_ENDIAN) + __ lbz(size, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()), cache); +#else __ lbz(size, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()) + 7, cache); +#endif __ sldi(size, size, Interpreter::logStackElementSize); __ add(R15_esp, R15_esp, size); __ dispatch_next(state, step); @@ -598,48 +602,6 @@ void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call, Regist // End of helpers -// ============================================================================ -// Various method entries -// - -// Empty method, generate a very fast return. We must skip this entry if -// someone's debugging, indicated by the flag -// "interp_mode" in the Thread obj. -// Note: empty methods are generated mostly methods that do assertions, which are -// disabled in the "java opt build". -address TemplateInterpreterGenerator::generate_empty_entry(void) { - if (!UseFastEmptyMethods) { - NOT_PRODUCT(__ should_not_reach_here();) - return Interpreter::entry_for_kind(Interpreter::zerolocals); - } - - Label Lslow_path; - const Register Rjvmti_mode = R11_scratch1; - address entry = __ pc(); - - __ lwz(Rjvmti_mode, thread_(interp_only_mode)); - __ cmpwi(CCR0, Rjvmti_mode, 0); - __ bne(CCR0, Lslow_path); // jvmti_mode!=0 - - // Noone's debuggin: Simply return. - // Pop c2i arguments (if any) off when we return. -#ifdef ASSERT - __ ld(R9_ARG7, 0, R1_SP); - __ ld(R10_ARG8, 0, R21_sender_SP); - __ cmpd(CCR0, R9_ARG7, R10_ARG8); - __ asm_assert_eq("backlink", 0x545); -#endif // ASSERT - __ mr(R1_SP, R21_sender_SP); // Cut the stack back to where the caller started. - - // And we're done. - __ blr(); - - __ bind(Lslow_path); - __ branch_to_entry(Interpreter::entry_for_kind(Interpreter::zerolocals), R11_scratch1); - __ flush(); - - return entry; -} // Support abs and sqrt like in compiler. // For others we can use a normal (native) entry. @@ -858,7 +820,9 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // Our signature handlers copy required arguments to the C stack // (outgoing C args), R3_ARG1 to R10_ARG8, and FARG1 to FARG13. __ mr(R3_ARG1, R18_locals); +#if !defined(ABI_ELFv2) __ ld(signature_handler_fd, 0, signature_handler_fd); +#endif __ call_stub(signature_handler_fd); @@ -1020,8 +984,13 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) { // native result across the call. No oop is present. __ mr(R3_ARG1, R16_thread); +#if defined(ABI_ELFv2) + __ call_c(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans), + relocInfo::none); +#else __ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, JavaThread::check_special_condition_for_native_trans), relocInfo::none); +#endif __ bind(sync_check_done); @@ -1278,45 +1247,6 @@ address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) { return entry; } -// ============================================================================= -// Entry points - -address AbstractInterpreterGenerator::generate_method_entry( - AbstractInterpreter::MethodKind kind) { - // Determine code generation flags. - bool synchronized = false; - address entry_point = NULL; - - switch (kind) { - case Interpreter::zerolocals : break; - case Interpreter::zerolocals_synchronized: synchronized = true; break; - case Interpreter::native : entry_point = ((InterpreterGenerator*) this)->generate_native_entry(false); break; - case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*) this)->generate_native_entry(true); break; - case Interpreter::empty : entry_point = ((InterpreterGenerator*) this)->generate_empty_entry(); break; - case Interpreter::accessor : entry_point = ((InterpreterGenerator*) this)->generate_accessor_entry(); break; - case Interpreter::abstract : entry_point = ((InterpreterGenerator*) this)->generate_abstract_entry(); break; - - case Interpreter::java_lang_math_sin : // fall thru - case Interpreter::java_lang_math_cos : // fall thru - case Interpreter::java_lang_math_tan : // fall thru - case Interpreter::java_lang_math_abs : // fall thru - case Interpreter::java_lang_math_log : // fall thru - case Interpreter::java_lang_math_log10 : // fall thru - case Interpreter::java_lang_math_sqrt : // fall thru - case Interpreter::java_lang_math_pow : // fall thru - case Interpreter::java_lang_math_exp : entry_point = ((InterpreterGenerator*) this)->generate_math_entry(kind); break; - case Interpreter::java_lang_ref_reference_get - : entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break; - default : ShouldNotReachHere(); break; - } - - if (entry_point) { - return entry_point; - } - - return ((InterpreterGenerator*) this)->generate_normal_entry(synchronized); -} - // These should never be compiled since the interpreter will prefer // the compiled version to the intrinsic version. bool AbstractInterpreter::can_be_compiled(methodHandle m) { @@ -1344,7 +1274,7 @@ int AbstractInterpreter::size_activation(int max_stack, int callee_locals, bool is_top_frame) { // Note: This calculation must exactly parallel the frame setup - // in AbstractInterpreterGenerator::generate_method_entry. + // in InterpreterGenerator::generate_fixed_frame. assert(Interpreter::stackElementWords == 1, "sanity"); const int max_alignment_space = StackAlignmentInBytes / Interpreter::stackElementSize; const int abi_scratch = is_top_frame ? (frame::abi_reg_args_size / Interpreter::stackElementSize) : diff --git a/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.cpp b/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.cpp index 0b375da582e..6a34053a496 100644 --- a/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.cpp +++ b/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.cpp @@ -189,8 +189,12 @@ void TemplateTable::patch_bytecode(Bytecodes::Code new_bc, Register Rnew_bc, Reg assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); assert(load_bc_into_bc_reg, "we use bc_reg as temp"); __ get_cache_and_index_at_bcp(Rtemp /* dst = cache */, 1); - // Big Endian: ((*(cache+indices))>>((1+byte_no)*8))&0xFF + // ((*(cache+indices))>>((1+byte_no)*8))&0xFF: +#if defined(VM_LITTLE_ENDIAN) + __ lbz(Rnew_bc, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()) + 1 + byte_no, Rtemp); +#else __ lbz(Rnew_bc, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()) + 7 - (1 + byte_no), Rtemp); +#endif __ cmpwi(CCR0, Rnew_bc, 0); __ li(Rnew_bc, (unsigned int)(unsigned char)new_bc); __ beq(CCR0, L_patch_done); @@ -1839,8 +1843,8 @@ void TemplateTable::tableswitch() { __ clrrdi(Rdef_offset_addr, Rdef_offset_addr, log2_long((jlong)BytesPerInt)); // Load lo & hi. - __ lwz(Rlow_byte, BytesPerInt, Rdef_offset_addr); - __ lwz(Rhigh_byte, BytesPerInt * 2, Rdef_offset_addr); + __ get_u4(Rlow_byte, Rdef_offset_addr, BytesPerInt, InterpreterMacroAssembler::Unsigned); + __ get_u4(Rhigh_byte, Rdef_offset_addr, 2 *BytesPerInt, InterpreterMacroAssembler::Unsigned); // Check for default case (=index outside [low,high]). __ cmpw(CCR0, R17_tos, Rlow_byte); @@ -1854,12 +1858,17 @@ void TemplateTable::tableswitch() { __ profile_switch_case(Rindex, Rhigh_byte /* scratch */, Rscratch1, Rscratch2); __ sldi(Rindex, Rindex, LogBytesPerInt); __ addi(Rindex, Rindex, 3 * BytesPerInt); +#if defined(VM_LITTLE_ENDIAN) + __ lwbrx(Roffset, Rdef_offset_addr, Rindex); + __ extsw(Roffset, Roffset); +#else __ lwax(Roffset, Rdef_offset_addr, Rindex); +#endif __ b(Ldispatch); __ bind(Ldefault_case); __ profile_switch_default(Rhigh_byte, Rscratch1); - __ lwa(Roffset, 0, Rdef_offset_addr); + __ get_u4(Roffset, Rdef_offset_addr, 0, InterpreterMacroAssembler::Signed); __ bind(Ldispatch); @@ -1875,12 +1884,11 @@ void TemplateTable::lookupswitch() { // Table switch using linear search through cases. // Bytecode stream format: // Bytecode (1) | 4-byte padding | default offset (4) | count (4) | value/offset pair1 (8) | value/offset pair2 (8) | ... -// Note: Everything is big-endian format here. So on little endian machines, we have to revers offset and count and cmp value. +// Note: Everything is big-endian format here. void TemplateTable::fast_linearswitch() { transition(itos, vtos); - Label Lloop_entry, Lsearch_loop, Lfound, Lcontinue_execution, Ldefault_case; - + Label Lloop_entry, Lsearch_loop, Lcontinue_execution, Ldefault_case; Register Rcount = R3_ARG1, Rcurrent_pair = R4_ARG2, Rdef_offset_addr = R5_ARG3, // Is going to contain address of default offset. @@ -1894,47 +1902,40 @@ void TemplateTable::fast_linearswitch() { __ clrrdi(Rdef_offset_addr, Rdef_offset_addr, log2_long((jlong)BytesPerInt)); // Setup loop counter and limit. - __ lwz(Rcount, BytesPerInt, Rdef_offset_addr); // Load count. + __ get_u4(Rcount, Rdef_offset_addr, BytesPerInt, InterpreterMacroAssembler::Unsigned); __ addi(Rcurrent_pair, Rdef_offset_addr, 2 * BytesPerInt); // Rcurrent_pair now points to first pair. - // Set up search loop. - __ cmpwi(CCR0, Rcount, 0); - __ beq(CCR0, Ldefault_case); - __ mtctr(Rcount); + __ cmpwi(CCR0, Rcount, 0); + __ bne(CCR0, Lloop_entry); - // linear table search - __ bind(Lsearch_loop); - - __ lwz(Rvalue, 0, Rcurrent_pair); - __ lwa(Roffset, 1 * BytesPerInt, Rcurrent_pair); - - __ cmpw(CCR0, Rvalue, Rcmp_value); - __ beq(CCR0, Lfound); - - __ addi(Rcurrent_pair, Rcurrent_pair, 2 * BytesPerInt); - __ bdnz(Lsearch_loop); - - // default case + // Default case __ bind(Ldefault_case); - - __ lwa(Roffset, 0, Rdef_offset_addr); + __ get_u4(Roffset, Rdef_offset_addr, 0, InterpreterMacroAssembler::Signed); if (ProfileInterpreter) { __ profile_switch_default(Rdef_offset_addr, Rcount/* scratch */); - __ b(Lcontinue_execution); + } + __ b(Lcontinue_execution); + + // Next iteration + __ bind(Lsearch_loop); + __ bdz(Ldefault_case); + __ addi(Rcurrent_pair, Rcurrent_pair, 2 * BytesPerInt); + __ bind(Lloop_entry); + __ get_u4(Rvalue, Rcurrent_pair, 0, InterpreterMacroAssembler::Unsigned); + __ cmpw(CCR0, Rvalue, Rcmp_value); + __ bne(CCR0, Lsearch_loop); + + // Found, load offset. + __ get_u4(Roffset, Rcurrent_pair, BytesPerInt, InterpreterMacroAssembler::Signed); + // Calculate case index and profile + __ mfctr(Rcurrent_pair); + if (ProfileInterpreter) { + __ sub(Rcurrent_pair, Rcount, Rcurrent_pair); + __ profile_switch_case(Rcurrent_pair, Rcount /*scratch*/, Rdef_offset_addr/*scratch*/, Rscratch); } - // Entry found, skip Roffset bytecodes and continue. - __ bind(Lfound); - if (ProfileInterpreter) { - // Calc the num of the pair we hit. Careful, Rcurrent_pair points 2 ints - // beyond the actual current pair due to the auto update load above! - __ sub(Rcurrent_pair, Rcurrent_pair, Rdef_offset_addr); - __ addi(Rcurrent_pair, Rcurrent_pair, - 2 * BytesPerInt); - __ srdi(Rcurrent_pair, Rcurrent_pair, LogBytesPerInt + 1); - __ profile_switch_case(Rcurrent_pair, Rcount /*scratch*/, Rdef_offset_addr/*scratch*/, Rscratch); - __ bind(Lcontinue_execution); - } + __ bind(Lcontinue_execution); __ add(R14_bcp, Roffset, R14_bcp); __ dispatch_next(vtos); } @@ -1990,7 +1991,7 @@ void TemplateTable::fast_binaryswitch() { // initialize i & j __ li(Ri,0); - __ lwz(Rj, -BytesPerInt, Rarray); + __ get_u4(Rj, Rarray, -BytesPerInt, InterpreterMacroAssembler::Unsigned); // and start. Label entry; @@ -2007,7 +2008,11 @@ void TemplateTable::fast_binaryswitch() { // i = h; // } __ sldi(Rscratch, Rh, log_entry_size); +#if defined(VM_LITTLE_ENDIAN) + __ lwbrx(Rscratch, Rscratch, Rarray); +#else __ lwzx(Rscratch, Rscratch, Rarray); +#endif // if (key < current value) // Rh = Rj @@ -2039,20 +2044,20 @@ void TemplateTable::fast_binaryswitch() { // Ri = value offset __ sldi(Ri, Ri, log_entry_size); __ add(Ri, Ri, Rarray); - __ lwz(Rscratch, 0, Ri); + __ get_u4(Rscratch, Ri, 0, InterpreterMacroAssembler::Unsigned); Label not_found; // Ri = offset offset __ cmpw(CCR0, Rkey, Rscratch); __ beq(CCR0, not_found); // entry not found -> j = default offset - __ lwz(Rj, -2 * BytesPerInt, Rarray); + __ get_u4(Rj, Rarray, -2 * BytesPerInt, InterpreterMacroAssembler::Unsigned); __ b(default_case); __ bind(not_found); // entry found -> j = offset __ profile_switch_case(Rh, Rj, Rscratch, Rkey); - __ lwz(Rj, BytesPerInt, Ri); + __ get_u4(Rj, Ri, BytesPerInt, InterpreterMacroAssembler::Unsigned); if (ProfileInterpreter) { __ b(continue_execution); @@ -2147,8 +2152,11 @@ void TemplateTable::resolve_cache_and_index(int byte_no, Register Rcache, Regist assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range"); // We are resolved if the indices offset contains the current bytecode. - // Big Endian: +#if defined(VM_LITTLE_ENDIAN) + __ lbz(Rscratch, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()) + byte_no + 1, Rcache); +#else __ lbz(Rscratch, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()) + 7 - (byte_no + 1), Rcache); +#endif // Acquire by cmp-br-isync (see below). __ cmpdi(CCR0, Rscratch, (int)bytecode()); __ beq(CCR0, Lresolved); diff --git a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp index 75047c7cb42..0e66115a3f8 100644 --- a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp +++ b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp @@ -29,6 +29,7 @@ #include "compiler/disassembler.hpp" #include "memory/resourceArea.hpp" #include "runtime/java.hpp" +#include "runtime/os.hpp" #include "runtime/stubCodeGenerator.hpp" #include "utilities/defaultStream.hpp" #include "vm_version_ppc.hpp" @@ -108,7 +109,7 @@ void VM_Version::initialize() { (has_vand() ? " vand" : "") // Make sure number of %s matches num_features! ); - _features_str = strdup(buf); + _features_str = os::strdup(buf); NOT_PRODUCT(if (Verbose) print_features();); // PPC64 supports 8-byte compare-exchange operations (see diff --git a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp index e5325211b5a..4a84b6c3387 100644 --- a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ #include "interpreter/interpreter.hpp" #include "interpreter/interpreterGenerator.hpp" #include "interpreter/interpreterRuntime.hpp" +#include "interpreter/interp_masm.hpp" #include "oops/arrayOop.hpp" #include "oops/methodData.hpp" #include "oops/method.hpp" @@ -68,9 +69,7 @@ bool CppInterpreter::contains(address pc) { #define STATE(field_name) Lstate, in_bytes(byte_offset_of(BytecodeInterpreter, field_name)) #define __ _masm-> -Label frame_manager_entry; -Label fast_accessor_slow_entry_path; // fast accessor methods need to be able to jmp to unsynchronized - // c++ interpreter entry point this holds that entry point label. +Label frame_manager_entry; // c++ interpreter entry point this holds that entry point label. static address unctrap_frame_manager_entry = NULL; @@ -452,110 +451,6 @@ address InterpreterGenerator::generate_empty_entry(void) { return NULL; } -// Call an accessor method (assuming it is resolved, otherwise drop into -// vanilla (slow path) entry - -// Generates code to elide accessor methods -// Uses G3_scratch and G1_scratch as scratch -address InterpreterGenerator::generate_accessor_entry(void) { - - // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites thereof; - // parameter size = 1 - // Note: We can only use this code if the getfield has been resolved - // and if we don't have a null-pointer exception => check for - // these conditions first and use slow path if necessary. - address entry = __ pc(); - Label slow_path; - - if ( UseFastAccessorMethods) { - // Check if we need to reach a safepoint and generate full interpreter - // frame if so. - AddressLiteral sync_state(SafepointSynchronize::address_of_state()); - __ load_contents(sync_state, G3_scratch); - __ cmp(G3_scratch, SafepointSynchronize::_not_synchronized); - __ br(Assembler::notEqual, false, Assembler::pn, slow_path); - __ delayed()->nop(); - - // Check if local 0 != NULL - __ ld_ptr(Gargs, G0, Otos_i ); // get local 0 - __ tst(Otos_i); // check if local 0 == NULL and go the slow path - __ brx(Assembler::zero, false, Assembler::pn, slow_path); - __ delayed()->nop(); - - - // read first instruction word and extract bytecode @ 1 and index @ 2 - // get first 4 bytes of the bytecodes (big endian!) - __ ld_ptr(Address(G5_method, in_bytes(Method::const_offset())), G1_scratch); - __ ld(Address(G1_scratch, in_bytes(ConstMethod::codes_offset())), G1_scratch); - - // move index @ 2 far left then to the right most two bytes. - __ sll(G1_scratch, 2*BitsPerByte, G1_scratch); - __ srl(G1_scratch, 2*BitsPerByte - exact_log2(in_words( - ConstantPoolCacheEntry::size()) * BytesPerWord), G1_scratch); - - // get constant pool cache - __ ld_ptr(G5_method, in_bytes(Method::const_offset()), G3_scratch); - __ ld_ptr(G3_scratch, in_bytes(ConstMethod::constants_offset()), G3_scratch); - __ ld_ptr(G3_scratch, ConstantPool::cache_offset_in_bytes(), G3_scratch); - - // get specific constant pool cache entry - __ add(G3_scratch, G1_scratch, G3_scratch); - - // Check the constant Pool cache entry to see if it has been resolved. - // If not, need the slow path. - ByteSize cp_base_offset = ConstantPoolCache::base_offset(); - __ ld_ptr(G3_scratch, in_bytes(cp_base_offset + ConstantPoolCacheEntry::indices_offset()), G1_scratch); - __ srl(G1_scratch, 2*BitsPerByte, G1_scratch); - __ and3(G1_scratch, 0xFF, G1_scratch); - __ cmp(G1_scratch, Bytecodes::_getfield); - __ br(Assembler::notEqual, false, Assembler::pn, slow_path); - __ delayed()->nop(); - - // Get the type and return field offset from the constant pool cache - __ ld_ptr(G3_scratch, in_bytes(cp_base_offset + ConstantPoolCacheEntry::flags_offset()), G1_scratch); - __ ld_ptr(G3_scratch, in_bytes(cp_base_offset + ConstantPoolCacheEntry::f2_offset()), G3_scratch); - - Label xreturn_path; - // Need to differentiate between igetfield, agetfield, bgetfield etc. - // because they are different sizes. - // Get the type from the constant pool cache - __ srl(G1_scratch, ConstantPoolCacheEntry::tos_state_shift, G1_scratch); - // Make sure we don't need to mask G1_scratch after the above shift - ConstantPoolCacheEntry::verify_tos_state_shift(); - __ cmp(G1_scratch, atos ); - __ br(Assembler::equal, true, Assembler::pt, xreturn_path); - __ delayed()->ld_ptr(Otos_i, G3_scratch, Otos_i); - __ cmp(G1_scratch, itos); - __ br(Assembler::equal, true, Assembler::pt, xreturn_path); - __ delayed()->ld(Otos_i, G3_scratch, Otos_i); - __ cmp(G1_scratch, stos); - __ br(Assembler::equal, true, Assembler::pt, xreturn_path); - __ delayed()->ldsh(Otos_i, G3_scratch, Otos_i); - __ cmp(G1_scratch, ctos); - __ br(Assembler::equal, true, Assembler::pt, xreturn_path); - __ delayed()->lduh(Otos_i, G3_scratch, Otos_i); -#ifdef ASSERT - __ cmp(G1_scratch, btos); - __ br(Assembler::equal, true, Assembler::pt, xreturn_path); - __ delayed()->ldsb(Otos_i, G3_scratch, Otos_i); - __ should_not_reach_here(); -#endif - __ ldsb(Otos_i, G3_scratch, Otos_i); - __ bind(xreturn_path); - - // _ireturn/_areturn - __ retl(); // return from leaf routine - __ delayed()->mov(O5_savedSP, SP); - - // Generate regular method entry - __ bind(slow_path); - __ ba(fast_accessor_slow_entry_path); - __ delayed()->nop(); - return entry; - } - return NULL; -} - address InterpreterGenerator::generate_Reference_get_entry(void) { #if INCLUDE_ALL_GCS if (UseG1GC) { @@ -573,7 +468,7 @@ address InterpreterGenerator::generate_Reference_get_entry(void) { // If G1 is not enabled then attempt to go through the accessor entry point // Reference.get is an accessor - return generate_accessor_entry(); + return generate_jump_to_normal_entry(); } // @@ -1870,23 +1765,6 @@ address InterpreterGenerator::generate_normal_entry(bool synchronized) { __ ba(call_interpreter_2); __ delayed()->st_ptr(O1, STATE(_stack)); - - // Fast accessor methods share this entry point. - // This works because frame manager is in the same codelet - // This can either be an entry via call_stub/c1/c2 or a recursive interpreter call - // we need to do a little register fixup here once we distinguish the two of them - if (UseFastAccessorMethods && !synchronized) { - // Call stub_return address still in O7 - __ bind(fast_accessor_slow_entry_path); - __ set((intptr_t)return_from_native_method - 8, Gtmp1); - __ cmp(Gtmp1, O7); // returning to interpreter? - __ brx(Assembler::equal, true, Assembler::pt, re_dispatch); // yep - __ delayed()->nop(); - __ ba(re_dispatch); - __ delayed()->mov(G0, prevState); // initial entry - - } - // interpreter returning to native code (call_stub/c1/c2) // convert result and unwind initial activation // L2_scratch - scaled result type index diff --git a/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp b/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp index 0558b7b3c3c..de4997bce7a 100644 --- a/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/interpreterGenerator_sparc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -32,9 +32,11 @@ address generate_normal_entry(bool synchronized); address generate_native_entry(bool synchronized); address generate_abstract_entry(void); - address generate_math_entry(AbstractInterpreter::MethodKind kind); - address generate_empty_entry(void); - address generate_accessor_entry(void); + // there are no math intrinsics on sparc + address generate_math_entry(AbstractInterpreter::MethodKind kind) { return NULL; } + address generate_jump_to_normal_entry(void); + address generate_accessor_entry(void) { return generate_jump_to_normal_entry(); } + address generate_empty_entry(void) { return generate_jump_to_normal_entry(); } address generate_Reference_get_entry(void); void lock_method(void); void save_native_result(void); @@ -43,4 +45,7 @@ void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); void generate_counter_overflow(Label& Lcontinue); + // Not supported + address generate_CRC32_update_entry() { return NULL; } + address generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind) { return NULL; } #endif // CPU_SPARC_VM_INTERPRETERGENERATOR_SPARC_HPP diff --git a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp index debdaeae7a4..f8d8f590c99 100644 --- a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp @@ -241,6 +241,15 @@ void InterpreterGenerator::generate_counter_overflow(Label& Lcontinue) { // Various method entries +address InterpreterGenerator::generate_jump_to_normal_entry(void) { + address entry = __ pc(); + assert(Interpreter::entry_for_kind(Interpreter::zerolocals) != NULL, "should already be generated"); + AddressLiteral al(Interpreter::entry_for_kind(Interpreter::zerolocals)); + __ jump_to(al, G3_scratch); + __ delayed()->nop(); + return entry; +} + // Abstract method entry // Attempt to execute abstract method. Throw exception // @@ -255,159 +264,6 @@ address InterpreterGenerator::generate_abstract_entry(void) { } - -//---------------------------------------------------------------------------------------------------- -// Entry points & stack frame layout -// -// Here we generate the various kind of entries into the interpreter. -// The two main entry type are generic bytecode methods and native call method. -// These both come in synchronized and non-synchronized versions but the -// frame layout they create is very similar. The other method entry -// types are really just special purpose entries that are really entry -// and interpretation all in one. These are for trivial methods like -// accessor, empty, or special math methods. -// -// When control flow reaches any of the entry types for the interpreter -// the following holds -> -// -// C2 Calling Conventions: -// -// The entry code below assumes that the following registers are set -// when coming in: -// G5_method: holds the Method* of the method to call -// Lesp: points to the TOS of the callers expression stack -// after having pushed all the parameters -// -// The entry code does the following to setup an interpreter frame -// pop parameters from the callers stack by adjusting Lesp -// set O0 to Lesp -// compute X = (max_locals - num_parameters) -// bump SP up by X to accomadate the extra locals -// compute X = max_expression_stack -// + vm_local_words -// + 16 words of register save area -// save frame doing a save sp, -X, sp growing towards lower addresses -// set Lbcp, Lmethod, LcpoolCache -// set Llocals to i0 -// set Lmonitors to FP - rounded_vm_local_words -// set Lesp to Lmonitors - 4 -// -// The frame has now been setup to do the rest of the entry code - -// Try this optimization: Most method entries could live in a -// "one size fits all" stack frame without all the dynamic size -// calculations. It might be profitable to do all this calculation -// statically and approximately for "small enough" methods. - -//----------------------------------------------------------------------------------------------- - -// C1 Calling conventions -// -// Upon method entry, the following registers are setup: -// -// g2 G2_thread: current thread -// g5 G5_method: method to activate -// g4 Gargs : pointer to last argument -// -// -// Stack: -// -// +---------------+ <--- sp -// | | -// : reg save area : -// | | -// +---------------+ <--- sp + 0x40 -// | | -// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) -// | | -// +---------------+ <--- sp + 0x5c -// | | -// : free : -// | | -// +---------------+ <--- Gargs -// | | -// : arguments : -// | | -// +---------------+ -// | | -// -// -// -// AFTER FRAME HAS BEEN SETUP for method interpretation the stack looks like: -// -// +---------------+ <--- sp -// | | -// : reg save area : -// | | -// +---------------+ <--- sp + 0x40 -// | | -// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) -// | | -// +---------------+ <--- sp + 0x5c -// | | -// : : -// | | <--- Lesp -// +---------------+ <--- Lmonitors (fp - 0x18) -// | VM locals | -// +---------------+ <--- fp -// | | -// : reg save area : -// | | -// +---------------+ <--- fp + 0x40 -// | | -// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) -// | | -// +---------------+ <--- fp + 0x5c -// | | -// : free : -// | | -// +---------------+ -// | | -// : nonarg locals : -// | | -// +---------------+ -// | | -// : arguments : -// | | <--- Llocals -// +---------------+ <--- Gargs -// | | - -address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) { - // determine code generation flags - bool synchronized = false; - address entry_point = NULL; - - switch (kind) { - case Interpreter::zerolocals : break; - case Interpreter::zerolocals_synchronized: synchronized = true; break; - case Interpreter::native : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(false); break; - case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(true); break; - case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break; - case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break; - case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break; - - case Interpreter::java_lang_math_sin : break; - case Interpreter::java_lang_math_cos : break; - case Interpreter::java_lang_math_tan : break; - case Interpreter::java_lang_math_sqrt : break; - case Interpreter::java_lang_math_abs : break; - case Interpreter::java_lang_math_log : break; - case Interpreter::java_lang_math_log10 : break; - case Interpreter::java_lang_math_pow : break; - case Interpreter::java_lang_math_exp : break; - case Interpreter::java_lang_ref_reference_get - : entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break; - default: - fatal(err_msg("unexpected method kind: %d", kind)); - break; - } - - if (entry_point) return entry_point; - - return ((InterpreterGenerator*)this)->generate_normal_entry(synchronized); -} - - bool AbstractInterpreter::can_be_compiled(methodHandle m) { // No special entry points that preclude compilation return true; diff --git a/hotspot/src/cpu/sparc/vm/sparc.ad b/hotspot/src/cpu/sparc/vm/sparc.ad index c261900003b..99ec87803d5 100644 --- a/hotspot/src/cpu/sparc/vm/sparc.ad +++ b/hotspot/src/cpu/sparc/vm/sparc.ad @@ -6184,7 +6184,11 @@ instruct loadConP_no_oop_cheap(iRegP dst, immP_no_oop_cheap con) %{ ins_cost(DEFAULT_COST * 3/2); format %{ "SET $con,$dst\t! non-oop ptr" %} ins_encode %{ - __ set($con$$constant, $dst$$Register); + if (_opnds[1]->constant_reloc() == relocInfo::metadata_type) { + __ set_metadata_constant((Metadata*)$con$$constant, $dst$$Register); + } else { + __ set($con$$constant, $dst$$Register); + } %} ins_pipe(loadConP); %} diff --git a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp index 1f8503161cc..93f87807482 100644 --- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp @@ -456,6 +456,115 @@ void TemplateInterpreterGenerator::generate_stack_overflow_check(Register Rframe // Generate a fixed interpreter frame. This is identical setup for interpreted // methods and for native methods hence the shared code. + +//---------------------------------------------------------------------------------------------------- +// Stack frame layout +// +// When control flow reaches any of the entry types for the interpreter +// the following holds -> +// +// C2 Calling Conventions: +// +// The entry code below assumes that the following registers are set +// when coming in: +// G5_method: holds the Method* of the method to call +// Lesp: points to the TOS of the callers expression stack +// after having pushed all the parameters +// +// The entry code does the following to setup an interpreter frame +// pop parameters from the callers stack by adjusting Lesp +// set O0 to Lesp +// compute X = (max_locals - num_parameters) +// bump SP up by X to accomadate the extra locals +// compute X = max_expression_stack +// + vm_local_words +// + 16 words of register save area +// save frame doing a save sp, -X, sp growing towards lower addresses +// set Lbcp, Lmethod, LcpoolCache +// set Llocals to i0 +// set Lmonitors to FP - rounded_vm_local_words +// set Lesp to Lmonitors - 4 +// +// The frame has now been setup to do the rest of the entry code + +// Try this optimization: Most method entries could live in a +// "one size fits all" stack frame without all the dynamic size +// calculations. It might be profitable to do all this calculation +// statically and approximately for "small enough" methods. + +//----------------------------------------------------------------------------------------------- + +// C1 Calling conventions +// +// Upon method entry, the following registers are setup: +// +// g2 G2_thread: current thread +// g5 G5_method: method to activate +// g4 Gargs : pointer to last argument +// +// +// Stack: +// +// +---------------+ <--- sp +// | | +// : reg save area : +// | | +// +---------------+ <--- sp + 0x40 +// | | +// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) +// | | +// +---------------+ <--- sp + 0x5c +// | | +// : free : +// | | +// +---------------+ <--- Gargs +// | | +// : arguments : +// | | +// +---------------+ +// | | +// +// +// +// AFTER FRAME HAS BEEN SETUP for method interpretation the stack looks like: +// +// +---------------+ <--- sp +// | | +// : reg save area : +// | | +// +---------------+ <--- sp + 0x40 +// | | +// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) +// | | +// +---------------+ <--- sp + 0x5c +// | | +// : : +// | | <--- Lesp +// +---------------+ <--- Lmonitors (fp - 0x18) +// | VM locals | +// +---------------+ <--- fp +// | | +// : reg save area : +// | | +// +---------------+ <--- fp + 0x40 +// | | +// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) +// | | +// +---------------+ <--- fp + 0x5c +// | | +// : free : +// | | +// +---------------+ +// | | +// : nonarg locals : +// | | +// +---------------+ +// | | +// : arguments : +// | | <--- Llocals +// +---------------+ <--- Gargs +// | | + void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) { // // @@ -599,136 +708,6 @@ void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) { } -// Empty method, generate a very fast return. - -address InterpreterGenerator::generate_empty_entry(void) { - - // A method that does nother but return... - - address entry = __ pc(); - Label slow_path; - - // do nothing for empty methods (do not even increment invocation counter) - if ( UseFastEmptyMethods) { - // If we need a safepoint check, generate full interpreter entry. - AddressLiteral sync_state(SafepointSynchronize::address_of_state()); - __ set(sync_state, G3_scratch); - __ cmp_and_br_short(G3_scratch, SafepointSynchronize::_not_synchronized, Assembler::notEqual, Assembler::pn, slow_path); - - // Code: _return - __ retl(); - __ delayed()->mov(O5_savedSP, SP); - - __ bind(slow_path); - (void) generate_normal_entry(false); - - return entry; - } - return NULL; -} - -// Call an accessor method (assuming it is resolved, otherwise drop into -// vanilla (slow path) entry - -// Generates code to elide accessor methods -// Uses G3_scratch and G1_scratch as scratch -address InterpreterGenerator::generate_accessor_entry(void) { - - // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites thereof; - // parameter size = 1 - // Note: We can only use this code if the getfield has been resolved - // and if we don't have a null-pointer exception => check for - // these conditions first and use slow path if necessary. - address entry = __ pc(); - Label slow_path; - - - // XXX: for compressed oops pointer loading and decoding doesn't fit in - // delay slot and damages G1 - if ( UseFastAccessorMethods && !UseCompressedOops ) { - // Check if we need to reach a safepoint and generate full interpreter - // frame if so. - AddressLiteral sync_state(SafepointSynchronize::address_of_state()); - __ load_contents(sync_state, G3_scratch); - __ cmp(G3_scratch, SafepointSynchronize::_not_synchronized); - __ cmp_and_br_short(G3_scratch, SafepointSynchronize::_not_synchronized, Assembler::notEqual, Assembler::pn, slow_path); - - // Check if local 0 != NULL - __ ld_ptr(Gargs, G0, Otos_i ); // get local 0 - // check if local 0 == NULL and go the slow path - __ br_null_short(Otos_i, Assembler::pn, slow_path); - - - // read first instruction word and extract bytecode @ 1 and index @ 2 - // get first 4 bytes of the bytecodes (big endian!) - __ ld_ptr(G5_method, Method::const_offset(), G1_scratch); - __ ld(G1_scratch, ConstMethod::codes_offset(), G1_scratch); - - // move index @ 2 far left then to the right most two bytes. - __ sll(G1_scratch, 2*BitsPerByte, G1_scratch); - __ srl(G1_scratch, 2*BitsPerByte - exact_log2(in_words( - ConstantPoolCacheEntry::size()) * BytesPerWord), G1_scratch); - - // get constant pool cache - __ ld_ptr(G5_method, Method::const_offset(), G3_scratch); - __ ld_ptr(G3_scratch, ConstMethod::constants_offset(), G3_scratch); - __ ld_ptr(G3_scratch, ConstantPool::cache_offset_in_bytes(), G3_scratch); - - // get specific constant pool cache entry - __ add(G3_scratch, G1_scratch, G3_scratch); - - // Check the constant Pool cache entry to see if it has been resolved. - // If not, need the slow path. - ByteSize cp_base_offset = ConstantPoolCache::base_offset(); - __ ld_ptr(G3_scratch, cp_base_offset + ConstantPoolCacheEntry::indices_offset(), G1_scratch); - __ srl(G1_scratch, 2*BitsPerByte, G1_scratch); - __ and3(G1_scratch, 0xFF, G1_scratch); - __ cmp_and_br_short(G1_scratch, Bytecodes::_getfield, Assembler::notEqual, Assembler::pn, slow_path); - - // Get the type and return field offset from the constant pool cache - __ ld_ptr(G3_scratch, cp_base_offset + ConstantPoolCacheEntry::flags_offset(), G1_scratch); - __ ld_ptr(G3_scratch, cp_base_offset + ConstantPoolCacheEntry::f2_offset(), G3_scratch); - - Label xreturn_path; - // Need to differentiate between igetfield, agetfield, bgetfield etc. - // because they are different sizes. - // Get the type from the constant pool cache - __ srl(G1_scratch, ConstantPoolCacheEntry::tos_state_shift, G1_scratch); - // Make sure we don't need to mask G1_scratch after the above shift - ConstantPoolCacheEntry::verify_tos_state_shift(); - __ cmp(G1_scratch, atos ); - __ br(Assembler::equal, true, Assembler::pt, xreturn_path); - __ delayed()->ld_ptr(Otos_i, G3_scratch, Otos_i); - __ cmp(G1_scratch, itos); - __ br(Assembler::equal, true, Assembler::pt, xreturn_path); - __ delayed()->ld(Otos_i, G3_scratch, Otos_i); - __ cmp(G1_scratch, stos); - __ br(Assembler::equal, true, Assembler::pt, xreturn_path); - __ delayed()->ldsh(Otos_i, G3_scratch, Otos_i); - __ cmp(G1_scratch, ctos); - __ br(Assembler::equal, true, Assembler::pt, xreturn_path); - __ delayed()->lduh(Otos_i, G3_scratch, Otos_i); -#ifdef ASSERT - __ cmp(G1_scratch, btos); - __ br(Assembler::equal, true, Assembler::pt, xreturn_path); - __ delayed()->ldsb(Otos_i, G3_scratch, Otos_i); - __ should_not_reach_here(); -#endif - __ ldsb(Otos_i, G3_scratch, Otos_i); - __ bind(xreturn_path); - - // _ireturn/_areturn - __ retl(); // return from leaf routine - __ delayed()->mov(O5_savedSP, SP); - - // Generate regular method entry - __ bind(slow_path); - (void) generate_normal_entry(false); - return entry; - } - return NULL; -} - // Method entry for java.lang.ref.Reference.get. address InterpreterGenerator::generate_Reference_get_entry(void) { #if INCLUDE_ALL_GCS @@ -806,7 +785,7 @@ address InterpreterGenerator::generate_Reference_get_entry(void) { // If G1 is not enabled then attempt to go through the accessor entry point // Reference.get is an accessor - return generate_accessor_entry(); + return generate_jump_to_normal_entry(); } // @@ -1242,8 +1221,6 @@ address InterpreterGenerator::generate_native_entry(bool synchronized) { // Generic method entry to (asm) interpreter -//------------------------------------------------------------------------------------------------------------------------ -// address InterpreterGenerator::generate_normal_entry(bool synchronized) { address entry = __ pc(); @@ -1410,123 +1387,6 @@ address InterpreterGenerator::generate_normal_entry(bool synchronized) { return entry; } - -//---------------------------------------------------------------------------------------------------- -// Entry points & stack frame layout -// -// Here we generate the various kind of entries into the interpreter. -// The two main entry type are generic bytecode methods and native call method. -// These both come in synchronized and non-synchronized versions but the -// frame layout they create is very similar. The other method entry -// types are really just special purpose entries that are really entry -// and interpretation all in one. These are for trivial methods like -// accessor, empty, or special math methods. -// -// When control flow reaches any of the entry types for the interpreter -// the following holds -> -// -// C2 Calling Conventions: -// -// The entry code below assumes that the following registers are set -// when coming in: -// G5_method: holds the Method* of the method to call -// Lesp: points to the TOS of the callers expression stack -// after having pushed all the parameters -// -// The entry code does the following to setup an interpreter frame -// pop parameters from the callers stack by adjusting Lesp -// set O0 to Lesp -// compute X = (max_locals - num_parameters) -// bump SP up by X to accomadate the extra locals -// compute X = max_expression_stack -// + vm_local_words -// + 16 words of register save area -// save frame doing a save sp, -X, sp growing towards lower addresses -// set Lbcp, Lmethod, LcpoolCache -// set Llocals to i0 -// set Lmonitors to FP - rounded_vm_local_words -// set Lesp to Lmonitors - 4 -// -// The frame has now been setup to do the rest of the entry code - -// Try this optimization: Most method entries could live in a -// "one size fits all" stack frame without all the dynamic size -// calculations. It might be profitable to do all this calculation -// statically and approximately for "small enough" methods. - -//----------------------------------------------------------------------------------------------- - -// C1 Calling conventions -// -// Upon method entry, the following registers are setup: -// -// g2 G2_thread: current thread -// g5 G5_method: method to activate -// g4 Gargs : pointer to last argument -// -// -// Stack: -// -// +---------------+ <--- sp -// | | -// : reg save area : -// | | -// +---------------+ <--- sp + 0x40 -// | | -// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) -// | | -// +---------------+ <--- sp + 0x5c -// | | -// : free : -// | | -// +---------------+ <--- Gargs -// | | -// : arguments : -// | | -// +---------------+ -// | | -// -// -// -// AFTER FRAME HAS BEEN SETUP for method interpretation the stack looks like: -// -// +---------------+ <--- sp -// | | -// : reg save area : -// | | -// +---------------+ <--- sp + 0x40 -// | | -// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) -// | | -// +---------------+ <--- sp + 0x5c -// | | -// : : -// | | <--- Lesp -// +---------------+ <--- Lmonitors (fp - 0x18) -// | VM locals | -// +---------------+ <--- fp -// | | -// : reg save area : -// | | -// +---------------+ <--- fp + 0x40 -// | | -// : extra 7 slots : note: these slots are not really needed for the interpreter (fix later) -// | | -// +---------------+ <--- fp + 0x5c -// | | -// : free : -// | | -// +---------------+ -// | | -// : nonarg locals : -// | | -// +---------------+ -// | | -// : arguments : -// | | <--- Llocals -// +---------------+ <--- Gargs -// | | - static int size_activation_helper(int callee_extra_locals, int max_stack, int monitor_size) { // Figure out the size of an interpreter frame (in words) given that we have a fully allocated diff --git a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp index 79bf7139a12..91a9f76af4c 100644 --- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp @@ -26,6 +26,7 @@ #include "asm/macroAssembler.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/java.hpp" +#include "runtime/os.hpp" #include "runtime/stubCodeGenerator.hpp" #include "vm_version_sparc.hpp" @@ -249,7 +250,7 @@ void VM_Version::initialize() { (!has_hardware_fsmuld() ? ", no-fsmuld" : "")); // buf is started with ", " or is empty - _features_str = strdup(strlen(buf) > 2 ? buf + 2 : buf); + _features_str = os::strdup(strlen(buf) > 2 ? buf + 2 : buf); // There are three 64-bit SPARC families that do not overlap, e.g., // both is_ultra3() and is_sparc64() cannot be true at the same time. diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.cpp b/hotspot/src/cpu/x86/vm/assembler_x86.cpp index 64b8ce731a5..8098e889ba9 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp @@ -3853,6 +3853,15 @@ void Assembler::vpbroadcastd(XMMRegister dst, XMMRegister src) { emit_int8((unsigned char)(0xC0 | encode)); } +// Carry-Less Multiplication Quadword +void Assembler::pclmulqdq(XMMRegister dst, XMMRegister src, int mask) { + assert(VM_Version::supports_clmul(), ""); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A); + emit_int8(0x44); + emit_int8((unsigned char)(0xC0 | encode)); + emit_int8((unsigned char)mask); +} + // Carry-Less Multiplication Quadword void Assembler::vpclmulqdq(XMMRegister dst, XMMRegister nds, XMMRegister src, int mask) { assert(VM_Version::supports_avx() && VM_Version::supports_clmul(), ""); @@ -4928,6 +4937,26 @@ void Assembler::addq(Register dst, Register src) { emit_arith(0x03, 0xC0, dst, src); } +void Assembler::adcxq(Register dst, Register src) { + //assert(VM_Version::supports_adx(), "adx instructions not supported"); + emit_int8((unsigned char)0x66); + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_int8(0x0F); + emit_int8(0x38); + emit_int8((unsigned char)0xF6); + emit_int8((unsigned char)(0xC0 | encode)); +} + +void Assembler::adoxq(Register dst, Register src) { + //assert(VM_Version::supports_adx(), "adx instructions not supported"); + emit_int8((unsigned char)0xF3); + int encode = prefixq_and_encode(dst->encoding(), src->encoding()); + emit_int8(0x0F); + emit_int8(0x38); + emit_int8((unsigned char)0xF6); + emit_int8((unsigned char)(0xC0 | encode)); +} + void Assembler::andq(Address dst, int32_t imm32) { InstructionMark im(this); prefixq(dst); @@ -5435,6 +5464,26 @@ void Assembler::movzwq(Register dst, Register src) { emit_int8((unsigned char)(0xC0 | encode)); } +void Assembler::mulq(Address src) { + InstructionMark im(this); + prefixq(src); + emit_int8((unsigned char)0xF7); + emit_operand(rsp, src); +} + +void Assembler::mulq(Register src) { + int encode = prefixq_and_encode(src->encoding()); + emit_int8((unsigned char)0xF7); + emit_int8((unsigned char)(0xE0 | encode)); +} + +void Assembler::mulxq(Register dst1, Register dst2, Register src) { + assert(VM_Version::supports_bmi2(), "bit manipulation instructions not supported"); + int encode = vex_prefix_and_encode(dst1->encoding(), dst2->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_38, true, false); + emit_int8((unsigned char)0xF6); + emit_int8((unsigned char)(0xC0 | encode)); +} + void Assembler::negq(Register dst) { int encode = prefixq_and_encode(dst->encoding()); emit_int8((unsigned char)0xF7); @@ -5563,6 +5612,28 @@ void Assembler::rclq(Register dst, int imm8) { emit_int8(imm8); } } + +void Assembler::rorq(Register dst, int imm8) { + assert(isShiftCount(imm8 >> 1), "illegal shift count"); + int encode = prefixq_and_encode(dst->encoding()); + if (imm8 == 1) { + emit_int8((unsigned char)0xD1); + emit_int8((unsigned char)(0xC8 | encode)); + } else { + emit_int8((unsigned char)0xC1); + emit_int8((unsigned char)(0xc8 | encode)); + emit_int8(imm8); + } +} + +void Assembler::rorxq(Register dst, Register src, int imm8) { + assert(VM_Version::supports_bmi2(), "bit manipulation instructions not supported"); + int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_3A, true, false); + emit_int8((unsigned char)0xF0); + emit_int8((unsigned char)(0xC0 | encode)); + emit_int8(imm8); +} + void Assembler::sarq(Register dst, int imm8) { assert(isShiftCount(imm8 >> 1), "illegal shift count"); int encode = prefixq_and_encode(dst->encoding()); diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.hpp b/hotspot/src/cpu/x86/vm/assembler_x86.hpp index 12bc14e7195..2ac9df8c927 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp @@ -888,6 +888,14 @@ private: void addq(Register dst, Address src); void addq(Register dst, Register src); +#ifdef _LP64 + //Add Unsigned Integers with Carry Flag + void adcxq(Register dst, Register src); + + //Add Unsigned Integers with Overflow Flag + void adoxq(Register dst, Register src); +#endif + void addr_nop_4(); void addr_nop_5(); void addr_nop_7(); @@ -1204,19 +1212,20 @@ private: void idivl(Register src); void divl(Register src); // Unsigned division +#ifdef _LP64 void idivq(Register src); +#endif void imull(Register dst, Register src); void imull(Register dst, Register src, int value); void imull(Register dst, Address src); +#ifdef _LP64 void imulq(Register dst, Register src); void imulq(Register dst, Register src, int value); -#ifdef _LP64 void imulq(Register dst, Address src); #endif - // jcc is the generic conditional branch generator to run- // time routines, jcc is used for branches to labels. jcc // takes a branch opcode (cc) and a label (L) and generates @@ -1408,9 +1417,16 @@ private: void movzwq(Register dst, Register src); #endif + // Unsigned multiply with RAX destination register void mull(Address src); void mull(Register src); +#ifdef _LP64 + void mulq(Address src); + void mulq(Register src); + void mulxq(Register dst1, Register dst2, Register src); +#endif + // Multiply Scalar Double-Precision Floating-Point Values void mulsd(XMMRegister dst, Address src); void mulsd(XMMRegister dst, XMMRegister src); @@ -1541,6 +1557,11 @@ private: void ret(int imm16); +#ifdef _LP64 + void rorq(Register dst, int imm8); + void rorxq(Register dst, Register src, int imm8); +#endif + void sahf(); void sarl(Register dst, int imm8); @@ -1837,6 +1858,7 @@ private: void vpbroadcastd(XMMRegister dst, XMMRegister src); // Carry-Less Multiplication Quadword + void pclmulqdq(XMMRegister dst, XMMRegister src, int mask); void vpclmulqdq(XMMRegister dst, XMMRegister nds, XMMRegister src, int mask); // AVX instruction which is used to clear upper 128 bits of YMM registers and diff --git a/hotspot/src/cpu/x86/vm/cppInterpreterGenerator_x86.hpp b/hotspot/src/cpu/x86/vm/cppInterpreterGenerator_x86.hpp index 2f9f1d47deb..9f81215b396 100644 --- a/hotspot/src/cpu/x86/vm/cppInterpreterGenerator_x86.hpp +++ b/hotspot/src/cpu/x86/vm/cppInterpreterGenerator_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,21 +27,6 @@ protected: -#if 0 - address generate_asm_interpreter_entry(bool synchronized); - address generate_native_entry(bool synchronized); - address generate_abstract_entry(void); - address generate_math_entry(AbstractInterpreter::MethodKind kind); - address generate_empty_entry(void); - address generate_accessor_entry(void); - address generate_Reference_get_entry(void); - void lock_method(void); - void generate_stack_overflow_check(void); - - void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue); - void generate_counter_overflow(Label* do_continue); -#endif - void generate_more_monitors(); void generate_deopt_handling(); address generate_interpreter_frame_manager(bool synchronized); // C++ interpreter only diff --git a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp index 9eb1a6ebf77..27f1b309727 100644 --- a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp +++ b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2014, 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 @@ -66,9 +66,6 @@ extern "C" void RecursiveInterpreterActivation(interpreterState istate ) #define __ _masm-> #define STATE(field_name) (Address(state, byte_offset_of(BytecodeInterpreter, field_name))) -Label fast_accessor_slow_entry_path; // fast accessor methods need to be able to jmp to unsynchronized - // c++ interpreter entry point this holds that entry point label. - // default registers for state and sender_sp // state and sender_sp are the same on 32bit because we have no choice. // state could be rsi on 64bit but it is an arg reg and not callee save @@ -660,7 +657,6 @@ void InterpreterGenerator::generate_stack_overflow_check(void) { // generate_method_entry) so the guard should work for them too. // - // monitor entry size: see picture of stack set (generate_method_entry) and frame_i486.hpp const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; // total overhead size: entry_size + (saved rbp, thru expr stack bottom). @@ -794,156 +790,6 @@ void InterpreterGenerator::lock_method(void) { __ lock_object(monitor); } -// Call an accessor method (assuming it is resolved, otherwise drop into vanilla (slow path) entry - -address InterpreterGenerator::generate_accessor_entry(void) { - - // rbx: Method* - - // rsi/r13: senderSP must preserved for slow path, set SP to it on fast path - - Label xreturn_path; - - // do fastpath for resolved accessor methods - if (UseFastAccessorMethods) { - - address entry_point = __ pc(); - - Label slow_path; - // If we need a safepoint check, generate full interpreter entry. - ExternalAddress state(SafepointSynchronize::address_of_state()); - __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), - SafepointSynchronize::_not_synchronized); - - __ jcc(Assembler::notEqual, slow_path); - // ASM/C++ Interpreter - // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites thereof; parameter size = 1 - // Note: We can only use this code if the getfield has been resolved - // and if we don't have a null-pointer exception => check for - // these conditions first and use slow path if necessary. - // rbx,: method - // rcx: receiver - __ movptr(rax, Address(rsp, wordSize)); - - // check if local 0 != NULL and read field - __ testptr(rax, rax); - __ jcc(Assembler::zero, slow_path); - - // read first instruction word and extract bytecode @ 1 and index @ 2 - __ movptr(rdx, Address(rbx, Method::const_offset())); - __ movptr(rdi, Address(rdx, ConstMethod::constants_offset())); - __ movl(rdx, Address(rdx, ConstMethod::codes_offset())); - // Shift codes right to get the index on the right. - // The bytecode fetched looks like <0xb4><0x2a> - __ shrl(rdx, 2*BitsPerByte); - __ shll(rdx, exact_log2(in_words(ConstantPoolCacheEntry::size()))); - __ movptr(rdi, Address(rdi, ConstantPool::cache_offset_in_bytes())); - - // rax,: local 0 - // rbx,: method - // rcx: receiver - do not destroy since it is needed for slow path! - // rcx: scratch - // rdx: constant pool cache index - // rdi: constant pool cache - // rsi/r13: sender sp - - // check if getfield has been resolved and read constant pool cache entry - // check the validity of the cache entry by testing whether _indices field - // contains Bytecode::_getfield in b1 byte. - assert(in_words(ConstantPoolCacheEntry::size()) == 4, "adjust shift below"); - __ movl(rcx, - Address(rdi, - rdx, - Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset())); - __ shrl(rcx, 2*BitsPerByte); - __ andl(rcx, 0xFF); - __ cmpl(rcx, Bytecodes::_getfield); - __ jcc(Assembler::notEqual, slow_path); - - // Note: constant pool entry is not valid before bytecode is resolved - __ movptr(rcx, - Address(rdi, - rdx, - Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset())); - __ movl(rdx, - Address(rdi, - rdx, - Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); - - Label notByte, notShort, notChar; - const Address field_address (rax, rcx, Address::times_1); - - // Need to differentiate between igetfield, agetfield, bgetfield etc. - // because they are different sizes. - // Use the type from the constant pool cache - __ shrl(rdx, ConstantPoolCacheEntry::tos_state_shift); - // Make sure we don't need to mask rdx after the above shift - ConstantPoolCacheEntry::verify_tos_state_shift(); -#ifdef _LP64 - Label notObj; - __ cmpl(rdx, atos); - __ jcc(Assembler::notEqual, notObj); - // atos - __ movptr(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notObj); -#endif // _LP64 - __ cmpl(rdx, btos); - __ jcc(Assembler::notEqual, notByte); - __ load_signed_byte(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notByte); - __ cmpl(rdx, stos); - __ jcc(Assembler::notEqual, notShort); - __ load_signed_short(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notShort); - __ cmpl(rdx, ctos); - __ jcc(Assembler::notEqual, notChar); - __ load_unsigned_short(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notChar); -#ifdef ASSERT - Label okay; -#ifndef _LP64 - __ cmpl(rdx, atos); - __ jcc(Assembler::equal, okay); -#endif // _LP64 - __ cmpl(rdx, itos); - __ jcc(Assembler::equal, okay); - __ stop("what type is this?"); - __ bind(okay); -#endif // ASSERT - // All the rest are a 32 bit wordsize - __ movl(rax, field_address); - - __ bind(xreturn_path); - - // _ireturn/_areturn - __ pop(rdi); // get return address - __ mov(rsp, sender_sp_on_entry); // set sp to sender sp - __ jmp(rdi); - - // generate a vanilla interpreter entry as the slow path - __ bind(slow_path); - // We will enter c++ interpreter looking like it was - // called by the call_stub this will cause it to return - // a tosca result to the invoker which might have been - // the c++ interpreter itself. - - __ jmp(fast_accessor_slow_entry_path); - return entry_point; - - } else { - return NULL; - } - -} - address InterpreterGenerator::generate_Reference_get_entry(void) { #if INCLUDE_ALL_GCS if (UseG1GC) { @@ -961,7 +807,7 @@ address InterpreterGenerator::generate_Reference_get_entry(void) { // If G1 is not enabled then attempt to go through the accessor entry point // Reference.get is an accessor - return generate_accessor_entry(); + return generate_jump_to_normal_entry(); } // @@ -1670,10 +1516,6 @@ address InterpreterGenerator::generate_normal_entry(bool synchronized) { address entry_point = __ pc(); - // Fast accessor methods share this entry point. - // This works because frame manager is in the same codelet - if (UseFastAccessorMethods && !synchronized) __ bind(fast_accessor_slow_entry_path); - Label dispatch_entry_2; __ movptr(rcx, sender_sp_on_entry); __ movptr(state, (int32_t)NULL_WORD); // no current activation @@ -2212,40 +2054,6 @@ address InterpreterGenerator::generate_normal_entry(bool synchronized) { return entry_point; } -address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) { - // determine code generation flags - bool synchronized = false; - address entry_point = NULL; - - switch (kind) { - case Interpreter::zerolocals : break; - case Interpreter::zerolocals_synchronized: synchronized = true; break; - case Interpreter::native : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(false); break; - case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*)this)->generate_native_entry(true); break; - case Interpreter::empty : entry_point = ((InterpreterGenerator*)this)->generate_empty_entry(); break; - case Interpreter::accessor : entry_point = ((InterpreterGenerator*)this)->generate_accessor_entry(); break; - case Interpreter::abstract : entry_point = ((InterpreterGenerator*)this)->generate_abstract_entry(); break; - - case Interpreter::java_lang_math_sin : // fall thru - case Interpreter::java_lang_math_cos : // fall thru - case Interpreter::java_lang_math_tan : // fall thru - case Interpreter::java_lang_math_abs : // fall thru - case Interpreter::java_lang_math_log : // fall thru - case Interpreter::java_lang_math_log10 : // fall thru - case Interpreter::java_lang_math_sqrt : // fall thru - case Interpreter::java_lang_math_pow : // fall thru - case Interpreter::java_lang_math_exp : // fall thru - entry_point = ((InterpreterGenerator*)this)->generate_math_entry(kind); break; - case Interpreter::java_lang_ref_reference_get - : entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break; - default : ShouldNotReachHere(); break; - } - - if (entry_point) return entry_point; - - return ((InterpreterGenerator*)this)->generate_normal_entry(synchronized); - -} InterpreterGenerator::InterpreterGenerator(StubQueue* code) : CppInterpreterGenerator(code) { diff --git a/hotspot/src/cpu/x86/vm/globals_x86.hpp b/hotspot/src/cpu/x86/vm/globals_x86.hpp index 5b34293ea6a..1401997b3a1 100644 --- a/hotspot/src/cpu/x86/vm/globals_x86.hpp +++ b/hotspot/src/cpu/x86/vm/globals_x86.hpp @@ -176,6 +176,8 @@ define_pd_global(uintx, TypeProfileLevel, 111); "Use count trailing zeros instruction") \ \ product(bool, UseBMI1Instructions, false, \ - "Use BMI instructions") - + "Use BMI1 instructions") \ + \ + product(bool, UseBMI2Instructions, false, \ + "Use BMI2 instructions") #endif // CPU_X86_VM_GLOBALS_X86_HPP diff --git a/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.cpp b/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.cpp new file mode 100644 index 00000000000..229803a80ff --- /dev/null +++ b/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "asm/macroAssembler.hpp" +#include "interpreter/interpreter.hpp" +#include "interpreter/interpreterGenerator.hpp" +#include "interpreter/interpreterRuntime.hpp" +#include "interpreter/interp_masm.hpp" + +#define __ _masm-> + +// Jump into normal path for accessor and empty entry to jump to normal entry +// The "fast" optimization don't update compilation count therefore can disable inlining +// for these functions that should be inlined. +address InterpreterGenerator::generate_jump_to_normal_entry(void) { + address entry_point = __ pc(); + + assert(Interpreter::entry_for_kind(Interpreter::zerolocals) != NULL, "should already be generated"); + __ jump(RuntimeAddress(Interpreter::entry_for_kind(Interpreter::zerolocals))); + return entry_point; +} + +// Abstract method entry +// Attempt to execute abstract method. Throw exception +address InterpreterGenerator::generate_abstract_entry(void) { + + address entry_point = __ pc(); + + // abstract method entry + +#ifndef CC_INTERP + // pop return address, reset last_sp to NULL + __ empty_expression_stack(); + __ restore_bcp(); // rsi must be correct for exception handler (was destroyed) + __ restore_locals(); // make sure locals pointer is correct as well (was destroyed) +#endif + + // throw exception + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); + // the call_VM checks for exception, so we should never return here. + __ should_not_reach_here(); + + return entry_point; +} diff --git a/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp b/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp index 08f47708cdc..b802e9c405b 100644 --- a/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp +++ b/hotspot/src/cpu/x86/vm/interpreterGenerator_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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,9 @@ address generate_native_entry(bool synchronized); address generate_abstract_entry(void); address generate_math_entry(AbstractInterpreter::MethodKind kind); - address generate_empty_entry(void); - address generate_accessor_entry(void); + address generate_jump_to_normal_entry(void); + address generate_accessor_entry(void) { return generate_jump_to_normal_entry(); } + address generate_empty_entry(void) { return generate_jump_to_normal_entry(); } address generate_Reference_get_entry(); address generate_CRC32_update_entry(); address generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind); diff --git a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp index abe354360ff..4a2903b3736 100644 --- a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp +++ b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp @@ -67,45 +67,6 @@ address AbstractInterpreterGenerator::generate_slow_signature_handler() { } -// -// Various method entries (that c++ and asm interpreter agree upon) -//------------------------------------------------------------------------------------------------------------------------ -// -// - -// Empty method, generate a very fast return. - -address InterpreterGenerator::generate_empty_entry(void) { - - // rbx,: Method* - // rcx: receiver (unused) - // rsi: previous interpreter state (C++ interpreter) must preserve - // rsi: sender sp must set sp to this value on return - - if (!UseFastEmptyMethods) return NULL; - - address entry_point = __ pc(); - - // If we need a safepoint check, generate full interpreter entry. - Label slow_path; - ExternalAddress state(SafepointSynchronize::address_of_state()); - __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), - SafepointSynchronize::_not_synchronized); - __ jcc(Assembler::notEqual, slow_path); - - // do nothing for empty methods (do not even increment invocation counter) - // Code: _return - // _return - // return w/o popping parameters - __ pop(rax); - __ mov(rsp, rsi); - __ jmp(rax); - - __ bind(slow_path); - (void) generate_normal_entry(false); - return entry_point; -} - address InterpreterGenerator::generate_math_entry(AbstractInterpreter::MethodKind kind) { // rbx,: Method* @@ -216,36 +177,6 @@ address InterpreterGenerator::generate_math_entry(AbstractInterpreter::MethodKin } -// Abstract method entry -// Attempt to execute abstract method. Throw exception -address InterpreterGenerator::generate_abstract_entry(void) { - - // rbx,: Method* - // rcx: receiver (unused) - // rsi: previous interpreter state (C++ interpreter) must preserve - - // rsi: sender SP - - address entry_point = __ pc(); - - // abstract method entry - -#ifndef CC_INTERP - // pop return address, reset last_sp to NULL - __ empty_expression_stack(); - __ restore_bcp(); // rsi must be correct for exception handler (was destroyed) - __ restore_locals(); // make sure locals pointer is correct as well (was destroyed) -#endif - - // throw exception - __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError)); - // the call_VM checks for exception, so we should never return here. - __ should_not_reach_here(); - - return entry_point; -} - - void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in diff --git a/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp index 65ffc6d2e97..af4f7fac388 100644 --- a/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp @@ -301,66 +301,6 @@ address InterpreterGenerator::generate_math_entry(AbstractInterpreter::MethodKin return entry_point; } - -// Abstract method entry -// Attempt to execute abstract method. Throw exception -address InterpreterGenerator::generate_abstract_entry(void) { - // rbx: Method* - // r13: sender SP - - address entry_point = __ pc(); - - // abstract method entry - -#ifndef CC_INTERP - // pop return address, reset last_sp to NULL - __ empty_expression_stack(); - __ restore_bcp(); // rsi must be correct for exception handler (was destroyed) - __ restore_locals(); // make sure locals pointer is correct as well (was destroyed) -#endif - - // throw exception - __ call_VM(noreg, CAST_FROM_FN_PTR(address, - InterpreterRuntime::throw_AbstractMethodError)); - // the call_VM checks for exception, so we should never return here. - __ should_not_reach_here(); - - return entry_point; -} - - -// Empty method, generate a very fast return. - -address InterpreterGenerator::generate_empty_entry(void) { - // rbx: Method* - // r13: sender sp must set sp to this value on return - - if (!UseFastEmptyMethods) { - return NULL; - } - - address entry_point = __ pc(); - - // If we need a safepoint check, generate full interpreter entry. - Label slow_path; - __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), - SafepointSynchronize::_not_synchronized); - __ jcc(Assembler::notEqual, slow_path); - - // do nothing for empty methods (do not even increment invocation counter) - // Code: _return - // _return - // return w/o popping parameters - __ pop(rax); - __ mov(rsp, r13); - __ jmp(rax); - - __ bind(slow_path); - (void) generate_normal_entry(false); - return entry_point; - -} - void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) { // This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in diff --git a/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp index 5b324124138..592b45c9fd0 100644 --- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp @@ -7293,6 +7293,467 @@ void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, bind(L_done); } +#ifdef _LP64 +/** + * Helper for multiply_to_len(). + */ +void MacroAssembler::add2_with_carry(Register dest_hi, Register dest_lo, Register src1, Register src2) { + addq(dest_lo, src1); + adcq(dest_hi, 0); + addq(dest_lo, src2); + adcq(dest_hi, 0); +} + +/** + * Multiply 64 bit by 64 bit first loop. + */ +void MacroAssembler::multiply_64_x_64_loop(Register x, Register xstart, Register x_xstart, + Register y, Register y_idx, Register z, + Register carry, Register product, + Register idx, Register kdx) { + // + // jlong carry, x[], y[], z[]; + // for (int idx=ystart, kdx=ystart+1+xstart; idx >= 0; idx-, kdx--) { + // huge_128 product = y[idx] * x[xstart] + carry; + // z[kdx] = (jlong)product; + // carry = (jlong)(product >>> 64); + // } + // z[xstart] = carry; + // + + Label L_first_loop, L_first_loop_exit; + Label L_one_x, L_one_y, L_multiply; + + decrementl(xstart); + jcc(Assembler::negative, L_one_x); + + movq(x_xstart, Address(x, xstart, Address::times_4, 0)); + rorq(x_xstart, 32); // convert big-endian to little-endian + + bind(L_first_loop); + decrementl(idx); + jcc(Assembler::negative, L_first_loop_exit); + decrementl(idx); + jcc(Assembler::negative, L_one_y); + movq(y_idx, Address(y, idx, Address::times_4, 0)); + rorq(y_idx, 32); // convert big-endian to little-endian + bind(L_multiply); + movq(product, x_xstart); + mulq(y_idx); // product(rax) * y_idx -> rdx:rax + addq(product, carry); + adcq(rdx, 0); + subl(kdx, 2); + movl(Address(z, kdx, Address::times_4, 4), product); + shrq(product, 32); + movl(Address(z, kdx, Address::times_4, 0), product); + movq(carry, rdx); + jmp(L_first_loop); + + bind(L_one_y); + movl(y_idx, Address(y, 0)); + jmp(L_multiply); + + bind(L_one_x); + movl(x_xstart, Address(x, 0)); + jmp(L_first_loop); + + bind(L_first_loop_exit); +} + +/** + * Multiply 64 bit by 64 bit and add 128 bit. + */ +void MacroAssembler::multiply_add_128_x_128(Register x_xstart, Register y, Register z, + Register yz_idx, Register idx, + Register carry, Register product, int offset) { + // huge_128 product = (y[idx] * x_xstart) + z[kdx] + carry; + // z[kdx] = (jlong)product; + + movq(yz_idx, Address(y, idx, Address::times_4, offset)); + rorq(yz_idx, 32); // convert big-endian to little-endian + movq(product, x_xstart); + mulq(yz_idx); // product(rax) * yz_idx -> rdx:product(rax) + movq(yz_idx, Address(z, idx, Address::times_4, offset)); + rorq(yz_idx, 32); // convert big-endian to little-endian + + add2_with_carry(rdx, product, carry, yz_idx); + + movl(Address(z, idx, Address::times_4, offset+4), product); + shrq(product, 32); + movl(Address(z, idx, Address::times_4, offset), product); + +} + +/** + * Multiply 128 bit by 128 bit. Unrolled inner loop. + */ +void MacroAssembler::multiply_128_x_128_loop(Register x_xstart, Register y, Register z, + Register yz_idx, Register idx, Register jdx, + Register carry, Register product, + Register carry2) { + // jlong carry, x[], y[], z[]; + // int kdx = ystart+1; + // for (int idx=ystart-2; idx >= 0; idx -= 2) { // Third loop + // huge_128 product = (y[idx+1] * x_xstart) + z[kdx+idx+1] + carry; + // z[kdx+idx+1] = (jlong)product; + // jlong carry2 = (jlong)(product >>> 64); + // product = (y[idx] * x_xstart) + z[kdx+idx] + carry2; + // z[kdx+idx] = (jlong)product; + // carry = (jlong)(product >>> 64); + // } + // idx += 2; + // if (idx > 0) { + // product = (y[idx] * x_xstart) + z[kdx+idx] + carry; + // z[kdx+idx] = (jlong)product; + // carry = (jlong)(product >>> 64); + // } + // + + Label L_third_loop, L_third_loop_exit, L_post_third_loop_done; + + movl(jdx, idx); + andl(jdx, 0xFFFFFFFC); + shrl(jdx, 2); + + bind(L_third_loop); + subl(jdx, 1); + jcc(Assembler::negative, L_third_loop_exit); + subl(idx, 4); + + multiply_add_128_x_128(x_xstart, y, z, yz_idx, idx, carry, product, 8); + movq(carry2, rdx); + + multiply_add_128_x_128(x_xstart, y, z, yz_idx, idx, carry2, product, 0); + movq(carry, rdx); + jmp(L_third_loop); + + bind (L_third_loop_exit); + + andl (idx, 0x3); + jcc(Assembler::zero, L_post_third_loop_done); + + Label L_check_1; + subl(idx, 2); + jcc(Assembler::negative, L_check_1); + + multiply_add_128_x_128(x_xstart, y, z, yz_idx, idx, carry, product, 0); + movq(carry, rdx); + + bind (L_check_1); + addl (idx, 0x2); + andl (idx, 0x1); + subl(idx, 1); + jcc(Assembler::negative, L_post_third_loop_done); + + movl(yz_idx, Address(y, idx, Address::times_4, 0)); + movq(product, x_xstart); + mulq(yz_idx); // product(rax) * yz_idx -> rdx:product(rax) + movl(yz_idx, Address(z, idx, Address::times_4, 0)); + + add2_with_carry(rdx, product, yz_idx, carry); + + movl(Address(z, idx, Address::times_4, 0), product); + shrq(product, 32); + + shlq(rdx, 32); + orq(product, rdx); + movq(carry, product); + + bind(L_post_third_loop_done); +} + +/** + * Multiply 128 bit by 128 bit using BMI2. Unrolled inner loop. + * + */ +void MacroAssembler::multiply_128_x_128_bmi2_loop(Register y, Register z, + Register carry, Register carry2, + Register idx, Register jdx, + Register yz_idx1, Register yz_idx2, + Register tmp, Register tmp3, Register tmp4) { + assert(UseBMI2Instructions, "should be used only when BMI2 is available"); + + // jlong carry, x[], y[], z[]; + // int kdx = ystart+1; + // for (int idx=ystart-2; idx >= 0; idx -= 2) { // Third loop + // huge_128 tmp3 = (y[idx+1] * rdx) + z[kdx+idx+1] + carry; + // jlong carry2 = (jlong)(tmp3 >>> 64); + // huge_128 tmp4 = (y[idx] * rdx) + z[kdx+idx] + carry2; + // carry = (jlong)(tmp4 >>> 64); + // z[kdx+idx+1] = (jlong)tmp3; + // z[kdx+idx] = (jlong)tmp4; + // } + // idx += 2; + // if (idx > 0) { + // yz_idx1 = (y[idx] * rdx) + z[kdx+idx] + carry; + // z[kdx+idx] = (jlong)yz_idx1; + // carry = (jlong)(yz_idx1 >>> 64); + // } + // + + Label L_third_loop, L_third_loop_exit, L_post_third_loop_done; + + movl(jdx, idx); + andl(jdx, 0xFFFFFFFC); + shrl(jdx, 2); + + bind(L_third_loop); + subl(jdx, 1); + jcc(Assembler::negative, L_third_loop_exit); + subl(idx, 4); + + movq(yz_idx1, Address(y, idx, Address::times_4, 8)); + rorxq(yz_idx1, yz_idx1, 32); // convert big-endian to little-endian + movq(yz_idx2, Address(y, idx, Address::times_4, 0)); + rorxq(yz_idx2, yz_idx2, 32); + + mulxq(tmp4, tmp3, yz_idx1); // yz_idx1 * rdx -> tmp4:tmp3 + mulxq(carry2, tmp, yz_idx2); // yz_idx2 * rdx -> carry2:tmp + + movq(yz_idx1, Address(z, idx, Address::times_4, 8)); + rorxq(yz_idx1, yz_idx1, 32); + movq(yz_idx2, Address(z, idx, Address::times_4, 0)); + rorxq(yz_idx2, yz_idx2, 32); + + if (VM_Version::supports_adx()) { + adcxq(tmp3, carry); + adoxq(tmp3, yz_idx1); + + adcxq(tmp4, tmp); + adoxq(tmp4, yz_idx2); + + movl(carry, 0); // does not affect flags + adcxq(carry2, carry); + adoxq(carry2, carry); + } else { + add2_with_carry(tmp4, tmp3, carry, yz_idx1); + add2_with_carry(carry2, tmp4, tmp, yz_idx2); + } + movq(carry, carry2); + + movl(Address(z, idx, Address::times_4, 12), tmp3); + shrq(tmp3, 32); + movl(Address(z, idx, Address::times_4, 8), tmp3); + + movl(Address(z, idx, Address::times_4, 4), tmp4); + shrq(tmp4, 32); + movl(Address(z, idx, Address::times_4, 0), tmp4); + + jmp(L_third_loop); + + bind (L_third_loop_exit); + + andl (idx, 0x3); + jcc(Assembler::zero, L_post_third_loop_done); + + Label L_check_1; + subl(idx, 2); + jcc(Assembler::negative, L_check_1); + + movq(yz_idx1, Address(y, idx, Address::times_4, 0)); + rorxq(yz_idx1, yz_idx1, 32); + mulxq(tmp4, tmp3, yz_idx1); // yz_idx1 * rdx -> tmp4:tmp3 + movq(yz_idx2, Address(z, idx, Address::times_4, 0)); + rorxq(yz_idx2, yz_idx2, 32); + + add2_with_carry(tmp4, tmp3, carry, yz_idx2); + + movl(Address(z, idx, Address::times_4, 4), tmp3); + shrq(tmp3, 32); + movl(Address(z, idx, Address::times_4, 0), tmp3); + movq(carry, tmp4); + + bind (L_check_1); + addl (idx, 0x2); + andl (idx, 0x1); + subl(idx, 1); + jcc(Assembler::negative, L_post_third_loop_done); + movl(tmp4, Address(y, idx, Address::times_4, 0)); + mulxq(carry2, tmp3, tmp4); // tmp4 * rdx -> carry2:tmp3 + movl(tmp4, Address(z, idx, Address::times_4, 0)); + + add2_with_carry(carry2, tmp3, tmp4, carry); + + movl(Address(z, idx, Address::times_4, 0), tmp3); + shrq(tmp3, 32); + + shlq(carry2, 32); + orq(tmp3, carry2); + movq(carry, tmp3); + + bind(L_post_third_loop_done); +} + +/** + * Code for BigInteger::multiplyToLen() instrinsic. + * + * rdi: x + * rax: xlen + * rsi: y + * rcx: ylen + * r8: z + * r11: zlen + * r12: tmp1 + * r13: tmp2 + * r14: tmp3 + * r15: tmp4 + * rbx: tmp5 + * + */ +void MacroAssembler::multiply_to_len(Register x, Register xlen, Register y, Register ylen, Register z, Register zlen, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5) { + ShortBranchVerifier sbv(this); + assert_different_registers(x, xlen, y, ylen, z, zlen, tmp1, tmp2, tmp3, tmp4, tmp5, rdx); + + push(tmp1); + push(tmp2); + push(tmp3); + push(tmp4); + push(tmp5); + + push(xlen); + push(zlen); + + const Register idx = tmp1; + const Register kdx = tmp2; + const Register xstart = tmp3; + + const Register y_idx = tmp4; + const Register carry = tmp5; + const Register product = xlen; + const Register x_xstart = zlen; // reuse register + + // First Loop. + // + // final static long LONG_MASK = 0xffffffffL; + // int xstart = xlen - 1; + // int ystart = ylen - 1; + // long carry = 0; + // for (int idx=ystart, kdx=ystart+1+xstart; idx >= 0; idx-, kdx--) { + // long product = (y[idx] & LONG_MASK) * (x[xstart] & LONG_MASK) + carry; + // z[kdx] = (int)product; + // carry = product >>> 32; + // } + // z[xstart] = (int)carry; + // + + movl(idx, ylen); // idx = ylen; + movl(kdx, zlen); // kdx = xlen+ylen; + xorq(carry, carry); // carry = 0; + + Label L_done; + + movl(xstart, xlen); + decrementl(xstart); + jcc(Assembler::negative, L_done); + + multiply_64_x_64_loop(x, xstart, x_xstart, y, y_idx, z, carry, product, idx, kdx); + + Label L_second_loop; + testl(kdx, kdx); + jcc(Assembler::zero, L_second_loop); + + Label L_carry; + subl(kdx, 1); + jcc(Assembler::zero, L_carry); + + movl(Address(z, kdx, Address::times_4, 0), carry); + shrq(carry, 32); + subl(kdx, 1); + + bind(L_carry); + movl(Address(z, kdx, Address::times_4, 0), carry); + + // Second and third (nested) loops. + // + // for (int i = xstart-1; i >= 0; i--) { // Second loop + // carry = 0; + // for (int jdx=ystart, k=ystart+1+i; jdx >= 0; jdx--, k--) { // Third loop + // long product = (y[jdx] & LONG_MASK) * (x[i] & LONG_MASK) + + // (z[k] & LONG_MASK) + carry; + // z[k] = (int)product; + // carry = product >>> 32; + // } + // z[i] = (int)carry; + // } + // + // i = xlen, j = tmp1, k = tmp2, carry = tmp5, x[i] = rdx + + const Register jdx = tmp1; + + bind(L_second_loop); + xorl(carry, carry); // carry = 0; + movl(jdx, ylen); // j = ystart+1 + + subl(xstart, 1); // i = xstart-1; + jcc(Assembler::negative, L_done); + + push (z); + + Label L_last_x; + lea(z, Address(z, xstart, Address::times_4, 4)); // z = z + k - j + subl(xstart, 1); // i = xstart-1; + jcc(Assembler::negative, L_last_x); + + if (UseBMI2Instructions) { + movq(rdx, Address(x, xstart, Address::times_4, 0)); + rorxq(rdx, rdx, 32); // convert big-endian to little-endian + } else { + movq(x_xstart, Address(x, xstart, Address::times_4, 0)); + rorq(x_xstart, 32); // convert big-endian to little-endian + } + + Label L_third_loop_prologue; + bind(L_third_loop_prologue); + + push (x); + push (xstart); + push (ylen); + + + if (UseBMI2Instructions) { + multiply_128_x_128_bmi2_loop(y, z, carry, x, jdx, ylen, product, tmp2, x_xstart, tmp3, tmp4); + } else { // !UseBMI2Instructions + multiply_128_x_128_loop(x_xstart, y, z, y_idx, jdx, ylen, carry, product, x); + } + + pop(ylen); + pop(xlen); + pop(x); + pop(z); + + movl(tmp3, xlen); + addl(tmp3, 1); + movl(Address(z, tmp3, Address::times_4, 0), carry); + subl(tmp3, 1); + jccb(Assembler::negative, L_done); + + shrq(carry, 32); + movl(Address(z, tmp3, Address::times_4, 0), carry); + jmp(L_second_loop); + + // Next infrequent code is moved outside loops. + bind(L_last_x); + if (UseBMI2Instructions) { + movl(rdx, Address(x, 0)); + } else { + movl(x_xstart, Address(x, 0)); + } + jmp(L_third_loop_prologue); + + bind(L_done); + + pop(zlen); + pop(xlen); + + pop(tmp5); + pop(tmp4); + pop(tmp3); + pop(tmp2); + pop(tmp1); +} +#endif + /** * Emits code to update CRC-32 with a byte value according to constants in table * @@ -7316,17 +7777,34 @@ void MacroAssembler::update_byte_crc32(Register crc, Register val, Register tabl * Fold 128-bit data chunk */ void MacroAssembler::fold_128bit_crc32(XMMRegister xcrc, XMMRegister xK, XMMRegister xtmp, Register buf, int offset) { - vpclmulhdq(xtmp, xK, xcrc); // [123:64] - vpclmulldq(xcrc, xK, xcrc); // [63:0] - vpxor(xcrc, xcrc, Address(buf, offset), false /* vector256 */); - pxor(xcrc, xtmp); + if (UseAVX > 0) { + vpclmulhdq(xtmp, xK, xcrc); // [123:64] + vpclmulldq(xcrc, xK, xcrc); // [63:0] + vpxor(xcrc, xcrc, Address(buf, offset), false /* vector256 */); + pxor(xcrc, xtmp); + } else { + movdqa(xtmp, xcrc); + pclmulhdq(xtmp, xK); // [123:64] + pclmulldq(xcrc, xK); // [63:0] + pxor(xcrc, xtmp); + movdqu(xtmp, Address(buf, offset)); + pxor(xcrc, xtmp); + } } void MacroAssembler::fold_128bit_crc32(XMMRegister xcrc, XMMRegister xK, XMMRegister xtmp, XMMRegister xbuf) { - vpclmulhdq(xtmp, xK, xcrc); - vpclmulldq(xcrc, xK, xcrc); - pxor(xcrc, xbuf); - pxor(xcrc, xtmp); + if (UseAVX > 0) { + vpclmulhdq(xtmp, xK, xcrc); + vpclmulldq(xcrc, xK, xcrc); + pxor(xcrc, xbuf); + pxor(xcrc, xtmp); + } else { + movdqa(xtmp, xcrc); + pclmulhdq(xtmp, xK); + pclmulldq(xcrc, xK); + pxor(xcrc, xbuf); + pxor(xcrc, xtmp); + } } /** @@ -7444,9 +7922,17 @@ void MacroAssembler::kernel_crc32(Register crc, Register buf, Register len, Regi // Fold 128 bits in xmm1 down into 32 bits in crc register. BIND(L_fold_128b); movdqu(xmm0, ExternalAddress(StubRoutines::x86::crc_by128_masks_addr())); - vpclmulqdq(xmm2, xmm0, xmm1, 0x1); - vpand(xmm3, xmm0, xmm2, false /* vector256 */); - vpclmulqdq(xmm0, xmm0, xmm3, 0x1); + if (UseAVX > 0) { + vpclmulqdq(xmm2, xmm0, xmm1, 0x1); + vpand(xmm3, xmm0, xmm2, false /* vector256 */); + vpclmulqdq(xmm0, xmm0, xmm3, 0x1); + } else { + movdqa(xmm2, xmm0); + pclmulqdq(xmm2, xmm1, 0x1); + movdqa(xmm3, xmm0); + pand(xmm3, xmm2); + pclmulqdq(xmm0, xmm3, 0x1); + } psrldq(xmm1, 8); psrldq(xmm2, 4); pxor(xmm0, xmm1); diff --git a/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp b/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp index 3d8802632a4..69c9e8aa360 100644 --- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp @@ -966,6 +966,16 @@ public: void mulss(XMMRegister dst, Address src) { Assembler::mulss(dst, src); } void mulss(XMMRegister dst, AddressLiteral src); + // Carry-Less Multiplication Quadword + void pclmulldq(XMMRegister dst, XMMRegister src) { + // 0x00 - multiply lower 64 bits [0:63] + Assembler::pclmulqdq(dst, src, 0x00); + } + void pclmulhdq(XMMRegister dst, XMMRegister src) { + // 0x11 - multiply upper 64 bits [64:127] + Assembler::pclmulqdq(dst, src, 0x11); + } + void sqrtsd(XMMRegister dst, XMMRegister src) { Assembler::sqrtsd(dst, src); } void sqrtsd(XMMRegister dst, Address src) { Assembler::sqrtsd(dst, src); } void sqrtsd(XMMRegister dst, AddressLiteral src); @@ -1211,6 +1221,28 @@ public: XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3, XMMRegister tmp4, Register tmp5, Register result); +#ifdef _LP64 + void add2_with_carry(Register dest_hi, Register dest_lo, Register src1, Register src2); + void multiply_64_x_64_loop(Register x, Register xstart, Register x_xstart, + Register y, Register y_idx, Register z, + Register carry, Register product, + Register idx, Register kdx); + void multiply_add_128_x_128(Register x_xstart, Register y, Register z, + Register yz_idx, Register idx, + Register carry, Register product, int offset); + void multiply_128_x_128_bmi2_loop(Register y, Register z, + Register carry, Register carry2, + Register idx, Register jdx, + Register yz_idx1, Register yz_idx2, + Register tmp, Register tmp3, Register tmp4); + void multiply_128_x_128_loop(Register x_xstart, Register y, Register z, + Register yz_idx, Register idx, Register jdx, + Register carry, Register product, + Register carry2); + void multiply_to_len(Register x, Register xlen, Register y, Register ylen, Register z, Register zlen, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5); +#endif + // CRC32 code for java.util.zip.CRC32::updateBytes() instrinsic. void update_byte_crc32(Register crc, Register val, Register table); void kernel_crc32(Register crc, Register buf, Register len, Register table, Register tmp); diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp index 0adb0d31e72..0000146f535 100644 --- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp @@ -3677,6 +3677,70 @@ class StubGenerator: public StubCodeGenerator { return start; } + + /** + * Arguments: + * + * Input: + * c_rarg0 - x address + * c_rarg1 - x length + * c_rarg2 - y address + * c_rarg3 - y lenth + * not Win64 + * c_rarg4 - z address + * c_rarg5 - z length + * Win64 + * rsp+40 - z address + * rsp+48 - z length + */ + address generate_multiplyToLen() { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "multiplyToLen"); + + address start = __ pc(); + // Win64: rcx, rdx, r8, r9 (c_rarg0, c_rarg1, ...) + // Unix: rdi, rsi, rdx, rcx, r8, r9 (c_rarg0, c_rarg1, ...) + const Register x = rdi; + const Register xlen = rax; + const Register y = rsi; + const Register ylen = rcx; + const Register z = r8; + const Register zlen = r11; + + // Next registers will be saved on stack in multiply_to_len(). + const Register tmp1 = r12; + const Register tmp2 = r13; + const Register tmp3 = r14; + const Register tmp4 = r15; + const Register tmp5 = rbx; + + BLOCK_COMMENT("Entry:"); + __ enter(); // required for proper stackwalking of RuntimeStub frame + +#ifndef _WIN64 + __ movptr(zlen, r9); // Save r9 in r11 - zlen +#endif + setup_arg_regs(4); // x => rdi, xlen => rsi, y => rdx + // ylen => rcx, z => r8, zlen => r11 + // r9 and r10 may be used to save non-volatile registers +#ifdef _WIN64 + // last 2 arguments (#4, #5) are on stack on Win64 + __ movptr(z, Address(rsp, 6 * wordSize)); + __ movptr(zlen, Address(rsp, 7 * wordSize)); +#endif + + __ movptr(xlen, rsi); + __ movptr(y, rdx); + __ multiply_to_len(x, xlen, y, ylen, z, zlen, tmp1, tmp2, tmp3, tmp4, tmp5); + + restore_arg_regs(); + + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + #undef __ #define __ masm-> @@ -3917,6 +3981,11 @@ class StubGenerator: public StubCodeGenerator { generate_safefetch("SafeFetchN", sizeof(intptr_t), &StubRoutines::_safefetchN_entry, &StubRoutines::_safefetchN_fault_pc, &StubRoutines::_safefetchN_continuation_pc); +#ifdef COMPILER2 + if (UseMultiplyToLenIntrinsic) { + StubRoutines::_multiplyToLen = generate_multiplyToLen(); + } +#endif } public: diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86.cpp index 23dcbee036e..7a12d82e9af 100644 --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86.cpp +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86.cpp @@ -38,7 +38,7 @@ int AbstractInterpreter::size_activation(int max_stack, int callee_locals, bool is_top_frame) { // Note: This calculation must exactly parallel the frame setup - // in AbstractInterpreterGenerator::generate_method_entry. + // in InterpreterGenerator::generate_fixed_frame. // fixed size of an interpreter frame: int overhead = frame::sender_sp_offset - diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp index e705dfbe0ce..93772f3df4c 100644 --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp @@ -468,10 +468,10 @@ void InterpreterGenerator::generate_stack_overflow_check(void) { // rax, // NOTE: since the additional locals are also always pushed (wasn't obvious in - // generate_method_entry) so the guard should work for them too. + // generate_fixed_frame) so the guard should work for them too. // - // monitor entry size: see picture of stack set (generate_method_entry) and frame_x86.hpp + // monitor entry size: see picture of stack in frame_x86.hpp const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; // total overhead size: entry_size + (saved rbp, thru expr stack bottom). @@ -633,145 +633,6 @@ void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) { __ movptr(Address(rsp, 0), rsp); // set expression stack bottom } -// End of helpers - -// -// Various method entries -//------------------------------------------------------------------------------------------------------------------------ -// -// - -// Call an accessor method (assuming it is resolved, otherwise drop into vanilla (slow path) entry - -address InterpreterGenerator::generate_accessor_entry(void) { - - // rbx,: Method* - // rcx: receiver (preserve for slow entry into asm interpreter) - - // rsi: senderSP must preserved for slow path, set SP to it on fast path - - address entry_point = __ pc(); - Label xreturn_path; - - // do fastpath for resolved accessor methods - if (UseFastAccessorMethods) { - Label slow_path; - // If we need a safepoint check, generate full interpreter entry. - ExternalAddress state(SafepointSynchronize::address_of_state()); - __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), - SafepointSynchronize::_not_synchronized); - - __ jcc(Assembler::notEqual, slow_path); - // ASM/C++ Interpreter - // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites thereof; parameter size = 1 - // Note: We can only use this code if the getfield has been resolved - // and if we don't have a null-pointer exception => check for - // these conditions first and use slow path if necessary. - // rbx,: method - // rcx: receiver - __ movptr(rax, Address(rsp, wordSize)); - - // check if local 0 != NULL and read field - __ testptr(rax, rax); - __ jcc(Assembler::zero, slow_path); - - // read first instruction word and extract bytecode @ 1 and index @ 2 - __ movptr(rdx, Address(rbx, Method::const_offset())); - __ movptr(rdi, Address(rdx, ConstMethod::constants_offset())); - __ movl(rdx, Address(rdx, ConstMethod::codes_offset())); - // Shift codes right to get the index on the right. - // The bytecode fetched looks like <0xb4><0x2a> - __ shrl(rdx, 2*BitsPerByte); - __ shll(rdx, exact_log2(in_words(ConstantPoolCacheEntry::size()))); - __ movptr(rdi, Address(rdi, ConstantPool::cache_offset_in_bytes())); - - // rax,: local 0 - // rbx,: method - // rcx: receiver - do not destroy since it is needed for slow path! - // rcx: scratch - // rdx: constant pool cache index - // rdi: constant pool cache - // rsi: sender sp - - // check if getfield has been resolved and read constant pool cache entry - // check the validity of the cache entry by testing whether _indices field - // contains Bytecode::_getfield in b1 byte. - assert(in_words(ConstantPoolCacheEntry::size()) == 4, "adjust shift below"); - __ movl(rcx, - Address(rdi, - rdx, - Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset())); - __ shrl(rcx, 2*BitsPerByte); - __ andl(rcx, 0xFF); - __ cmpl(rcx, Bytecodes::_getfield); - __ jcc(Assembler::notEqual, slow_path); - - // Note: constant pool entry is not valid before bytecode is resolved - __ movptr(rcx, - Address(rdi, - rdx, - Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f2_offset())); - __ movl(rdx, - Address(rdi, - rdx, - Address::times_ptr, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset())); - - Label notByte, notShort, notChar; - const Address field_address (rax, rcx, Address::times_1); - - // Need to differentiate between igetfield, agetfield, bgetfield etc. - // because they are different sizes. - // Use the type from the constant pool cache - __ shrl(rdx, ConstantPoolCacheEntry::tos_state_shift); - // Make sure we don't need to mask rdx after the above shift - ConstantPoolCacheEntry::verify_tos_state_shift(); - __ cmpl(rdx, btos); - __ jcc(Assembler::notEqual, notByte); - __ load_signed_byte(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notByte); - __ cmpl(rdx, stos); - __ jcc(Assembler::notEqual, notShort); - __ load_signed_short(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notShort); - __ cmpl(rdx, ctos); - __ jcc(Assembler::notEqual, notChar); - __ load_unsigned_short(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notChar); -#ifdef ASSERT - Label okay; - __ cmpl(rdx, atos); - __ jcc(Assembler::equal, okay); - __ cmpl(rdx, itos); - __ jcc(Assembler::equal, okay); - __ stop("what type is this?"); - __ bind(okay); -#endif // ASSERT - // All the rest are a 32 bit wordsize - // This is ok for now. Since fast accessors should be going away - __ movptr(rax, field_address); - - __ bind(xreturn_path); - - // _ireturn/_areturn - __ pop(rdi); // get return address - __ mov(rsp, rsi); // set sp to sender sp - __ jmp(rdi); - - // generate a vanilla interpreter entry as the slow path - __ bind(slow_path); - - (void) generate_normal_entry(false); - return entry_point; - } - return NULL; - -} // Method entry for java.lang.ref.Reference.get. address InterpreterGenerator::generate_Reference_get_entry(void) { @@ -862,7 +723,7 @@ address InterpreterGenerator::generate_Reference_get_entry(void) { // If G1 is not enabled then attempt to go through the accessor entry point // Reference.get is an accessor - return generate_accessor_entry(); + return generate_jump_to_normal_entry(); } /** @@ -1557,100 +1418,6 @@ address InterpreterGenerator::generate_normal_entry(bool synchronized) { return entry_point; } -//------------------------------------------------------------------------------------------------------------------------ -// Entry points -// -// Here we generate the various kind of entries into the interpreter. -// The two main entry type are generic bytecode methods and native call method. -// These both come in synchronized and non-synchronized versions but the -// frame layout they create is very similar. The other method entry -// types are really just special purpose entries that are really entry -// and interpretation all in one. These are for trivial methods like -// accessor, empty, or special math methods. -// -// When control flow reaches any of the entry types for the interpreter -// the following holds -> -// -// Arguments: -// -// rbx,: Method* -// rcx: receiver -// -// -// Stack layout immediately at entry -// -// [ return address ] <--- rsp -// [ parameter n ] -// ... -// [ parameter 1 ] -// [ expression stack ] (caller's java expression stack) - -// Assuming that we don't go to one of the trivial specialized -// entries the stack will look like below when we are ready to execute -// the first bytecode (or call the native routine). The register usage -// will be as the template based interpreter expects (see interpreter_x86.hpp). -// -// local variables follow incoming parameters immediately; i.e. -// the return address is moved to the end of the locals). -// -// [ monitor entry ] <--- rsp -// ... -// [ monitor entry ] -// [ expr. stack bottom ] -// [ saved rsi ] -// [ current rdi ] -// [ Method* ] -// [ saved rbp, ] <--- rbp, -// [ return address ] -// [ local variable m ] -// ... -// [ local variable 1 ] -// [ parameter n ] -// ... -// [ parameter 1 ] <--- rdi - -address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) { - // determine code generation flags - bool synchronized = false; - address entry_point = NULL; - InterpreterGenerator* ig_this = (InterpreterGenerator*)this; - - switch (kind) { - case Interpreter::zerolocals : break; - case Interpreter::zerolocals_synchronized: synchronized = true; break; - case Interpreter::native : entry_point = ig_this->generate_native_entry(false); break; - case Interpreter::native_synchronized : entry_point = ig_this->generate_native_entry(true); break; - case Interpreter::empty : entry_point = ig_this->generate_empty_entry(); break; - case Interpreter::accessor : entry_point = ig_this->generate_accessor_entry(); break; - case Interpreter::abstract : entry_point = ig_this->generate_abstract_entry(); break; - - case Interpreter::java_lang_math_sin : // fall thru - case Interpreter::java_lang_math_cos : // fall thru - case Interpreter::java_lang_math_tan : // fall thru - case Interpreter::java_lang_math_abs : // fall thru - case Interpreter::java_lang_math_log : // fall thru - case Interpreter::java_lang_math_log10 : // fall thru - case Interpreter::java_lang_math_sqrt : // fall thru - case Interpreter::java_lang_math_pow : // fall thru - case Interpreter::java_lang_math_exp : entry_point = ig_this->generate_math_entry(kind); break; - case Interpreter::java_lang_ref_reference_get - : entry_point = ig_this->generate_Reference_get_entry(); break; - case Interpreter::java_util_zip_CRC32_update - : entry_point = ig_this->generate_CRC32_update_entry(); break; - case Interpreter::java_util_zip_CRC32_updateBytes - : // fall thru - case Interpreter::java_util_zip_CRC32_updateByteBuffer - : entry_point = ig_this->generate_CRC32_updateBytes_entry(kind); break; - default: - fatal(err_msg("unexpected method kind: %d", kind)); - break; - } - - if (entry_point) return entry_point; - - return ig_this->generate_normal_entry(synchronized); - -} // These should never be compiled since the interpreter will prefer // the compiled version to the intrinsic version. diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp index 9c81d6787a1..f3390c94ca9 100644 --- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp @@ -400,7 +400,7 @@ void InterpreterGenerator::generate_counter_overflow(Label* do_continue) { // page mechanism will work for that. // // NOTE: Since the additional locals are also always pushed (wasn't -// obvious in generate_method_entry) so the guard should work for them +// obvious in generate_fixed_frame) so the guard should work for them // too. // // Args: @@ -411,8 +411,7 @@ void InterpreterGenerator::generate_counter_overflow(Label* do_continue) { // rax void InterpreterGenerator::generate_stack_overflow_check(void) { - // monitor entry size: see picture of stack set - // (generate_method_entry) and frame_amd64.hpp + // monitor entry size: see picture of stack in frame_x86.hpp const int entry_size = frame::interpreter_frame_monitor_size() * wordSize; // total overhead size: entry_size + (saved rbp through expr stack @@ -600,153 +599,6 @@ void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call) { // End of helpers -// Various method entries -//------------------------------------------------------------------------------------------------------------------------ -// -// - -// Call an accessor method (assuming it is resolved, otherwise drop -// into vanilla (slow path) entry -address InterpreterGenerator::generate_accessor_entry(void) { - // rbx: Method* - - // r13: senderSP must preserver for slow path, set SP to it on fast path - - address entry_point = __ pc(); - Label xreturn_path; - - // do fastpath for resolved accessor methods - if (UseFastAccessorMethods) { - // Code: _aload_0, _(i|a)getfield, _(i|a)return or any rewrites - // thereof; parameter size = 1 - // Note: We can only use this code if the getfield has been resolved - // and if we don't have a null-pointer exception => check for - // these conditions first and use slow path if necessary. - Label slow_path; - // If we need a safepoint check, generate full interpreter entry. - __ cmp32(ExternalAddress(SafepointSynchronize::address_of_state()), - SafepointSynchronize::_not_synchronized); - - __ jcc(Assembler::notEqual, slow_path); - // rbx: method - __ movptr(rax, Address(rsp, wordSize)); - - // check if local 0 != NULL and read field - __ testptr(rax, rax); - __ jcc(Assembler::zero, slow_path); - - // read first instruction word and extract bytecode @ 1 and index @ 2 - __ movptr(rdx, Address(rbx, Method::const_offset())); - __ movptr(rdi, Address(rdx, ConstMethod::constants_offset())); - __ movl(rdx, Address(rdx, ConstMethod::codes_offset())); - // Shift codes right to get the index on the right. - // The bytecode fetched looks like <0xb4><0x2a> - __ shrl(rdx, 2 * BitsPerByte); - __ shll(rdx, exact_log2(in_words(ConstantPoolCacheEntry::size()))); - __ movptr(rdi, Address(rdi, ConstantPool::cache_offset_in_bytes())); - - // rax: local 0 - // rbx: method - // rdx: constant pool cache index - // rdi: constant pool cache - - // check if getfield has been resolved and read constant pool cache entry - // check the validity of the cache entry by testing whether _indices field - // contains Bytecode::_getfield in b1 byte. - assert(in_words(ConstantPoolCacheEntry::size()) == 4, - "adjust shift below"); - __ movl(rcx, - Address(rdi, - rdx, - Address::times_8, - ConstantPoolCache::base_offset() + - ConstantPoolCacheEntry::indices_offset())); - __ shrl(rcx, 2 * BitsPerByte); - __ andl(rcx, 0xFF); - __ cmpl(rcx, Bytecodes::_getfield); - __ jcc(Assembler::notEqual, slow_path); - - // Note: constant pool entry is not valid before bytecode is resolved - __ movptr(rcx, - Address(rdi, - rdx, - Address::times_8, - ConstantPoolCache::base_offset() + - ConstantPoolCacheEntry::f2_offset())); - // edx: flags - __ movl(rdx, - Address(rdi, - rdx, - Address::times_8, - ConstantPoolCache::base_offset() + - ConstantPoolCacheEntry::flags_offset())); - - Label notObj, notInt, notByte, notShort; - const Address field_address(rax, rcx, Address::times_1); - - // Need to differentiate between igetfield, agetfield, bgetfield etc. - // because they are different sizes. - // Use the type from the constant pool cache - __ shrl(rdx, ConstantPoolCacheEntry::tos_state_shift); - // Make sure we don't need to mask edx after the above shift - ConstantPoolCacheEntry::verify_tos_state_shift(); - - __ cmpl(rdx, atos); - __ jcc(Assembler::notEqual, notObj); - // atos - __ load_heap_oop(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notObj); - __ cmpl(rdx, itos); - __ jcc(Assembler::notEqual, notInt); - // itos - __ movl(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notInt); - __ cmpl(rdx, btos); - __ jcc(Assembler::notEqual, notByte); - // btos - __ load_signed_byte(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notByte); - __ cmpl(rdx, stos); - __ jcc(Assembler::notEqual, notShort); - // stos - __ load_signed_short(rax, field_address); - __ jmp(xreturn_path); - - __ bind(notShort); -#ifdef ASSERT - Label okay; - __ cmpl(rdx, ctos); - __ jcc(Assembler::equal, okay); - __ stop("what type is this?"); - __ bind(okay); -#endif - // ctos - __ load_unsigned_short(rax, field_address); - - __ bind(xreturn_path); - - // _ireturn/_areturn - __ pop(rdi); - __ mov(rsp, r13); - __ jmp(rdi); - __ ret(0); - - // generate a vanilla interpreter entry as the slow path - __ bind(slow_path); - (void) generate_normal_entry(false); - } else { - (void) generate_normal_entry(false); - } - - return entry_point; -} - // Method entry for java.lang.ref.Reference.get. address InterpreterGenerator::generate_Reference_get_entry(void) { #if INCLUDE_ALL_GCS @@ -773,8 +625,6 @@ address InterpreterGenerator::generate_Reference_get_entry(void) { // and so we don't need to call the G1 pre-barrier. Thus we can use the // regular method entry code to generate the NPE. // - // This code is based on generate_accessor_enty. - // // rbx: Method* // r13: senderSP must preserve for slow path, set SP to it on fast path @@ -832,7 +682,7 @@ address InterpreterGenerator::generate_Reference_get_entry(void) { // If G1 is not enabled then attempt to go through the accessor entry point // Reference.get is an accessor - return generate_accessor_entry(); + return generate_jump_to_normal_entry(); } /** @@ -1566,100 +1416,6 @@ address InterpreterGenerator::generate_normal_entry(bool synchronized) { return entry_point; } -// Entry points -// -// Here we generate the various kind of entries into the interpreter. -// The two main entry type are generic bytecode methods and native -// call method. These both come in synchronized and non-synchronized -// versions but the frame layout they create is very similar. The -// other method entry types are really just special purpose entries -// that are really entry and interpretation all in one. These are for -// trivial methods like accessor, empty, or special math methods. -// -// When control flow reaches any of the entry types for the interpreter -// the following holds -> -// -// Arguments: -// -// rbx: Method* -// -// Stack layout immediately at entry -// -// [ return address ] <--- rsp -// [ parameter n ] -// ... -// [ parameter 1 ] -// [ expression stack ] (caller's java expression stack) - -// Assuming that we don't go to one of the trivial specialized entries -// the stack will look like below when we are ready to execute the -// first bytecode (or call the native routine). The register usage -// will be as the template based interpreter expects (see -// interpreter_amd64.hpp). -// -// local variables follow incoming parameters immediately; i.e. -// the return address is moved to the end of the locals). -// -// [ monitor entry ] <--- rsp -// ... -// [ monitor entry ] -// [ expr. stack bottom ] -// [ saved r13 ] -// [ current r14 ] -// [ Method* ] -// [ saved ebp ] <--- rbp -// [ return address ] -// [ local variable m ] -// ... -// [ local variable 1 ] -// [ parameter n ] -// ... -// [ parameter 1 ] <--- r14 - -address AbstractInterpreterGenerator::generate_method_entry( - AbstractInterpreter::MethodKind kind) { - // determine code generation flags - bool synchronized = false; - address entry_point = NULL; - InterpreterGenerator* ig_this = (InterpreterGenerator*)this; - - switch (kind) { - case Interpreter::zerolocals : break; - case Interpreter::zerolocals_synchronized: synchronized = true; break; - case Interpreter::native : entry_point = ig_this->generate_native_entry(false); break; - case Interpreter::native_synchronized : entry_point = ig_this->generate_native_entry(true); break; - case Interpreter::empty : entry_point = ig_this->generate_empty_entry(); break; - case Interpreter::accessor : entry_point = ig_this->generate_accessor_entry(); break; - case Interpreter::abstract : entry_point = ig_this->generate_abstract_entry(); break; - - case Interpreter::java_lang_math_sin : // fall thru - case Interpreter::java_lang_math_cos : // fall thru - case Interpreter::java_lang_math_tan : // fall thru - case Interpreter::java_lang_math_abs : // fall thru - case Interpreter::java_lang_math_log : // fall thru - case Interpreter::java_lang_math_log10 : // fall thru - case Interpreter::java_lang_math_sqrt : // fall thru - case Interpreter::java_lang_math_pow : // fall thru - case Interpreter::java_lang_math_exp : entry_point = ig_this->generate_math_entry(kind); break; - case Interpreter::java_lang_ref_reference_get - : entry_point = ig_this->generate_Reference_get_entry(); break; - case Interpreter::java_util_zip_CRC32_update - : entry_point = ig_this->generate_CRC32_update_entry(); break; - case Interpreter::java_util_zip_CRC32_updateBytes - : // fall thru - case Interpreter::java_util_zip_CRC32_updateByteBuffer - : entry_point = ig_this->generate_CRC32_updateBytes_entry(kind); break; - default: - fatal(err_msg("unexpected method kind: %d", kind)); - break; - } - - if (entry_point) { - return entry_point; - } - - return ig_this->generate_normal_entry(synchronized); -} // These should never be compiled since the interpreter will prefer // the compiled version to the intrinsic version. diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp index 57a1545166a..e73f93c705f 100644 --- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp @@ -27,6 +27,7 @@ #include "asm/macroAssembler.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/java.hpp" +#include "runtime/os.hpp" #include "runtime/stubCodeGenerator.hpp" #include "vm_version_x86.hpp" @@ -484,7 +485,7 @@ void VM_Version::get_processor_features() { } char buf[256]; - jio_snprintf(buf, sizeof(buf), "(%u cores per cpu, %u threads per core) family %d model %d stepping %d%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + jio_snprintf(buf, sizeof(buf), "(%u cores per cpu, %u threads per core) family %d model %d stepping %d%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", cores_per_cpu(), threads_per_core(), cpu_family(), _model, _stepping, (supports_cmov() ? ", cmov" : ""), @@ -513,8 +514,9 @@ void VM_Version::get_processor_features() { (supports_tscinv_bit() ? ", tscinvbit": ""), (supports_tscinv() ? ", tscinv": ""), (supports_bmi1() ? ", bmi1" : ""), - (supports_bmi2() ? ", bmi2" : "")); - _features_str = strdup(buf); + (supports_bmi2() ? ", bmi2" : ""), + (supports_adx() ? ", adx" : "")); + _features_str = os::strdup(buf); // UseSSE is set to the smaller of what hardware supports and what // the command line requires. I.e., you cannot set UseSSE to 2 on @@ -559,13 +561,13 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseCLMUL, false); } - if (UseCLMUL && (UseAVX > 0) && (UseSSE > 2)) { + if (UseCLMUL && (UseSSE > 2)) { if (FLAG_IS_DEFAULT(UseCRC32Intrinsics)) { UseCRC32Intrinsics = true; } } else if (UseCRC32Intrinsics) { if (!FLAG_IS_DEFAULT(UseCRC32Intrinsics)) - warning("CRC32 Intrinsics requires AVX and CLMUL instructions (not available on this CPU)"); + warning("CRC32 Intrinsics requires CLMUL instructions (not available on this CPU)"); FLAG_SET_DEFAULT(UseCRC32Intrinsics, false); } @@ -603,6 +605,17 @@ void VM_Version::get_processor_features() { #if INCLUDE_RTM_OPT if (UseRTMLocking) { + if (is_intel_family_core()) { + if ((_model == CPU_MODEL_HASWELL_E3) || + (_model == CPU_MODEL_HASWELL_E7 && _stepping < 3) || + (_model == CPU_MODEL_BROADWELL && _stepping < 4)) { + if (!UnlockExperimentalVMOptions) { + vm_exit_during_initialization("UseRTMLocking is only available as experimental option on this platform. It must be enabled via -XX:+UnlockExperimentalVMOptions flag."); + } else { + warning("UseRTMLocking is only available as experimental option on this platform."); + } + } + } if (!FLAG_IS_CMDLINE(UseRTMLocking)) { // RTM locking should be used only for applications with // high lock contention. For now we do not use it by default. @@ -677,7 +690,20 @@ void VM_Version::get_processor_features() { } #endif } + +#ifdef _LP64 + if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { + UseMultiplyToLenIntrinsic = true; + } +#else + if (UseMultiplyToLenIntrinsic) { + if (!FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { + warning("multiplyToLen intrinsic is not available in 32-bit VM"); + } + FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, false); + } #endif +#endif // COMPILER2 // On new cpus instructions which update whole XMM register should be used // to prevent partial register stall due to dependencies on high half. @@ -805,6 +831,24 @@ void VM_Version::get_processor_features() { } } } + if ((cpu_family() == 0x06) && + ((extended_cpu_model() == 0x36) || // Centerton + (extended_cpu_model() == 0x37) || // Silvermont + (extended_cpu_model() == 0x4D))) { +#ifdef COMPILER2 + if (FLAG_IS_DEFAULT(OptoScheduling)) { + OptoScheduling = true; + } +#endif + if (supports_sse4_2()) { // Silvermont + if (FLAG_IS_DEFAULT(UseUnalignedLoadStores)) { + UseUnalignedLoadStores = true; // use movdqu on newest Intel cpus + } + } + } + if(FLAG_IS_DEFAULT(AllocatePrefetchInstr) && supports_3dnow_prefetch()) { + AllocatePrefetchInstr = 3; + } } // Use count leading zeros count instruction if available. @@ -817,25 +861,37 @@ void VM_Version::get_processor_features() { FLAG_SET_DEFAULT(UseCountLeadingZerosInstruction, false); } - if (supports_bmi1()) { - if (FLAG_IS_DEFAULT(UseBMI1Instructions)) { - UseBMI1Instructions = true; - } - } else if (UseBMI1Instructions) { - warning("BMI1 instructions are not available on this CPU"); - FLAG_SET_DEFAULT(UseBMI1Instructions, false); - } - // Use count trailing zeros instruction if available if (supports_bmi1()) { + // tzcnt does not require VEX prefix if (FLAG_IS_DEFAULT(UseCountTrailingZerosInstruction)) { - UseCountTrailingZerosInstruction = UseBMI1Instructions; + UseCountTrailingZerosInstruction = true; } } else if (UseCountTrailingZerosInstruction) { warning("tzcnt instruction is not available on this CPU"); FLAG_SET_DEFAULT(UseCountTrailingZerosInstruction, false); } + // BMI instructions use an encoding with VEX prefix. + // VEX prefix is generated only when AVX > 0. + if (supports_bmi1() && supports_avx()) { + if (FLAG_IS_DEFAULT(UseBMI1Instructions)) { + UseBMI1Instructions = true; + } + } else if (UseBMI1Instructions) { + warning("BMI1 instructions are not available on this CPU (AVX is also required)"); + FLAG_SET_DEFAULT(UseBMI1Instructions, false); + } + + if (supports_bmi2() && supports_avx()) { + if (FLAG_IS_DEFAULT(UseBMI2Instructions)) { + UseBMI2Instructions = true; + } + } else if (UseBMI2Instructions) { + warning("BMI2 instructions are not available on this CPU (AVX is also required)"); + FLAG_SET_DEFAULT(UseBMI2Instructions, false); + } + // Use population count instruction if available. if (supports_popcnt()) { if (FLAG_IS_DEFAULT(UsePopCountInstruction)) { @@ -892,23 +948,25 @@ void VM_Version::get_processor_features() { AllocatePrefetchDistance = allocate_prefetch_distance(); AllocatePrefetchStyle = allocate_prefetch_style(); - if( is_intel() && cpu_family() == 6 && supports_sse3() ) { - if( AllocatePrefetchStyle == 2 ) { // watermark prefetching on Core + if (is_intel() && cpu_family() == 6 && supports_sse3()) { + if (AllocatePrefetchStyle == 2) { // watermark prefetching on Core #ifdef _LP64 AllocatePrefetchDistance = 384; #else AllocatePrefetchDistance = 320; #endif } - if( supports_sse4_2() && supports_ht() ) { // Nehalem based cpus + if (supports_sse4_2() && supports_ht()) { // Nehalem based cpus AllocatePrefetchDistance = 192; AllocatePrefetchLines = 4; + } #ifdef COMPILER2 - if (AggressiveOpts && FLAG_IS_DEFAULT(UseFPUForSpilling)) { + if (supports_sse4_2()) { + if (FLAG_IS_DEFAULT(UseFPUForSpilling)) { FLAG_SET_DEFAULT(UseFPUForSpilling, true); } -#endif } +#endif } assert(AllocatePrefetchDistance % AllocatePrefetchStepSize == 0, "invalid value"); diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.hpp b/hotspot/src/cpu/x86/vm/vm_version_x86.hpp index 20c384c3722..17c34cd525a 100644 --- a/hotspot/src/cpu/x86/vm/vm_version_x86.hpp +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.hpp @@ -209,7 +209,9 @@ public: erms : 1, : 1, rtm : 1, - : 20; + : 7, + adx : 1, + : 12; } bits; }; @@ -260,7 +262,8 @@ protected: CPU_CLMUL = (1 << 21), // carryless multiply for CRC CPU_BMI1 = (1 << 22), CPU_BMI2 = (1 << 23), - CPU_RTM = (1 << 24) // Restricted Transactional Memory instructions + CPU_RTM = (1 << 24), // Restricted Transactional Memory instructions + CPU_ADX = (1 << 25) } cpuFeatureFlags; enum { @@ -276,7 +279,10 @@ protected: CPU_MODEL_WESTMERE_EX = 0x2f, CPU_MODEL_SANDYBRIDGE = 0x2a, CPU_MODEL_SANDYBRIDGE_EP = 0x2d, - CPU_MODEL_IVYBRIDGE_EP = 0x3a + CPU_MODEL_IVYBRIDGE_EP = 0x3a, + CPU_MODEL_HASWELL_E3 = 0x3c, + CPU_MODEL_HASWELL_E7 = 0x3f, + CPU_MODEL_BROADWELL = 0x3d } cpuExtendedFamily; // cpuid information block. All info derived from executing cpuid with @@ -462,10 +468,16 @@ protected: } // Intel features. if(is_intel()) { + if(_cpuid_info.sef_cpuid7_ebx.bits.adx != 0) + result |= CPU_ADX; if(_cpuid_info.sef_cpuid7_ebx.bits.bmi2 != 0) result |= CPU_BMI2; if(_cpuid_info.ext_cpuid1_ecx.bits.lzcnt_intel != 0) result |= CPU_LZCNT; + // for Intel, ecx.bits.misalignsse bit (bit 8) indicates support for prefetchw + if (_cpuid_info.ext_cpuid1_ecx.bits.misalignsse != 0) { + result |= CPU_3DNOW_PREFETCH; + } } return result; @@ -622,6 +634,7 @@ public: static bool supports_rtm() { return (_cpuFeatures & CPU_RTM) != 0; } static bool supports_bmi1() { return (_cpuFeatures & CPU_BMI1) != 0; } static bool supports_bmi2() { return (_cpuFeatures & CPU_BMI2) != 0; } + static bool supports_adx() { return (_cpuFeatures & CPU_ADX) != 0; } // Intel features static bool is_intel_family_core() { return is_intel() && extended_cpu_family() == CPU_FAMILY_INTEL_CORE; } diff --git a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp index fe9115be9ec..068cc4f9ffe 100644 --- a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp +++ b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2009, 2010, 2011 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -731,7 +731,7 @@ InterpreterFrame *InterpreterFrame::build(Method* const method, TRAPS) { if (method->is_static()) object = method->constants()->pool_holder()->java_mirror(); else - object = (oop) locals[0]; + object = (oop) (void*)locals[0]; monitor->set_obj(object); } @@ -831,60 +831,6 @@ address InterpreterGenerator::generate_normal_entry(bool synchronized) { return generate_entry((address) CppInterpreter::normal_entry); } -address AbstractInterpreterGenerator::generate_method_entry( - AbstractInterpreter::MethodKind kind) { - address entry_point = NULL; - - switch (kind) { - case Interpreter::zerolocals: - case Interpreter::zerolocals_synchronized: - break; - - case Interpreter::native: - entry_point = ((InterpreterGenerator*) this)->generate_native_entry(false); - break; - - case Interpreter::native_synchronized: - entry_point = ((InterpreterGenerator*) this)->generate_native_entry(false); - break; - - case Interpreter::empty: - entry_point = ((InterpreterGenerator*) this)->generate_empty_entry(); - break; - - case Interpreter::accessor: - entry_point = ((InterpreterGenerator*) this)->generate_accessor_entry(); - break; - - case Interpreter::abstract: - entry_point = ((InterpreterGenerator*) this)->generate_abstract_entry(); - break; - - case Interpreter::java_lang_math_sin: - case Interpreter::java_lang_math_cos: - case Interpreter::java_lang_math_tan: - case Interpreter::java_lang_math_abs: - case Interpreter::java_lang_math_log: - case Interpreter::java_lang_math_log10: - case Interpreter::java_lang_math_sqrt: - case Interpreter::java_lang_math_pow: - case Interpreter::java_lang_math_exp: - entry_point = ((InterpreterGenerator*) this)->generate_math_entry(kind); - break; - - case Interpreter::java_lang_ref_reference_get: - entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); - break; - - default: - ShouldNotReachHere(); - } - - if (entry_point == NULL) - entry_point = ((InterpreterGenerator*) this)->generate_normal_entry(false); - - return entry_point; -} InterpreterGenerator::InterpreterGenerator(StubQueue* code) : CppInterpreterGenerator(code) { diff --git a/hotspot/src/cpu/zero/vm/frame_zero.inline.hpp b/hotspot/src/cpu/zero/vm/frame_zero.inline.hpp index 64e7f5fdf1f..0ea5bbc4f77 100644 --- a/hotspot/src/cpu/zero/vm/frame_zero.inline.hpp +++ b/hotspot/src/cpu/zero/vm/frame_zero.inline.hpp @@ -26,6 +26,8 @@ #ifndef CPU_ZERO_VM_FRAME_ZERO_INLINE_HPP #define CPU_ZERO_VM_FRAME_ZERO_INLINE_HPP +#include "code/codeCache.hpp" + // Constructors inline frame::frame() { diff --git a/hotspot/src/cpu/zero/vm/globals_zero.hpp b/hotspot/src/cpu/zero/vm/globals_zero.hpp index 0f88bba8537..7698a7a2480 100644 --- a/hotspot/src/cpu/zero/vm/globals_zero.hpp +++ b/hotspot/src/cpu/zero/vm/globals_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2009, 2010, 2011 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -61,6 +61,12 @@ define_pd_global(uintx, CMSYoungGenPerWorker, 16*M); // default max size of CMS define_pd_global(uintx, TypeProfileLevel, 0); -#define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct) +#define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct) \ + product(bool, UseFastEmptyMethods, true, \ + "Use fast method entry code for empty methods") \ + \ + product(bool, UseFastAccessorMethods, true, \ + "Use fast method entry code for accessor methods") \ + \ #endif // CPU_ZERO_VM_GLOBALS_ZERO_HPP diff --git a/hotspot/src/cpu/zero/vm/interpreterGenerator_zero.hpp b/hotspot/src/cpu/zero/vm/interpreterGenerator_zero.hpp index eb170370630..68516b4daf9 100644 --- a/hotspot/src/cpu/zero/vm/interpreterGenerator_zero.hpp +++ b/hotspot/src/cpu/zero/vm/interpreterGenerator_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright 2007 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -39,4 +39,7 @@ address generate_accessor_entry(); address generate_Reference_get_entry(); + // Not supported + address generate_CRC32_update_entry() { return NULL; } + address generate_CRC32_updateBytes_entry(AbstractInterpreter::MethodKind kind) { return NULL; } #endif // CPU_ZERO_VM_INTERPRETERGENERATOR_ZERO_HPP diff --git a/hotspot/src/os/aix/vm/os_aix.cpp b/hotspot/src/os/aix/vm/os_aix.cpp index 70ca91a62ce..6d8c55bf541 100644 --- a/hotspot/src/os/aix/vm/os_aix.cpp +++ b/hotspot/src/os/aix/vm/os_aix.cpp @@ -58,6 +58,7 @@ #include "runtime/mutexLocker.hpp" #include "runtime/objectMonitor.hpp" #include "runtime/orderAccess.inline.hpp" +#include "runtime/os.hpp" #include "runtime/osThread.hpp" #include "runtime/perfMemory.hpp" #include "runtime/sharedRuntime.hpp" @@ -378,10 +379,10 @@ void os::Aix::query_multipage_support() { // default should be 4K. size_t data_page_size = SIZE_4K; { - void* p = ::malloc(SIZE_16M); + void* p = os::malloc(SIZE_16M, mtInternal); guarantee(p != NULL, "malloc failed"); data_page_size = os::Aix::query_pagesize(p); - ::free(p); + os::free(p); } // query default shm page size (LDR_CNTRL SHMPSIZE) diff --git a/hotspot/src/os/aix/vm/porting_aix.cpp b/hotspot/src/os/aix/vm/porting_aix.cpp index 5e6a78ddcc9..038693cfdf1 100644 --- a/hotspot/src/os/aix/vm/porting_aix.cpp +++ b/hotspot/src/os/aix/vm/porting_aix.cpp @@ -24,6 +24,8 @@ #include "asm/assembler.hpp" #include "memory/allocation.hpp" +#include "memory/allocation.inline.hpp" +#include "runtime/os.hpp" #include "loadlib_aix.hpp" #include "porting_aix.hpp" #include "utilities/debug.hpp" @@ -83,7 +85,7 @@ class fixed_strings { while (n) { node* p = n; n = n->next; - free(p->v); + os::free(p->v); delete p; } } @@ -95,7 +97,7 @@ class fixed_strings { } } node* p = new node; - p->v = strdup(s); + p->v = os::strdup_check_oom(s); p->next = first; first = p; return p->v; diff --git a/hotspot/src/os/bsd/vm/os_bsd.cpp b/hotspot/src/os/bsd/vm/os_bsd.cpp index b0756b8bc2b..4f54ad94a6a 100644 --- a/hotspot/src/os/bsd/vm/os_bsd.cpp +++ b/hotspot/src/os/bsd/vm/os_bsd.cpp @@ -1678,14 +1678,9 @@ void os::print_dll_info(outputStream *st) { dlclose(handle); #elif defined(__APPLE__) - uint32_t count; - uint32_t i; - - count = _dyld_image_count(); - for (i = 1; i < count; i++) { - const char *name = _dyld_get_image_name(i); - intptr_t slide = _dyld_get_image_vmaddr_slide(i); - st->print_cr(PTR_FORMAT " \t%s", slide, name); + for (uint32_t i = 1; i < _dyld_image_count(); i++) { + st->print_cr(PTR_FORMAT " \t%s", _dyld_get_image_header(i), + _dyld_get_image_name(i)); } #else st->print_cr("Error: Cannot print dynamic libraries."); @@ -2439,23 +2434,25 @@ char* os::reserve_memory_special(size_t bytes, size_t alignment, char* req_addr, } // The memory is committed - MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, mtNone, CALLER_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, CALLER_PC); return addr; } bool os::release_memory_special(char* base, size_t bytes) { - MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); - // detaching the SHM segment will also delete it, see reserve_memory_special() - int rslt = shmdt(base); - if (rslt == 0) { - tkr.record((address)base, bytes); - return true; + if (MemTracker::tracking_level() > NMT_minimal) { + Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + // detaching the SHM segment will also delete it, see reserve_memory_special() + int rslt = shmdt(base); + if (rslt == 0) { + tkr.record((address)base, bytes); + return true; + } else { + return false; + } } else { - tkr.discard(); - return false; + return shmdt(base) == 0; } - } size_t os::large_page_size() { diff --git a/hotspot/src/os/bsd/vm/perfMemory_bsd.cpp b/hotspot/src/os/bsd/vm/perfMemory_bsd.cpp index e812a76f51b..2737fe38ce1 100644 --- a/hotspot/src/os/bsd/vm/perfMemory_bsd.cpp +++ b/hotspot/src/os/bsd/vm/perfMemory_bsd.cpp @@ -753,7 +753,7 @@ static char* mmap_create_shared(size_t size) { (void)::memset((void*) mapAddress, 0, size); // it does not go through os api, the operation has to record from here - MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, size, CURRENT_PC, mtInternal); return mapAddress; } @@ -918,7 +918,7 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor } // it does not go through os api, the operation has to record from here - MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, size, CURRENT_PC, mtInternal); *addr = mapAddress; *sizep = size; diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp index ed12d800c5b..586f46a26b3 100644 --- a/hotspot/src/os/linux/vm/os_linux.cpp +++ b/hotspot/src/os/linux/vm/os_linux.cpp @@ -2246,7 +2246,7 @@ void os::print_siginfo(outputStream* st, void* siginfo) { const siginfo_t* si = (const siginfo_t*)siginfo; os::Posix::print_siginfo_brief(st, si); - +#if INCLUDE_CDS if (si && (si->si_signo == SIGBUS || si->si_signo == SIGSEGV) && UseSharedSpaces) { FileMapInfo* mapinfo = FileMapInfo::current_info(); @@ -2256,6 +2256,7 @@ void os::print_siginfo(outputStream* st, void* siginfo) { " possible disk/network problem."); } } +#endif st->cr(); } @@ -3504,9 +3505,12 @@ char* os::Linux::reserve_memory_special_huge_tlbfs_mixed(size_t bytes, size_t al assert(is_ptr_aligned(start, alignment), "Must be"); - // os::reserve_memory_special will record this memory area. - // Need to release it here to prevent overlapping reservations. - MemTracker::record_virtual_memory_release((address)start, bytes); + if (MemTracker::tracking_level() > NMT_minimal) { + // os::reserve_memory_special will record this memory area. + // Need to release it here to prevent overlapping reservations. + Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + tkr.record((address)start, bytes); + } char* end = start + bytes; @@ -3601,7 +3605,7 @@ char* os::reserve_memory_special(size_t bytes, size_t alignment, char* req_addr, } // The memory is committed - MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, mtNone, CALLER_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)addr, bytes, CALLER_PC); } return addr; @@ -3617,24 +3621,30 @@ bool os::Linux::release_memory_special_huge_tlbfs(char* base, size_t bytes) { } bool os::release_memory_special(char* base, size_t bytes) { - assert(UseLargePages, "only for large pages"); - - MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); - bool res; + if (MemTracker::tracking_level() > NMT_minimal) { + Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + res = os::Linux::release_memory_special_impl(base, bytes); + if (res) { + tkr.record((address)base, bytes); + } + + } else { + res = os::Linux::release_memory_special_impl(base, bytes); + } + return res; +} + +bool os::Linux::release_memory_special_impl(char* base, size_t bytes) { + assert(UseLargePages, "only for large pages"); + bool res; + if (UseSHM) { res = os::Linux::release_memory_special_shm(base, bytes); } else { assert(UseHugeTLBFS, "must be"); res = os::Linux::release_memory_special_huge_tlbfs(base, bytes); } - - if (res) { - tkr.record((address)base, bytes); - } else { - tkr.discard(); - } - return res; } diff --git a/hotspot/src/os/linux/vm/os_linux.hpp b/hotspot/src/os/linux/vm/os_linux.hpp index 1b62f3f35ea..cf9b71b4033 100644 --- a/hotspot/src/os/linux/vm/os_linux.hpp +++ b/hotspot/src/os/linux/vm/os_linux.hpp @@ -108,6 +108,7 @@ class Linux { static char* reserve_memory_special_huge_tlbfs_only(size_t bytes, char* req_addr, bool exec); static char* reserve_memory_special_huge_tlbfs_mixed(size_t bytes, size_t alignment, char* req_addr, bool exec); + static bool release_memory_special_impl(char* base, size_t bytes); static bool release_memory_special_shm(char* base, size_t bytes); static bool release_memory_special_huge_tlbfs(char* base, size_t bytes); diff --git a/hotspot/src/os/linux/vm/perfMemory_linux.cpp b/hotspot/src/os/linux/vm/perfMemory_linux.cpp index 9708734295f..2eef5da69f9 100644 --- a/hotspot/src/os/linux/vm/perfMemory_linux.cpp +++ b/hotspot/src/os/linux/vm/perfMemory_linux.cpp @@ -753,7 +753,7 @@ static char* mmap_create_shared(size_t size) { (void)::memset((void*) mapAddress, 0, size); // it does not go through os api, the operation has to record from here - MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, size, CURRENT_PC, mtInternal); return mapAddress; } @@ -924,7 +924,7 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor } // it does not go through os api, the operation has to record from here - MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, size, CURRENT_PC, mtInternal); *addr = mapAddress; *sizep = size; diff --git a/hotspot/src/os/posix/vm/os_posix.cpp b/hotspot/src/os/posix/vm/os_posix.cpp index d17bc15cbae..66ad5d8a904 100644 --- a/hotspot/src/os/posix/vm/os_posix.cpp +++ b/hotspot/src/os/posix/vm/os_posix.cpp @@ -75,21 +75,41 @@ void os::check_or_create_dump(void* exceptionRecord, void* contextRecord, char* VMError::report_coredump_status(buffer, success); } -address os::get_caller_pc(int n) { +int os::get_native_stack(address* stack, int frames, int toSkip) { #ifdef _NMT_NOINLINE_ - n ++; + toSkip++; #endif + + int frame_idx = 0; + int num_of_frames; // number of frames captured frame fr = os::current_frame(); - while (n > 0 && fr.pc() && - !os::is_first_C_frame(&fr) && fr.sender_pc()) { - fr = os::get_sender_for_C_frame(&fr); - n --; + while (fr.pc() && frame_idx < frames) { + if (toSkip > 0) { + toSkip --; + } else { + stack[frame_idx ++] = fr.pc(); + } + if (fr.fp() == NULL || os::is_first_C_frame(&fr) + ||fr.sender_pc() == NULL || fr.cb() != NULL) break; + + if (fr.sender_pc() && !os::is_first_C_frame(&fr)) { + fr = os::get_sender_for_C_frame(&fr); + } else { + break; + } } - if (n == 0) { - return fr.pc(); - } else { - return NULL; + num_of_frames = frame_idx; + for (; frame_idx < frames; frame_idx ++) { + stack[frame_idx] = NULL; } + + return num_of_frames; +} + + +bool os::unsetenv(const char* name) { + assert(name != NULL, "Null pointer"); + return (::unsetenv(name) == 0); } int os::get_last_error() { diff --git a/hotspot/src/os/solaris/vm/attachListener_solaris.cpp b/hotspot/src/os/solaris/vm/attachListener_solaris.cpp index bee98a1a7fe..1426356dade 100644 --- a/hotspot/src/os/solaris/vm/attachListener_solaris.cpp +++ b/hotspot/src/os/solaris/vm/attachListener_solaris.cpp @@ -199,23 +199,29 @@ class ArgumentIterator : public StackObj { // Calls from the door function to check that the client credentials // match this process. Returns 0 if credentials okay, otherwise -1. static int check_credentials() { - door_cred_t cred_info; + ucred_t *cred_info = NULL; + int ret = -1; // deny by default // get client credentials - if (door_cred(&cred_info) == -1) { - return -1; // unable to get them + if (door_ucred(&cred_info) == -1) { + return -1; // unable to get them, deny } // get our euid/eguid (probably could cache these) uid_t euid = geteuid(); gid_t egid = getegid(); - // check that the effective uid/gid matches - discuss this with Jeff. - if (cred_info.dc_euid == euid && cred_info.dc_egid == egid) { - return 0; // okay - } else { - return -1; // denied + // get euid/egid from ucred_free + uid_t ucred_euid = ucred_geteuid(cred_info); + gid_t ucred_egid = ucred_getegid(cred_info); + + // check that the effective uid/gid matches + if (ucred_euid == euid && ucred_egid == egid) { + ret = 0; // allow } + + ucred_free(cred_info); + return ret; } diff --git a/hotspot/src/os/solaris/vm/perfMemory_solaris.cpp b/hotspot/src/os/solaris/vm/perfMemory_solaris.cpp index 1fa30012d6b..2d279af0c2b 100644 --- a/hotspot/src/os/solaris/vm/perfMemory_solaris.cpp +++ b/hotspot/src/os/solaris/vm/perfMemory_solaris.cpp @@ -770,7 +770,8 @@ static char* mmap_create_shared(size_t size) { (void)::memset((void*) mapAddress, 0, size); // it does not go through os api, the operation has to record from here - MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, + size, CURRENT_PC, mtInternal); return mapAddress; } @@ -941,7 +942,8 @@ static void mmap_attach_shared(const char* user, int vmid, PerfMemory::PerfMemor } // it does not go through os api, the operation has to record from here - MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, + size, CURRENT_PC, mtInternal); *addr = mapAddress; *sizep = size; diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp index d312b975abc..ae9cbe3b397 100644 --- a/hotspot/src/os/windows/vm/os_windows.cpp +++ b/hotspot/src/os/windows/vm/os_windows.cpp @@ -135,12 +135,6 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID reserved) { if (ForceTimeHighResolution) timeEndPeriod(1L); - // Workaround for issue when a custom launcher doesn't call - // DestroyJavaVM and NMT is trying to track memory when free is - // called from a static destructor - if (MemTracker::is_on()) { - MemTracker::shutdown(MemTracker::NMT_normal); - } break; default: break; @@ -163,6 +157,10 @@ bool os::getenv(const char* name, char* buffer, int len) { return result > 0 && result < len; } +bool os::unsetenv(const char* name) { + assert(name != NULL, "Null pointer"); + return (SetEnvironmentVariable(name, NULL) == TRUE); +} // No setuid programs under Windows. bool os::have_special_privileges() { @@ -319,15 +317,16 @@ extern "C" void breakpoint() { * So far, this method is only used by Native Memory Tracking, which is * only supported on Windows XP or later. */ -address os::get_caller_pc(int n) { +int os::get_native_stack(address* stack, int frames, int toSkip) { #ifdef _NMT_NOINLINE_ - n++; + toSkip ++; #endif - address pc; - if (os::Kernel32Dll::RtlCaptureStackBackTrace(n + 1, 1, (PVOID*)&pc, NULL) == 1) { - return pc; + int captured = Kernel32Dll::RtlCaptureStackBackTrace(toSkip + 1, frames, + (PVOID*)stack, NULL); + for (int index = captured; index < frames; index ++) { + stack[index] = NULL; } - return NULL; + return captured; } @@ -410,6 +409,8 @@ struct tm* os::localtime_pd(const time_t* clock, struct tm* res) { LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo); +extern jint volatile vm_getting_terminated; + // Thread start routine for all new Java threads static unsigned __stdcall java_start(Thread* thread) { // Try to randomize the cache line index of hot stack frames. @@ -431,9 +432,17 @@ static unsigned __stdcall java_start(Thread* thread) { } } + // Diagnostic code to investigate JDK-6573254 (Part I) + unsigned res = 90115; // non-java thread + if (thread->is_Java_thread()) { + JavaThread* java_thread = (JavaThread*)thread; + res = java_lang_Thread::is_daemon(java_thread->threadObj()) + ? 70115 // java daemon thread + : 80115; // java non-daemon thread + } // Install a win32 structured exception handler around every thread created - // by VM, so VM can genrate error dump when an exception occurred in non- + // by VM, so VM can generate error dump when an exception occurred in non- // Java thread (e.g. VM thread). __try { thread->run(); @@ -449,6 +458,11 @@ static unsigned __stdcall java_start(Thread* thread) { Atomic::dec_ptr((intptr_t*)&os::win32::_os_thread_count); } + // Diagnostic code to investigate JDK-6573254 (Part II) + if (OrderAccess::load_acquire(&vm_getting_terminated)) { + return res; + } + return 0; } @@ -2901,7 +2915,7 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, PAGE_READWRITE); // If reservation failed, return NULL if (p_buf == NULL) return NULL; - MemTracker::record_virtual_memory_reserve((address)p_buf, size_of_reserve, mtNone, CALLER_PC); + MemTracker::record_virtual_memory_reserve((address)p_buf, size_of_reserve, CALLER_PC); os::release_memory(p_buf, bytes + chunk_size); // we still need to round up to a page boundary (in case we are using large pages) @@ -2967,7 +2981,7 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, // need to create a dummy 'reserve' record to match // the release. MemTracker::record_virtual_memory_reserve((address)p_buf, - bytes_to_release, mtNone, CALLER_PC); + bytes_to_release, CALLER_PC); os::release_memory(p_buf, bytes_to_release); } #ifdef ASSERT @@ -2986,11 +3000,10 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, } // Although the memory is allocated individually, it is returned as one. // NMT records it as one block. - address pc = CALLER_PC; if ((flags & MEM_COMMIT) != 0) { - MemTracker::record_virtual_memory_reserve_and_commit((address)p_buf, bytes, mtNone, pc); + MemTracker::record_virtual_memory_reserve_and_commit((address)p_buf, bytes, CALLER_PC); } else { - MemTracker::record_virtual_memory_reserve((address)p_buf, bytes, mtNone, pc); + MemTracker::record_virtual_memory_reserve((address)p_buf, bytes, CALLER_PC); } // made it this far, success @@ -3188,8 +3201,7 @@ char* os::reserve_memory_special(size_t bytes, size_t alignment, char* addr, boo DWORD flag = MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES; char * res = (char *)VirtualAlloc(addr, bytes, flag, prot); if (res != NULL) { - address pc = CALLER_PC; - MemTracker::record_virtual_memory_reserve_and_commit((address)res, bytes, mtNone, pc); + MemTracker::record_virtual_memory_reserve_and_commit((address)res, bytes, CALLER_PC); } return res; diff --git a/hotspot/src/os/windows/vm/perfMemory_windows.cpp b/hotspot/src/os/windows/vm/perfMemory_windows.cpp index 1bf27296520..e1b59253e82 100644 --- a/hotspot/src/os/windows/vm/perfMemory_windows.cpp +++ b/hotspot/src/os/windows/vm/perfMemory_windows.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ #include "oops/oop.inline.hpp" #include "os_windows.inline.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/os.hpp" #include "runtime/perfMemory.hpp" #include "services/memTracker.hpp" #include "utilities/exceptions.hpp" @@ -1388,7 +1389,7 @@ static HANDLE create_sharedmem_resources(const char* dirname, const char* filena // the file has been successfully created and the file mapping // object has been created. sharedmem_fileHandle = fh; - sharedmem_fileName = strdup(filename); + sharedmem_fileName = os::strdup(filename); return fmh; } @@ -1498,7 +1499,8 @@ static char* mapping_create_shared(size_t size) { (void)memset(mapAddress, '\0', size); // it does not go through os api, the operation has to record from here - MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, + size, CURRENT_PC, mtInternal); return (char*) mapAddress; } @@ -1680,7 +1682,8 @@ static void open_file_mapping(const char* user, int vmid, } // it does not go through os api, the operation has to record from here - MemTracker::record_virtual_memory_reserve((address)mapAddress, size, mtInternal, CURRENT_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)mapAddress, size, + CURRENT_PC, mtInternal); *addrp = (char*)mapAddress; @@ -1834,10 +1837,14 @@ void PerfMemory::detach(char* addr, size_t bytes, TRAPS) { return; } - MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); - remove_file_mapping(addr); - // it does not go through os api, the operation has to record from here - tkr.record((address)addr, bytes); + 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(); + remove_file_mapping(addr); + tkr.record((address)addr, bytes); + } else { + remove_file_mapping(addr); + } } char* PerfMemory::backing_store_filename() { diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp b/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp index 028e33f37d4..f30ad48669a 100644 --- a/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp +++ b/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp @@ -23,6 +23,8 @@ */ #include "precompiled.hpp" +#include "memory/allocation.hpp" +#include "memory/allocation.inline.hpp" #include "runtime/os.hpp" #include "vm_version_sparc.hpp" @@ -48,7 +50,7 @@ static void do_sysinfo(int si, const char* string, int* features, int mask) { // All SI defines used below must be supported. guarantee(bufsize != -1, "must be supported"); - char* buf = (char*) malloc(bufsize); + char* buf = (char*) os::malloc(bufsize, mtInternal); if (buf == NULL) return; @@ -60,133 +62,96 @@ static void do_sysinfo(int si, const char* string, int* features, int mask) { } } - free(buf); + os::free(buf); } int VM_Version::platform_features(int features) { - // getisax(2), SI_ARCHITECTURE_32, and SI_ARCHITECTURE_64 are - // supported on Solaris 10 and later. - if (os::Solaris::supports_getisax()) { + assert(os::Solaris::supports_getisax(), "getisax() must be available"); - // Check 32-bit architecture. - do_sysinfo(SI_ARCHITECTURE_32, "sparc", &features, v8_instructions_m); + // Check 32-bit architecture. + do_sysinfo(SI_ARCHITECTURE_32, "sparc", &features, v8_instructions_m); - // Check 64-bit architecture. - do_sysinfo(SI_ARCHITECTURE_64, "sparcv9", &features, generic_v9_m); + // Check 64-bit architecture. + do_sysinfo(SI_ARCHITECTURE_64, "sparcv9", &features, generic_v9_m); - // Extract valid instruction set extensions. - uint_t avs[2]; - uint_t avn = os::Solaris::getisax(avs, 2); - assert(avn <= 2, "should return two or less av's"); - uint_t av = avs[0]; + // Extract valid instruction set extensions. + uint_t avs[2]; + uint_t avn = os::Solaris::getisax(avs, 2); + assert(avn <= 2, "should return two or less av's"); + uint_t av = avs[0]; #ifndef PRODUCT - if (PrintMiscellaneous && Verbose) { - tty->print("getisax(2) returned: " PTR32_FORMAT, av); - if (avn > 1) { - tty->print(", " PTR32_FORMAT, avs[1]); - } - tty->cr(); + if (PrintMiscellaneous && Verbose) { + tty->print("getisax(2) returned: " PTR32_FORMAT, av); + if (avn > 1) { + tty->print(", " PTR32_FORMAT, avs[1]); } + tty->cr(); + } #endif - if (av & AV_SPARC_MUL32) features |= hardware_mul32_m; - if (av & AV_SPARC_DIV32) features |= hardware_div32_m; - if (av & AV_SPARC_FSMULD) features |= hardware_fsmuld_m; - if (av & AV_SPARC_V8PLUS) features |= v9_instructions_m; - if (av & AV_SPARC_POPC) features |= hardware_popc_m; - if (av & AV_SPARC_VIS) features |= vis1_instructions_m; - if (av & AV_SPARC_VIS2) features |= vis2_instructions_m; - if (avn > 1) { - uint_t av2 = avs[1]; + if (av & AV_SPARC_MUL32) features |= hardware_mul32_m; + if (av & AV_SPARC_DIV32) features |= hardware_div32_m; + if (av & AV_SPARC_FSMULD) features |= hardware_fsmuld_m; + if (av & AV_SPARC_V8PLUS) features |= v9_instructions_m; + if (av & AV_SPARC_POPC) features |= hardware_popc_m; + if (av & AV_SPARC_VIS) features |= vis1_instructions_m; + if (av & AV_SPARC_VIS2) features |= vis2_instructions_m; + if (avn > 1) { + uint_t av2 = avs[1]; #ifndef AV2_SPARC_SPARC5 #define AV2_SPARC_SPARC5 0x00000008 /* The 29 new fp and sub instructions */ #endif - if (av2 & AV2_SPARC_SPARC5) features |= sparc5_instructions_m; - } + if (av2 & AV2_SPARC_SPARC5) features |= sparc5_instructions_m; + } - // Next values are not defined before Solaris 10 - // but Solaris 8 is used for jdk6 update builds. + // We only build on Solaris 10 and up, but some of the values below + // are not defined on all versions of Solaris 10, so we define them, + // if necessary. #ifndef AV_SPARC_ASI_BLK_INIT #define AV_SPARC_ASI_BLK_INIT 0x0080 /* ASI_BLK_INIT_xxx ASI */ #endif - if (av & AV_SPARC_ASI_BLK_INIT) features |= blk_init_instructions_m; + if (av & AV_SPARC_ASI_BLK_INIT) features |= blk_init_instructions_m; #ifndef AV_SPARC_FMAF #define AV_SPARC_FMAF 0x0100 /* Fused Multiply-Add */ #endif - if (av & AV_SPARC_FMAF) features |= fmaf_instructions_m; + if (av & AV_SPARC_FMAF) features |= fmaf_instructions_m; #ifndef AV_SPARC_FMAU -#define AV_SPARC_FMAU 0x0200 /* Unfused Multiply-Add */ +#define AV_SPARC_FMAU 0x0200 /* Unfused Multiply-Add */ #endif - if (av & AV_SPARC_FMAU) features |= fmau_instructions_m; + if (av & AV_SPARC_FMAU) features |= fmau_instructions_m; #ifndef AV_SPARC_VIS3 -#define AV_SPARC_VIS3 0x0400 /* VIS3 instruction set extensions */ +#define AV_SPARC_VIS3 0x0400 /* VIS3 instruction set extensions */ #endif - if (av & AV_SPARC_VIS3) features |= vis3_instructions_m; + if (av & AV_SPARC_VIS3) features |= vis3_instructions_m; #ifndef AV_SPARC_CBCOND #define AV_SPARC_CBCOND 0x10000000 /* compare and branch instrs supported */ #endif - if (av & AV_SPARC_CBCOND) features |= cbcond_instructions_m; + if (av & AV_SPARC_CBCOND) features |= cbcond_instructions_m; #ifndef AV_SPARC_AES #define AV_SPARC_AES 0x00020000 /* aes instrs supported */ #endif - if (av & AV_SPARC_AES) features |= aes_instructions_m; + if (av & AV_SPARC_AES) features |= aes_instructions_m; #ifndef AV_SPARC_SHA1 #define AV_SPARC_SHA1 0x00400000 /* sha1 instruction supported */ #endif - if (av & AV_SPARC_SHA1) features |= sha1_instruction_m; + if (av & AV_SPARC_SHA1) features |= sha1_instruction_m; #ifndef AV_SPARC_SHA256 #define AV_SPARC_SHA256 0x00800000 /* sha256 instruction supported */ #endif - if (av & AV_SPARC_SHA256) features |= sha256_instruction_m; + if (av & AV_SPARC_SHA256) features |= sha256_instruction_m; #ifndef AV_SPARC_SHA512 #define AV_SPARC_SHA512 0x01000000 /* sha512 instruction supported */ #endif - if (av & AV_SPARC_SHA512) features |= sha512_instruction_m; - - } else { - // getisax(2) failed, use the old legacy code. -#ifndef PRODUCT - if (PrintMiscellaneous && Verbose) - tty->print_cr("getisax(2) is not supported."); -#endif - - char tmp; - size_t bufsize = sysinfo(SI_ISALIST, &tmp, 1); - char* buf = (char*) malloc(bufsize); - - if (buf != NULL) { - if (sysinfo(SI_ISALIST, buf, bufsize) == bufsize) { - // Figure out what kind of sparc we have - char *sparc_string = strstr(buf, "sparc"); - if (sparc_string != NULL) { features |= v8_instructions_m; - if (sparc_string[5] == 'v') { - if (sparc_string[6] == '8') { - if (sparc_string[7] == '-') { features |= hardware_mul32_m; - features |= hardware_div32_m; - } else if (sparc_string[7] == 'p') features |= generic_v9_m; - else features |= generic_v8_m; - } else if (sparc_string[6] == '9') features |= generic_v9_m; - } - } - - // Check for visualization instructions - char *vis = strstr(buf, "vis"); - if (vis != NULL) { features |= vis1_instructions_m; - if (vis[3] == '2') features |= vis2_instructions_m; - } - } - free(buf); - } - } + if (av & AV_SPARC_SHA512) features |= sha512_instruction_m; // Determine the machine type. do_sysinfo(SI_MACHINE, "sun4v", &features, sun4v_m); @@ -201,37 +166,18 @@ int VM_Version::platform_features(int features) { kstat_named_t* knm = (kstat_named_t *)ksp->ks_data; for (int i = 0; i < ksp->ks_ndata; i++) { if (strcmp((const char*)&(knm[i].name),"implementation") == 0) { -#ifndef KSTAT_DATA_STRING -#define KSTAT_DATA_STRING 9 -#endif - if (knm[i].data_type == KSTAT_DATA_CHAR) { - // VM is running on Solaris 8 which does not have value.str. - implementation = &(knm[i].value.c[0]); - } else if (knm[i].data_type == KSTAT_DATA_STRING) { - // VM is running on Solaris 10. -#ifndef KSTAT_NAMED_STR_PTR - // Solaris 8 was used to build VM, define the structure it misses. - struct str_t { - union { - char *ptr; /* NULL-term string */ - char __pad[8]; /* 64-bit padding */ - } addr; - uint32_t len; /* # bytes for strlen + '\0' */ - }; -#define KSTAT_NAMED_STR_PTR(knptr) (( (str_t*)&((knptr)->value) )->addr.ptr) -#endif - implementation = KSTAT_NAMED_STR_PTR(&knm[i]); - } + implementation = KSTAT_NAMED_STR_PTR(&knm[i]); #ifndef PRODUCT if (PrintMiscellaneous && Verbose) { tty->print_cr("cpu_info.implementation: %s", implementation); } #endif // Convert to UPPER case before compare. - char* impl = strdup(implementation); + char* impl = os::strdup_check_oom(implementation); for (int i = 0; impl[i] != 0; i++) impl[i] = (char)toupper((uint)impl[i]); + if (strstr(impl, "SPARC64") != NULL) { features |= sparc64_family_m; } else if (strstr(impl, "SPARC-M") != NULL) { @@ -246,13 +192,15 @@ int VM_Version::platform_features(int features) { if (strstr(impl, "SPARC") == NULL) { #ifndef PRODUCT // kstat on Solaris 8 virtual machines (branded zones) - // returns "(unsupported)" implementation. - warning("kstat cpu_info implementation = '%s', should contain SPARC", impl); + // returns "(unsupported)" implementation. Solaris 8 is not + // supported anymore, but include this check to be on the + // safe side. + warning("kstat cpu_info implementation = '%s', assume generic SPARC", impl); #endif implementation = "SPARC"; } } - free((void*)impl); + os::free((void*)impl); break; } } // for( diff --git a/hotspot/src/share/tools/ProjectCreator/BuildConfig.java b/hotspot/src/share/tools/ProjectCreator/BuildConfig.java index 7c95db62f0c..f743cf27e9e 100644 --- a/hotspot/src/share/tools/ProjectCreator/BuildConfig.java +++ b/hotspot/src/share/tools/ProjectCreator/BuildConfig.java @@ -504,7 +504,7 @@ abstract class GenericDebugConfig extends BuildConfig { super.init(includes, defines); - getV("CompilerFlags").addAll(getCI().getDebugCompilerFlags(getOptFlag())); + getV("CompilerFlags").addAll(getCI().getDebugCompilerFlags(getOptFlag(), get("PlatformName"))); getV("LinkerFlags").addAll(getCI().getDebugLinkerFlags()); } } @@ -619,7 +619,7 @@ class TieredProductConfig extends ProductConfig { abstract class CompilerInterface { abstract Vector getBaseCompilerFlags(Vector defines, Vector includes, String outDir); abstract Vector getBaseLinkerFlags(String outDir, String outDll, String platformName); - abstract Vector getDebugCompilerFlags(String opt); + abstract Vector getDebugCompilerFlags(String opt, String platformName); abstract Vector getDebugLinkerFlags(); abstract void getAdditionalNonKernelLinkerFlags(Vector rv); abstract Vector getProductCompilerFlags(); diff --git a/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC10.java b/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC10.java index 0e0c846c318..08768b623f5 100644 --- a/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC10.java +++ b/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC10.java @@ -357,7 +357,7 @@ class CompilerInterfaceVC10 extends CompilerInterface { } @Override - Vector getDebugCompilerFlags(String opt) { + Vector getDebugCompilerFlags(String opt, String platformName) { Vector rv = new Vector(); // Set /On option @@ -369,6 +369,10 @@ class CompilerInterfaceVC10 extends CompilerInterface { addAttr(rv, "RuntimeLibrary", "MultiThreadedDLL"); // Set /Oy- option addAttr(rv, "OmitFramePointers", "false"); + // Set /homeparams for x64 debug builds + if(platformName.equals("x64")) { + addAttr(rv, "AdditionalOptions", "/homeparams"); + } return rv; } diff --git a/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC7.java b/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC7.java index 9afeb42d422..f9f44a467a2 100644 --- a/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC7.java +++ b/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC7.java @@ -284,7 +284,7 @@ class CompilerInterfaceVC7 extends CompilerInterface { } - Vector getDebugCompilerFlags(String opt) { + Vector getDebugCompilerFlags(String opt, String platformName) { Vector rv = new Vector(); getDebugCompilerFlags_common(opt, rv); diff --git a/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC8.java b/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC8.java index 3980a141d3e..5aee9ac66f0 100644 --- a/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC8.java +++ b/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC8.java @@ -48,7 +48,7 @@ class CompilerInterfaceVC8 extends CompilerInterfaceVC7 { } - Vector getDebugCompilerFlags(String opt) { + Vector getDebugCompilerFlags(String opt, String platformName) { Vector rv = new Vector(); getDebugCompilerFlags_common(opt,rv); diff --git a/hotspot/src/share/vm/adlc/output_c.cpp b/hotspot/src/share/vm/adlc/output_c.cpp index 6169c8f62b7..68e92380cf2 100644 --- a/hotspot/src/share/vm/adlc/output_c.cpp +++ b/hotspot/src/share/vm/adlc/output_c.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2014, 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 @@ -997,7 +997,7 @@ void ArchDesc::build_pipe_classes(FILE *fp_cpp) { int nopcnt = 0; for ( _pipeline->_noplist.reset(); (nop = _pipeline->_noplist.iter()) != NULL; nopcnt++ ); - fprintf(fp_cpp, "void Bundle::initialize_nops(MachNode * nop_list[%d], Compile *C) {\n", nopcnt); + fprintf(fp_cpp, "void Bundle::initialize_nops(MachNode * nop_list[%d]) {\n", nopcnt); int i = 0; for ( _pipeline->_noplist.reset(); (nop = _pipeline->_noplist.iter()) != NULL; i++ ) { fprintf(fp_cpp, " nop_list[%d] = (MachNode *) new %sNode();\n", i, nop); @@ -1369,7 +1369,7 @@ static void generate_peepreplace( FILE *fp, FormDict &globals, PeepMatch *pmatch fprintf(fp, " ra_->add_reference(root, inst%d);\n", inst_num); fprintf(fp, " ra_->set_oop (root, ra_->is_oop(inst%d));\n", inst_num); fprintf(fp, " ra_->set_pair(root->_idx, ra_->get_reg_second(inst%d), ra_->get_reg_first(inst%d));\n", inst_num, inst_num); - fprintf(fp, " root->_opnds[0] = inst%d->_opnds[0]->clone(C); // result\n", inst_num); + fprintf(fp, " root->_opnds[0] = inst%d->_opnds[0]->clone(); // result\n", inst_num); fprintf(fp, " // ----- Done with initial setup -----\n"); } else { if( (op_form == NULL) || (op_form->is_base_constant(globals) == Form::none) ) { @@ -1382,7 +1382,7 @@ static void generate_peepreplace( FILE *fp, FormDict &globals, PeepMatch *pmatch } else { fprintf(fp, " // no ideal edge for constants after matching\n"); } - fprintf(fp, " root->_opnds[%d] = inst%d->_opnds[%d]->clone(C);\n", + fprintf(fp, " root->_opnds[%d] = inst%d->_opnds[%d]->clone();\n", opnds_index, inst_num, inst_op_num ); } ++opnds_index; @@ -1402,7 +1402,7 @@ static void generate_peepreplace( FILE *fp, FormDict &globals, PeepMatch *pmatch // Define the Peephole method for an instruction node void ArchDesc::definePeephole(FILE *fp, InstructForm *node) { // Generate Peephole function header - fprintf(fp, "MachNode *%sNode::peephole( Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted, Compile* C ) {\n", node->_ident); + fprintf(fp, "MachNode *%sNode::peephole(Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted) {\n", node->_ident); fprintf(fp, " bool matches = true;\n"); // Identify the maximum instruction position, @@ -1593,7 +1593,7 @@ void ArchDesc::defineExpand(FILE *fp, InstructForm *node) { } const char *resultOper = new_inst->reduce_result(); - fprintf(fp," n%d->set_opnd_array(0, state->MachOperGenerator( %s, C ));\n", + fprintf(fp," n%d->set_opnd_array(0, state->MachOperGenerator(%s));\n", cnt, machOperEnum(resultOper)); // get the formal operand NameList @@ -1634,7 +1634,7 @@ void ArchDesc::defineExpand(FILE *fp, InstructForm *node) { // If there is no use of the created operand, just skip it if (new_pos != NameList::Not_in_list) { //Copy the operand from the original made above - fprintf(fp," n%d->set_opnd_array(%d, op%d->clone(C)); // %s\n", + fprintf(fp," n%d->set_opnd_array(%d, op%d->clone()); // %s\n", cnt, new_pos, exp_pos-node->num_opnds(), opid); // Check for who defines this operand & add edge if needed fprintf(fp," if(tmp%d != NULL)\n", exp_pos); @@ -1662,7 +1662,7 @@ void ArchDesc::defineExpand(FILE *fp, InstructForm *node) { new_pos = new_inst->operand_position(parameter,Component::USE); if (new_pos != -1) { // Copy the operand from the ExpandNode to the new node - fprintf(fp," n%d->set_opnd_array(%d, opnd_array(%d)->clone(C)); // %s\n", + fprintf(fp," n%d->set_opnd_array(%d, opnd_array(%d)->clone()); // %s\n", cnt, new_pos, exp_pos, opid); // For each operand add appropriate input edges by looking at tmp's fprintf(fp," if(tmp%d == this) {\n", exp_pos); @@ -1729,14 +1729,14 @@ void ArchDesc::defineExpand(FILE *fp, InstructForm *node) { declared_def = true; } if (op && op->_interface && op->_interface->is_RegInterface()) { - fprintf(fp," def = new MachTempNode(state->MachOperGenerator( %s, C ));\n", + fprintf(fp," def = new MachTempNode(state->MachOperGenerator(%s));\n", machOperEnum(op->_ident)); fprintf(fp," add_req(def);\n"); // The operand for TEMP is already constructed during // this mach node construction, see buildMachNode(). // // int idx = node->operand_position_format(comp->_name); - // fprintf(fp," set_opnd_array(%d, state->MachOperGenerator( %s, C ));\n", + // fprintf(fp," set_opnd_array(%d, state->MachOperGenerator(%s));\n", // idx, machOperEnum(op->_ident)); } else { assert(false, "can't have temps which aren't registers"); @@ -1802,7 +1802,7 @@ void ArchDesc::defineExpand(FILE *fp, InstructForm *node) { uint j = node->unique_opnds_idx(i); // unique_opnds_idx(i) is unique if unique_opnds_idx(j) is not unique. if( j != node->unique_opnds_idx(j) ) { - fprintf(fp," set_opnd_array(%d, opnd_array(%d)->clone(C)); // %s\n", + fprintf(fp," set_opnd_array(%d, opnd_array(%d)->clone()); // %s\n", new_num_opnds, i, comp->_name); // delete not unique edges here fprintf(fp," for(unsigned i = 0; i < num%d; i++) {\n", i); @@ -2839,12 +2839,12 @@ static void defineIn_RegMask(FILE *fp, FormDict &globals, OperandForm &oper) { // generate code to create a clone for a class derived from MachOper // -// (0) MachOper *MachOperXOper::clone(Compile* C) const { +// (0) MachOper *MachOperXOper::clone() const { // (1) return new MachXOper( _ccode, _c0, _c1, ..., _cn); // (2) } // static void defineClone(FILE *fp, FormDict &globalNames, OperandForm &oper) { - fprintf(fp,"MachOper *%sOper::clone(Compile* C) const {\n", oper._ident); + fprintf(fp,"MachOper *%sOper::clone() const {\n", oper._ident); // Check for constants that need to be copied over const int num_consts = oper.num_consts(globalNames); const bool is_ideal_bool = oper.is_ideal_bool(); @@ -3043,7 +3043,7 @@ void ArchDesc::define_oper_interface(FILE *fp, OperandForm &oper, FormDict &glob static void define_fill_new_machnode(bool used, FILE *fp_cpp) { fprintf(fp_cpp, "\n"); fprintf(fp_cpp, "// Copy _idx, inputs and operands to new node\n"); - fprintf(fp_cpp, "void MachNode::fill_new_machnode( MachNode* node, Compile* C) const {\n"); + fprintf(fp_cpp, "void MachNode::fill_new_machnode(MachNode* node) const {\n"); if( !used ) { fprintf(fp_cpp, " // This architecture does not have cisc or short branch instructions\n"); fprintf(fp_cpp, " ShouldNotCallThis();\n"); @@ -3064,7 +3064,7 @@ static void define_fill_new_machnode(bool used, FILE *fp_cpp) { fprintf(fp_cpp, " MachOper **to = node->_opnds;\n"); fprintf(fp_cpp, " for( int i = 0; i < nopnds; i++ ) {\n"); fprintf(fp_cpp, " if( i != cisc_operand() ) \n"); - fprintf(fp_cpp, " to[i] = _opnds[i]->clone(C);\n"); + fprintf(fp_cpp, " to[i] = _opnds[i]->clone();\n"); fprintf(fp_cpp, " }\n"); fprintf(fp_cpp, "}\n"); } @@ -3105,7 +3105,7 @@ void ArchDesc::defineClasses(FILE *fp) { if ( strcmp(oper->_ident,"label") == 0 ) { defineIn_RegMask(_CPP_MISC_file._fp, _globalNames, *oper); - fprintf(fp,"MachOper *%sOper::clone(Compile* C) const {\n", oper->_ident); + fprintf(fp,"MachOper *%sOper::clone() const {\n", oper->_ident); fprintf(fp," return new %sOper(_label, _block_num);\n", oper->_ident); fprintf(fp,"}\n"); @@ -3124,7 +3124,7 @@ void ArchDesc::defineClasses(FILE *fp) { if ( strcmp(oper->_ident,"method") == 0 ) { defineIn_RegMask(_CPP_MISC_file._fp, _globalNames, *oper); - fprintf(fp,"MachOper *%sOper::clone(Compile* C) const {\n", oper->_ident); + fprintf(fp,"MachOper *%sOper::clone() const {\n", oper->_ident); fprintf(fp," return new %sOper(_method);\n", oper->_ident); fprintf(fp,"}\n"); @@ -3845,7 +3845,7 @@ void ArchDesc::buildMachOperGenerator(FILE *fp_cpp) { "// that invokes 'new' on the corresponding class constructor.\n"); fprintf(fp_cpp, "\n"); fprintf(fp_cpp, "MachOper *State::MachOperGenerator"); - fprintf(fp_cpp, "(int opcode, Compile* C)"); + fprintf(fp_cpp, "(int opcode)"); fprintf(fp_cpp, "{\n"); fprintf(fp_cpp, "\n"); fprintf(fp_cpp, " switch(opcode) {\n"); @@ -3921,7 +3921,7 @@ void ArchDesc::buildMachNode(FILE *fp_cpp, InstructForm *inst, const char *inden int index = clist.operand_position(comp->_name, comp->_usedef, inst); const char *opcode = machOperEnum(comp->_type); fprintf(fp_cpp, "%s node->set_opnd_array(%d, ", indent, index); - fprintf(fp_cpp, "MachOperGenerator(%s, C));\n", opcode); + fprintf(fp_cpp, "MachOperGenerator(%s));\n", opcode); } } else if ( inst->is_chain_of_constant(_globalNames, opType) ) { @@ -3978,7 +3978,7 @@ void InstructForm::declare_cisc_version(ArchDesc &AD, FILE *fp_hpp) { InstructForm *inst_cisc = cisc_spill_alternate(); if (inst_cisc != NULL) { fprintf(fp_hpp, " virtual int cisc_operand() const { return %d; }\n", cisc_spill_operand()); - fprintf(fp_hpp, " virtual MachNode *cisc_version(int offset, Compile* C);\n"); + fprintf(fp_hpp, " virtual MachNode *cisc_version(int offset);\n"); fprintf(fp_hpp, " virtual void use_cisc_RegMask();\n"); fprintf(fp_hpp, " virtual const RegMask *cisc_RegMask() const { return _cisc_RegMask; }\n"); } @@ -4008,7 +4008,7 @@ bool InstructForm::define_cisc_version(ArchDesc &AD, FILE *fp_cpp) { // Construct CISC version of this instruction fprintf(fp_cpp, "\n"); fprintf(fp_cpp, "// Build CISC version of this instruction\n"); - fprintf(fp_cpp, "MachNode *%sNode::cisc_version( int offset, Compile* C ) {\n", this->_ident); + fprintf(fp_cpp, "MachNode *%sNode::cisc_version(int offset) {\n", this->_ident); // Create the MachNode object fprintf(fp_cpp, " %sNode *node = new %sNode();\n", name, name); // Fill in the bottom_type where requested @@ -4023,7 +4023,7 @@ bool InstructForm::define_cisc_version(ArchDesc &AD, FILE *fp_cpp) { fprintf(fp_cpp, "\n"); fprintf(fp_cpp, " // Copy _idx, inputs and operands to new node\n"); - fprintf(fp_cpp, " fill_new_machnode(node, C);\n"); + fprintf(fp_cpp, " fill_new_machnode(node);\n"); // Construct operand to access [stack_pointer + offset] fprintf(fp_cpp, " // Construct operand to access [stack_pointer + offset]\n"); fprintf(fp_cpp, " node->set_opnd_array(cisc_operand(), new %sOper(offset));\n", cisc_oper_name); @@ -4042,7 +4042,7 @@ bool InstructForm::define_cisc_version(ArchDesc &AD, FILE *fp_cpp) { // Build prototypes for short branch methods void InstructForm::declare_short_branch_methods(FILE *fp_hpp) { if (has_short_branch_form()) { - fprintf(fp_hpp, " virtual MachNode *short_branch_version(Compile* C);\n"); + fprintf(fp_hpp, " virtual MachNode *short_branch_version();\n"); } } @@ -4055,7 +4055,7 @@ bool InstructForm::define_short_branch_methods(ArchDesc &AD, FILE *fp_cpp) { // Construct short_branch_version() method. fprintf(fp_cpp, "// Build short branch version of this instruction\n"); - fprintf(fp_cpp, "MachNode *%sNode::short_branch_version(Compile* C) {\n", this->_ident); + fprintf(fp_cpp, "MachNode *%sNode::short_branch_version() {\n", this->_ident); // Create the MachNode object fprintf(fp_cpp, " %sNode *node = new %sNode();\n", name, name); if( is_ideal_if() ) { @@ -4071,7 +4071,7 @@ bool InstructForm::define_short_branch_methods(ArchDesc &AD, FILE *fp_cpp) { // Short branch version must use same node index for access // through allocator's tables fprintf(fp_cpp, " // Copy _idx, inputs and operands to new node\n"); - fprintf(fp_cpp, " fill_new_machnode(node, C);\n"); + fprintf(fp_cpp, " fill_new_machnode(node);\n"); // Return result and exit scope fprintf(fp_cpp, " return node;\n"); @@ -4097,7 +4097,7 @@ void ArchDesc::buildMachNodeGenerator(FILE *fp_cpp) { "// that invokes 'new' on the corresponding class constructor.\n"); fprintf(fp_cpp, "\n"); fprintf(fp_cpp, "MachNode *State::MachNodeGenerator"); - fprintf(fp_cpp, "(int opcode, Compile* C)"); + fprintf(fp_cpp, "(int opcode)"); fprintf(fp_cpp, "{\n"); fprintf(fp_cpp, " switch(opcode) {\n"); diff --git a/hotspot/src/share/vm/adlc/output_h.cpp b/hotspot/src/share/vm/adlc/output_h.cpp index 7e020fb1726..c204af9f13b 100644 --- a/hotspot/src/share/vm/adlc/output_h.cpp +++ b/hotspot/src/share/vm/adlc/output_h.cpp @@ -1119,7 +1119,7 @@ void ArchDesc::declare_pipe_classes(FILE *fp_hpp) { fprintf(fp_hpp, " _nop_count = %d\n", _pipeline->_nopcnt); fprintf(fp_hpp, " };\n\n"); - fprintf(fp_hpp, " static void initialize_nops(MachNode *nop_list[%d], Compile* C);\n\n", + fprintf(fp_hpp, " static void initialize_nops(MachNode *nop_list[%d]);\n\n", _pipeline->_nopcnt); fprintf(fp_hpp, "#ifndef PRODUCT\n"); fprintf(fp_hpp, " void dump(outputStream *st = tty) const;\n"); @@ -1240,7 +1240,7 @@ void ArchDesc::declareClasses(FILE *fp) { constant_type, _globalNames); // Clone function - fprintf(fp," virtual MachOper *clone(Compile* C) const;\n"); + fprintf(fp," virtual MachOper *clone() const;\n"); // Support setting a spill offset into a constant operand. // We only support setting an 'int' offset, while in the @@ -1718,7 +1718,7 @@ void ArchDesc::declareClasses(FILE *fp) { // If there is an explicit peephole rule, build it if ( instr->peepholes() != NULL ) { - fprintf(fp," virtual MachNode *peephole(Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted, Compile *C);\n"); + fprintf(fp," virtual MachNode *peephole(Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted);\n"); } // Output the declaration for number of relocation entries @@ -1863,7 +1863,7 @@ void ArchDesc::declareClasses(FILE *fp) { } if ( instr->num_post_match_opnds() != 0 || instr->is_chain_of_constant(_globalNames) ) { - fprintf(fp," friend MachNode *State::MachNodeGenerator(int opcode, Compile* C);\n"); + fprintf(fp," friend MachNode *State::MachNodeGenerator(int opcode);\n"); } if ( instr->rematerialize(_globalNames, get_registers()) ) { fprintf(fp," // Rematerialize %s\n", instr->_ident); @@ -2071,8 +2071,8 @@ void ArchDesc::defineStateClass(FILE *fp) { fprintf(fp," DEBUG_ONLY( ~State(void); ) // Destructor\n"); fprintf(fp,"\n"); fprintf(fp," // Methods created by ADLC and invoked by Reduce\n"); - fprintf(fp," MachOper *MachOperGenerator( int opcode, Compile* C );\n"); - fprintf(fp," MachNode *MachNodeGenerator( int opcode, Compile* C );\n"); + fprintf(fp," MachOper *MachOperGenerator(int opcode);\n"); + fprintf(fp," MachNode *MachNodeGenerator(int opcode);\n"); fprintf(fp,"\n"); fprintf(fp," // Assign a state to a node, definition of method produced by ADLC\n"); fprintf(fp," bool DFA( int opcode, const Node *ideal );\n"); diff --git a/hotspot/src/share/vm/asm/codeBuffer.cpp b/hotspot/src/share/vm/asm/codeBuffer.cpp index 9c4bc206158..025880fa71a 100644 --- a/hotspot/src/share/vm/asm/codeBuffer.cpp +++ b/hotspot/src/share/vm/asm/codeBuffer.cpp @@ -134,6 +134,10 @@ CodeBuffer::~CodeBuffer() { // free any overflow storage delete _overflow_arena; + // Claim is that stack allocation ensures resources are cleaned up. + // This is resource clean up, let's hope that all were properly copied out. + free_strings(); + #ifdef ASSERT // Save allocation type to execute assert in ~ResourceObj() // which is called after this destructor. @@ -269,7 +273,7 @@ address CodeBuffer::decode_begin() { GrowableArray* CodeBuffer::create_patch_overflow() { if (_overflow_arena == NULL) { - _overflow_arena = new (mtCode) Arena(); + _overflow_arena = new (mtCode) Arena(mtCode); } return new (_overflow_arena) GrowableArray(_overflow_arena, 8, 0, 0); } @@ -705,7 +709,7 @@ void CodeBuffer::copy_code_to(CodeBlob* dest_blob) { relocate_code_to(&dest); // transfer strings and comments from buffer to blob - dest_blob->set_strings(_strings); + dest_blob->set_strings(_code_strings); // Done moving code bytes; were they the right size? assert(round_to(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity"); @@ -1005,11 +1009,11 @@ void CodeSection::decode() { void CodeBuffer::block_comment(intptr_t offset, const char * comment) { - _strings.add_comment(offset, comment); + _code_strings.add_comment(offset, comment); } const char* CodeBuffer::code_string(const char* str) { - return _strings.add_string(str); + return _code_strings.add_string(str); } class CodeString: public CHeapObj { @@ -1075,6 +1079,7 @@ CodeString* CodeStrings::find_last(intptr_t offset) const { } void CodeStrings::add_comment(intptr_t offset, const char * comment) { + check_valid(); CodeString* c = new CodeString(comment, offset); CodeString* inspos = (_strings == NULL) ? NULL : find_last(offset); @@ -1090,11 +1095,32 @@ void CodeStrings::add_comment(intptr_t offset, const char * comment) { } void CodeStrings::assign(CodeStrings& other) { + other.check_valid(); + // Cannot do following because CodeStrings constructor is not alway run! + assert(is_null(), "Cannot assign onto non-empty CodeStrings"); _strings = other._strings; + other.set_null_and_invalidate(); +} + +// Deep copy of CodeStrings for consistent memory management. +// Only used for actual disassembly so this is cheaper than reference counting +// for the "normal" fastdebug case. +void CodeStrings::copy(CodeStrings& other) { + other.check_valid(); + check_valid(); + assert(is_null(), "Cannot copy onto non-empty CodeStrings"); + CodeString* n = other._strings; + CodeString** ps = &_strings; + while (n != NULL) { + *ps = new CodeString(n->string(),n->offset()); + ps = &((*ps)->_next); + n = n->next(); + } } void CodeStrings::print_block_comment(outputStream* stream, intptr_t offset) const { - if (_strings != NULL) { + check_valid(); + if (_strings != NULL) { CodeString* c = find(offset); while (c && c->offset() == offset) { stream->bol(); @@ -1105,7 +1131,7 @@ void CodeStrings::print_block_comment(outputStream* stream, intptr_t offset) con } } - +// Also sets isNull() void CodeStrings::free() { CodeString* n = _strings; while (n) { @@ -1115,10 +1141,11 @@ void CodeStrings::free() { delete n; n = p; } - _strings = NULL; + set_null_and_invalidate(); } const char* CodeStrings::add_string(const char * string) { + check_valid(); CodeString* s = new CodeString(string); s->set_next(_strings); _strings = s; diff --git a/hotspot/src/share/vm/asm/codeBuffer.hpp b/hotspot/src/share/vm/asm/codeBuffer.hpp index 5572a39af7d..023360ee67c 100644 --- a/hotspot/src/share/vm/asm/codeBuffer.hpp +++ b/hotspot/src/share/vm/asm/codeBuffer.hpp @@ -27,6 +27,7 @@ #include "code/oopRecorder.hpp" #include "code/relocInfo.hpp" +#include "utilities/debug.hpp" class CodeStrings; class PhaseCFG; @@ -245,15 +246,39 @@ class CodeStrings VALUE_OBJ_CLASS_SPEC { private: #ifndef PRODUCT CodeString* _strings; +#ifdef ASSERT + // Becomes true after copy-out, forbids further use. + bool _defunct; // Zero bit pattern is "valid", see memset call in decode_env::decode_env +#endif #endif CodeString* find(intptr_t offset) const; CodeString* find_last(intptr_t offset) const; + void set_null_and_invalidate() { +#ifndef PRODUCT + _strings = NULL; +#ifdef ASSERT + _defunct = true; +#endif +#endif + } + public: CodeStrings() { #ifndef PRODUCT _strings = NULL; +#ifdef ASSERT + _defunct = false; +#endif +#endif + } + + bool is_null() { +#ifdef ASSERT + return _strings == NULL; +#else + return true; #endif } @@ -261,8 +286,17 @@ public: void add_comment(intptr_t offset, const char * comment) PRODUCT_RETURN; void print_block_comment(outputStream* stream, intptr_t offset) const PRODUCT_RETURN; + // MOVE strings from other to this; invalidate other. void assign(CodeStrings& other) PRODUCT_RETURN; + // COPY strings from other to this; leave other valid. + void copy(CodeStrings& other) PRODUCT_RETURN; void free() PRODUCT_RETURN; + // Guarantee that _strings are used at most once; assign invalidates a buffer. + inline void check_valid() const { +#ifdef ASSERT + assert(!_defunct, "Use of invalid CodeStrings"); +#endif + } }; // A CodeBuffer describes a memory space into which assembly @@ -330,7 +364,7 @@ class CodeBuffer: public StackObj { csize_t _total_size; // size in bytes of combined memory buffer OopRecorder* _oop_recorder; - CodeStrings _strings; + CodeStrings _code_strings; OopRecorder _default_oop_recorder; // override with initialize_oop_recorder Arena* _overflow_arena; @@ -531,7 +565,13 @@ class CodeBuffer: public StackObj { void initialize_oop_recorder(OopRecorder* r); OopRecorder* oop_recorder() const { return _oop_recorder; } - CodeStrings& strings() { return _strings; } + CodeStrings& strings() { return _code_strings; } + + void free_strings() { + if (!_code_strings.is_null()) { + _code_strings.free(); // sets _strings Null as a side-effect. + } + } // Code generation void relocate(address at, RelocationHolder const& rspec, int format = 0) { diff --git a/hotspot/src/share/vm/asm/register.hpp b/hotspot/src/share/vm/asm/register.hpp index d9918517dd9..7244b9f5dec 100644 --- a/hotspot/src/share/vm/asm/register.hpp +++ b/hotspot/src/share/vm/asm/register.hpp @@ -275,4 +275,101 @@ inline void assert_different_registers( ); } +inline void assert_different_registers( + AbstractRegister a, + AbstractRegister b, + AbstractRegister c, + AbstractRegister d, + AbstractRegister e, + AbstractRegister f, + AbstractRegister g, + AbstractRegister h, + AbstractRegister i, + AbstractRegister j +) { + assert( + a != b && a != c && a != d && a != e && a != f && a != g && a != h && a != i && a != j + && b != c && b != d && b != e && b != f && b != g && b != h && b != i && b != j + && c != d && c != e && c != f && c != g && c != h && c != i && c != j + && d != e && d != f && d != g && d != h && d != i && d != j + && e != f && e != g && e != h && e != i && e != j + && f != g && f != h && f != i && f != j + && g != h && g != i && g != j + && h != i && h != j + && i != j, + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT + ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT + ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT ", h=" INTPTR_FORMAT + ", i=" INTPTR_FORMAT ", j=" INTPTR_FORMAT "", + p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g), p2i(h), p2i(i), p2i(j)) + ); +} + +inline void assert_different_registers( + AbstractRegister a, + AbstractRegister b, + AbstractRegister c, + AbstractRegister d, + AbstractRegister e, + AbstractRegister f, + AbstractRegister g, + AbstractRegister h, + AbstractRegister i, + AbstractRegister j, + AbstractRegister k +) { + assert( + a != b && a != c && a != d && a != e && a != f && a != g && a != h && a != i && a != j && a !=k + && b != c && b != d && b != e && b != f && b != g && b != h && b != i && b != j && b !=k + && c != d && c != e && c != f && c != g && c != h && c != i && c != j && c !=k + && d != e && d != f && d != g && d != h && d != i && d != j && d !=k + && e != f && e != g && e != h && e != i && e != j && e !=k + && f != g && f != h && f != i && f != j && f !=k + && g != h && g != i && g != j && g !=k + && h != i && h != j && h !=k + && i != j && i !=k + && j !=k, + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT + ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT + ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT ", h=" INTPTR_FORMAT + ", i=" INTPTR_FORMAT ", j=" INTPTR_FORMAT ", k=" INTPTR_FORMAT "", + p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g), p2i(h), p2i(i), p2i(j), p2i(k)) + ); +} + +inline void assert_different_registers( + AbstractRegister a, + AbstractRegister b, + AbstractRegister c, + AbstractRegister d, + AbstractRegister e, + AbstractRegister f, + AbstractRegister g, + AbstractRegister h, + AbstractRegister i, + AbstractRegister j, + AbstractRegister k, + AbstractRegister l +) { + assert( + a != b && a != c && a != d && a != e && a != f && a != g && a != h && a != i && a != j && a !=k && a !=l + && b != c && b != d && b != e && b != f && b != g && b != h && b != i && b != j && b !=k && b !=l + && c != d && c != e && c != f && c != g && c != h && c != i && c != j && c !=k && c !=l + && d != e && d != f && d != g && d != h && d != i && d != j && d !=k && d !=l + && e != f && e != g && e != h && e != i && e != j && e !=k && e !=l + && f != g && f != h && f != i && f != j && f !=k && f !=l + && g != h && g != i && g != j && g !=k && g !=l + && h != i && h != j && h !=k && h !=l + && i != j && i !=k && i !=l + && j !=k && j !=l + && k !=l, + err_msg_res("registers must be different: a=" INTPTR_FORMAT ", b=" INTPTR_FORMAT + ", c=" INTPTR_FORMAT ", d=" INTPTR_FORMAT ", e=" INTPTR_FORMAT + ", f=" INTPTR_FORMAT ", g=" INTPTR_FORMAT ", h=" INTPTR_FORMAT + ", i=" INTPTR_FORMAT ", j=" INTPTR_FORMAT ", k=" INTPTR_FORMAT + ", l=" INTPTR_FORMAT "", + p2i(a), p2i(b), p2i(c), p2i(d), p2i(e), p2i(f), p2i(g), p2i(h), p2i(i), p2i(j), p2i(k), p2i(l)) + ); +} + #endif // SHARE_VM_ASM_REGISTER_HPP diff --git a/hotspot/src/share/vm/c1/c1_Compiler.cpp b/hotspot/src/share/vm/c1/c1_Compiler.cpp index f673b236f19..b547c1d3554 100644 --- a/hotspot/src/share/vm/c1/c1_Compiler.cpp +++ b/hotspot/src/share/vm/c1/c1_Compiler.cpp @@ -48,7 +48,7 @@ Compiler::Compiler() : AbstractCompiler(c1) { void Compiler::init_c1_runtime() { BufferBlob* buffer_blob = CompilerThread::current()->get_buffer_blob(); - Arena* arena = new (mtCompiler) Arena(); + Arena* arena = new (mtCompiler) Arena(mtCompiler); Runtime1::initialize(buffer_blob); FrameMap::initialize(); // initialize data structures diff --git a/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp b/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp index 48b560840e3..f2753cbff9d 100644 --- a/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp +++ b/hotspot/src/share/vm/c1/c1_LIRAssembler.cpp @@ -30,6 +30,7 @@ #include "c1/c1_MacroAssembler.hpp" #include "c1/c1_ValueStack.hpp" #include "ci/ciInstance.hpp" +#include "runtime/os.hpp" void LIR_Assembler::patching_epilog(PatchingStub* patch, LIR_PatchCode patch_code, Register obj, CodeEmitInfo* info) { // we must have enough patching space so that call can be inserted @@ -848,7 +849,7 @@ void LIR_Assembler::verify_oop_map(CodeEmitInfo* info) { stringStream st; st.print("bad oop %s at %d", r->as_Register()->name(), _masm->offset()); #ifdef SPARC - _masm->_verify_oop(r->as_Register(), strdup(st.as_string()), __FILE__, __LINE__); + _masm->_verify_oop(r->as_Register(), os::strdup(st.as_string(), mtCompiler), __FILE__, __LINE__); #else _masm->verify_oop(r->as_Register()); #endif diff --git a/hotspot/src/share/vm/c1/c1_LinearScan.cpp b/hotspot/src/share/vm/c1/c1_LinearScan.cpp index afed1de995f..833b357d2e4 100644 --- a/hotspot/src/share/vm/c1/c1_LinearScan.cpp +++ b/hotspot/src/share/vm/c1/c1_LinearScan.cpp @@ -1613,25 +1613,22 @@ void LinearScan::allocate_registers() { Interval* precolored_cpu_intervals, *not_precolored_cpu_intervals; Interval* precolored_fpu_intervals, *not_precolored_fpu_intervals; - create_unhandled_lists(&precolored_cpu_intervals, ¬_precolored_cpu_intervals, is_precolored_cpu_interval, is_virtual_cpu_interval); - if (has_fpu_registers()) { - create_unhandled_lists(&precolored_fpu_intervals, ¬_precolored_fpu_intervals, is_precolored_fpu_interval, is_virtual_fpu_interval); -#ifdef ASSERT - } else { - // fpu register allocation is omitted because no virtual fpu registers are present - // just check this again... - create_unhandled_lists(&precolored_fpu_intervals, ¬_precolored_fpu_intervals, is_precolored_fpu_interval, is_virtual_fpu_interval); - assert(not_precolored_fpu_intervals == Interval::end(), "missed an uncolored fpu interval"); -#endif - } - // allocate cpu registers + create_unhandled_lists(&precolored_cpu_intervals, ¬_precolored_cpu_intervals, + is_precolored_cpu_interval, is_virtual_cpu_interval); + + // allocate fpu registers + create_unhandled_lists(&precolored_fpu_intervals, ¬_precolored_fpu_intervals, + is_precolored_fpu_interval, is_virtual_fpu_interval); + + // the fpu interval allocation cannot be moved down below with the fpu section as + // the cpu_lsw.walk() changes interval positions. + LinearScanWalker cpu_lsw(this, precolored_cpu_intervals, not_precolored_cpu_intervals); cpu_lsw.walk(); cpu_lsw.finish_allocation(); if (has_fpu_registers()) { - // allocate fpu registers LinearScanWalker fpu_lsw(this, precolored_fpu_intervals, not_precolored_fpu_intervals); fpu_lsw.walk(); fpu_lsw.finish_allocation(); diff --git a/hotspot/src/share/vm/c1/c1_Runtime1.cpp b/hotspot/src/share/vm/c1/c1_Runtime1.cpp index b8dd1bbfb83..d480a70838e 100644 --- a/hotspot/src/share/vm/c1/c1_Runtime1.cpp +++ b/hotspot/src/share/vm/c1/c1_Runtime1.cpp @@ -546,13 +546,18 @@ JRT_ENTRY_NO_ASYNC(static address, exception_handler_for_pc_helper(JavaThread* t // normal bytecode execution. thread->clear_exception_oop_and_pc(); + Handle original_exception(thread, exception()); + continuation = SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, false, false); // If an exception was thrown during exception dispatch, the exception oop may have changed thread->set_exception_oop(exception()); thread->set_exception_pc(pc); // the exception cache is used only by non-implicit exceptions - if (continuation != NULL) { + // Update the exception cache only when there didn't happen + // another exception during the computation of the compiled + // exception handler. + if (continuation != NULL && original_exception() == exception()) { nm->add_handler_for_exception_and_pc(exception, pc, continuation); } } diff --git a/hotspot/src/share/vm/ci/ciArrayKlass.hpp b/hotspot/src/share/vm/ci/ciArrayKlass.hpp index da749cdc61a..e5c74e8865c 100644 --- a/hotspot/src/share/vm/ci/ciArrayKlass.hpp +++ b/hotspot/src/share/vm/ci/ciArrayKlass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2014, 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 @@ -52,11 +52,6 @@ public: ciType* base_element_type(); // JLS calls this the "element type" bool is_leaf_type(); // No subtypes of this array type. - ciInstance* component_mirror() { - // This is a real field in ArrayKlass, but we derive it from element_type. - return element_type()->java_mirror(); - } - // What kind of vmObject is this? bool is_array_klass() const { return true; } bool is_java_klass() const { return true; } diff --git a/hotspot/src/share/vm/ci/ciEnv.cpp b/hotspot/src/share/vm/ci/ciEnv.cpp index 310b4c2a407..5353a61964d 100644 --- a/hotspot/src/share/vm/ci/ciEnv.cpp +++ b/hotspot/src/share/vm/ci/ciEnv.cpp @@ -86,7 +86,8 @@ static bool firstEnv = true; // ------------------------------------------------------------------ // ciEnv::ciEnv -ciEnv::ciEnv(CompileTask* task, int system_dictionary_modification_counter) { +ciEnv::ciEnv(CompileTask* task, int system_dictionary_modification_counter) + : _ciEnv_arena(mtCompiler) { VM_ENTRY_MARK; // Set up ciEnv::current immediately, for the sake of ciObjectFactory, etc. @@ -144,7 +145,7 @@ ciEnv::ciEnv(CompileTask* task, int system_dictionary_modification_counter) { _jvmti_can_pop_frame = false; } -ciEnv::ciEnv(Arena* arena) { +ciEnv::ciEnv(Arena* arena) : _ciEnv_arena(mtCompiler) { ASSERT_IN_VM; // Set up ciEnv::current immediately, for the sake of ciObjectFactory, etc. diff --git a/hotspot/src/share/vm/ci/ciMethod.cpp b/hotspot/src/share/vm/ci/ciMethod.cpp index 2e4ef93a946..27400cd8f8b 100644 --- a/hotspot/src/share/vm/ci/ciMethod.cpp +++ b/hotspot/src/share/vm/ci/ciMethod.cpp @@ -1101,6 +1101,22 @@ bool ciMethod::has_option(const char* option) { return CompilerOracle::has_option_string(mh, option); } +// ------------------------------------------------------------------ +// ciMethod::has_option_value +// +template +bool ciMethod::has_option_value(const char* option, T& value) { + check_is_loaded(); + VM_ENTRY_MARK; + methodHandle mh(THREAD, get_Method()); + return CompilerOracle::has_option_value(mh, option, value); +} +// Explicit instantiation for all OptionTypes supported. +template bool ciMethod::has_option_value(const char* option, intx& value); +template bool ciMethod::has_option_value(const char* option, uintx& value); +template bool ciMethod::has_option_value(const char* option, bool& value); +template bool ciMethod::has_option_value(const char* option, ccstr& value); + // ------------------------------------------------------------------ // ciMethod::can_be_compiled // diff --git a/hotspot/src/share/vm/ci/ciMethod.hpp b/hotspot/src/share/vm/ci/ciMethod.hpp index 2b9cf376991..c1641000f93 100644 --- a/hotspot/src/share/vm/ci/ciMethod.hpp +++ b/hotspot/src/share/vm/ci/ciMethod.hpp @@ -269,6 +269,8 @@ class ciMethod : public ciMetadata { bool should_print_assembly(); bool break_at_execute(); bool has_option(const char *option); + template + bool has_option_value(const char* option, T& value); bool can_be_compiled(); bool can_be_osr_compiled(int entry_bci); void set_not_compilable(const char* reason = NULL); diff --git a/hotspot/src/share/vm/ci/ciMethodData.cpp b/hotspot/src/share/vm/ci/ciMethodData.cpp index aa157e15283..08ce7963963 100644 --- a/hotspot/src/share/vm/ci/ciMethodData.cpp +++ b/hotspot/src/share/vm/ci/ciMethodData.cpp @@ -81,19 +81,38 @@ ciMethodData::ciMethodData() : ciMetadata(NULL) { void ciMethodData::load_extra_data() { MethodData* mdo = get_MethodData(); + MutexLocker(mdo->extra_data_lock()); + // speculative trap entries also hold a pointer to a Method so need to be translated DataLayout* dp_src = mdo->extra_data_base(); - DataLayout* end_src = mdo->extra_data_limit(); + DataLayout* end_src = mdo->args_data_limit(); DataLayout* dp_dst = extra_data_base(); for (;; dp_src = MethodData::next_extra(dp_src), dp_dst = MethodData::next_extra(dp_dst)) { assert(dp_src < end_src, "moved past end of extra data"); - // New traps in the MDO can be added as we translate the copy so - // look at the entries in the copy. - switch(dp_dst->tag()) { + assert(((intptr_t)dp_dst) - ((intptr_t)extra_data_base()) == ((intptr_t)dp_src) - ((intptr_t)mdo->extra_data_base()), "source and destination don't match"); + + // New traps in the MDO may have been added since we copied the + // data (concurrent deoptimizations before we acquired + // extra_data_lock above) or can be removed (a safepoint may occur + // in the translate_from call below) as we translate the copy: + // update the copy as we go. + int tag = dp_src->tag(); + if (tag != DataLayout::arg_info_data_tag) { + memcpy(dp_dst, dp_src, ((intptr_t)MethodData::next_extra(dp_src)) - ((intptr_t)dp_src)); + } + + switch(tag) { case DataLayout::speculative_trap_data_tag: { ciSpeculativeTrapData* data_dst = new ciSpeculativeTrapData(dp_dst); SpeculativeTrapData* data_src = new SpeculativeTrapData(dp_src); + data_dst->translate_from(data_src); + +#ifdef ASSERT + SpeculativeTrapData* data_src2 = new SpeculativeTrapData(dp_src); + assert(data_src2->method() == data_src->method() && data_src2->bci() == data_src->bci(), "entries changed while translating"); +#endif + break; } case DataLayout::bit_data_tag: @@ -244,8 +263,8 @@ ciProfileData* ciMethodData::next_data(ciProfileData* current) { } ciProfileData* ciMethodData::bci_to_extra_data(int bci, ciMethod* m, bool& two_free_slots) { - DataLayout* dp = data_layout_at(data_size()); - DataLayout* end = data_layout_at(data_size() + extra_data_size()); + DataLayout* dp = extra_data_base(); + DataLayout* end = args_data_limit(); two_free_slots = false; for (;dp < end; dp = MethodData::next_extra(dp)) { switch(dp->tag()) { @@ -492,8 +511,8 @@ ByteSize ciMethodData::offset_of_slot(ciProfileData* data, ByteSize slot_offset_ ciArgInfoData *ciMethodData::arg_info() const { // Should be last, have to skip all traps. - DataLayout* dp = data_layout_at(data_size()); - DataLayout* end = data_layout_at(data_size() + extra_data_size()); + DataLayout* dp = extra_data_base(); + DataLayout* end = args_data_limit(); for (; dp < end; dp = MethodData::next_extra(dp)) { if (dp->tag() == DataLayout::arg_info_data_tag) return new ciArgInfoData(dp); @@ -535,8 +554,8 @@ template void ciMethodData::dump_replay_data_call_type_helper(outputStr } void ciMethodData::dump_replay_data_extra_data_helper(outputStream* out, int round, int& count) { - DataLayout* dp = data_layout_at(data_size()); - DataLayout* end = data_layout_at(data_size() + extra_data_size()); + DataLayout* dp = extra_data_base(); + DataLayout* end = args_data_limit(); for (;dp < end; dp = MethodData::next_extra(dp)) { switch(dp->tag()) { @@ -653,8 +672,8 @@ void ciMethodData::print_data_on(outputStream* st) { data->print_data_on(st); } st->print_cr("--- Extra data:"); - DataLayout* dp = data_layout_at(data_size()); - DataLayout* end = data_layout_at(data_size() + extra_data_size()); + DataLayout* dp = extra_data_base(); + DataLayout* end = args_data_limit(); for (;; dp = MethodData::next_extra(dp)) { assert(dp < end, "moved past end of extra data"); switch (dp->tag()) { diff --git a/hotspot/src/share/vm/ci/ciMethodData.hpp b/hotspot/src/share/vm/ci/ciMethodData.hpp index ec88c5dd26c..b96f479682d 100644 --- a/hotspot/src/share/vm/ci/ciMethodData.hpp +++ b/hotspot/src/share/vm/ci/ciMethodData.hpp @@ -410,6 +410,9 @@ private: // Area dedicated to parameters. NULL if no parameter profiling for // this method. DataLayout* _parameters; + int parameters_size() const { + return _parameters == NULL ? 0 : parameters_type_data()->size_in_bytes(); + } ciMethodData(MethodData* md); ciMethodData(); @@ -461,9 +464,6 @@ private: address data_base() const { return (address) _data; } - DataLayout* limit_data_position() const { - return (DataLayout*)((address)data_base() + _data_size); - } void load_extra_data(); ciProfileData* bci_to_extra_data(int bci, ciMethod* m, bool& two_free_slots); @@ -524,7 +524,9 @@ public: ciProfileData* next_data(ciProfileData* current); bool is_valid(ciProfileData* current) { return current != NULL; } - DataLayout* extra_data_base() const { return limit_data_position(); } + DataLayout* extra_data_base() const { return data_layout_at(data_size()); } + DataLayout* args_data_limit() const { return data_layout_at(data_size() + extra_data_size() - + parameters_size()); } // Get the data at an arbitrary bci, or NULL if there is none. If m // is not NULL look for a SpeculativeTrapData if any first. diff --git a/hotspot/src/share/vm/ci/ciObjectFactory.cpp b/hotspot/src/share/vm/ci/ciObjectFactory.cpp index 6bacf2b2f1f..aaa607ff135 100644 --- a/hotspot/src/share/vm/ci/ciObjectFactory.cpp +++ b/hotspot/src/share/vm/ci/ciObjectFactory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -112,7 +112,7 @@ void ciObjectFactory::initialize() { // This Arena is long lived and exists in the resource mark of the // compiler thread that initializes the initial ciObjectFactory which // creates the shared ciObjects that all later ciObjectFactories use. - Arena* arena = new (mtCompiler) Arena(); + Arena* arena = new (mtCompiler) Arena(mtCompiler); ciEnv initial(arena); ciEnv* env = ciEnv::current(); env->_factory->init_shared_objects(); diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index c6de3e2e4f7..8b3ef37d590 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.cpp +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -31,6 +31,9 @@ #include "classfile/javaClasses.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#if INCLUDE_CDS +#include "classfile/systemDictionaryShared.hpp" +#endif #include "classfile/verificationType.hpp" #include "classfile/verifier.hpp" #include "classfile/vmSymbols.hpp" @@ -60,6 +63,7 @@ #include "services/threadService.hpp" #include "utilities/array.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" // We generally try to create the oops directly when parsing, rather than // allocating temporary data structures and copying the bytes twice. A @@ -3786,7 +3790,15 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, instanceKlassHandle nullHandle; // Figure out whether we can skip format checking (matching classic VM behavior) - _need_verify = Verifier::should_verify_for(class_loader(), verify); + if (DumpSharedSpaces) { + // verify == true means it's a 'remote' class (i.e., non-boot class) + // Verification decision is based on BytecodeVerificationRemote flag + // for those classes. + _need_verify = (verify) ? BytecodeVerificationRemote : + BytecodeVerificationLocal; + } else { + _need_verify = Verifier::should_verify_for(class_loader(), verify); + } // Set the verify flag in stream cfs->set_verify(_need_verify); @@ -3805,6 +3817,18 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, u2 minor_version = cfs->get_u2_fast(); u2 major_version = cfs->get_u2_fast(); + if (DumpSharedSpaces && major_version < JAVA_1_5_VERSION) { + ResourceMark rm; + warning("Pre JDK 1.5 class not supported by CDS: %u.%u %s", + major_version, minor_version, name->as_C_string()); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_UnsupportedClassVersionError(), + "Unsupported major.minor version for dump time %u.%u", + major_version, + minor_version); + } + // Check version numbers - we check this even with verifier off if (!is_supported_version(major_version, minor_version)) { if (name == NULL) { @@ -3912,6 +3936,18 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, if (cfs->source() != NULL) tty->print(" from %s", cfs->source()); tty->print_cr("]"); } +#if INCLUDE_CDS + if (DumpLoadedClassList != NULL && cfs->source() != NULL && classlist_file->is_open()) { + // Only dump the classes that can be stored into CDS archive + if (SystemDictionaryShared::is_sharing_possible(loader_data)) { + if (name != NULL) { + ResourceMark rm(THREAD); + classlist_file->print_cr("%s", name->as_C_string()); + classlist_file->flush(); + } + } + } +#endif u2 super_class_index = cfs->get_u2_fast(); instanceKlassHandle super_klass = parse_super_class(super_class_index, diff --git a/hotspot/src/share/vm/classfile/classFileStream.cpp b/hotspot/src/share/vm/classfile/classFileStream.cpp index 27479acb64a..4cd811f88b3 100644 --- a/hotspot/src/share/vm/classfile/classFileStream.cpp +++ b/hotspot/src/share/vm/classfile/classFileStream.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ void ClassFileStream::truncated_file_error(TRAPS) { THROW_MSG(vmSymbols::java_lang_ClassFormatError(), "Truncated class file"); } -ClassFileStream::ClassFileStream(u1* buffer, int length, char* source) { +ClassFileStream::ClassFileStream(u1* buffer, int length, const char* source) { _buffer_start = buffer; _buffer_end = buffer + length; _current = buffer; diff --git a/hotspot/src/share/vm/classfile/classFileStream.hpp b/hotspot/src/share/vm/classfile/classFileStream.hpp index de392e7fa13..df44070252e 100644 --- a/hotspot/src/share/vm/classfile/classFileStream.hpp +++ b/hotspot/src/share/vm/classfile/classFileStream.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,20 +39,20 @@ class ClassFileStream: public ResourceObj { u1* _buffer_start; // Buffer bottom u1* _buffer_end; // Buffer top (one past last element) u1* _current; // Current buffer position - char* _source; // Source of stream (directory name, ZIP/JAR archive name) + const char* _source; // Source of stream (directory name, ZIP/JAR archive name) bool _need_verify; // True if verification is on for the class file void truncated_file_error(TRAPS); public: // Constructor - ClassFileStream(u1* buffer, int length, char* source); + ClassFileStream(u1* buffer, int length, const char* source); // Buffer access u1* buffer() const { return _buffer_start; } int length() const { return _buffer_end - _buffer_start; } u1* current() const { return _current; } void set_current(u1* pos) { _current = pos; } - char* source() const { return _source; } + const char* source() const { return _source; } void set_verify(bool flag) { _need_verify = flag; } void check_truncated_file(bool b, TRAPS) { diff --git a/hotspot/src/share/vm/classfile/classLoader.cpp b/hotspot/src/share/vm/classfile/classLoader.cpp index 1664411337f..5eab1812576 100644 --- a/hotspot/src/share/vm/classfile/classLoader.cpp +++ b/hotspot/src/share/vm/classfile/classLoader.cpp @@ -26,8 +26,13 @@ #include "classfile/classFileParser.hpp" #include "classfile/classFileStream.hpp" #include "classfile/classLoader.hpp" +#include "classfile/classLoaderExt.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/javaClasses.hpp" +#if INCLUDE_CDS +#include "classfile/sharedPathsMiscInfo.hpp" +#include "classfile/sharedClassUtil.hpp" +#endif #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "compiler/compileBroker.hpp" @@ -35,6 +40,7 @@ #include "interpreter/bytecodeStream.hpp" #include "interpreter/oopMapCache.hpp" #include "memory/allocation.inline.hpp" +#include "memory/filemap.hpp" #include "memory/generation.hpp" #include "memory/oopFactory.hpp" #include "memory/universe.inline.hpp" @@ -114,8 +120,12 @@ PerfCounter* ClassLoader::_load_instance_class_failCounter = NULL; ClassPathEntry* ClassLoader::_first_entry = NULL; ClassPathEntry* ClassLoader::_last_entry = NULL; +int ClassLoader::_num_entries = 0; PackageHashtable* ClassLoader::_package_hash_table = NULL; +#if INCLUDE_CDS +SharedPathsMiscInfo* ClassLoader::_shared_paths_misc_info = NULL; +#endif // helper routines bool string_starts_with(const char* str, const char* str_to_find) { size_t str_len = strlen(str); @@ -179,9 +189,10 @@ bool ClassPathEntry::is_lazy() { return false; } -ClassPathDirEntry::ClassPathDirEntry(char* dir) : ClassPathEntry() { - _dir = NEW_C_HEAP_ARRAY(char, strlen(dir)+1, mtClass); - strcpy(_dir, dir); +ClassPathDirEntry::ClassPathDirEntry(const char* dir) : ClassPathEntry() { + char* copy = NEW_C_HEAP_ARRAY(char, strlen(dir)+1, mtClass); + strcpy(copy, dir); + _dir = copy; } @@ -194,6 +205,14 @@ ClassFileStream* ClassPathDirEntry::open_stream(const char* name, TRAPS) { // check if file exists struct stat st; if (os::stat(path, &st) == 0) { +#if INCLUDE_CDS + if (DumpSharedSpaces) { + // We have already check in ClassLoader::check_shared_classpath() that the directory is empty, so + // we should never find a file underneath it -- unless user has added a new file while we are running + // the dump, in which case let's quit! + ShouldNotReachHere(); + } +#endif // found file, open it int file_handle = os::open(path, 0, 0); if (file_handle != -1) { @@ -217,8 +236,9 @@ ClassFileStream* ClassPathDirEntry::open_stream(const char* name, TRAPS) { ClassPathZipEntry::ClassPathZipEntry(jzfile* zip, const char* zip_name) : ClassPathEntry() { _zip = zip; - _zip_name = NEW_C_HEAP_ARRAY(char, strlen(zip_name)+1, mtClass); - strcpy(_zip_name, zip_name); + char *copy = NEW_C_HEAP_ARRAY(char, strlen(zip_name)+1, mtClass); + strcpy(copy, zip_name); + _zip_name = copy; } ClassPathZipEntry::~ClassPathZipEntry() { @@ -228,13 +248,13 @@ ClassPathZipEntry::~ClassPathZipEntry() { FREE_C_HEAP_ARRAY(char, _zip_name, mtClass); } -ClassFileStream* ClassPathZipEntry::open_stream(const char* name, TRAPS) { - // enable call to C land +u1* ClassPathZipEntry::open_entry(const char* name, jint* filesize, bool nul_terminate, TRAPS) { + // enable call to C land JavaThread* thread = JavaThread::current(); ThreadToNativeFromVM ttn(thread); // check whether zip archive contains name - jint filesize, name_len; - jzentry* entry = (*FindEntry)(_zip, name, &filesize, &name_len); + jint name_len; + jzentry* entry = (*FindEntry)(_zip, name, filesize, &name_len); if (entry == NULL) return NULL; u1* buffer; char name_buf[128]; @@ -245,19 +265,33 @@ ClassFileStream* ClassPathZipEntry::open_stream(const char* name, TRAPS) { filename = NEW_RESOURCE_ARRAY(char, name_len + 1); } - // file found, get pointer to class in mmaped jar file. + // file found, get pointer to the entry in mmapped jar file. if (ReadMappedEntry == NULL || !(*ReadMappedEntry)(_zip, entry, &buffer, filename)) { - // mmaped access not available, perhaps due to compression, + // mmapped access not available, perhaps due to compression, // read contents into resource array - buffer = NEW_RESOURCE_ARRAY(u1, filesize); + int size = (*filesize) + ((nul_terminate) ? 1 : 0); + buffer = NEW_RESOURCE_ARRAY(u1, size); if (!(*ReadEntry)(_zip, entry, buffer, filename)) return NULL; } + + // return result + if (nul_terminate) { + buffer[*filesize] = 0; + } + return buffer; +} + +ClassFileStream* ClassPathZipEntry::open_stream(const char* name, TRAPS) { + jint filesize; + u1* buffer = open_entry(name, &filesize, false, CHECK_NULL); + if (buffer == NULL) { + return NULL; + } if (UsePerfData) { ClassLoader::perf_sys_classfile_bytes_read()->inc(filesize); } - // return result - return new ClassFileStream(buffer, filesize, _zip_name); // Resource allocated + return new ClassFileStream(buffer, filesize, _zip_name); // Resource allocated } // invoke function for each entry in the zip file @@ -272,12 +306,17 @@ void ClassPathZipEntry::contents_do(void f(const char* name, void* context), voi } } -LazyClassPathEntry::LazyClassPathEntry(char* path, const struct stat* st) : ClassPathEntry() { - _path = strdup(path); +LazyClassPathEntry::LazyClassPathEntry(const char* path, const struct stat* st, bool throw_exception) : ClassPathEntry() { + _path = os::strdup_check_oom(path); _st = *st; _meta_index = NULL; _resolved_entry = NULL; _has_error = false; + _throw_exception = throw_exception; +} + +LazyClassPathEntry::~LazyClassPathEntry() { + os::free((void*)_path); } bool LazyClassPathEntry::is_jar_file() { @@ -289,7 +328,11 @@ ClassPathEntry* LazyClassPathEntry::resolve_entry(TRAPS) { return (ClassPathEntry*) _resolved_entry; } ClassPathEntry* new_entry = NULL; - new_entry = ClassLoader::create_class_path_entry(_path, &_st, false, CHECK_NULL); + new_entry = ClassLoader::create_class_path_entry(_path, &_st, false, _throw_exception, CHECK_NULL); + if (!_throw_exception && new_entry == NULL) { + assert(!HAS_PENDING_EXCEPTION, "must be"); + return NULL; + } { ThreadCritical tc; if (_resolved_entry == NULL) { @@ -323,6 +366,23 @@ bool LazyClassPathEntry::is_lazy() { return true; } +u1* LazyClassPathEntry::open_entry(const char* name, jint* filesize, bool nul_terminate, TRAPS) { + if (_has_error) { + return NULL; + } + ClassPathEntry* cpe = resolve_entry(THREAD); + if (cpe == NULL) { + _has_error = true; + return NULL; + } else if (cpe->is_jar_file()) { + return ((ClassPathZipEntry*)cpe)->open_entry(name, filesize, nul_terminate,THREAD); + } else { + ShouldNotReachHere(); + *filesize = 0; + return NULL; + } +} + static void print_meta_index(LazyClassPathEntry* entry, GrowableArray& meta_packages) { tty->print("[Meta index for %s=", entry->name()); @@ -333,15 +393,62 @@ static void print_meta_index(LazyClassPathEntry* entry, tty->print_cr("]"); } +#if INCLUDE_CDS +void ClassLoader::exit_with_path_failure(const char* error, const char* message) { + assert(DumpSharedSpaces, "only called at dump time"); + tty->print_cr("Hint: enable -XX:+TraceClassPaths to diagnose the failure"); + vm_exit_during_initialization(error, message); +} +#endif -void ClassLoader::setup_meta_index() { +void ClassLoader::trace_class_path(const char* msg, const char* name) { + if (!TraceClassPaths) { + return; + } + + if (msg) { + tty->print("%s", msg); + } + if (name) { + if (strlen(name) < 256) { + tty->print("%s", name); + } else { + // For very long paths, we need to print each character separately, + // as print_cr() has a length limit + while (name[0] != '\0') { + tty->print("%c", name[0]); + name++; + } + } + } + if (msg && msg[0] == '[') { + tty->print_cr("]"); + } else { + tty->cr(); + } +} + +void ClassLoader::setup_bootstrap_meta_index() { // Set up meta index which allows us to open boot jars lazily if // class data sharing is enabled + const char* meta_index_path = Arguments::get_meta_index_path(); + const char* meta_index_dir = Arguments::get_meta_index_dir(); + setup_meta_index(meta_index_path, meta_index_dir, 0); +} + +void ClassLoader::setup_meta_index(const char* meta_index_path, const char* meta_index_dir, int start_index) { const char* known_version = "% VERSION 2"; - char* meta_index_path = Arguments::get_meta_index_path(); - char* meta_index_dir = Arguments::get_meta_index_dir(); FILE* file = fopen(meta_index_path, "r"); int line_no = 0; +#if INCLUDE_CDS + if (DumpSharedSpaces) { + if (file != NULL) { + _shared_paths_misc_info->add_required_file(meta_index_path); + } else { + _shared_paths_misc_info->add_nonexist_path(meta_index_path); + } + } +#endif if (file != NULL) { ResourceMark rm; LazyClassPathEntry* cur_entry = NULL; @@ -376,7 +483,7 @@ void ClassLoader::setup_meta_index() { // Hand off current packages to current lazy entry (if any) if ((cur_entry != NULL) && (boot_class_path_packages.length() > 0)) { - if (TraceClassLoading && Verbose) { + if ((TraceClassLoading || TraceClassPaths) && Verbose) { print_meta_index(cur_entry, boot_class_path_packages); } MetaIndex* index = new MetaIndex(boot_class_path_packages.adr_at(0), @@ -387,8 +494,10 @@ void ClassLoader::setup_meta_index() { boot_class_path_packages.clear(); // Find lazy entry corresponding to this jar file - for (ClassPathEntry* entry = _first_entry; entry != NULL; entry = entry->next()) { - if (entry->is_lazy() && + int count = 0; + for (ClassPathEntry* entry = _first_entry; entry != NULL; entry = entry->next(), count++) { + if (count >= start_index && + entry->is_lazy() && string_starts_with(entry->name(), meta_index_dir) && string_ends_with(entry->name(), &package_name[2])) { cur_entry = (LazyClassPathEntry*) entry; @@ -416,7 +525,7 @@ void ClassLoader::setup_meta_index() { default: { if (!skipCurrentJar && cur_entry != NULL) { - char* new_name = strdup(package_name); + char* new_name = os::strdup_check_oom(package_name); boot_class_path_packages.append(new_name); } } @@ -425,7 +534,7 @@ void ClassLoader::setup_meta_index() { // Hand off current packages to current lazy entry (if any) if ((cur_entry != NULL) && (boot_class_path_packages.length() > 0)) { - if (TraceClassLoading && Verbose) { + if ((TraceClassLoading || TraceClassPaths) && Verbose) { print_meta_index(cur_entry, boot_class_path_packages); } MetaIndex* index = new MetaIndex(boot_class_path_packages.adr_at(0), @@ -436,36 +545,90 @@ void ClassLoader::setup_meta_index() { } } -void ClassLoader::setup_bootstrap_search_path() { - assert(_first_entry == NULL, "should not setup bootstrap class search path twice"); - char* sys_class_path = os::strdup(Arguments::get_sysclasspath()); - if (TraceClassLoading && Verbose) { - tty->print_cr("[Bootstrap loader class path=%s]", sys_class_path); +#if INCLUDE_CDS +void ClassLoader::check_shared_classpath(const char *path) { + if (strcmp(path, "") == 0) { + exit_with_path_failure("Cannot have empty path in archived classpaths", NULL); } - int len = (int)strlen(sys_class_path); + struct stat st; + if (os::stat(path, &st) == 0) { + if ((st.st_mode & S_IFREG) != S_IFREG) { // is directory + if (!os::dir_is_empty(path)) { + tty->print_cr("Error: non-empty directory '%s'", path); + exit_with_path_failure("CDS allows only empty directories in archived classpaths", NULL); + } + } + } +} +#endif + +void ClassLoader::setup_bootstrap_search_path() { + assert(_first_entry == NULL, "should not setup bootstrap class search path twice"); + const char* sys_class_path = Arguments::get_sysclasspath(); + if (PrintSharedArchiveAndExit) { + // Don't print sys_class_path - this is the bootcp of this current VM process, not necessarily + // the same as the bootcp of the shared archive. + } else { + trace_class_path("[Bootstrap loader class path=", sys_class_path); + } +#if INCLUDE_CDS + if (DumpSharedSpaces) { + _shared_paths_misc_info->add_boot_classpath(sys_class_path); + } +#endif + setup_search_path(sys_class_path); +} + +#if INCLUDE_CDS +int ClassLoader::get_shared_paths_misc_info_size() { + return _shared_paths_misc_info->get_used_bytes(); +} + +void* ClassLoader::get_shared_paths_misc_info() { + return _shared_paths_misc_info->buffer(); +} + +bool ClassLoader::check_shared_paths_misc_info(void *buf, int size) { + SharedPathsMiscInfo* checker = SharedClassUtil::allocate_shared_paths_misc_info((char*)buf, size); + bool result = checker->check(); + delete checker; + return result; +} +#endif + +void ClassLoader::setup_search_path(const char *class_path) { + int offset = 0; + int len = (int)strlen(class_path); int end = 0; // Iterate over class path entries for (int start = 0; start < len; start = end) { - while (sys_class_path[end] && sys_class_path[end] != os::path_separator()[0]) { + while (class_path[end] && class_path[end] != os::path_separator()[0]) { end++; } - char* path = NEW_C_HEAP_ARRAY(char, end-start+1, mtClass); - strncpy(path, &sys_class_path[start], end-start); - path[end-start] = '\0'; + EXCEPTION_MARK; + ResourceMark rm(THREAD); + char* path = NEW_RESOURCE_ARRAY(char, end - start + 1); + strncpy(path, &class_path[start], end - start); + path[end - start] = '\0'; update_class_path_entry_list(path, false); - FREE_C_HEAP_ARRAY(char, path, mtClass); - while (sys_class_path[end] == os::path_separator()[0]) { +#if INCLUDE_CDS + if (DumpSharedSpaces) { + check_shared_classpath(path); + } +#endif + while (class_path[end] == os::path_separator()[0]) { end++; } } } -ClassPathEntry* ClassLoader::create_class_path_entry(char *path, const struct stat* st, bool lazy, TRAPS) { +ClassPathEntry* ClassLoader::create_class_path_entry(const char *path, const struct stat* st, + bool lazy, bool throw_exception, TRAPS) { JavaThread* thread = JavaThread::current(); if (lazy) { - return new LazyClassPathEntry(path, st); + return new LazyClassPathEntry(path, st, throw_exception); } ClassPathEntry* new_entry = NULL; if ((st->st_mode & S_IFREG) == S_IFREG) { @@ -474,7 +637,11 @@ ClassPathEntry* ClassLoader::create_class_path_entry(char *path, const struct st char canonical_path[JVM_MAXPATHLEN]; if (!get_canonical_path(path, canonical_path, JVM_MAXPATHLEN)) { // This matches the classic VM - THROW_MSG_(vmSymbols::java_io_IOException(), "Bad pathname", NULL); + if (throw_exception) { + THROW_MSG_(vmSymbols::java_io_IOException(), "Bad pathname", NULL); + } else { + return NULL; + } } char* error_msg = NULL; jzfile* zip; @@ -486,7 +653,7 @@ ClassPathEntry* ClassLoader::create_class_path_entry(char *path, const struct st } if (zip != NULL && error_msg == NULL) { new_entry = new ClassPathZipEntry(zip, path); - if (TraceClassLoading) { + if (TraceClassLoading || TraceClassPaths) { tty->print_cr("[Opened %s]", path); } } else { @@ -500,12 +667,16 @@ ClassPathEntry* ClassLoader::create_class_path_entry(char *path, const struct st msg = NEW_RESOURCE_ARRAY(char, len); ; jio_snprintf(msg, len - 1, "error in opening JAR file <%s> %s", error_msg, path); } - THROW_MSG_(vmSymbols::java_lang_ClassNotFoundException(), msg, NULL); + if (throw_exception) { + THROW_MSG_(vmSymbols::java_lang_ClassNotFoundException(), msg, NULL); + } else { + return NULL; + } } } else { // Directory new_entry = new ClassPathDirEntry(path); - if (TraceClassLoading) { + if (TraceClassLoading || TraceClassPaths) { tty->print_cr("[Path %s]", path); } } @@ -520,11 +691,8 @@ ClassPathZipEntry* ClassLoader::create_class_path_zip_entry(const char *path) { struct stat st; if (os::stat(path, &st) == 0) { if ((st.st_mode & S_IFREG) == S_IFREG) { - char orig_path[JVM_MAXPATHLEN]; char canonical_path[JVM_MAXPATHLEN]; - - strcpy(orig_path, path); - if (get_canonical_path(orig_path, canonical_path, JVM_MAXPATHLEN)) { + if (get_canonical_path(path, canonical_path, JVM_MAXPATHLEN)) { char* error_msg = NULL; jzfile* zip; { @@ -566,23 +734,37 @@ void ClassLoader::add_to_list(ClassPathEntry *new_entry) { _last_entry = new_entry; } } + _num_entries ++; } -void ClassLoader::update_class_path_entry_list(char *path, - bool check_for_duplicates) { +// Returns true IFF the file/dir exists and the entry was successfully created. +bool ClassLoader::update_class_path_entry_list(const char *path, + bool check_for_duplicates, + bool throw_exception) { struct stat st; if (os::stat(path, &st) == 0) { // File or directory found ClassPathEntry* new_entry = NULL; Thread* THREAD = Thread::current(); - new_entry = create_class_path_entry(path, &st, LazyBootClassLoader, CHECK); + new_entry = create_class_path_entry(path, &st, LazyBootClassLoader, throw_exception, CHECK_(false)); + if (new_entry == NULL) { + return false; + } // The kernel VM adds dynamically to the end of the classloader path and // doesn't reorder the bootclasspath which would break java.lang.Package // (see PackageInfo). // Add new entry to linked list if (!check_for_duplicates || !contains_entry(new_entry)) { - add_to_list(new_entry); + ClassLoaderExt::add_class_path_entry(path, check_for_duplicates, new_entry); } + return true; + } else { +#if INCLUDE_CDS + if (DumpSharedSpaces) { + _shared_paths_misc_info->add_nonexist_path(path); + } +#endif + return false; } } @@ -734,10 +916,10 @@ public: assert(n == number_of_entries(), "just checking"); } - void copy_table(char** top, char* end, PackageHashtable* table); + CDS_ONLY(void copy_table(char** top, char* end, PackageHashtable* table);) }; - +#if INCLUDE_CDS void PackageHashtable::copy_table(char** top, char* end, PackageHashtable* table) { // Copy (relocate) the table to the shared space. @@ -745,33 +927,30 @@ void PackageHashtable::copy_table(char** top, char* end, // Calculate the space needed for the package name strings. int i; - int n = 0; - for (i = 0; i < table_size(); ++i) { - for (PackageInfo* pp = table->bucket(i); - pp != NULL; - pp = pp->next()) { - n += (int)(strlen(pp->pkgname()) + 1); - } - } - if (*top + n + sizeof(intptr_t) >= end) { - report_out_of_shared_space(SharedMiscData); - } - - // Copy the table data (the strings) to the shared space. - n = align_size_up(n, sizeof(HeapWord)); - *(intptr_t*)(*top) = n; - *top += sizeof(intptr_t); + intptr_t* tableSize = (intptr_t*)(*top); + *top += sizeof(intptr_t); // For table size + char* tableStart = *top; for (i = 0; i < table_size(); ++i) { for (PackageInfo* pp = table->bucket(i); pp != NULL; pp = pp->next()) { int n1 = (int)(strlen(pp->pkgname()) + 1); + if (*top + n1 >= end) { + report_out_of_shared_space(SharedMiscData); + } pp->set_pkgname((char*)memcpy(*top, pp->pkgname(), n1)); *top += n1; } } *top = (char*)align_size_up((intptr_t)*top, sizeof(HeapWord)); + if (*top >= end) { + report_out_of_shared_space(SharedMiscData); + } + + // Write table size + intptr_t len = *top - (char*)tableStart; + *tableSize = len; } @@ -782,7 +961,7 @@ void ClassLoader::copy_package_info_buckets(char** top, char* end) { void ClassLoader::copy_package_info_table(char** top, char* end) { _package_hash_table->copy_table(top, end, _package_hash_table); } - +#endif PackageInfo* ClassLoader::lookup_package(const char *pkgname) { const char *cp = strrchr(pkgname, '/'); @@ -875,7 +1054,8 @@ objArrayOop ClassLoader::get_system_packages(TRAPS) { instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) { ResourceMark rm(THREAD); - EventMark m("loading class %s", h_name->as_C_string()); + const char* class_name = h_name->as_C_string(); + EventMark m("loading class %s", class_name); ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion); stringStream st; @@ -883,18 +1063,24 @@ instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) { // st.print("%s.class", h_name->as_utf8()); st.print_raw(h_name->as_utf8()); st.print_raw(".class"); - char* name = st.as_string(); + const char* file_name = st.as_string(); + ClassLoaderExt::Context context(class_name, file_name, THREAD); // Lookup stream for parsing .class file ClassFileStream* stream = NULL; int classpath_index = 0; + ClassPathEntry* e = NULL; + instanceKlassHandle h; { PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(), ((JavaThread*) THREAD)->get_thread_stat()->perf_timers_addr(), PerfClassTraceTime::CLASS_LOAD); - ClassPathEntry* e = _first_entry; + e = _first_entry; while (e != NULL) { - stream = e->open_stream(name, CHECK_NULL); + stream = e->open_stream(file_name, CHECK_NULL); + if (!context.check(stream, classpath_index)) { + return h; // NULL + } if (stream != NULL) { break; } @@ -903,9 +1089,7 @@ instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) { } } - instanceKlassHandle h; if (stream != NULL) { - // class file found, parse it ClassFileParser parser(stream); ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); @@ -915,12 +1099,19 @@ instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) { loader_data, protection_domain, parsed_name, - false, - CHECK_(h)); - - // add to package table - if (add_package(name, classpath_index, THREAD)) { - h = result; + context.should_verify(classpath_index), + THREAD); + if (HAS_PENDING_EXCEPTION) { + ResourceMark rm; + if (DumpSharedSpaces) { + tty->print_cr("Preload Error: Failed to load %s", class_name); + } + return h; + } + h = context.record_result(classpath_index, e, result, THREAD); + } else { + if (DumpSharedSpaces) { + tty->print_cr("Preload Error: Cannot find %s", class_name); } } @@ -1015,14 +1206,27 @@ void ClassLoader::initialize() { // lookup zip library entry points load_zip_library(); +#if INCLUDE_CDS // initialize search path + if (DumpSharedSpaces) { + _shared_paths_misc_info = SharedClassUtil::allocate_shared_paths_misc_info(); + } +#endif setup_bootstrap_search_path(); if (LazyBootClassLoader) { // set up meta index which makes boot classpath initialization lazier - setup_meta_index(); + setup_bootstrap_meta_index(); } } +#if INCLUDE_CDS +void ClassLoader::initialize_shared_path() { + if (DumpSharedSpaces) { + ClassLoaderExt::setup_search_paths(); + _shared_paths_misc_info->write_jint(0); // see comments in SharedPathsMiscInfo::check() + } +} +#endif jlong ClassLoader::classloader_time_ms() { return UsePerfData ? @@ -1066,11 +1270,17 @@ void classLoader_init() { } -bool ClassLoader::get_canonical_path(char* orig, char* out, int len) { +bool ClassLoader::get_canonical_path(const char* orig, char* out, int len) { assert(orig != NULL && out != NULL && len > 0, "bad arguments"); if (CanonicalizeEntry != NULL) { - JNIEnv* env = JavaThread::current()->jni_environment(); - if ((CanonicalizeEntry)(env, os::native_path(orig), out, len) < 0) { + JavaThread* THREAD = JavaThread::current(); + JNIEnv* env = THREAD->jni_environment(); + ResourceMark rm(THREAD); + + // os::native_path writes into orig_copy + char* orig_copy = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, strlen(orig)+1); + strcpy(orig_copy, orig); + if ((CanonicalizeEntry)(env, os::native_path(orig_copy), out, len) < 0) { return false; } } else { diff --git a/hotspot/src/share/vm/classfile/classLoader.hpp b/hotspot/src/share/vm/classfile/classLoader.hpp index 2cfcde6387e..8f0bf77ac37 100644 --- a/hotspot/src/share/vm/classfile/classLoader.hpp +++ b/hotspot/src/share/vm/classfile/classLoader.hpp @@ -72,11 +72,11 @@ class ClassPathEntry: public CHeapObj { class ClassPathDirEntry: public ClassPathEntry { private: - char* _dir; // Name of directory + const char* _dir; // Name of directory public: bool is_jar_file() { return false; } const char* name() { return _dir; } - ClassPathDirEntry(char* dir); + ClassPathDirEntry(const char* dir); ClassFileStream* open_stream(const char* name, TRAPS); // Debugging NOT_PRODUCT(void compile_the_world(Handle loader, TRAPS);) @@ -100,13 +100,14 @@ typedef struct { class ClassPathZipEntry: public ClassPathEntry { private: - jzfile* _zip; // The zip archive - char* _zip_name; // Name of zip archive + jzfile* _zip; // The zip archive + const char* _zip_name; // Name of zip archive public: bool is_jar_file() { return true; } const char* name() { return _zip_name; } ClassPathZipEntry(jzfile* zip, const char* zip_name); ~ClassPathZipEntry(); + u1* open_entry(const char* name, jint* filesize, bool nul_terminate, TRAPS); ClassFileStream* open_stream(const char* name, TRAPS); void contents_do(void f(const char* name, void* context), void* context); // Debugging @@ -118,16 +119,20 @@ class ClassPathZipEntry: public ClassPathEntry { // For lazier loading of boot class path entries class LazyClassPathEntry: public ClassPathEntry { private: - char* _path; // dir or file + const char* _path; // dir or file struct stat _st; MetaIndex* _meta_index; bool _has_error; + bool _throw_exception; volatile ClassPathEntry* _resolved_entry; ClassPathEntry* resolve_entry(TRAPS); public: bool is_jar_file(); const char* name() { return _path; } - LazyClassPathEntry(char* path, const struct stat* st); + LazyClassPathEntry(const char* path, const struct stat* st, bool throw_exception); + virtual ~LazyClassPathEntry(); + u1* open_entry(const char* name, jint* filesize, bool nul_terminate, TRAPS); + ClassFileStream* open_stream(const char* name, TRAPS); void set_meta_index(MetaIndex* meta_index) { _meta_index = meta_index; } virtual bool is_lazy(); @@ -138,6 +143,7 @@ class LazyClassPathEntry: public ClassPathEntry { class PackageHashtable; class PackageInfo; +class SharedPathsMiscInfo; template class HashtableBucket; class ClassLoader: AllStatic { @@ -145,7 +151,7 @@ class ClassLoader: AllStatic { enum SomeConstants { package_hash_table_size = 31 // Number of buckets }; - private: + protected: friend class LazyClassPathEntry; // Performance counters @@ -187,10 +193,15 @@ class ClassLoader: AllStatic { static ClassPathEntry* _first_entry; // Last entry in linked list of ClassPathEntry instances static ClassPathEntry* _last_entry; + static int _num_entries; + // Hash table used to keep track of loaded packages static PackageHashtable* _package_hash_table; static const char* _shared_archive; + // Info used by CDS + CDS_ONLY(static SharedPathsMiscInfo * _shared_paths_misc_info;) + // Hash function static unsigned int hash(const char *s, int n); // Returns the package file name corresponding to the specified package @@ -201,19 +212,23 @@ class ClassLoader: AllStatic { static bool add_package(const char *pkgname, int classpath_index, TRAPS); // Initialization - static void setup_meta_index(); + static void setup_bootstrap_meta_index(); + static void setup_meta_index(const char* meta_index_path, const char* meta_index_dir, + int start_index); static void setup_bootstrap_search_path(); + static void setup_search_path(const char *class_path); + static void load_zip_library(); - static ClassPathEntry* create_class_path_entry(char *path, const struct stat* st, - bool lazy, TRAPS); + static ClassPathEntry* create_class_path_entry(const char *path, const struct stat* st, + bool lazy, bool throw_exception, TRAPS); // Canonicalizes path names, so strcmp will work properly. This is mainly // to avoid confusing the zip library - static bool get_canonical_path(char* orig, char* out, int len); + static bool get_canonical_path(const char* orig, char* out, int len); public: - // Used by the kernel jvm. - static void update_class_path_entry_list(char *path, - bool check_for_duplicates); + static bool update_class_path_entry_list(const char *path, + bool check_for_duplicates, + bool throw_exception=true); static void print_bootclasspath(); // Timing @@ -296,6 +311,7 @@ class ClassLoader: AllStatic { // Initialization static void initialize(); + CDS_ONLY(static void initialize_shared_path();) static void create_package_info_table(); static void create_package_info_table(HashtableBucket *t, int length, int number_of_entries); @@ -310,10 +326,21 @@ class ClassLoader: AllStatic { return e; } +#if INCLUDE_CDS // Sharing dump and restore static void copy_package_info_buckets(char** top, char* end); static void copy_package_info_table(char** top, char* end); + static void check_shared_classpath(const char *path); + static void finalize_shared_paths_misc_info(); + static int get_shared_paths_misc_info_size(); + static void* get_shared_paths_misc_info(); + static bool check_shared_paths_misc_info(void* info, int size); + static void exit_with_path_failure(const char* error, const char* message); +#endif + + static void trace_class_path(const char* msg, const char* name = NULL); + // VM monitoring and management support static jlong classloader_time_ms(); static jlong class_method_total_size(); @@ -337,7 +364,7 @@ class ClassLoader: AllStatic { // Force compilation of all methods in all classes in bootstrap class path (stress test) #ifndef PRODUCT - private: + protected: static int _compile_the_world_class_counter; static int _compile_the_world_method_counter; public: diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp index 1b2bd8a26b1..3b2cd141b0c 100644 --- a/hotspot/src/share/vm/classfile/classLoaderData.cpp +++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp @@ -332,27 +332,6 @@ void ClassLoaderData::unload() { } } -#ifdef ASSERT -class AllAliveClosure : public OopClosure { - BoolObjectClosure* _is_alive_closure; - bool _found_dead; - public: - AllAliveClosure(BoolObjectClosure* is_alive_closure) : _is_alive_closure(is_alive_closure), _found_dead(false) {} - template void do_oop_work(T* p) { - T heap_oop = oopDesc::load_heap_oop(p); - if (!oopDesc::is_null(heap_oop)) { - oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); - if (!_is_alive_closure->do_object_b(obj)) { - _found_dead = true; - } - } - } - void do_oop(oop* p) { do_oop_work(p); } - void do_oop(narrowOop* p) { do_oop_work(p); } - bool found_dead() { return _found_dead; } -}; -#endif - oop ClassLoaderData::keep_alive_object() const { assert(!keep_alive(), "Don't use with CLDs that are artificially kept alive"); return is_anonymous() ? _klasses->java_mirror() : class_loader(); @@ -362,15 +341,6 @@ bool ClassLoaderData::is_alive(BoolObjectClosure* is_alive_closure) const { bool alive = keep_alive() // null class loader and incomplete anonymous klasses. || is_alive_closure->do_object_b(keep_alive_object()); -#ifdef ASSERT - if (alive) { - AllAliveClosure all_alive_closure(is_alive_closure); - KlassToOopClosure klass_closure(&all_alive_closure); - const_cast(this)->oops_do(&all_alive_closure, &klass_closure, false); - assert(!all_alive_closure.found_dead(), err_msg("Found dead oop in alive cld: " PTR_FORMAT, p2i(this))); - } -#endif - return alive; } diff --git a/hotspot/src/share/vm/classfile/classLoaderExt.hpp b/hotspot/src/share/vm/classfile/classLoaderExt.hpp new file mode 100644 index 00000000000..ee2e0ec6851 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classLoaderExt.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_CLASSFILE_CLASSLOADEREXT_HPP +#define SHARE_VM_CLASSFILE_CLASSLOADEREXT_HPP + +#include "classfile/classLoader.hpp" + +class ClassLoaderExt: public ClassLoader { // AllStatic +public: + + class Context { + const char* _file_name; + public: + Context(const char* class_name, const char* file_name, TRAPS) { + _file_name = file_name; + } + + bool check(ClassFileStream* stream, const int classpath_index) { + return true; + } + + bool should_verify(int classpath_index) { + return false; + } + + instanceKlassHandle record_result(const int classpath_index, + ClassPathEntry* e, instanceKlassHandle result, TRAPS) { + if (ClassLoader::add_package(_file_name, classpath_index, THREAD)) { + if (DumpSharedSpaces) { + result->set_shared_classpath_index(classpath_index); + } + return result; + } else { + return instanceKlassHandle(); // NULL + } + } + }; + + + static void add_class_path_entry(const char* path, bool check_for_duplicates, + ClassPathEntry* new_entry) { + ClassLoader::add_to_list(new_entry); + } + static void setup_search_paths() {} +}; + +#endif // SHARE_VM_CLASSFILE_CLASSLOADEREXT_HPP diff --git a/hotspot/src/share/vm/classfile/dictionary.cpp b/hotspot/src/share/vm/classfile/dictionary.cpp index dcb295687aa..37381afbd65 100644 --- a/hotspot/src/share/vm/classfile/dictionary.cpp +++ b/hotspot/src/share/vm/classfile/dictionary.cpp @@ -130,15 +130,13 @@ void DictionaryEntry::add_protection_domain(Dictionary* dict, oop protection_dom } -bool Dictionary::do_unloading() { +void Dictionary::do_unloading() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); - bool class_was_unloaded = false; - int index = 0; // Defined here for portability! Do not move // Remove unloadable entries and classes from system dictionary // The placeholder array has been handled in always_strong_oops_do. DictionaryEntry* probe = NULL; - for (index = 0; index < table_size(); index++) { + for (int index = 0; index < table_size(); index++) { for (DictionaryEntry** p = bucket_addr(index); *p != NULL; ) { probe = *p; Klass* e = probe->klass(); @@ -158,16 +156,8 @@ bool Dictionary::do_unloading() { // Do we need to delete this system dictionary entry? if (loader_data->is_unloading()) { // If the loader is not live this entry should always be - // removed (will never be looked up again). Note that this is - // not the same as unloading the referred class. - if (k_def_class_loader_data == loader_data) { - // This is the defining entry, so the referred class is about - // to be unloaded. - class_was_unloaded = true; - } - // Also remove this system dictionary entry. + // removed (will never be looked up again). purge_entry = true; - } else { // The loader in this entry is alive. If the klass is dead, // (determined by checking the defining class loader) @@ -196,7 +186,6 @@ bool Dictionary::do_unloading() { p = probe->next_addr(); } } - return class_was_unloaded; } void Dictionary::roots_oops_do(OopClosure* strong, OopClosure* weak) { @@ -220,6 +209,29 @@ void Dictionary::roots_oops_do(OopClosure* strong, OopClosure* weak) { _pd_cache_table->roots_oops_do(strong, weak); } +void Dictionary::remove_classes_in_error_state() { + assert(DumpSharedSpaces, "supported only when dumping"); + DictionaryEntry* probe = NULL; + for (int index = 0; index < table_size(); index++) { + for (DictionaryEntry** p = bucket_addr(index); *p != NULL; ) { + probe = *p; + InstanceKlass* ik = InstanceKlass::cast(probe->klass()); + if (ik->is_in_error_state()) { // purge this entry + *p = probe->next(); + if (probe == _current_class_entry) { + _current_class_entry = NULL; + } + free_entry(probe); + ResourceMark rm; + tty->print_cr("Removed error class: %s", ik->external_name()); + continue; + } + + p = probe->next_addr(); + } + } +} + void Dictionary::always_strong_oops_do(OopClosure* blk) { // Follow all system classes and temporary placeholders in dictionary; only // protection domain oops contain references into the heap. In a first @@ -693,16 +705,17 @@ void SymbolPropertyTable::methods_do(void f(Method*)) { // ---------------------------------------------------------------------------- -#ifndef PRODUCT -void Dictionary::print() { +void Dictionary::print(bool details) { ResourceMark rm; HandleMark hm; - tty->print_cr("Java system dictionary (table_size=%d, classes=%d)", - table_size(), number_of_entries()); - tty->print_cr("^ indicates that initiating loader is different from " - "defining loader"); + if (details) { + tty->print_cr("Java system dictionary (table_size=%d, classes=%d)", + table_size(), number_of_entries()); + tty->print_cr("^ indicates that initiating loader is different from " + "defining loader"); + } for (int index = 0; index < table_size(); index++) { for (DictionaryEntry* probe = bucket(index); @@ -713,21 +726,28 @@ void Dictionary::print() { ClassLoaderData* loader_data = probe->loader_data(); bool is_defining_class = (loader_data == InstanceKlass::cast(e)->class_loader_data()); - tty->print("%s%s", is_defining_class ? " " : "^", + tty->print("%s%s", ((!details) || is_defining_class) ? " " : "^", e->external_name()); + if (details) { tty->print(", loader "); - loader_data->print_value(); + if (loader_data != NULL) { + loader_data->print_value(); + } else { + tty->print("NULL"); + } + } tty->cr(); } } - tty->cr(); - _pd_cache_table->print(); + + if (details) { + tty->cr(); + _pd_cache_table->print(); + } tty->cr(); } -#endif - void Dictionary::verify() { guarantee(number_of_entries() >= 0, "Verify of system dictionary failed"); diff --git a/hotspot/src/share/vm/classfile/dictionary.hpp b/hotspot/src/share/vm/classfile/dictionary.hpp index 042772f45cc..180f07e9e01 100644 --- a/hotspot/src/share/vm/classfile/dictionary.hpp +++ b/hotspot/src/share/vm/classfile/dictionary.hpp @@ -100,6 +100,7 @@ public: void methods_do(void f(Method*)); void unlink(BoolObjectClosure* is_alive); + void remove_classes_in_error_state(); // Classes loaded by the bootstrap loader are always strongly reachable. // If we're not doing class unloading, all classes are strongly reachable. @@ -108,9 +109,8 @@ public: return (loader_data->is_the_null_class_loader_data() || !ClassUnloading); } - // Unload (that is, break root links to) all unmarked classes and - // loaders. Returns "true" iff something was unloaded. - bool do_unloading(); + // Unload (that is, break root links to) all unmarked classes and loaders. + void do_unloading(); // Protection domains Klass* find(int index, unsigned int hash, Symbol* name, @@ -127,9 +127,7 @@ public: ProtectionDomainCacheEntry* cache_get(oop protection_domain); -#ifndef PRODUCT - void print(); -#endif + void print(bool details = true); void verify(); }; diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp index cfd3a68356c..5b5e20e610c 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.cpp +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp @@ -620,7 +620,6 @@ void java_lang_Class::create_mirror(KlassHandle k, Handle class_loader, // Two-way link between the array klass and its component mirror: // (array_klass) k -> mirror -> component_mirror -> array_klass -> k set_component_mirror(mirror(), comp_mirror()); - ArrayKlass::cast(k())->set_component_mirror(comp_mirror()); set_array_klass(comp_mirror(), k()); } else { assert(k->oop_is_instance(), "Must be"); @@ -682,10 +681,9 @@ void java_lang_Class::set_protection_domain(oop java_class, oop pd) { } void java_lang_Class::set_component_mirror(oop java_class, oop comp_mirror) { - if (_component_mirror_offset != 0) { + assert(_component_mirror_offset != 0, "must be set"); java_class->obj_field_put(_component_mirror_offset, comp_mirror); } -} oop java_lang_Class::component_mirror(oop java_class) { assert(_component_mirror_offset != 0, "must be set"); return java_class->obj_field(_component_mirror_offset); @@ -875,22 +873,27 @@ void java_lang_Class::compute_offsets() { assert(!offsets_computed, "offsets should be initialized only once"); offsets_computed = true; - Klass* klass_oop = SystemDictionary::Class_klass(); + Klass* k = SystemDictionary::Class_klass(); // The classRedefinedCount field is only present starting in 1.5, // so don't go fatal. compute_optional_offset(classRedefinedCount_offset, - klass_oop, vmSymbols::classRedefinedCount_name(), vmSymbols::int_signature()); + k, vmSymbols::classRedefinedCount_name(), vmSymbols::int_signature()); // Needs to be optional because the old build runs Queens during bootstrapping // and jdk8-9 doesn't have coordinated pushes yet. compute_optional_offset(_class_loader_offset, - klass_oop, vmSymbols::classLoader_name(), + k, vmSymbols::classLoader_name(), vmSymbols::classloader_signature()); - compute_optional_offset(_component_mirror_offset, - klass_oop, vmSymbols::componentType_name(), + compute_offset(_component_mirror_offset, + k, vmSymbols::componentType_name(), vmSymbols::class_signature()); + // Init lock is a C union with component_mirror. Only instanceKlass mirrors have + // init_lock and only ArrayKlass mirrors have component_mirror. Since both are oops + // GC treats them the same. + _init_lock_offset = _component_mirror_offset; + CLASS_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET); } diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp index aeafbfbf8b1..ef3e8ec78b0 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.hpp +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp @@ -222,7 +222,6 @@ class java_lang_String : AllStatic { macro(java_lang_Class, oop_size, int_signature, false) \ macro(java_lang_Class, static_oop_field_count, int_signature, false) \ macro(java_lang_Class, protection_domain, object_signature, false) \ - macro(java_lang_Class, init_lock, object_signature, false) \ macro(java_lang_Class, signers, object_signature, false) class java_lang_Class : AllStatic { diff --git a/hotspot/src/share/vm/classfile/sharedClassUtil.hpp b/hotspot/src/share/vm/classfile/sharedClassUtil.hpp new file mode 100644 index 00000000000..13be2b1b5de --- /dev/null +++ b/hotspot/src/share/vm/classfile/sharedClassUtil.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_CLASSFILE_SHAREDCLASSUTIL_HPP +#define SHARE_VM_CLASSFILE_SHAREDCLASSUTIL_HPP + +#include "classfile/sharedPathsMiscInfo.hpp" +#include "memory/filemap.hpp" + +class SharedClassUtil : AllStatic { +public: + + static SharedPathsMiscInfo* allocate_shared_paths_misc_info() { + return new SharedPathsMiscInfo(); + } + + static SharedPathsMiscInfo* allocate_shared_paths_misc_info(char* buf, int size) { + return new SharedPathsMiscInfo(buf, size); + } + + static FileMapInfo::FileMapHeader* allocate_file_map_header() { + return new FileMapInfo::FileMapHeader(); + } + + static size_t file_map_header_size() { + return sizeof(FileMapInfo::FileMapHeader); + } + + static size_t shared_class_path_entry_size() { + return sizeof(SharedClassPathEntry); + } + + static void update_shared_classpath(ClassPathEntry *cpe, + SharedClassPathEntry* ent, + time_t timestamp, + long filesize, TRAPS) { + ent->_timestamp = timestamp; + ent->_filesize = filesize; + } + + static void initialize(TRAPS) {} + + inline static bool is_shared_boot_class(Klass* klass) { + return (klass->_shared_class_path_index >= 0); + } +}; + +#endif // SHARE_VM_CLASSFILE_SHAREDCLASSUTIL_HPP diff --git a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.cpp b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.cpp new file mode 100644 index 00000000000..79457a0e5e2 --- /dev/null +++ b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "classfile/classLoader.hpp" +#include "classfile/classLoaderData.inline.hpp" +#include "classfile/sharedPathsMiscInfo.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/metaspaceShared.hpp" +#include "runtime/arguments.hpp" + +void SharedPathsMiscInfo::add_path(const char* path, int type) { + if (TraceClassPaths) { + tty->print("[type=%s] ", type_name(type)); + trace_class_path("[Add misc shared path ", path); + } + write(path, strlen(path) + 1); + write_jint(jint(type)); +} + +void SharedPathsMiscInfo::ensure_size(size_t needed_bytes) { + assert(_allocated, "cannot modify buffer during validation."); + int used = get_used_bytes(); + int target = used + int(needed_bytes); + if (target > _buf_size) { + _buf_size = _buf_size * 2 + (int)needed_bytes; + _buf_start = REALLOC_C_HEAP_ARRAY(char, _buf_start, _buf_size, mtClass); + _cur_ptr = _buf_start + used; + _end_ptr = _buf_start + _buf_size; + } +} + +void SharedPathsMiscInfo::write(const void* ptr, size_t size) { + ensure_size(size); + memcpy(_cur_ptr, ptr, size); + _cur_ptr += size; +} + +bool SharedPathsMiscInfo::read(void* ptr, size_t size) { + if (_cur_ptr + size <= _end_ptr) { + memcpy(ptr, _cur_ptr, size); + _cur_ptr += size; + return true; + } + return false; +} + +bool SharedPathsMiscInfo::fail(const char* msg, const char* name) { + ClassLoader::trace_class_path(msg, name); + MetaspaceShared::set_archive_loading_failed(); + return false; +} + +bool SharedPathsMiscInfo::check() { + // The whole buffer must be 0 terminated so that we can use strlen and strcmp + // without fear. + _end_ptr -= sizeof(jint); + if (_cur_ptr >= _end_ptr) { + return fail("Truncated archive file header"); + } + if (*_end_ptr != 0) { + return fail("Corrupted archive file header"); + } + + while (_cur_ptr < _end_ptr) { + jint type; + const char* path = _cur_ptr; + _cur_ptr += strlen(path) + 1; + if (!read_jint(&type)) { + return fail("Corrupted archive file header"); + } + if (TraceClassPaths) { + tty->print("[type=%s ", type_name(type)); + print_path(tty, type, path); + tty->print_cr("]"); + } + if (!check(type, path)) { + if (!PrintSharedArchiveAndExit) { + return false; + } + } else { + trace_class_path("[ok"); + } + } + + return true; +} + +bool SharedPathsMiscInfo::check(jint type, const char* path) { + switch (type) { + case BOOT: + if (strcmp(path, Arguments::get_sysclasspath()) != 0) { + return fail("[BOOT classpath mismatch, actual: -Dsun.boot.class.path=", Arguments::get_sysclasspath()); + } + break; + case NON_EXIST: // fall-through + case REQUIRED: + { + struct stat st; + if (os::stat(path, &st) != 0) { + // The file does not actually exist + if (type == REQUIRED) { + // but we require it to exist -> fail + return fail("Required file doesn't exist"); + } + } else { + // The file actually exists + if (type == NON_EXIST) { + // But we want it to not exist -> fail + return fail("File must not exist"); + } + time_t timestamp; + long filesize; + + if (!read_time(×tamp) || !read_long(&filesize)) { + return fail("Corrupted archive file header"); + } + if (timestamp != st.st_mtime) { + return fail("Timestamp mismatch"); + } + if (filesize != st.st_size) { + return fail("File size mismatch"); + } + } + } + break; + + default: + return fail("Corrupted archive file header"); + } + + return true; +} diff --git a/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp new file mode 100644 index 00000000000..3f52648310f --- /dev/null +++ b/hotspot/src/share/vm/classfile/sharedPathsMiscInfo.hpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_CLASSFILE_SHAREDPATHSMISCINFO_HPP +#define SHARE_VM_CLASSFILE_SHAREDPATHSMISCINFO_HPP + +#include "runtime/os.hpp" + +// During dumping time, when processing class paths, we build up the dump-time +// classpath. The JAR files that exist are stored in the list ClassLoader::_first_entry. +// However, we need to store other "misc" information for run-time checking, such as +// +// + The values of Arguments::get_sysclasspath() used during dumping. +// +// + The meta-index file(s) used during dumping (incl modification time and size) +// +// + The class path elements specified during dumping but did not exist -- +// these elements must also be specified at run time, and they also must not +// exist at run time. +// +// These misc items are stored in a linear buffer in SharedPathsMiscInfo. +// The storage format is stream oriented to minimize its size. +// +// When writing the information to the archive file, SharedPathsMiscInfo is stored in +// the archive file header. At run-time, this information is used only during initialization +// (accessed using read() instead of mmap()), and is deallocated afterwards to save space. +// +// The SharedPathsMiscInfo class is used for both creating the the information (during +// dumping time) and validation (at run time). Different constructors are used in the +// two situations. See below. + +class SharedPathsMiscInfo : public CHeapObj { +protected: + char* _buf_start; + char* _cur_ptr; + char* _end_ptr; + int _buf_size; + bool _allocated; // was _buf_start allocated by me? + void ensure_size(size_t needed_bytes); + void add_path(const char* path, int type); + + void write(const void* ptr, size_t size); + bool read(void* ptr, size_t size); + + static void trace_class_path(const char* msg, const char* name = NULL) { + ClassLoader::trace_class_path(msg, name); + } +protected: + static bool fail(const char* msg, const char* name = NULL); + virtual bool check(jint type, const char* path); + +public: + enum { + INITIAL_BUF_SIZE = 128 + }; + // This constructor is used when creating the misc information (during dump) + SharedPathsMiscInfo() { + _buf_size = INITIAL_BUF_SIZE; + _cur_ptr = _buf_start = NEW_C_HEAP_ARRAY(char, _buf_size, mtClass); + _allocated = true; + } + // This constructor is used when validating the misc info (during run time) + SharedPathsMiscInfo(char *buff, int size) { + _cur_ptr = _buf_start = buff; + _end_ptr = _buf_start + size; + _buf_size = size; + _allocated = false; + } + ~SharedPathsMiscInfo() { + if (_allocated) { + FREE_C_HEAP_ARRAY(char, _buf_start, mtClass); + } + } + int get_used_bytes() { + return _cur_ptr - _buf_start; + } + void* buffer() { + return _buf_start; + } + + // writing -- + + // The path must not exist at run-time + void add_nonexist_path(const char* path) { + add_path(path, NON_EXIST); + } + + // The path must exist and have required size and modification time + void add_required_file(const char* path) { + add_path(path, REQUIRED); + + struct stat st; + if (os::stat(path, &st) != 0) { + assert(0, "sanity"); + ClassLoader::exit_with_path_failure("failed to os::stat(%s)", path); // should not happen + } + write_time(st.st_mtime); + write_long(st.st_size); + } + + // The path must exist, and must contain exactly files/dirs + void add_boot_classpath(const char* path) { + add_path(path, BOOT); + } + int write_jint(jint num) { + write(&num, sizeof(num)); + return 0; + } + void write_time(time_t t) { + write(&t, sizeof(t)); + } + void write_long(long l) { + write(&l, sizeof(l)); + } + + bool dump_to_file(int fd) { + int n = get_used_bytes(); + return (os::write(fd, _buf_start, n) == (size_t)n); + } + + // reading -- + + enum { + BOOT = 1, + NON_EXIST = 2, + REQUIRED = 3 + }; + + virtual const char* type_name(int type) { + switch (type) { + case BOOT: return "BOOT"; + case NON_EXIST: return "NON_EXIST"; + case REQUIRED: return "REQUIRED"; + default: ShouldNotReachHere(); return "?"; + } + } + + virtual void print_path(outputStream* out, int type, const char* path) { + switch (type) { + case BOOT: + out->print("Expecting -Dsun.boot.class.path=%s", path); + break; + case NON_EXIST: + out->print("Expecting that %s does not exist", path); + break; + case REQUIRED: + out->print("Expecting that file %s must exist and is not altered", path); + break; + default: + ShouldNotReachHere(); + } + } + + bool check(); + bool read_jint(jint *ptr) { + return read(ptr, sizeof(jint)); + } + bool read_long(long *ptr) { + return read(ptr, sizeof(long)); + } + bool read_time(time_t *ptr) { + return read(ptr, sizeof(time_t)); + } +}; + +#endif // SHARE_VM_CLASSFILE_SHAREDPATHSMISCINFO_HPP diff --git a/hotspot/src/share/vm/classfile/stackMapFrame.cpp b/hotspot/src/share/vm/classfile/stackMapFrame.cpp index c3f69463c48..9cb1d307a52 100644 --- a/hotspot/src/share/vm/classfile/stackMapFrame.cpp +++ b/hotspot/src/share/vm/classfile/stackMapFrame.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,21 +54,6 @@ StackMapFrame* StackMapFrame::frame_in_exception_handler(u1 flags) { return frame; } -bool StackMapFrame::has_new_object() const { - int32_t i; - for (i = 0; i < _max_locals; i++) { - if (_locals[i].is_uninitialized()) { - return true; - } - } - for (i = 0; i < _stack_size; i++) { - if (_stack[i].is_uninitialized()) { - return true; - } - } - return false; -} - void StackMapFrame::initialize_object( VerificationType old_object, VerificationType new_object) { int32_t i; diff --git a/hotspot/src/share/vm/classfile/stackMapFrame.hpp b/hotspot/src/share/vm/classfile/stackMapFrame.hpp index 237accec0d4..53582c1673c 100644 --- a/hotspot/src/share/vm/classfile/stackMapFrame.hpp +++ b/hotspot/src/share/vm/classfile/stackMapFrame.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2014, 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 @@ -154,10 +154,6 @@ class StackMapFrame : public ResourceObj { VerificationType set_locals_from_arg( const methodHandle m, VerificationType thisKlass, TRAPS); - // Search local variable type array and stack type array. - // Return true if an uninitialized object is found. - bool has_new_object() const; - // Search local variable type array and stack type array. // Set every element with type of old_object to new_object. void initialize_object( diff --git a/hotspot/src/share/vm/classfile/stackMapTable.cpp b/hotspot/src/share/vm/classfile/stackMapTable.cpp index feb5ae250d2..f74adbe3172 100644 --- a/hotspot/src/share/vm/classfile/stackMapTable.cpp +++ b/hotspot/src/share/vm/classfile/stackMapTable.cpp @@ -70,24 +70,26 @@ int StackMapTable::get_index_from_offset(int32_t offset) const { bool StackMapTable::match_stackmap( StackMapFrame* frame, int32_t target, - bool match, bool update, ErrorContext* ctx, TRAPS) const { + bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const { int index = get_index_from_offset(target); - return match_stackmap(frame, target, index, match, update, ctx, THREAD); + return match_stackmap(frame, target, index, match, update, handler, ctx, THREAD); } // Match and/or update current_frame to the frame in stackmap table with // specified offset and frame index. Return true if the two frames match. +// handler is true if the frame in stackmap_table is for an exception handler. // -// The values of match and update are: _match__update_ +// The values of match and update are: _match__update__handler // -// checking a branch target/exception handler: true false +// checking a branch target: true false false +// checking an exception handler: true false true // linear bytecode verification following an -// unconditional branch: false true +// unconditional branch: false true false // linear bytecode verification not following an -// unconditional branch: true true +// unconditional branch: true true false bool StackMapTable::match_stackmap( StackMapFrame* frame, int32_t target, int32_t frame_index, - bool match, bool update, ErrorContext* ctx, TRAPS) const { + bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const { if (frame_index < 0 || frame_index >= _frame_count) { *ctx = ErrorContext::missing_stackmap(frame->offset()); frame->verifier()->verify_error( @@ -98,11 +100,9 @@ bool StackMapTable::match_stackmap( StackMapFrame *stackmap_frame = _frame_array[frame_index]; bool result = true; if (match) { - // when checking handler target, match == true && update == false - bool is_exception_handler = !update; // Has direct control flow from last instruction, need to match the two // frames. - result = frame->is_assignable_to(stackmap_frame, is_exception_handler, + result = frame->is_assignable_to(stackmap_frame, handler, ctx, CHECK_VERIFY_(frame->verifier(), result)); } if (update) { @@ -126,24 +126,10 @@ void StackMapTable::check_jump_target( StackMapFrame* frame, int32_t target, TRAPS) const { ErrorContext ctx; bool match = match_stackmap( - frame, target, true, false, &ctx, CHECK_VERIFY(frame->verifier())); + frame, target, true, false, false, &ctx, CHECK_VERIFY(frame->verifier())); if (!match || (target < 0 || target >= _code_length)) { frame->verifier()->verify_error(ctx, "Inconsistent stackmap frames at branch target %d", target); - return; - } - // check if uninitialized objects exist on backward branches - check_new_object(frame, target, CHECK_VERIFY(frame->verifier())); - frame->verifier()->update_furthest_jump(target); -} - -void StackMapTable::check_new_object( - const StackMapFrame* frame, int32_t target, TRAPS) const { - if (frame->offset() > target && frame->has_new_object()) { - frame->verifier()->verify_error( - ErrorContext::bad_code(frame->offset()), - "Uninitialized object exists on backward branch %d", target); - return; } } diff --git a/hotspot/src/share/vm/classfile/stackMapTable.hpp b/hotspot/src/share/vm/classfile/stackMapTable.hpp index 385aef3a7e6..590adda5f67 100644 --- a/hotspot/src/share/vm/classfile/stackMapTable.hpp +++ b/hotspot/src/share/vm/classfile/stackMapTable.hpp @@ -60,12 +60,12 @@ class StackMapTable : public StackObj { // specified offset. Return true if the two frames match. bool match_stackmap( StackMapFrame* current_frame, int32_t offset, - bool match, bool update, ErrorContext* ctx, TRAPS) const; + bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const; // Match and/or update current_frame to the frame in stackmap table with // specified offset and frame index. Return true if the two frames match. bool match_stackmap( StackMapFrame* current_frame, int32_t offset, int32_t frame_index, - bool match, bool update, ErrorContext* ctx, TRAPS) const; + bool match, bool update, bool handler, ErrorContext* ctx, TRAPS) const; // Check jump instructions. Make sure there are no uninitialized // instances on backward branch. @@ -76,10 +76,6 @@ class StackMapTable : public StackObj { // Returns the frame array index where the frame with offset is stored. int get_index_from_offset(int32_t offset) const; - // Make sure that there's no uninitialized object exist on backward branch. - void check_new_object( - const StackMapFrame* frame, int32_t target, TRAPS) const; - void print_on(outputStream* str) const; }; diff --git a/hotspot/src/share/vm/classfile/stringTable.cpp b/hotspot/src/share/vm/classfile/stringTable.cpp index 46f4eba59cd..b12134a1252 100644 --- a/hotspot/src/share/vm/classfile/stringTable.cpp +++ b/hotspot/src/share/vm/classfile/stringTable.cpp @@ -109,7 +109,7 @@ oop StringTable::lookup(int index, jchar* name, } } // If the bucket size is too deep check if this hash code is insufficient. - if (count >= BasicHashtable::rehash_count && !needs_rehashing()) { + if (count >= rehash_count && !needs_rehashing()) { _needs_rehashing = check_rehash_table(count); } return NULL; diff --git a/hotspot/src/share/vm/classfile/stringTable.hpp b/hotspot/src/share/vm/classfile/stringTable.hpp index af6f909f200..e8f42834d07 100644 --- a/hotspot/src/share/vm/classfile/stringTable.hpp +++ b/hotspot/src/share/vm/classfile/stringTable.hpp @@ -28,7 +28,7 @@ #include "memory/allocation.inline.hpp" #include "utilities/hashtable.hpp" -class StringTable : public Hashtable { +class StringTable : public RehashableHashtable { friend class VMStructs; friend class Symbol; @@ -55,11 +55,11 @@ private: // in the range [start_idx, end_idx). static void buckets_unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f, int start_idx, int end_idx, int* processed, int* removed); - StringTable() : Hashtable((int)StringTableSize, + StringTable() : RehashableHashtable((int)StringTableSize, sizeof (HashtableEntry)) {} StringTable(HashtableBucket* t, int number_of_entries) - : Hashtable((int)StringTableSize, sizeof (HashtableEntry), t, + : RehashableHashtable((int)StringTableSize, sizeof (HashtableEntry), t, number_of_entries) {} public: // The string table diff --git a/hotspot/src/share/vm/classfile/symbolTable.cpp b/hotspot/src/share/vm/classfile/symbolTable.cpp index 00761e478ad..ba9d2a00b25 100644 --- a/hotspot/src/share/vm/classfile/symbolTable.cpp +++ b/hotspot/src/share/vm/classfile/symbolTable.cpp @@ -70,9 +70,9 @@ Symbol* SymbolTable::allocate_symbol(const u1* name, int len, bool c_heap, TRAPS void SymbolTable::initialize_symbols(int arena_alloc_size) { // Initialize the arena for global symbols, size passed in depends on CDS. if (arena_alloc_size == 0) { - _arena = new (mtSymbol) Arena(); + _arena = new (mtSymbol) Arena(mtSymbol); } else { - _arena = new (mtSymbol) Arena(arena_alloc_size); + _arena = new (mtSymbol) Arena(mtSymbol, arena_alloc_size); } } @@ -201,7 +201,7 @@ Symbol* SymbolTable::lookup(int index, const char* name, } } // If the bucket size is too deep check if this hash code is insufficient. - if (count >= BasicHashtable::rehash_count && !needs_rehashing()) { + if (count >= rehash_count && !needs_rehashing()) { _needs_rehashing = check_rehash_table(count); } return NULL; diff --git a/hotspot/src/share/vm/classfile/symbolTable.hpp b/hotspot/src/share/vm/classfile/symbolTable.hpp index c5dd75b82ea..81f3c085a00 100644 --- a/hotspot/src/share/vm/classfile/symbolTable.hpp +++ b/hotspot/src/share/vm/classfile/symbolTable.hpp @@ -73,7 +73,7 @@ class TempNewSymbol : public StackObj { operator Symbol*() { return _temp; } }; -class SymbolTable : public Hashtable { +class SymbolTable : public RehashableHashtable { friend class VMStructs; friend class ClassFileParser; @@ -109,10 +109,10 @@ private: Symbol* lookup(int index, const char* name, int len, unsigned int hash); SymbolTable() - : Hashtable(SymbolTableSize, sizeof (HashtableEntry)) {} + : RehashableHashtable(SymbolTableSize, sizeof (HashtableEntry)) {} SymbolTable(HashtableBucket* t, int number_of_entries) - : Hashtable(SymbolTableSize, sizeof (HashtableEntry), t, + : RehashableHashtable(SymbolTableSize, sizeof (HashtableEntry), t, number_of_entries) {} // Arena for permanent symbols (null class loader) that are never unloaded diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp index 3b058b5a625..6f865acd431 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp @@ -31,10 +31,15 @@ #include "classfile/resolutionErrors.hpp" #include "classfile/stringTable.hpp" #include "classfile/systemDictionary.hpp" +#if INCLUDE_CDS +#include "classfile/sharedClassUtil.hpp" +#include "classfile/systemDictionaryShared.hpp" +#endif #include "classfile/vmSymbols.hpp" #include "compiler/compileBroker.hpp" #include "interpreter/bytecodeStream.hpp" #include "interpreter/interpreter.hpp" +#include "memory/filemap.hpp" #include "memory/gcLocker.hpp" #include "memory/oopFactory.hpp" #include "oops/instanceKlass.hpp" @@ -110,6 +115,8 @@ void SystemDictionary::compute_java_system_loader(TRAPS) { CHECK); _java_system_loader = (oop)result.get_jobject(); + + CDS_ONLY(SystemDictionaryShared::initialize(CHECK);) } @@ -974,6 +981,7 @@ Klass* SystemDictionary::parse_stream(Symbol* class_name, // Create a new CLD for anonymous class, that uses the same class loader // as the host_klass guarantee(host_klass->class_loader() == class_loader(), "should be the same"); + guarantee(!DumpSharedSpaces, "must not create anonymous classes when dumping"); loader_data = ClassLoaderData::anonymous_class_loader_data(class_loader(), CHECK_NULL); loader_data->record_dependency(host_klass(), CHECK_NULL); } else { @@ -1134,7 +1142,7 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, return k(); } - +#if INCLUDE_CDS void SystemDictionary::set_shared_dictionary(HashtableBucket* t, int length, int number_of_entries) { assert(length == _nof_buckets * sizeof(HashtableBucket), @@ -1167,15 +1175,21 @@ Klass* SystemDictionary::find_shared_class(Symbol* class_name) { instanceKlassHandle SystemDictionary::load_shared_class( Symbol* class_name, Handle class_loader, TRAPS) { instanceKlassHandle ik (THREAD, find_shared_class(class_name)); - return load_shared_class(ik, class_loader, THREAD); + // Make sure we only return the boot class for the NULL classloader. + if (ik.not_null() && + SharedClassUtil::is_shared_boot_class(ik()) && class_loader.is_null()) { + Handle protection_domain; + return load_shared_class(ik, class_loader, protection_domain, THREAD); + } + return instanceKlassHandle(); } -instanceKlassHandle SystemDictionary::load_shared_class( - instanceKlassHandle ik, Handle class_loader, TRAPS) { - assert(class_loader.is_null(), "non-null classloader for shared class?"); +instanceKlassHandle SystemDictionary::load_shared_class(instanceKlassHandle ik, + Handle class_loader, + Handle protection_domain, TRAPS) { if (ik.not_null()) { instanceKlassHandle nh = instanceKlassHandle(); // null Handle - Symbol* class_name = ik->name(); + Symbol* class_name = ik->name(); // Found the class, now load the superclass and interfaces. If they // are shared, add them to the main system dictionary and reset @@ -1184,7 +1198,7 @@ instanceKlassHandle SystemDictionary::load_shared_class( if (ik->super() != NULL) { Symbol* cn = ik->super()->name(); resolve_super_or_fail(class_name, cn, - class_loader, Handle(), true, CHECK_(nh)); + class_loader, protection_domain, true, CHECK_(nh)); } Array* interfaces = ik->local_interfaces(); @@ -1197,7 +1211,7 @@ instanceKlassHandle SystemDictionary::load_shared_class( // reinitialized yet (they will be once the interface classes // are loaded) Symbol* name = k->name(); - resolve_super_or_fail(class_name, name, class_loader, Handle(), false, CHECK_(nh)); + resolve_super_or_fail(class_name, name, class_loader, protection_domain, false, CHECK_(nh)); } // Adjust methods to recover missing data. They need addresses for @@ -1206,30 +1220,45 @@ instanceKlassHandle SystemDictionary::load_shared_class( // Updating methods must be done under a lock so multiple // threads don't update these in parallel - // Shared classes are all currently loaded by the bootstrap - // classloader, so this will never cause a deadlock on - // a custom class loader lock. + // + // Shared classes are all currently loaded by either the bootstrap or + // internal parallel class loaders, so this will never cause a deadlock + // on a custom class loader lock. + ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader()); { Handle lockObject = compute_loader_lock_object(class_loader, THREAD); check_loader_lock_contention(lockObject, THREAD); ObjectLocker ol(lockObject, THREAD, true); - ik->restore_unshareable_info(CHECK_(nh)); + ik->restore_unshareable_info(loader_data, protection_domain, CHECK_(nh)); } if (TraceClassLoading) { ResourceMark rm; tty->print("[Loaded %s", ik->external_name()); tty->print(" from shared objects file"); + if (class_loader.not_null()) { + tty->print(" by %s", loader_data->loader_name()); + } tty->print_cr("]"); } + + if (DumpLoadedClassList != NULL && classlist_file->is_open()) { + // Only dump the classes that can be stored into CDS archive + if (SystemDictionaryShared::is_sharing_possible(loader_data)) { + ResourceMark rm(THREAD); + classlist_file->print_cr("%s", ik->name()->as_C_string()); + classlist_file->flush(); + } + } + // notify a class loaded from shared object ClassLoadingService::notify_class_loaded(InstanceKlass::cast(ik()), true /* shared class */); } return ik; } - +#endif // INCLUDE_CDS instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) { instanceKlassHandle nh = instanceKlassHandle(); // null Handle @@ -1239,8 +1268,10 @@ instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Ha // shared spaces. instanceKlassHandle k; { +#if INCLUDE_CDS PerfTraceTime vmtimer(ClassLoader::perf_shared_classload_time()); k = load_shared_class(class_name, class_loader, THREAD); +#endif } if (k.is_null()) { @@ -1599,7 +1630,6 @@ void SystemDictionary::add_to_hierarchy(instanceKlassHandle k, TRAPS) { Universe::flush_dependents_on(k); } - // ---------------------------------------------------------------------------- // GC support @@ -1661,10 +1691,9 @@ public: // Note: anonymous classes are not in the SD. bool SystemDictionary::do_unloading(BoolObjectClosure* is_alive) { // First, mark for unload all ClassLoaderData referencing a dead class loader. - bool has_dead_loaders = ClassLoaderDataGraph::do_unloading(is_alive); - bool unloading_occurred = false; - if (has_dead_loaders) { - unloading_occurred = dictionary()->do_unloading(); + bool unloading_occurred = ClassLoaderDataGraph::do_unloading(is_alive); + if (unloading_occurred) { + dictionary()->do_unloading(); constraints()->purge_loader_constraints(); resolution_errors()->purge_resolution_errors(); } @@ -1682,6 +1711,7 @@ bool SystemDictionary::do_unloading(BoolObjectClosure* is_alive) { void SystemDictionary::roots_oops_do(OopClosure* strong, OopClosure* weak) { strong->do_oop(&_java_system_loader); strong->do_oop(&_system_loader_lock_obj); + CDS_ONLY(SystemDictionaryShared::roots_oops_do(strong);) // Adjust dictionary dictionary()->roots_oops_do(strong, weak); @@ -1693,6 +1723,7 @@ void SystemDictionary::roots_oops_do(OopClosure* strong, OopClosure* weak) { void SystemDictionary::oops_do(OopClosure* f) { f->do_oop(&_java_system_loader); f->do_oop(&_system_loader_lock_obj); + CDS_ONLY(SystemDictionaryShared::oops_do(f);) // Adjust dictionary dictionary()->oops_do(f); @@ -1754,6 +1785,10 @@ void SystemDictionary::methods_do(void f(Method*)) { invoke_method_table()->methods_do(f); } +void SystemDictionary::remove_classes_in_error_state() { + dictionary()->remove_classes_in_error_state(); +} + // ---------------------------------------------------------------------------- // Lazily load klasses @@ -2563,10 +2598,12 @@ int SystemDictionary::number_of_classes() { // ---------------------------------------------------------------------------- -#ifndef PRODUCT +void SystemDictionary::print_shared(bool details) { + shared_dictionary()->print(details); +} -void SystemDictionary::print() { - dictionary()->print(); +void SystemDictionary::print(bool details) { + dictionary()->print(details); // Placeholders GCMutexLocker mu(SystemDictionary_lock); @@ -2576,7 +2613,6 @@ void SystemDictionary::print() { constraints()->print(); } -#endif void SystemDictionary::verify() { guarantee(dictionary() != NULL, "Verify of system dictionary failed"); diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp index f46f67bd9b5..33ecc83480b 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.hpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp @@ -111,6 +111,7 @@ class Ticks; do_klass(SecurityManager_klass, java_lang_SecurityManager, Pre ) \ do_klass(ProtectionDomain_klass, java_security_ProtectionDomain, Pre ) \ do_klass(AccessControlContext_klass, java_security_AccessControlContext, Pre ) \ + do_klass(SecureClassLoader_klass, java_security_SecureClassLoader, Pre ) \ do_klass(ClassNotFoundException_klass, java_lang_ClassNotFoundException, Pre ) \ do_klass(NoClassDefFoundError_klass, java_lang_NoClassDefFoundError, Pre ) \ do_klass(LinkageError_klass, java_lang_LinkageError, Pre ) \ @@ -166,6 +167,15 @@ class Ticks; do_klass(StringBuilder_klass, java_lang_StringBuilder, Pre ) \ do_klass(misc_Unsafe_klass, sun_misc_Unsafe, Pre ) \ \ + /* support for CDS */ \ + do_klass(ByteArrayInputStream_klass, java_io_ByteArrayInputStream, Pre ) \ + do_klass(File_klass, java_io_File, Pre ) \ + do_klass(URLClassLoader_klass, java_net_URLClassLoader, Pre ) \ + do_klass(URL_klass, java_net_URL, Pre ) \ + do_klass(Jar_Manifest_klass, java_util_jar_Manifest, Pre ) \ + do_klass(sun_misc_Launcher_klass, sun_misc_Launcher, Pre ) \ + do_klass(CodeSource_klass, java_security_CodeSource, Pre ) \ + \ /* It's NULL in non-1.4 JDKs. */ \ do_klass(StackTraceElement_klass, java_lang_StackTraceElement, Opt ) \ /* It's okay if this turns out to be NULL in non-1.4 JDKs. */ \ @@ -221,7 +231,7 @@ class SystemDictionary : AllStatic { static Klass* resolve_or_fail(Symbol* class_name, Handle class_loader, Handle protection_domain, bool throw_error, TRAPS); // Convenient call for null loader and protection domain. static Klass* resolve_or_fail(Symbol* class_name, bool throw_error, TRAPS); -private: +protected: // handle error translation for resolve_or_null results static Klass* handle_resolution_exception(Symbol* class_name, bool throw_error, KlassHandle klass_h, TRAPS); @@ -326,6 +336,9 @@ public: // loaders. Returns "true" iff something was unloaded. static bool do_unloading(BoolObjectClosure* is_alive); + // Used by DumpSharedSpaces only to remove classes that failed verification + static void remove_classes_in_error_state(); + static int calculate_systemdictionary_size(int loadedclasses); // Applies "f->do_oop" to all root oops in the system dictionary. @@ -335,7 +348,7 @@ public: // System loader lock static oop system_loader_lock() { return _system_loader_lock_obj; } -private: +protected: // Extended Redefine classes support (tbi) static void preloaded_classes_do(KlassClosure* f); static void lazily_loaded_classes_do(KlassClosure* f); @@ -348,7 +361,8 @@ public: static void set_shared_dictionary(HashtableBucket* t, int length, int number_of_entries); // Printing - static void print() PRODUCT_RETURN; + static void print(bool details = true); + static void print_shared(bool details = true); static void print_class_statistics() PRODUCT_RETURN; static void print_method_statistics() PRODUCT_RETURN; @@ -424,7 +438,7 @@ public: static void load_abstract_ownable_synchronizer_klass(TRAPS); -private: +protected: // Tells whether ClassLoader.loadClassInternal is present static bool has_loadClassInternal() { return _has_loadClassInternal; } @@ -452,7 +466,7 @@ public: // Register a new class loader static ClassLoaderData* register_loader(Handle class_loader, TRAPS); -private: +protected: // Mirrors for primitive classes (created eagerly) static oop check_mirror(oop m) { assert(m != NULL, "mirror not initialized"); @@ -523,7 +537,7 @@ public: static Symbol* find_resolution_error(constantPoolHandle pool, int which, Symbol** message); - private: + protected: enum Constants { _loader_constraint_size = 107, // number of entries in constraint table @@ -574,7 +588,7 @@ public: friend class CounterDecay; static Klass* try_get_next_class(); -private: +protected: static void validate_protection_domain(instanceKlassHandle klass, Handle class_loader, Handle protection_domain, TRAPS); @@ -601,10 +615,10 @@ private: static instanceKlassHandle find_or_define_instance_class(Symbol* class_name, Handle class_loader, instanceKlassHandle k, TRAPS); - static instanceKlassHandle load_shared_class(Symbol* class_name, - Handle class_loader, TRAPS); static instanceKlassHandle load_shared_class(instanceKlassHandle ik, - Handle class_loader, TRAPS); + Handle class_loader, + Handle protection_domain, + TRAPS); static instanceKlassHandle load_instance_class(Symbol* class_name, Handle class_loader, TRAPS); static Handle compute_loader_lock_object(Handle class_loader, TRAPS); static void check_loader_lock_contention(Handle loader_lock, TRAPS); @@ -612,9 +626,12 @@ private: static bool is_parallelDefine(Handle class_loader); public: + static instanceKlassHandle load_shared_class(Symbol* class_name, + Handle class_loader, + TRAPS); static bool is_ext_class_loader(Handle class_loader); -private: +protected: static Klass* find_shared_class(Symbol* class_name); // Setup link to hierarchy diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp new file mode 100644 index 00000000000..bc688798ce8 --- /dev/null +++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +#ifndef SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP +#define SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP + +#include "classfile/systemDictionary.hpp" + +class SystemDictionaryShared: public SystemDictionary { +public: + static void initialize(TRAPS) {} + static instanceKlassHandle find_or_load_shared_class(Symbol* class_name, + Handle class_loader, + TRAPS) { + return instanceKlassHandle(); + } + static void roots_oops_do(OopClosure* blk) {} + static void oops_do(OopClosure* f) {} + static bool is_sharing_possible(ClassLoaderData* loader_data) { + oop class_loader = loader_data->class_loader(); + return (class_loader == NULL); + } +}; + +#endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP diff --git a/hotspot/src/share/vm/classfile/verifier.cpp b/hotspot/src/share/vm/classfile/verifier.cpp index 6fc9ed239af..89d9d8b5323 100644 --- a/hotspot/src/share/vm/classfile/verifier.cpp +++ b/hotspot/src/share/vm/classfile/verifier.cpp @@ -620,8 +620,6 @@ void ClassVerifier::verify_method(methodHandle m, TRAPS) { // flow from current instruction to the next // instruction in sequence - set_furthest_jump(0); - Bytecodes::Code opcode; while (!bcs.is_last_bytecode()) { // Check for recursive re-verification before each bytecode. @@ -1780,7 +1778,7 @@ u2 ClassVerifier::verify_stackmap_table(u2 stackmap_index, u2 bci, // If matched, current_frame will be updated by this method. bool matches = stackmap_table->match_stackmap( current_frame, this_offset, stackmap_index, - !no_control_flow, true, &ctx, CHECK_VERIFY_(this, 0)); + !no_control_flow, true, false, &ctx, CHECK_VERIFY_(this, 0)); if (!matches) { // report type error verify_error(ctx, "Instruction type does not match stack map"); @@ -1827,7 +1825,7 @@ void ClassVerifier::verify_exception_handler_targets(u2 bci, bool this_uninit, S } ErrorContext ctx; bool matches = stackmap_table->match_stackmap( - new_frame, handler_pc, true, false, &ctx, CHECK_VERIFY(this)); + new_frame, handler_pc, true, false, true, &ctx, CHECK_VERIFY(this)); if (!matches) { verify_error(ctx, "Stack map does not match the one at " "exception handler %d", handler_pc); @@ -2219,6 +2217,181 @@ void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs, } } +// Look at the method's handlers. If the bci is in the handler's try block +// then check if the handler_pc is already on the stack. If not, push it. +void ClassVerifier::push_handlers(ExceptionTable* exhandlers, + GrowableArray* handler_stack, + u4 bci) { + int exlength = exhandlers->length(); + for(int x = 0; x < exlength; x++) { + if (bci >= exhandlers->start_pc(x) && bci < exhandlers->end_pc(x)) { + handler_stack->append_if_missing(exhandlers->handler_pc(x)); + } + } +} + +// Return TRUE if all code paths starting with start_bc_offset end in +// bytecode athrow or loop. +bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) { + ResourceMark rm; + // Create bytecode stream. + RawBytecodeStream bcs(method()); + u4 code_length = method()->code_size(); + bcs.set_start(start_bc_offset); + u4 target; + // Create stack for storing bytecode start offsets for if* and *switch. + GrowableArray* bci_stack = new GrowableArray(30); + // Create stack for handlers for try blocks containing this handler. + GrowableArray* handler_stack = new GrowableArray(30); + // Create list of visited branch opcodes (goto* and if*). + GrowableArray* visited_branches = new GrowableArray(30); + ExceptionTable exhandlers(_method()); + + while (true) { + if (bcs.is_last_bytecode()) { + // if no more starting offsets to parse or if at the end of the + // method then return false. + if ((bci_stack->is_empty()) || ((u4)bcs.end_bci() == code_length)) + return false; + // Pop a bytecode starting offset and scan from there. + bcs.set_start(bci_stack->pop()); + } + Bytecodes::Code opcode = bcs.raw_next(); + u4 bci = bcs.bci(); + + // If the bytecode is in a TRY block, push its handlers so they + // will get parsed. + push_handlers(&exhandlers, handler_stack, bci); + + switch (opcode) { + case Bytecodes::_if_icmpeq: + case Bytecodes::_if_icmpne: + case Bytecodes::_if_icmplt: + case Bytecodes::_if_icmpge: + case Bytecodes::_if_icmpgt: + case Bytecodes::_if_icmple: + case Bytecodes::_ifeq: + case Bytecodes::_ifne: + case Bytecodes::_iflt: + case Bytecodes::_ifge: + case Bytecodes::_ifgt: + case Bytecodes::_ifle: + case Bytecodes::_if_acmpeq: + case Bytecodes::_if_acmpne: + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + target = bcs.dest(); + if (visited_branches->contains(bci)) { + if (bci_stack->is_empty()) return true; + // Pop a bytecode starting offset and scan from there. + bcs.set_start(bci_stack->pop()); + } else { + if (target > bci) { // forward branch + if (target >= code_length) return false; + // Push the branch target onto the stack. + bci_stack->push(target); + // then, scan bytecodes starting with next. + bcs.set_start(bcs.next_bci()); + } else { // backward branch + // Push bytecode offset following backward branch onto the stack. + bci_stack->push(bcs.next_bci()); + // Check bytecodes starting with branch target. + bcs.set_start(target); + } + // Record target so we don't branch here again. + visited_branches->append(bci); + } + break; + + case Bytecodes::_goto: + case Bytecodes::_goto_w: + target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w()); + if (visited_branches->contains(bci)) { + if (bci_stack->is_empty()) return true; + // Been here before, pop new starting offset from stack. + bcs.set_start(bci_stack->pop()); + } else { + if (target >= code_length) return false; + // Continue scanning from the target onward. + bcs.set_start(target); + // Record target so we don't branch here again. + visited_branches->append(bci); + } + break; + + // Check that all switch alternatives end in 'athrow' bytecodes. Since it + // is difficult to determine where each switch alternative ends, parse + // each switch alternative until either hit a 'return', 'athrow', or reach + // the end of the method's bytecodes. This is gross but should be okay + // because: + // 1. tableswitch and lookupswitch byte codes in handlers for ctor explicit + // constructor invocations should be rare. + // 2. if each switch alternative ends in an athrow then the parsing should be + // short. If there is no athrow then it is bogus code, anyway. + case Bytecodes::_lookupswitch: + case Bytecodes::_tableswitch: + { + address aligned_bcp = (address) round_to((intptr_t)(bcs.bcp() + 1), jintSize); + u4 default_offset = Bytes::get_Java_u4(aligned_bcp) + bci; + int keys, delta; + if (opcode == Bytecodes::_tableswitch) { + jint low = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize); + jint high = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize); + // This is invalid, but let the regular bytecode verifier + // report this because the user will get a better error message. + if (low > high) return true; + keys = high - low + 1; + delta = 1; + } else { + keys = (int)Bytes::get_Java_u4(aligned_bcp + jintSize); + delta = 2; + } + // Invalid, let the regular bytecode verifier deal with it. + if (keys < 0) return true; + + // Push the offset of the next bytecode onto the stack. + bci_stack->push(bcs.next_bci()); + + // Push the switch alternatives onto the stack. + for (int i = 0; i < keys; i++) { + u4 target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize); + if (target > code_length) return false; + bci_stack->push(target); + } + + // Start bytecode parsing for the switch at the default alternative. + if (default_offset > code_length) return false; + bcs.set_start(default_offset); + break; + } + + case Bytecodes::_return: + return false; + + case Bytecodes::_athrow: + { + if (bci_stack->is_empty()) { + if (handler_stack->is_empty()) { + return true; + } else { + // Parse the catch handlers for try blocks containing athrow. + bcs.set_start(handler_stack->pop()); + } + } else { + // Pop a bytecode offset and starting scanning from there. + bcs.set_start(bci_stack->pop()); + } + } + break; + + default: + ; + } // end switch + } // end while loop + + return false; +} + void ClassVerifier::verify_invoke_init( RawBytecodeStream* bcs, u2 ref_class_index, VerificationType ref_class_type, StackMapFrame* current_frame, u4 code_length, bool *this_uninit, @@ -2238,25 +2411,26 @@ void ClassVerifier::verify_invoke_init( return; } - // Make sure that this call is not jumped over. - if (bci < furthest_jump()) { - verify_error(ErrorContext::bad_code(bci), - "Bad method call from inside of a branch"); - return; - } - - // Make sure that this call is not done from within a TRY block because - // that can result in returning an incomplete object. Simply checking - // (bci >= start_pc) also ensures that this call is not done after a TRY - // block. That is also illegal because this call must be the first Java - // statement in the constructor. + // Check if this call is done from inside of a TRY block. If so, make + // sure that all catch clause paths end in a throw. Otherwise, this + // can result in returning an incomplete object. ExceptionTable exhandlers(_method()); int exlength = exhandlers.length(); for(int i = 0; i < exlength; i++) { - if (bci >= exhandlers.start_pc(i)) { - verify_error(ErrorContext::bad_code(bci), - "Bad method call from after the start of a try block"); - return; + u2 start_pc = exhandlers.start_pc(i); + u2 end_pc = exhandlers.end_pc(i); + + if (bci >= start_pc && bci < end_pc) { + if (!ends_in_athrow(exhandlers.handler_pc(i))) { + verify_error(ErrorContext::bad_code(bci), + "Bad method call from after the start of a try block"); + return; + } else if (VerboseVerification) { + ResourceMark rm; + tty->print_cr( + "Survived call to ends_in_athrow(): %s", + current_class()->name()->as_C_string()); + } } } diff --git a/hotspot/src/share/vm/classfile/verifier.hpp b/hotspot/src/share/vm/classfile/verifier.hpp index 6eecd4b2d4c..4c8b5895a21 100644 --- a/hotspot/src/share/vm/classfile/verifier.hpp +++ b/hotspot/src/share/vm/classfile/verifier.hpp @@ -30,6 +30,7 @@ #include "oops/klass.hpp" #include "oops/method.hpp" #include "runtime/handles.hpp" +#include "utilities/growableArray.hpp" #include "utilities/exceptions.hpp" // The verifier class @@ -258,9 +259,6 @@ class ClassVerifier : public StackObj { ErrorContext _error_context; // contains information about an error - // Used to detect illegal jumps over calls to super() nd this() in ctors. - int32_t _furthest_jump; - void verify_method(methodHandle method, TRAPS); char* generate_code_data(methodHandle m, u4 code_length, TRAPS); void verify_exception_handler_table(u4 code_length, char* code_data, @@ -306,6 +304,16 @@ class ClassVerifier : public StackObj { StackMapFrame* current_frame, u4 code_length, bool* this_uninit, constantPoolHandle cp, TRAPS); + // Used by ends_in_athrow() to push all handlers that contain bci onto + // the handler_stack, if the handler is not already on the stack. + void push_handlers(ExceptionTable* exhandlers, + GrowableArray* handler_stack, + u4 bci); + + // Returns true if all paths starting with start_bc_offset end in athrow + // bytecode or loop. + bool ends_in_athrow(u4 start_bc_offset); + void verify_invoke_instructions( RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame, bool* this_uninit, VerificationType return_type, @@ -407,19 +415,6 @@ class ClassVerifier : public StackObj { TypeOrigin ref_ctx(const char* str, TRAPS); - // Keep track of the furthest branch done in a method to make sure that - // there are no branches over calls to super() or this() from inside of - // a constructor. - int32_t furthest_jump() { return _furthest_jump; } - - void set_furthest_jump(int32_t target) { - _furthest_jump = target; - } - - void update_furthest_jump(int32_t target) { - if (target > _furthest_jump) _furthest_jump = target; - } - }; inline int ClassVerifier::change_sig_to_verificationType( diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index db09171dbe6..a4cf130d5c9 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -91,11 +91,17 @@ template(java_lang_CharSequence, "java/lang/CharSequence") \ template(java_lang_SecurityManager, "java/lang/SecurityManager") \ template(java_security_AccessControlContext, "java/security/AccessControlContext") \ + template(java_security_CodeSource, "java/security/CodeSource") \ template(java_security_ProtectionDomain, "java/security/ProtectionDomain") \ + template(java_security_SecureClassLoader, "java/security/SecureClassLoader") \ + template(java_net_URLClassLoader, "java/net/URLClassLoader") \ + template(java_net_URL, "java/net/URL") \ + template(java_util_jar_Manifest, "java/util/jar/Manifest") \ template(impliesCreateAccessControlContext_name, "impliesCreateAccessControlContext") \ template(java_io_OutputStream, "java/io/OutputStream") \ template(java_io_Reader, "java/io/Reader") \ template(java_io_BufferedReader, "java/io/BufferedReader") \ + template(java_io_File, "java/io/File") \ template(java_io_FileInputStream, "java/io/FileInputStream") \ template(java_io_ByteArrayInputStream, "java/io/ByteArrayInputStream") \ template(java_io_Serializable, "java/io/Serializable") \ @@ -106,6 +112,7 @@ template(java_util_Hashtable, "java/util/Hashtable") \ template(java_lang_Compiler, "java/lang/Compiler") \ template(sun_misc_Signal, "sun/misc/Signal") \ + template(sun_misc_Launcher, "sun/misc/Launcher") \ template(java_lang_AssertionStatusDirectives, "java/lang/AssertionStatusDirectives") \ template(getBootClassPathEntryForClass_name, "getBootClassPathEntryForClass") \ template(sun_misc_PostVMInitHook, "sun/misc/PostVMInitHook") \ @@ -392,10 +399,17 @@ template(oop_size_name, "oop_size") \ template(static_oop_field_count_name, "static_oop_field_count") \ template(protection_domain_name, "protection_domain") \ - template(init_lock_name, "init_lock") \ template(signers_name, "signers_name") \ template(loader_data_name, "loader_data") \ template(dependencies_name, "dependencies") \ + template(input_stream_void_signature, "(Ljava/io/InputStream;)V") \ + template(getFileURL_name, "getFileURL") \ + template(getFileURL_signature, "(Ljava/io/File;)Ljava/net/URL;") \ + template(definePackageInternal_name, "definePackageInternal") \ + template(definePackageInternal_signature, "(Ljava/lang/String;Ljava/util/jar/Manifest;Ljava/net/URL;)V") \ + template(getProtectionDomain_name, "getProtectionDomain") \ + template(getProtectionDomain_signature, "(Ljava/security/CodeSource;)Ljava/security/ProtectionDomain;") \ + template(url_code_signer_array_void_signature, "(Ljava/net/URL;[Ljava/security/CodeSigner;)V") \ \ /* non-intrinsic name/signature pairs: */ \ template(register_method_name, "register") \ @@ -732,8 +746,6 @@ do_name( isPrimitive_name, "isPrimitive") \ do_intrinsic(_getSuperclass, java_lang_Class, getSuperclass_name, void_class_signature, F_RN) \ do_name( getSuperclass_name, "getSuperclass") \ - do_intrinsic(_getComponentType, java_lang_Class, getComponentType_name, void_class_signature, F_RN) \ - do_name( getComponentType_name, "getComponentType") \ \ do_intrinsic(_getClassAccessFlags, sun_reflect_Reflection, getClassAccessFlags_name, class_int_signature, F_SN) \ do_name( getClassAccessFlags_name, "getClassAccessFlags") \ @@ -773,6 +785,11 @@ do_name( encodeISOArray_name, "encodeISOArray") \ do_signature(encodeISOArray_signature, "([CI[BII)I") \ \ + do_class(java_math_BigInteger, "java/math/BigInteger") \ + do_intrinsic(_multiplyToLen, java_math_BigInteger, multiplyToLen_name, multiplyToLen_signature, F_R) \ + do_name( multiplyToLen_name, "multiplyToLen") \ + do_signature(multiplyToLen_signature, "([II[II[I)[I") \ + \ /* java/lang/ref/Reference */ \ do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \ \ diff --git a/hotspot/src/share/vm/code/codeBlob.cpp b/hotspot/src/share/vm/code/codeBlob.cpp index 710ca948ddc..0e71d2d4c64 100644 --- a/hotspot/src/share/vm/code/codeBlob.cpp +++ b/hotspot/src/share/vm/code/codeBlob.cpp @@ -238,6 +238,7 @@ void* BufferBlob::operator new(size_t s, unsigned size, bool is_critical) throw( void BufferBlob::free( BufferBlob *blob ) { ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock + blob->flush(); { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); CodeCache::free((CodeBlob*)blob); diff --git a/hotspot/src/share/vm/code/dependencies.cpp b/hotspot/src/share/vm/code/dependencies.cpp index 0f50425bcdd..9761d8d98b3 100644 --- a/hotspot/src/share/vm/code/dependencies.cpp +++ b/hotspot/src/share/vm/code/dependencies.cpp @@ -407,56 +407,66 @@ void Dependencies::check_valid_dependency_type(DepType dept) { // for the sake of the compiler log, print out current dependencies: void Dependencies::log_all_dependencies() { if (log() == NULL) return; - ciBaseObject* args[max_arg_count]; + ResourceMark rm; for (int deptv = (int)FIRST_TYPE; deptv < (int)TYPE_LIMIT; deptv++) { DepType dept = (DepType)deptv; GrowableArray* deps = _deps[dept]; - if (deps->length() == 0) continue; + int deplen = deps->length(); + if (deplen == 0) { + continue; + } int stride = dep_args(dept); + GrowableArray* ciargs = new GrowableArray(stride); for (int i = 0; i < deps->length(); i += stride) { for (int j = 0; j < stride; j++) { // flush out the identities before printing - args[j] = deps->at(i+j); + ciargs->push(deps->at(i+j)); } - write_dependency_to(log(), dept, stride, args); + write_dependency_to(log(), dept, ciargs); + ciargs->clear(); } + guarantee(deplen == deps->length(), "deps array cannot grow inside nested ResoureMark scope"); } } void Dependencies::write_dependency_to(CompileLog* log, DepType dept, - int nargs, DepArgument args[], + GrowableArray* args, Klass* witness) { if (log == NULL) { return; } + ResourceMark rm; ciEnv* env = ciEnv::current(); - ciBaseObject* ciargs[max_arg_count]; - assert(nargs <= max_arg_count, "oob"); - for (int j = 0; j < nargs; j++) { - if (args[j].is_oop()) { - ciargs[j] = env->get_object(args[j].oop_value()); + GrowableArray* ciargs = new GrowableArray(args->length()); + for (GrowableArrayIterator it = args->begin(); it != args->end(); ++it) { + DepArgument arg = *it; + if (arg.is_oop()) { + ciargs->push(env->get_object(arg.oop_value())); } else { - ciargs[j] = env->get_metadata(args[j].metadata_value()); + ciargs->push(env->get_metadata(arg.metadata_value())); } } - Dependencies::write_dependency_to(log, dept, nargs, ciargs, witness); + int argslen = ciargs->length(); + Dependencies::write_dependency_to(log, dept, ciargs, witness); + guarantee(argslen == ciargs->length(), "ciargs array cannot grow inside nested ResoureMark scope"); } void Dependencies::write_dependency_to(CompileLog* log, DepType dept, - int nargs, ciBaseObject* args[], + GrowableArray* args, Klass* witness) { - if (log == NULL) return; - assert(nargs <= max_arg_count, "oob"); - int argids[max_arg_count]; - int ctxkj = dep_context_arg(dept); // -1 if no context arg - int j; - for (j = 0; j < nargs; j++) { - if (args[j]->is_object()) { - argids[j] = log->identify(args[j]->as_object()); + if (log == NULL) { + return; + } + ResourceMark rm; + GrowableArray* argids = new GrowableArray(args->length()); + for (GrowableArrayIterator it = args->begin(); it != args->end(); ++it) { + ciBaseObject* obj = *it; + if (obj->is_object()) { + argids->push(log->identify(obj->as_object())); } else { - argids[j] = log->identify(args[j]->as_metadata()); + argids->push(log->identify(obj->as_metadata())); } } if (witness != NULL) { @@ -465,16 +475,17 @@ void Dependencies::write_dependency_to(CompileLog* log, log->begin_elem("dependency"); } log->print(" type='%s'", dep_name(dept)); - if (ctxkj >= 0) { - log->print(" ctxk='%d'", argids[ctxkj]); + const int ctxkj = dep_context_arg(dept); // -1 if no context arg + if (ctxkj >= 0 && ctxkj < argids->length()) { + log->print(" ctxk='%d'", argids->at(ctxkj)); } // write remaining arguments, if any. - for (j = 0; j < nargs; j++) { + for (int j = 0; j < argids->length(); j++) { if (j == ctxkj) continue; // already logged if (j == 1) { - log->print( " x='%d'", argids[j]); + log->print( " x='%d'", argids->at(j)); } else { - log->print(" x%d='%d'", j, argids[j]); + log->print(" x%d='%d'", j, argids->at(j)); } } if (witness != NULL) { @@ -486,9 +497,12 @@ void Dependencies::write_dependency_to(CompileLog* log, void Dependencies::write_dependency_to(xmlStream* xtty, DepType dept, - int nargs, DepArgument args[], + GrowableArray* args, Klass* witness) { - if (xtty == NULL) return; + if (xtty == NULL) { + return; + } + ResourceMark rm; ttyLocker ttyl; int ctxkj = dep_context_arg(dept); // -1 if no context arg if (witness != NULL) { @@ -498,23 +512,24 @@ void Dependencies::write_dependency_to(xmlStream* xtty, } xtty->print(" type='%s'", dep_name(dept)); if (ctxkj >= 0) { - xtty->object("ctxk", args[ctxkj].metadata_value()); + xtty->object("ctxk", args->at(ctxkj).metadata_value()); } // write remaining arguments, if any. - for (int j = 0; j < nargs; j++) { + for (int j = 0; j < args->length(); j++) { if (j == ctxkj) continue; // already logged + DepArgument arg = args->at(j); if (j == 1) { - if (args[j].is_oop()) { - xtty->object("x", args[j].oop_value()); + if (arg.is_oop()) { + xtty->object("x", arg.oop_value()); } else { - xtty->object("x", args[j].metadata_value()); + xtty->object("x", arg.metadata_value()); } } else { char xn[10]; sprintf(xn, "x%d", j); - if (args[j].is_oop()) { - xtty->object(xn, args[j].oop_value()); + if (arg.is_oop()) { + xtty->object(xn, arg.oop_value()); } else { - xtty->object(xn, args[j].metadata_value()); + xtty->object(xn, arg.metadata_value()); } } } @@ -525,7 +540,7 @@ void Dependencies::write_dependency_to(xmlStream* xtty, xtty->end_elem(); } -void Dependencies::print_dependency(DepType dept, int nargs, DepArgument args[], +void Dependencies::print_dependency(DepType dept, GrowableArray* args, Klass* witness) { ResourceMark rm; ttyLocker ttyl; // keep the following output all in one block @@ -534,8 +549,8 @@ void Dependencies::print_dependency(DepType dept, int nargs, DepArgument args[], dep_name(dept)); // print arguments int ctxkj = dep_context_arg(dept); // -1 if no context arg - for (int j = 0; j < nargs; j++) { - DepArgument arg = args[j]; + for (int j = 0; j < args->length(); j++) { + DepArgument arg = args->at(j); bool put_star = false; if (arg.is_null()) continue; const char* what; @@ -571,31 +586,33 @@ void Dependencies::print_dependency(DepType dept, int nargs, DepArgument args[], void Dependencies::DepStream::log_dependency(Klass* witness) { if (_deps == NULL && xtty == NULL) return; // fast cutout for runtime ResourceMark rm; - int nargs = argument_count(); - DepArgument args[max_arg_count]; + const int nargs = argument_count(); + GrowableArray* args = new GrowableArray(nargs); for (int j = 0; j < nargs; j++) { if (type() == call_site_target_value) { - args[j] = argument_oop(j); + args->push(argument_oop(j)); } else { - args[j] = argument(j); + args->push(argument(j)); } } + int argslen = args->length(); if (_deps != NULL && _deps->log() != NULL) { - Dependencies::write_dependency_to(_deps->log(), - type(), nargs, args, witness); + Dependencies::write_dependency_to(_deps->log(), type(), args, witness); } else { - Dependencies::write_dependency_to(xtty, - type(), nargs, args, witness); + Dependencies::write_dependency_to(xtty, type(), args, witness); } + guarantee(argslen == args->length(), "args array cannot grow inside nested ResoureMark scope"); } void Dependencies::DepStream::print_dependency(Klass* witness, bool verbose) { + ResourceMark rm; int nargs = argument_count(); - DepArgument args[max_arg_count]; + GrowableArray* args = new GrowableArray(nargs); for (int j = 0; j < nargs; j++) { - args[j] = argument(j); + args->push(argument(j)); } - Dependencies::print_dependency(type(), nargs, args, witness); + int argslen = args->length(); + Dependencies::print_dependency(type(), args, witness); if (verbose) { if (_code != NULL) { tty->print(" code: "); @@ -603,6 +620,7 @@ void Dependencies::DepStream::print_dependency(Klass* witness, bool verbose) { tty->cr(); } } + guarantee(argslen == args->length(), "args array cannot grow inside nested ResoureMark scope"); } diff --git a/hotspot/src/share/vm/code/dependencies.hpp b/hotspot/src/share/vm/code/dependencies.hpp index 3501b57fd51..ff4ff328f8d 100644 --- a/hotspot/src/share/vm/code/dependencies.hpp +++ b/hotspot/src/share/vm/code/dependencies.hpp @@ -369,20 +369,36 @@ class Dependencies: public ResourceObj { void copy_to(nmethod* nm); void log_all_dependencies(); - void log_dependency(DepType dept, int nargs, ciBaseObject* args[]) { - write_dependency_to(log(), dept, nargs, args); + + void log_dependency(DepType dept, GrowableArray* args) { + ResourceMark rm; + int argslen = args->length(); + write_dependency_to(log(), dept, args); + guarantee(argslen == args->length(), + "args array cannot grow inside nested ResoureMark scope"); } + void log_dependency(DepType dept, ciBaseObject* x0, ciBaseObject* x1 = NULL, ciBaseObject* x2 = NULL) { - if (log() == NULL) return; - ciBaseObject* args[max_arg_count]; - args[0] = x0; - args[1] = x1; - args[2] = x2; - assert(2 < max_arg_count, ""); - log_dependency(dept, dep_args(dept), args); + if (log() == NULL) { + return; + } + ResourceMark rm; + GrowableArray* ciargs = + new GrowableArray(dep_args(dept)); + assert (x0 != NULL, "no log x0"); + ciargs->push(x0); + + if (x1 != NULL) { + ciargs->push(x1); + } + if (x2 != NULL) { + ciargs->push(x2); + } + assert(ciargs->length() == dep_args(dept), ""); + log_dependency(dept, ciargs); } class DepArgument : public ResourceObj { @@ -405,20 +421,8 @@ class Dependencies: public ResourceObj { Metadata* metadata_value() const { assert(!_is_oop && _valid, "must be"); return (Metadata*) _value; } }; - static void write_dependency_to(CompileLog* log, - DepType dept, - int nargs, ciBaseObject* args[], - Klass* witness = NULL); - static void write_dependency_to(CompileLog* log, - DepType dept, - int nargs, DepArgument args[], - Klass* witness = NULL); - static void write_dependency_to(xmlStream* xtty, - DepType dept, - int nargs, DepArgument args[], - Klass* witness = NULL); static void print_dependency(DepType dept, - int nargs, DepArgument args[], + GrowableArray* args, Klass* witness = NULL); private: @@ -427,6 +431,18 @@ class Dependencies: public ResourceObj { static Klass* ctxk_encoded_as_null(DepType dept, Metadata* x); + static void write_dependency_to(CompileLog* log, + DepType dept, + GrowableArray* args, + Klass* witness = NULL); + static void write_dependency_to(CompileLog* log, + DepType dept, + GrowableArray* args, + Klass* witness = NULL); + static void write_dependency_to(xmlStream* xtty, + DepType dept, + GrowableArray* args, + Klass* witness = NULL); public: // Use this to iterate over an nmethod's dependency set. // Works on new and old dependency sets. diff --git a/hotspot/src/share/vm/compiler/compilerOracle.cpp b/hotspot/src/share/vm/compiler/compilerOracle.cpp index b646158af28..851b390e1e7 100644 --- a/hotspot/src/share/vm/compiler/compilerOracle.cpp +++ b/hotspot/src/share/vm/compiler/compilerOracle.cpp @@ -33,6 +33,7 @@ #include "oops/symbol.hpp" #include "runtime/handles.inline.hpp" #include "runtime/jniHandles.hpp" +#include "runtime/os.hpp" class MethodMatcher : public CHeapObj { public: @@ -167,44 +168,134 @@ bool MethodMatcher::match(Symbol* candidate, Symbol* match, Mode match_mode) { } } +enum OptionType { + IntxType, + UintxType, + BoolType, + CcstrType, + UnknownType +}; -class MethodOptionMatcher: public MethodMatcher { - const char * option; - public: - MethodOptionMatcher(Symbol* class_name, Mode class_mode, - Symbol* method_name, Mode method_mode, - Symbol* signature, const char * opt, MethodMatcher* next): - MethodMatcher(class_name, class_mode, method_name, method_mode, signature, next) { - option = opt; +/* Methods to map real type names to OptionType */ +template +static OptionType get_type_for() { + return UnknownType; +}; + +template<> OptionType get_type_for() { + return IntxType; +} + +template<> OptionType get_type_for() { + return UintxType; +} + +template<> OptionType get_type_for() { + return BoolType; +} + +template<> OptionType get_type_for() { + return CcstrType; +} + +template +static const T copy_value(const T value) { + return value; +} + +template<> const ccstr copy_value(const ccstr value) { + return (const ccstr)os::strdup_check_oom(value); +} + +template +class TypedMethodOptionMatcher : public MethodMatcher { + const char* _option; + OptionType _type; + const T _value; + +public: + TypedMethodOptionMatcher(Symbol* class_name, Mode class_mode, + Symbol* method_name, Mode method_mode, + Symbol* signature, const char* opt, + const T value, MethodMatcher* next) : + MethodMatcher(class_name, class_mode, method_name, method_mode, signature, next), + _type(get_type_for()), _value(copy_value(value)) { + _option = os::strdup_check_oom(opt); } - bool match(methodHandle method, const char* opt) { - MethodOptionMatcher* current = this; + ~TypedMethodOptionMatcher() { + os::free((void*)_option); + } + + TypedMethodOptionMatcher* match(methodHandle method, const char* opt) { + TypedMethodOptionMatcher* current = this; while (current != NULL) { - current = (MethodOptionMatcher*)current->find(method); + current = (TypedMethodOptionMatcher*)current->find(method); if (current == NULL) { - return false; + return NULL; } - if (strcmp(current->option, opt) == 0) { - return true; + if (strcmp(current->_option, opt) == 0) { + return current; } current = current->next(); } - return false; + return NULL; } - MethodOptionMatcher* next() { - return (MethodOptionMatcher*)_next; + TypedMethodOptionMatcher* next() { + return (TypedMethodOptionMatcher*)_next; } - virtual void print() { + OptionType get_type(void) { + return _type; + }; + + T value() { return _value; } + + void print() { + ttyLocker ttyl; print_base(); - tty->print(" %s", option); + tty->print(" %s", _option); + tty->print(" "); tty->cr(); } }; +template<> +void TypedMethodOptionMatcher::print() { + ttyLocker ttyl; + print_base(); + tty->print(" intx %s", _option); + tty->print(" = " INTX_FORMAT, _value); + tty->cr(); +}; +template<> +void TypedMethodOptionMatcher::print() { + ttyLocker ttyl; + print_base(); + tty->print(" uintx %s", _option); + tty->print(" = " UINTX_FORMAT, _value); + tty->cr(); +}; + +template<> +void TypedMethodOptionMatcher::print() { + ttyLocker ttyl; + print_base(); + tty->print(" bool %s", _option); + tty->print(" = %s", _value ? "true" : "false"); + tty->cr(); +}; + +template<> +void TypedMethodOptionMatcher::print() { + ttyLocker ttyl; + print_base(); + tty->print(" const char* %s", _option); + tty->print(" = '%s'", _value); + tty->cr(); +}; // this must parallel the command_names below enum OracleCommand { @@ -259,23 +350,46 @@ static MethodMatcher* add_predicate(OracleCommand command, return lists[command]; } - - +template static MethodMatcher* add_option_string(Symbol* class_name, MethodMatcher::Mode c_mode, Symbol* method_name, MethodMatcher::Mode m_mode, Symbol* signature, - const char* option) { - lists[OptionCommand] = new MethodOptionMatcher(class_name, c_mode, method_name, m_mode, - signature, option, lists[OptionCommand]); + const char* option, + T value) { + lists[OptionCommand] = new TypedMethodOptionMatcher(class_name, c_mode, method_name, m_mode, + signature, option, value, lists[OptionCommand]); return lists[OptionCommand]; } - -bool CompilerOracle::has_option_string(methodHandle method, const char* option) { - return lists[OptionCommand] != NULL && - ((MethodOptionMatcher*)lists[OptionCommand])->match(method, option); +template +static bool get_option_value(methodHandle method, const char* option, T& value) { + TypedMethodOptionMatcher* m; + if (lists[OptionCommand] != NULL + && (m = ((TypedMethodOptionMatcher*)lists[OptionCommand])->match(method, option)) != NULL + && m->get_type() == get_type_for()) { + value = m->value(); + return true; + } else { + return false; + } } +bool CompilerOracle::has_option_string(methodHandle method, const char* option) { + bool value = false; + get_option_value(method, option, value); + return value; +} + +template +bool CompilerOracle::has_option_value(methodHandle method, const char* option, T& value) { + return ::get_option_value(method, option, value); +} + +// Explicit instantiation for all OptionTypes supported. +template bool CompilerOracle::has_option_value(methodHandle method, const char* option, intx& value); +template bool CompilerOracle::has_option_value(methodHandle method, const char* option, uintx& value); +template bool CompilerOracle::has_option_value(methodHandle method, const char* option, bool& value); +template bool CompilerOracle::has_option_value(methodHandle method, const char* option, ccstr& value); bool CompilerOracle::should_exclude(methodHandle method, bool& quietly) { quietly = true; @@ -417,6 +531,94 @@ static bool scan_line(const char * line, +// Scan next flag and value in line, return MethodMatcher object on success, NULL on failure. +// On failure, error_msg contains description for the first error. +// For future extensions: set error_msg on first error. +static MethodMatcher* scan_flag_and_value(const char* type, const char* line, int& total_bytes_read, + Symbol* c_name, MethodMatcher::Mode c_match, + Symbol* m_name, MethodMatcher::Mode m_match, + Symbol* signature, + char* errorbuf, const int buf_size) { + total_bytes_read = 0; + int bytes_read = 0; + char flag[256]; + + // Read flag name. + if (sscanf(line, "%*[ \t]%255[a-zA-Z0-9]%n", flag, &bytes_read) == 1) { + line += bytes_read; + total_bytes_read += bytes_read; + + // Read value. + if (strcmp(type, "intx") == 0) { + intx value; + if (sscanf(line, "%*[ \t]" INTX_FORMAT "%n", &value, &bytes_read) == 1) { + total_bytes_read += bytes_read; + return add_option_string(c_name, c_match, m_name, m_match, signature, flag, value); + } else { + jio_snprintf(errorbuf, buf_size, " Value cannot be read for flag %s of type %s ", flag, type); + } + } else if (strcmp(type, "uintx") == 0) { + uintx value; + if (sscanf(line, "%*[ \t]" UINTX_FORMAT "%n", &value, &bytes_read) == 1) { + total_bytes_read += bytes_read; + return add_option_string(c_name, c_match, m_name, m_match, signature, flag, value); + } else { + jio_snprintf(errorbuf, buf_size, " Value cannot be read for flag %s of type %s", flag, type); + } + } else if (strcmp(type, "ccstr") == 0) { + ResourceMark rm; + char* value = NEW_RESOURCE_ARRAY(char, strlen(line) + 1); + if (sscanf(line, "%*[ \t]%255[_a-zA-Z0-9]%n", value, &bytes_read) == 1) { + total_bytes_read += bytes_read; + return add_option_string(c_name, c_match, m_name, m_match, signature, flag, (ccstr)value); + } else { + jio_snprintf(errorbuf, buf_size, " Value cannot be read for flag %s of type %s", flag, type); + } + } else if (strcmp(type, "ccstrlist") == 0) { + // Accumulates several strings into one. The internal type is ccstr. + ResourceMark rm; + char* value = NEW_RESOURCE_ARRAY(char, strlen(line) + 1); + char* next_value = value; + if (sscanf(line, "%*[ \t]%255[_a-zA-Z0-9]%n", next_value, &bytes_read) == 1) { + total_bytes_read += bytes_read; + line += bytes_read; + next_value += bytes_read; + char* end_value = next_value-1; + while (sscanf(line, "%*[ \t]%255[_a-zA-Z0-9]%n", next_value, &bytes_read) == 1) { + total_bytes_read += bytes_read; + line += bytes_read; + *end_value = ' '; // override '\0' + next_value += bytes_read; + end_value = next_value-1; + } + return add_option_string(c_name, c_match, m_name, m_match, signature, flag, (ccstr)value); + } else { + jio_snprintf(errorbuf, buf_size, " Value cannot be read for flag %s of type %s", flag, type); + } + } else if (strcmp(type, "bool") == 0) { + char value[256]; + if (sscanf(line, "%*[ \t]%255[a-zA-Z]%n", value, &bytes_read) == 1) { + if (strcmp(value, "true") == 0) { + total_bytes_read += bytes_read; + return add_option_string(c_name, c_match, m_name, m_match, signature, flag, true); + } else if (strcmp(value, "false") == 0) { + total_bytes_read += bytes_read; + return add_option_string(c_name, c_match, m_name, m_match, signature, flag, false); + } else { + jio_snprintf(errorbuf, buf_size, " Value cannot be read for flag %s of type %s", flag, type); + } + } else { + jio_snprintf(errorbuf, sizeof(errorbuf), " Value cannot be read for flag %s of type %s", flag, type); + } + } else { + jio_snprintf(errorbuf, sizeof(errorbuf), " Type %s not supported ", type); + } + } else { + jio_snprintf(errorbuf, sizeof(errorbuf), " Flag name for type %s should be alphanumeric ", type); + } + return NULL; +} + void CompilerOracle::parse_from_line(char* line) { if (line[0] == '\0') return; if (line[0] == '#') return; @@ -446,8 +648,10 @@ void CompilerOracle::parse_from_line(char* line) { int bytes_read; OracleCommand command = parse_command_name(line, &bytes_read); line += bytes_read; + ResourceMark rm; if (command == UnknownCommand) { + ttyLocker ttyl; tty->print_cr("CompilerOracle: unrecognized line"); tty->print_cr(" \"%s\"", original_line); return; @@ -469,7 +673,7 @@ void CompilerOracle::parse_from_line(char* line) { char method_name[256]; char sig[1024]; char errorbuf[1024]; - const char* error_msg = NULL; + const char* error_msg = NULL; // description of first error that appears MethodMatcher* match = NULL; if (scan_line(line, class_name, &c_match, method_name, &m_match, &bytes_read, error_msg)) { @@ -488,43 +692,77 @@ void CompilerOracle::parse_from_line(char* line) { } if (command == OptionCommand) { - // Look for trailing options to support - // ciMethod::has_option("string") to control features in the - // compiler. Multiple options may follow the method name. - char option[256]; + // Look for trailing options. + // + // Two types of trailing options are + // supported: + // + // (1) CompileCommand=option,Klass::method,flag + // (2) CompileCommand=option,Klass::method,type,flag,value + // + // Type (1) is used to support ciMethod::has_option("someflag") + // (i.e., to check if a flag "someflag" is enabled for a method). + // + // Type (2) is used to support options with a value. Values can have the + // the following types: intx, uintx, bool, ccstr, and ccstrlist. + // + // For future extensions: extend scan_flag_and_value() + char option[256]; // stores flag for Type (1) and type of Type (2) while (sscanf(line, "%*[ \t]%255[a-zA-Z0-9]%n", option, &bytes_read) == 1) { if (match != NULL && !_quiet) { // Print out the last match added + ttyLocker ttyl; tty->print("CompilerOracle: %s ", command_names[command]); match->print(); } - match = add_option_string(c_name, c_match, m_name, m_match, signature, strdup(option)); line += bytes_read; - } + + if (strcmp(option, "intx") == 0 + || strcmp(option, "uintx") == 0 + || strcmp(option, "bool") == 0 + || strcmp(option, "ccstr") == 0 + || strcmp(option, "ccstrlist") == 0 + ) { + + // Type (2) option: parse flag name and value. + match = scan_flag_and_value(option, line, bytes_read, + c_name, c_match, m_name, m_match, signature, + errorbuf, sizeof(errorbuf)); + if (match == NULL) { + error_msg = errorbuf; + break; + } + line += bytes_read; + } else { + // Type (1) option + match = add_option_string(c_name, c_match, m_name, m_match, signature, option, true); + } + } // while( } else { - bytes_read = 0; - sscanf(line, "%*[ \t]%n", &bytes_read); - if (line[bytes_read] != '\0') { - jio_snprintf(errorbuf, sizeof(errorbuf), " Unrecognized text after command: %s", line); - error_msg = errorbuf; - } else { - match = add_predicate(command, c_name, c_match, m_name, m_match, signature); - } + match = add_predicate(command, c_name, c_match, m_name, m_match, signature); } } - if (match != NULL) { - if (!_quiet) { - ResourceMark rm; - tty->print("CompilerOracle: %s ", command_names[command]); - match->print(); - } - } else { + ttyLocker ttyl; + if (error_msg != NULL) { + // an error has happened tty->print_cr("CompilerOracle: unrecognized line"); tty->print_cr(" \"%s\"", original_line); if (error_msg != NULL) { tty->print_cr("%s", error_msg); } + } else { + // check for remaining characters + bytes_read = 0; + sscanf(line, "%*[ \t]%n", &bytes_read); + if (line[bytes_read] != '\0') { + tty->print_cr("CompilerOracle: unrecognized line"); + tty->print_cr(" \"%s\"", original_line); + tty->print_cr(" Unrecognized text %s after command ", line); + } else if (match != NULL && !_quiet) { + tty->print("CompilerOracle: %s ", command_names[command]); + match->print(); + } } } diff --git a/hotspot/src/share/vm/compiler/compilerOracle.hpp b/hotspot/src/share/vm/compiler/compilerOracle.hpp index df025f76be5..8a5981ff1f8 100644 --- a/hotspot/src/share/vm/compiler/compilerOracle.hpp +++ b/hotspot/src/share/vm/compiler/compilerOracle.hpp @@ -64,6 +64,11 @@ class CompilerOracle : AllStatic { // Check to see if this method has option set for it static bool has_option_string(methodHandle method, const char * option); + // Check if method has option and value set. If yes, overwrite value and return true, + // otherwise leave value unchanged and return false. + template + static bool has_option_value(methodHandle method, const char* option, T& value); + // Reads from string instead of file static void parse_from_string(const char* command_string, void (*parser)(char*)); diff --git a/hotspot/src/share/vm/compiler/disassembler.cpp b/hotspot/src/share/vm/compiler/disassembler.cpp index e9ad157ecd5..e4f6d3f5040 100644 --- a/hotspot/src/share/vm/compiler/disassembler.cpp +++ b/hotspot/src/share/vm/compiler/disassembler.cpp @@ -246,12 +246,12 @@ class decode_env { }; decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c) { - memset(this, 0, sizeof(*this)); + memset(this, 0, sizeof(*this)); // Beware, this zeroes bits of fields. _output = output ? output : tty; _code = code; if (code != NULL && code->is_nmethod()) _nm = (nmethod*) code; - _strings.assign(c); + _strings.copy(c); // by default, output pc but not bytes: _print_pc = true; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp index c2df7a29512..aed729017c0 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsCollectorPolicy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2014, 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 @@ -52,7 +52,8 @@ void ConcurrentMarkSweepPolicy::initialize_alignments() { } void ConcurrentMarkSweepPolicy::initialize_generations() { - _generations = NEW_C_HEAP_ARRAY3(GenerationSpecPtr, number_of_generations(), mtGC, 0, AllocFailStrategy::RETURN_NULL); + _generations = NEW_C_HEAP_ARRAY3(GenerationSpecPtr, number_of_generations(), mtGC, + CURRENT_PC, AllocFailStrategy::RETURN_NULL); if (_generations == NULL) vm_exit_during_initialization("Unable to allocate gen spec"); diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp index 2164628a77f..460692b5cea 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp @@ -2734,10 +2734,12 @@ void CFLS_LAB::retire(int tid) { } } -void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n, AdaptiveFreeList* fl) { - assert(fl->count() == 0, "Precondition."); - assert(word_sz < CompactibleFreeListSpace::IndexSetSize, - "Precondition"); +// Used by par_get_chunk_of_blocks() for the chunks from the +// indexed_free_lists. Looks for a chunk with size that is a multiple +// of "word_sz" and if found, splits it into "word_sz" chunks and add +// to the free list "fl". "n" is the maximum number of chunks to +// be added to "fl". +bool CompactibleFreeListSpace:: par_get_chunk_of_blocks_IFL(size_t word_sz, size_t n, AdaptiveFreeList* fl) { // We'll try all multiples of word_sz in the indexed set, starting with // word_sz itself and, if CMSSplitIndexedFreeListBlocks, try larger multiples, @@ -2818,11 +2820,15 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n Mutex::_no_safepoint_check_flag); ssize_t births = _indexedFreeList[word_sz].split_births() + num; _indexedFreeList[word_sz].set_split_births(births); - return; + return true; } } + return found; } - // Otherwise, we'll split a block from the dictionary. +} + +FreeChunk* CompactibleFreeListSpace::get_n_way_chunk_to_split(size_t word_sz, size_t n) { + FreeChunk* fc = NULL; FreeChunk* rem_fc = NULL; size_t rem; @@ -2833,16 +2839,12 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n fc = dictionary()->get_chunk(MAX2(n * word_sz, _dictionary->min_size()), FreeBlockDictionary::atLeast); if (fc != NULL) { - _bt.allocated((HeapWord*)fc, fc->size(), true /* reducing */); // update _unallocated_blk - dictionary()->dict_census_update(fc->size(), - true /*split*/, - false /*birth*/); break; } else { n--; } } - if (fc == NULL) return; + if (fc == NULL) return NULL; // Otherwise, split up that block. assert((ssize_t)n >= 1, "Control point invariant"); assert(fc->is_free(), "Error: should be a free block"); @@ -2864,10 +2866,14 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n // dictionary and return, leaving "fl" empty. if (n == 0) { returnChunkToDictionary(fc); - assert(fl->count() == 0, "We never allocated any blocks"); - return; + return NULL; } + _bt.allocated((HeapWord*)fc, fc->size(), true /* reducing */); // update _unallocated_blk + dictionary()->dict_census_update(fc->size(), + true /*split*/, + false /*birth*/); + // First return the remainder, if any. // Note that we hold the lock until we decide if we're going to give // back the remainder to the dictionary, since a concurrent allocation @@ -2900,7 +2906,24 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n _indexedFreeList[rem].return_chunk_at_head(rem_fc); smallSplitBirth(rem); } - assert((ssize_t)n > 0 && fc != NULL, "Consistency"); + assert(n * word_sz == fc->size(), + err_msg("Chunk size " SIZE_FORMAT " is not exactly splittable by " + SIZE_FORMAT " sized chunks of size " SIZE_FORMAT, + fc->size(), n, word_sz)); + return fc; +} + +void CompactibleFreeListSpace:: par_get_chunk_of_blocks_dictionary(size_t word_sz, size_t targetted_number_of_chunks, AdaptiveFreeList* fl) { + + FreeChunk* fc = get_n_way_chunk_to_split(word_sz, targetted_number_of_chunks); + + if (fc == NULL) { + return; + } + + size_t n = fc->size() / word_sz; + + assert((ssize_t)n > 0, "Consistency"); // Now do the splitting up. // Must do this in reverse order, so that anybody attempting to // access the main chunk sees it as a single free block until we @@ -2948,6 +2971,20 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n assert(fl->tail()->next() == NULL, "List invariant."); } +void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n, AdaptiveFreeList* fl) { + assert(fl->count() == 0, "Precondition."); + assert(word_sz < CompactibleFreeListSpace::IndexSetSize, + "Precondition"); + + if (par_get_chunk_of_blocks_IFL(word_sz, n, fl)) { + // Got it + return; + } + + // Otherwise, we'll split a block from the dictionary. + par_get_chunk_of_blocks_dictionary(word_sz, n, fl); +} + // Set up the space's par_seq_tasks structure for work claiming // for parallel rescan. See CMSParRemarkTask where this is currently used. // XXX Need to suitably abstract and generalize this and the next diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp index ba2ff9c55ff..4b10e6fb2bc 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp @@ -172,6 +172,20 @@ class CompactibleFreeListSpace: public CompactibleSpace { // list of size "word_sz", and must now be decremented. void par_get_chunk_of_blocks(size_t word_sz, size_t n, AdaptiveFreeList* fl); + // Used by par_get_chunk_of_blocks() for the chunks from the + // indexed_free_lists. + bool par_get_chunk_of_blocks_IFL(size_t word_sz, size_t n, AdaptiveFreeList* fl); + + // Used by par_get_chunk_of_blocks_dictionary() to get a chunk + // evenly splittable into "n" "word_sz" chunks. Returns that + // evenly splittable chunk. May split a larger chunk to get the + // evenly splittable chunk. + FreeChunk* get_n_way_chunk_to_split(size_t word_sz, size_t n); + + // Used by par_get_chunk_of_blocks() for the chunks from the + // dictionary. + void par_get_chunk_of_blocks_dictionary(size_t word_sz, size_t n, AdaptiveFreeList* fl); + // Allocation helper functions // Allocate using a strategy that takes from the indexed free lists // first. This allocation strategy assumes a companion sweeping diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index 7dbc7f1d503..c396c946981 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -328,9 +328,11 @@ AdaptiveSizePolicy* CMSCollector::size_policy() { void ConcurrentMarkSweepGeneration::initialize_performance_counters() { const char* gen_name = "old"; + GenCollectorPolicy* gcp = (GenCollectorPolicy*) GenCollectedHeap::heap()->collector_policy(); // Generation Counters - generation 1, 1 subspace - _gen_counters = new GenerationCounters(gen_name, 1, 1, &_virtual_space); + _gen_counters = new GenerationCounters(gen_name, 1, 1, + gcp->min_old_size(), gcp->max_old_size(), &_virtual_space); _space_counters = new GSpaceCounters(gen_name, 0, _virtual_space.reserved_size(), @@ -5987,6 +5989,8 @@ public: }; void CMSRefProcTaskProxy::work(uint worker_id) { + ResourceMark rm; + HandleMark hm; assert(_collector->_span.equals(_span), "Inconsistency in _span"); CMSParKeepAliveClosure par_keep_alive(_collector, _span, _mark_bit_map, diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp index 90e0aedd084..f01f77ce1fa 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp @@ -81,8 +81,8 @@ void ConcurrentG1Refine::reset_threshold_step() { } } -void ConcurrentG1Refine::init() { - _hot_card_cache.initialize(); +void ConcurrentG1Refine::init(G1RegionToSpaceMapper* card_counts_storage) { + _hot_card_cache.initialize(card_counts_storage); } void ConcurrentG1Refine::stop() { diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp index f466e0de339..7f77163818c 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp @@ -34,6 +34,7 @@ class ConcurrentG1RefineThread; class G1CollectedHeap; class G1HotCardCache; +class G1RegionToSpaceMapper; class G1RemSet; class DirtyCardQueue; @@ -74,7 +75,7 @@ class ConcurrentG1Refine: public CHeapObj { ConcurrentG1Refine(G1CollectedHeap* g1h, CardTableEntryClosure* refine_closure); ~ConcurrentG1Refine(); - void init(); // Accomplish some initialization that has to wait. + void init(G1RegionToSpaceMapper* card_counts_storage); void stop(); void reinitialize_threads(); diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp index 3b9c0aa1906..e02b4e7a320 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp @@ -34,8 +34,9 @@ #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.hpp" #include "gc_implementation/g1/heapRegion.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionSet.inline.hpp" #include "gc_implementation/shared/vmGCOperations.hpp" #include "gc_implementation/shared/gcTimer.hpp" #include "gc_implementation/shared/gcTrace.hpp" @@ -99,12 +100,12 @@ int CMBitMapRO::heapWordDiffToOffsetDiff(size_t diff) const { } #ifndef PRODUCT -bool CMBitMapRO::covers(ReservedSpace heap_rs) const { +bool CMBitMapRO::covers(MemRegion heap_rs) const { // assert(_bm.map() == _virtual_space.low(), "map inconsistency"); assert(((size_t)_bm.size() * ((size_t)1 << _shifter)) == _bmWordSize, "size inconsistency"); - return _bmStartWord == (HeapWord*)(heap_rs.base()) && - _bmWordSize == heap_rs.size()>>LogHeapWordSize; + return _bmStartWord == (HeapWord*)(heap_rs.start()) && + _bmWordSize == heap_rs.word_size(); } #endif @@ -112,33 +113,73 @@ void CMBitMapRO::print_on_error(outputStream* st, const char* prefix) const { _bm.print_on_error(st, prefix); } -bool CMBitMap::allocate(ReservedSpace heap_rs) { - _bmStartWord = (HeapWord*)(heap_rs.base()); - _bmWordSize = heap_rs.size()/HeapWordSize; // heap_rs.size() is in bytes - ReservedSpace brs(ReservedSpace::allocation_align_size_up( - (_bmWordSize >> (_shifter + LogBitsPerByte)) + 1)); - if (!brs.is_reserved()) { - warning("ConcurrentMark marking bit map allocation failure"); - return false; - } - MemTracker::record_virtual_memory_type((address)brs.base(), mtGC); - // For now we'll just commit all of the bit map up front. - // Later on we'll try to be more parsimonious with swap. - if (!_virtual_space.initialize(brs, brs.size())) { - warning("ConcurrentMark marking bit map backing store failure"); - return false; - } - assert(_virtual_space.committed_size() == brs.size(), - "didn't reserve backing store for all of concurrent marking bit map?"); - _bm.set_map((BitMap::bm_word_t*)_virtual_space.low()); - assert(_virtual_space.committed_size() << (_shifter + LogBitsPerByte) >= - _bmWordSize, "inconsistency in bit map sizing"); - _bm.set_size(_bmWordSize >> _shifter); - return true; +size_t CMBitMap::compute_size(size_t heap_size) { + return heap_size / mark_distance(); } +size_t CMBitMap::mark_distance() { + return MinObjAlignmentInBytes * BitsPerByte; +} + +void CMBitMap::initialize(MemRegion heap, G1RegionToSpaceMapper* storage) { + _bmStartWord = heap.start(); + _bmWordSize = heap.word_size(); + + _bm.set_map((BitMap::bm_word_t*) storage->reserved().start()); + _bm.set_size(_bmWordSize >> _shifter); + + storage->set_mapping_changed_listener(&_listener); +} + +void CMBitMapMappingChangedListener::on_commit(uint start_region, size_t num_regions) { + // We need to clear the bitmap on commit, removing any existing information. + MemRegion mr(G1CollectedHeap::heap()->bottom_addr_for_region(start_region), num_regions * HeapRegion::GrainWords); + _bm->clearRange(mr); +} + +// Closure used for clearing the given mark bitmap. +class ClearBitmapHRClosure : public HeapRegionClosure { + private: + ConcurrentMark* _cm; + CMBitMap* _bitmap; + bool _may_yield; // The closure may yield during iteration. If yielded, abort the iteration. + public: + ClearBitmapHRClosure(ConcurrentMark* cm, CMBitMap* bitmap, bool may_yield) : HeapRegionClosure(), _cm(cm), _bitmap(bitmap), _may_yield(may_yield) { + assert(!may_yield || cm != NULL, "CM must be non-NULL if this closure is expected to yield."); + } + + virtual bool doHeapRegion(HeapRegion* r) { + size_t const chunk_size_in_words = M / HeapWordSize; + + HeapWord* cur = r->bottom(); + HeapWord* const end = r->end(); + + while (cur < end) { + MemRegion mr(cur, MIN2(cur + chunk_size_in_words, end)); + _bitmap->clearRange(mr); + + cur += chunk_size_in_words; + + // Abort iteration if after yielding the marking has been aborted. + if (_may_yield && _cm->do_yield_check() && _cm->has_aborted()) { + return true; + } + // Repeat the asserts from before the start of the closure. We will do them + // as asserts here to minimize their overhead on the product. However, we + // will have them as guarantees at the beginning / end of the bitmap + // clearing to get some checking in the product. + assert(!_may_yield || _cm->cmThread()->during_cycle(), "invariant"); + assert(!_may_yield || !G1CollectedHeap::heap()->mark_in_progress(), "invariant"); + } + + return false; + } +}; + void CMBitMap::clearAll() { - _bm.clear(); + ClearBitmapHRClosure cl(NULL, this, false /* may_yield */); + G1CollectedHeap::heap()->heap_region_iterate(&cl); + guarantee(cl.complete(), "Must have completed iteration."); return; } @@ -393,10 +434,6 @@ void CMMarkStack::oops_do(OopClosure* f) { } } -bool ConcurrentMark::not_yet_marked(oop obj) const { - return _g1h->is_obj_ill(obj); -} - CMRootRegions::CMRootRegions() : _young_list(NULL), _cm(NULL), _scan_in_progress(false), _should_abort(false), _next_survivor(NULL) { } @@ -483,10 +520,10 @@ uint ConcurrentMark::scale_parallel_threads(uint n_par_threads) { return MAX2((n_par_threads + 2) / 4, 1U); } -ConcurrentMark::ConcurrentMark(G1CollectedHeap* g1h, ReservedSpace heap_rs) : +ConcurrentMark::ConcurrentMark(G1CollectedHeap* g1h, G1RegionToSpaceMapper* prev_bitmap_storage, G1RegionToSpaceMapper* next_bitmap_storage) : _g1h(g1h), - _markBitMap1(log2_intptr(MinObjAlignment)), - _markBitMap2(log2_intptr(MinObjAlignment)), + _markBitMap1(), + _markBitMap2(), _parallel_marking_threads(0), _max_parallel_marking_threads(0), _sleep_factor(0.0), @@ -495,7 +532,7 @@ ConcurrentMark::ConcurrentMark(G1CollectedHeap* g1h, ReservedSpace heap_rs) : _cleanup_task_overhead(1.0), _cleanup_list("Cleanup List"), _region_bm((BitMap::idx_t)(g1h->max_regions()), false /* in_resource_area*/), - _card_bm((heap_rs.size() + CardTableModRefBS::card_size - 1) >> + _card_bm((g1h->reserved_region().byte_size() + CardTableModRefBS::card_size - 1) >> CardTableModRefBS::card_shift, false /* in_resource_area*/), @@ -545,14 +582,8 @@ ConcurrentMark::ConcurrentMark(G1CollectedHeap* g1h, ReservedSpace heap_rs) : "heap end = " PTR_FORMAT, p2i(_heap_start), p2i(_heap_end)); } - if (!_markBitMap1.allocate(heap_rs)) { - warning("Failed to allocate first CM bit map"); - return; - } - if (!_markBitMap2.allocate(heap_rs)) { - warning("Failed to allocate second CM bit map"); - return; - } + _markBitMap1.initialize(g1h->reserved_region(), prev_bitmap_storage); + _markBitMap2.initialize(g1h->reserved_region(), next_bitmap_storage); // Create & start a ConcurrentMark thread. _cmThread = new ConcurrentMarkThread(this); @@ -563,8 +594,8 @@ ConcurrentMark::ConcurrentMark(G1CollectedHeap* g1h, ReservedSpace heap_rs) : } assert(CGC_lock != NULL, "Where's the CGC_lock?"); - assert(_markBitMap1.covers(heap_rs), "_markBitMap1 inconsistency"); - assert(_markBitMap2.covers(heap_rs), "_markBitMap2 inconsistency"); + assert(_markBitMap1.covers(g1h->reserved_region()), "_markBitMap1 inconsistency"); + assert(_markBitMap2.covers(g1h->reserved_region()), "_markBitMap2 inconsistency"); SATBMarkQueueSet& satb_qs = JavaThread::satb_mark_queue_set(); satb_qs.set_buffer_size(G1SATBBufferSize); @@ -724,38 +755,17 @@ ConcurrentMark::ConcurrentMark(G1CollectedHeap* g1h, ReservedSpace heap_rs) : clear_all_count_data(); // so that the call below can read a sensible value - _heap_start = (HeapWord*) heap_rs.base(); + _heap_start = g1h->reserved_region().start(); set_non_marking_state(); _completed_initialization = true; } -void ConcurrentMark::update_g1_committed(bool force) { - // If concurrent marking is not in progress, then we do not need to - // update _heap_end. - if (!concurrent_marking_in_progress() && !force) return; - - MemRegion committed = _g1h->g1_committed(); - assert(committed.start() == _heap_start, "start shouldn't change"); - HeapWord* new_end = committed.end(); - if (new_end > _heap_end) { - // The heap has been expanded. - - _heap_end = new_end; - } - // Notice that the heap can also shrink. However, this only happens - // during a Full GC (at least currently) and the entire marking - // phase will bail out and the task will not be restarted. So, let's - // do nothing. -} - void ConcurrentMark::reset() { // Starting values for these two. This should be called in a STW - // phase. CM will be notified of any future g1_committed expansions - // will be at the end of evacuation pauses, when tasks are - // inactive. - MemRegion committed = _g1h->g1_committed(); - _heap_start = committed.start(); - _heap_end = committed.end(); + // phase. + MemRegion reserved = _g1h->g1_reserved(); + _heap_start = reserved.start(); + _heap_end = reserved.end(); // Separated the asserts so that we know which one fires. assert(_heap_start != NULL, "heap bounds should look ok"); @@ -827,7 +837,6 @@ void ConcurrentMark::set_concurrency_and_phase(uint active_tasks, bool concurren assert(out_of_regions(), err_msg("only way to get here: _finger: "PTR_FORMAT", _heap_end: "PTR_FORMAT, p2i(_finger), p2i(_heap_end))); - update_g1_committed(true); } } @@ -846,7 +855,6 @@ ConcurrentMark::~ConcurrentMark() { void ConcurrentMark::clearNextBitmap() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - G1CollectorPolicy* g1p = g1h->g1_policy(); // Make sure that the concurrent mark thread looks to still be in // the current cycle. @@ -858,41 +866,45 @@ void ConcurrentMark::clearNextBitmap() { // is the case. guarantee(!g1h->mark_in_progress(), "invariant"); - // clear the mark bitmap (no grey objects to start with). - // We need to do this in chunks and offer to yield in between - // each chunk. - HeapWord* start = _nextMarkBitMap->startWord(); - HeapWord* end = _nextMarkBitMap->endWord(); - HeapWord* cur = start; - size_t chunkSize = M; - while (cur < end) { - HeapWord* next = cur + chunkSize; - if (next > end) { - next = end; - } - MemRegion mr(cur,next); - _nextMarkBitMap->clearRange(mr); - cur = next; - do_yield_check(); + ClearBitmapHRClosure cl(this, _nextMarkBitMap, true /* may_yield */); + g1h->heap_region_iterate(&cl); - // Repeat the asserts from above. We'll do them as asserts here to - // minimize their overhead on the product. However, we'll have - // them as guarantees at the beginning / end of the bitmap - // clearing to get some checking in the product. - assert(cmThread()->during_cycle(), "invariant"); - assert(!g1h->mark_in_progress(), "invariant"); + // Clear the liveness counting data. If the marking has been aborted, the abort() + // call already did that. + if (cl.complete()) { + clear_all_count_data(); } - // Clear the liveness counting data - clear_all_count_data(); - // Repeat the asserts from above. guarantee(cmThread()->during_cycle(), "invariant"); guarantee(!g1h->mark_in_progress(), "invariant"); } +class CheckBitmapClearHRClosure : public HeapRegionClosure { + CMBitMap* _bitmap; + bool _error; + public: + CheckBitmapClearHRClosure(CMBitMap* bitmap) : _bitmap(bitmap) { + } + + virtual bool doHeapRegion(HeapRegion* r) { + // This closure can be called concurrently to the mutator, so we must make sure + // that the result of the getNextMarkedWordAddress() call is compared to the + // value passed to it as limit to detect any found bits. + // We can use the region's orig_end() for the limit and the comparison value + // as it always contains the "real" end of the region that never changes and + // has no side effects. + // Due to the latter, there can also be no problem with the compiler generating + // reloads of the orig_end() call. + HeapWord* end = r->orig_end(); + return _bitmap->getNextMarkedWordAddress(r->bottom(), end) != end; + } +}; + bool ConcurrentMark::nextMarkBitmapIsClear() { - return _nextMarkBitMap->getNextMarkedWordAddress(_heap_start, _heap_end) == _heap_end; + CheckBitmapClearHRClosure cl(_nextMarkBitMap); + _g1h->heap_region_iterate(&cl); + return cl.complete(); } class NoteStartOfMarkHRClosure: public HeapRegionClosure { @@ -1110,20 +1122,17 @@ public: if (!_cm->has_aborted()) { do { double start_vtime_sec = os::elapsedVTime(); - double start_time_sec = os::elapsedTime(); double mark_step_duration_ms = G1ConcMarkStepDurationMillis; the_task->do_marking_step(mark_step_duration_ms, true /* do_termination */, false /* is_serial*/); - double end_time_sec = os::elapsedTime(); double end_vtime_sec = os::elapsedVTime(); double elapsed_vtime_sec = end_vtime_sec - start_vtime_sec; - double elapsed_time_sec = end_time_sec - start_time_sec; _cm->clear_has_overflown(); - bool ret = _cm->do_yield_check(worker_id); + _cm->do_yield_check(worker_id); jlong sleep_time_ms; if (!_cm->has_aborted() && the_task->has_aborted()) { @@ -1133,17 +1142,6 @@ public: os::sleep(Thread::current(), sleep_time_ms, false); SuspendibleThreadSet::join(); } - double end_time2_sec = os::elapsedTime(); - double elapsed_time2_sec = end_time2_sec - start_time_sec; - -#if 0 - gclog_or_tty->print_cr("CM: elapsed %1.4lf ms, sleep %1.4lf ms, " - "overhead %1.4lf", - elapsed_vtime_sec * 1000.0, (double) sleep_time_ms, - the_task->conc_overhead(os::elapsedTime()) * 8.0); - gclog_or_tty->print_cr("elapsed time %1.4lf ms, time 2: %1.4lf ms", - elapsed_time_sec * 1000.0, elapsed_time2_sec * 1000.0); -#endif } while (!_cm->has_aborted() && the_task->has_aborted()); } the_task->record_end_time(); @@ -1402,7 +1400,7 @@ protected: void set_bit_for_region(HeapRegion* hr) { assert(!hr->continuesHumongous(), "should have filtered those out"); - BitMap::idx_t index = (BitMap::idx_t) hr->hrs_index(); + BitMap::idx_t index = (BitMap::idx_t) hr->hrm_index(); if (!hr->startsHumongous()) { // Normal (non-humongous) case: just set the bit. _region_bm->par_at_put(index, true); @@ -1590,7 +1588,7 @@ public: if (_verbose) { gclog_or_tty->print_cr("Region %u: marked bytes mismatch: " "expected: " SIZE_FORMAT ", actual: " SIZE_FORMAT, - hr->hrs_index(), exp_marked_bytes, act_marked_bytes); + hr->hrm_index(), exp_marked_bytes, act_marked_bytes); } failures += 1; } @@ -1599,7 +1597,7 @@ public: // (which was just calculated) region bit maps. // We're not OK if the bit in the calculated expected region // bitmap is set and the bit in the actual region bitmap is not. - BitMap::idx_t index = (BitMap::idx_t) hr->hrs_index(); + BitMap::idx_t index = (BitMap::idx_t) hr->hrm_index(); bool expected = _exp_region_bm->at(index); bool actual = _region_bm->at(index); @@ -1607,7 +1605,7 @@ public: if (_verbose) { gclog_or_tty->print_cr("Region %u: region bitmap mismatch: " "expected: %s, actual: %s", - hr->hrs_index(), + hr->hrm_index(), BOOL_TO_STR(expected), BOOL_TO_STR(actual)); } failures += 1; @@ -1628,7 +1626,7 @@ public: if (_verbose) { gclog_or_tty->print_cr("Region %u: card bitmap mismatch at " SIZE_FORMAT ": " "expected: %s, actual: %s", - hr->hrs_index(), i, + hr->hrm_index(), i, BOOL_TO_STR(expected), BOOL_TO_STR(actual)); } failures += 1; @@ -2167,7 +2165,9 @@ void ConcurrentMark::cleanup() { g1h->increment_total_collections(); // Clean out dead classes and update Metaspace sizes. - ClassLoaderDataGraph::purge(); + if (ClassUnloadingWithConcurrentMark) { + ClassLoaderDataGraph::purge(); + } MetaspaceGC::compute_new_size(); // We reclaimed old regions so we should calculate the sizes to make @@ -2191,10 +2191,10 @@ void ConcurrentMark::completeCleanup() { _cleanup_list.length()); } - // Noone else should be accessing the _cleanup_list at this point, - // so it's not necessary to take any locks + // No one else should be accessing the _cleanup_list at this point, + // so it is not necessary to take any locks while (!_cleanup_list.is_empty()) { - HeapRegion* hr = _cleanup_list.remove_head(); + HeapRegion* hr = _cleanup_list.remove_region(true /* from_head */); assert(hr != NULL, "Got NULL from a non-empty list"); hr->par_clear(); tmp_free_list.add_ordered(hr); @@ -2403,6 +2403,8 @@ public: } virtual void work(uint worker_id) { + ResourceMark rm; + HandleMark hm; CMTask* task = _cm->task(worker_id); G1CMIsAliveClosure g1_is_alive(_g1h); G1CMKeepAliveAndDrainClosure g1_par_keep_alive(_cm, task, false /* is_serial */); @@ -2595,24 +2597,27 @@ void ConcurrentMark::weakRefsWork(bool clear_all_soft_refs) { assert(_markStack.isEmpty(), "Marking should have completed"); // Unload Klasses, String, Symbols, Code Cache, etc. - - G1RemarkGCTraceTime trace("Unloading", G1Log::finer()); - - bool purged_classes; - { - G1RemarkGCTraceTime trace("System Dictionary Unloading", G1Log::finest()); - purged_classes = SystemDictionary::do_unloading(&g1_is_alive); - } + G1RemarkGCTraceTime trace("Unloading", G1Log::finer()); - { - G1RemarkGCTraceTime trace("Parallel Unloading", G1Log::finest()); - weakRefsWorkParallelPart(&g1_is_alive, purged_classes); - } + if (ClassUnloadingWithConcurrentMark) { + bool purged_classes; - if (G1StringDedup::is_enabled()) { - G1RemarkGCTraceTime trace("String Deduplication Unlink", G1Log::finest()); - G1StringDedup::unlink(&g1_is_alive); + { + G1RemarkGCTraceTime trace("System Dictionary Unloading", G1Log::finest()); + purged_classes = SystemDictionary::do_unloading(&g1_is_alive); + } + + { + G1RemarkGCTraceTime trace("Parallel Unloading", G1Log::finest()); + weakRefsWorkParallelPart(&g1_is_alive, purged_classes); + } + } + + if (G1StringDedup::is_enabled()) { + G1RemarkGCTraceTime trace("String Deduplication Unlink", G1Log::finest()); + G1StringDedup::unlink(&g1_is_alive); + } } } @@ -2935,11 +2940,6 @@ void ConcurrentMark::clearRangeNextBitmap(MemRegion mr) { _nextMarkBitMap->clearRange(mr); } -void ConcurrentMark::clearRangeBothBitmaps(MemRegion mr) { - clearRangePrevBitmap(mr); - clearRangeNextBitmap(mr); -} - HeapRegion* ConcurrentMark::claim_region(uint worker_id) { // "checkpoint" the finger @@ -2973,22 +2973,25 @@ ConcurrentMark::claim_region(uint worker_id) { // claim_region() and a humongous object allocation might force us // to do a bit of unnecessary work (due to some unnecessary bitmap // iterations) but it should not introduce and correctness issues. - HeapRegion* curr_region = _g1h->heap_region_containing_raw(finger); - HeapWord* bottom = curr_region->bottom(); - HeapWord* end = curr_region->end(); - HeapWord* limit = curr_region->next_top_at_mark_start(); + HeapRegion* curr_region = _g1h->heap_region_containing_raw(finger); - if (verbose_low()) { - gclog_or_tty->print_cr("[%u] curr_region = "PTR_FORMAT" " - "["PTR_FORMAT", "PTR_FORMAT"), " - "limit = "PTR_FORMAT, - worker_id, p2i(curr_region), p2i(bottom), p2i(end), p2i(limit)); - } + // Above heap_region_containing_raw may return NULL as we always scan claim + // until the end of the heap. In this case, just jump to the next region. + HeapWord* end = curr_region != NULL ? curr_region->end() : finger + HeapRegion::GrainWords; // Is the gap between reading the finger and doing the CAS too long? HeapWord* res = (HeapWord*) Atomic::cmpxchg_ptr(end, &_finger, finger); - if (res == finger) { + if (res == finger && curr_region != NULL) { // we succeeded + HeapWord* bottom = curr_region->bottom(); + HeapWord* limit = curr_region->next_top_at_mark_start(); + + if (verbose_low()) { + gclog_or_tty->print_cr("[%u] curr_region = "PTR_FORMAT" " + "["PTR_FORMAT", "PTR_FORMAT"), " + "limit = "PTR_FORMAT, + worker_id, p2i(curr_region), p2i(bottom), p2i(end), p2i(limit)); + } // notice that _finger == end cannot be guaranteed here since, // someone else might have moved the finger even further @@ -3019,10 +3022,17 @@ ConcurrentMark::claim_region(uint worker_id) { } else { assert(_finger > finger, "the finger should have moved forward"); if (verbose_low()) { - gclog_or_tty->print_cr("[%u] somebody else moved the finger, " - "global finger = "PTR_FORMAT", " - "our finger = "PTR_FORMAT, - worker_id, p2i(_finger), p2i(finger)); + if (curr_region == NULL) { + gclog_or_tty->print_cr("[%u] found uncommitted region, moving finger, " + "global finger = "PTR_FORMAT", " + "our finger = "PTR_FORMAT, + worker_id, p2i(_finger), p2i(finger)); + } else { + gclog_or_tty->print_cr("[%u] somebody else moved the finger, " + "global finger = "PTR_FORMAT", " + "our finger = "PTR_FORMAT, + worker_id, p2i(_finger), p2i(finger)); + } } // read it again @@ -3137,8 +3147,10 @@ void ConcurrentMark::verify_no_cset_oops(bool verify_stacks, // happens, heap_region_containing() will return the bottom of the // corresponding starts humongous region and the check below will // not hold any more. + // Since we always iterate over all regions, we might get a NULL HeapRegion + // here. HeapRegion* global_hr = _g1h->heap_region_containing_raw(global_finger); - guarantee(global_finger == global_hr->bottom(), + guarantee(global_hr == NULL || global_finger == global_hr->bottom(), err_msg("global finger: "PTR_FORMAT" region: "HR_FORMAT, p2i(global_finger), HR_FORMAT_PARAMS(global_hr))); } @@ -3151,7 +3163,7 @@ void ConcurrentMark::verify_no_cset_oops(bool verify_stacks, if (task_finger != NULL && task_finger < _heap_end) { // See above note on the global finger verification. HeapRegion* task_hr = _g1h->heap_region_containing_raw(task_finger); - guarantee(task_finger == task_hr->bottom() || + guarantee(task_hr == NULL || task_finger == task_hr->bottom() || !task_hr->in_collection_set(), err_msg("task finger: "PTR_FORMAT" region: "HR_FORMAT, p2i(task_finger), HR_FORMAT_PARAMS(task_hr))); @@ -3230,7 +3242,7 @@ class AggregateCountDataHRClosure: public HeapRegionClosure { assert(limit_idx <= end_idx, "or else use atomics"); // Aggregate the "stripe" in the count data associated with hr. - uint hrs_index = hr->hrs_index(); + uint hrm_index = hr->hrm_index(); size_t marked_bytes = 0; for (uint i = 0; i < _max_worker_id; i += 1) { @@ -3239,7 +3251,7 @@ class AggregateCountDataHRClosure: public HeapRegionClosure { // Fetch the marked_bytes in this region for task i and // add it to the running total for this region. - marked_bytes += marked_bytes_array[hrs_index]; + marked_bytes += marked_bytes_array[hrm_index]; // Now union the bitmaps[0,max_worker_id)[start_idx..limit_idx) // into the global card bitmap. @@ -3473,17 +3485,6 @@ bool ConcurrentMark::do_yield_check(uint worker_id) { } } -bool ConcurrentMark::containing_card_is_marked(void* p) { - size_t offset = pointer_delta(p, _g1h->reserved_region().start(), 1); - return _card_bm.at(offset >> CardTableModRefBS::card_shift); -} - -bool ConcurrentMark::containing_cards_are_marked(void* start, - void* last) { - return containing_card_is_marked(start) && - containing_card_is_marked(last); -} - #ifndef PRODUCT // for debugging purposes void ConcurrentMark::print_finger() { @@ -3736,7 +3737,7 @@ void CMTask::regular_clock_call() { if (_cm->verbose_medium()) { gclog_or_tty->print_cr("[%u] regular clock, interval = %1.2lfms, " - "scanned = %d%s, refs reached = %d%s", + "scanned = "SIZE_FORMAT"%s, refs reached = "SIZE_FORMAT"%s", _worker_id, last_interval_ms, _words_scanned, (_words_scanned >= _words_scanned_limit) ? " (*)" : "", @@ -4667,7 +4668,6 @@ G1PrintRegionLivenessInfoClosure(outputStream* out, const char* phase_name) _hum_prev_live_bytes(0), _hum_next_live_bytes(0), _total_remset_bytes(0), _total_strong_code_roots_bytes(0) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - MemRegion g1_committed = g1h->g1_committed(); MemRegion g1_reserved = g1h->g1_reserved(); double now = os::elapsedTime(); @@ -4675,10 +4675,8 @@ G1PrintRegionLivenessInfoClosure(outputStream* out, const char* phase_name) _out->cr(); _out->print_cr(G1PPRL_LINE_PREFIX" PHASE %s @ %1.3f", phase_name, now); _out->print_cr(G1PPRL_LINE_PREFIX" HEAP" - G1PPRL_SUM_ADDR_FORMAT("committed") G1PPRL_SUM_ADDR_FORMAT("reserved") G1PPRL_SUM_BYTE_FORMAT("region-size"), - p2i(g1_committed.start()), p2i(g1_committed.end()), p2i(g1_reserved.start()), p2i(g1_reserved.end()), HeapRegion::GrainBytes); _out->print_cr(G1PPRL_LINE_PREFIX); diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp index a564855277b..e4412eacaef 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp @@ -27,10 +27,12 @@ #include "classfile/javaClasses.hpp" #include "gc_implementation/g1/heapRegionSet.hpp" +#include "gc_implementation/g1/g1RegionToSpaceMapper.hpp" #include "gc_implementation/shared/gcId.hpp" #include "utilities/taskqueue.hpp" class G1CollectedHeap; +class CMBitMap; class CMTask; typedef GenericTaskQueue CMTaskQueue; typedef GenericTaskQueueSet CMTaskQueueSet; @@ -57,7 +59,6 @@ class CMBitMapRO VALUE_OBJ_CLASS_SPEC { HeapWord* _bmStartWord; // base address of range covered by map size_t _bmWordSize; // map size (in #HeapWords covered) const int _shifter; // map to char or bit - VirtualSpace _virtual_space; // underlying the bit map BitMap _bm; // the bit map itself public: @@ -115,42 +116,41 @@ class CMBitMapRO VALUE_OBJ_CLASS_SPEC { void print_on_error(outputStream* st, const char* prefix) const; // debugging - NOT_PRODUCT(bool covers(ReservedSpace rs) const;) + NOT_PRODUCT(bool covers(MemRegion rs) const;) +}; + +class CMBitMapMappingChangedListener : public G1MappingChangedListener { + private: + CMBitMap* _bm; + public: + CMBitMapMappingChangedListener() : _bm(NULL) {} + + void set_bitmap(CMBitMap* bm) { _bm = bm; } + + virtual void on_commit(uint start_idx, size_t num_regions); }; class CMBitMap : public CMBitMapRO { + private: + CMBitMapMappingChangedListener _listener; public: - // constructor - CMBitMap(int shifter) : - CMBitMapRO(shifter) {} + static size_t compute_size(size_t heap_size); + // Returns the amount of bytes on the heap between two marks in the bitmap. + static size_t mark_distance(); - // Allocates the back store for the marking bitmap - bool allocate(ReservedSpace heap_rs); + CMBitMap() : CMBitMapRO(LogMinObjAlignment), _listener() { _listener.set_bitmap(this); } + + // Initializes the underlying BitMap to cover the given area. + void initialize(MemRegion heap, G1RegionToSpaceMapper* storage); + + // Write marks. + inline void mark(HeapWord* addr); + inline void clear(HeapWord* addr); + inline bool parMark(HeapWord* addr); + inline bool parClear(HeapWord* addr); - // write marks - void mark(HeapWord* addr) { - assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize), - "outside underlying space?"); - _bm.set_bit(heapWordToOffset(addr)); - } - void clear(HeapWord* addr) { - assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize), - "outside underlying space?"); - _bm.clear_bit(heapWordToOffset(addr)); - } - bool parMark(HeapWord* addr) { - assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize), - "outside underlying space?"); - return _bm.par_set_bit(heapWordToOffset(addr)); - } - bool parClear(HeapWord* addr) { - assert(_bmStartWord <= addr && addr < (_bmStartWord + _bmWordSize), - "outside underlying space?"); - return _bm.par_clear_bit(heapWordToOffset(addr)); - } void markRange(MemRegion mr); - void clearAll(); void clearRange(MemRegion mr); // Starting at the bit corresponding to "addr" (inclusive), find the next @@ -161,6 +161,9 @@ class CMBitMap : public CMBitMapRO { // the run. If there is no "1" bit at or after "addr", return an empty // MemRegion. MemRegion getAndClearMarkedRegion(HeapWord* addr, HeapWord* end_addr); + + // Clear the whole mark bitmap. + void clearAll(); }; // Represents a marking stack used by ConcurrentMarking in the G1 collector. @@ -680,7 +683,9 @@ public: return _task_queues->steal(worker_id, hash_seed, obj); } - ConcurrentMark(G1CollectedHeap* g1h, ReservedSpace heap_rs); + ConcurrentMark(G1CollectedHeap* g1h, + G1RegionToSpaceMapper* prev_bitmap_storage, + G1RegionToSpaceMapper* next_bitmap_storage); ~ConcurrentMark(); ConcurrentMarkThread* cmThread() { return _cmThread; } @@ -709,8 +714,10 @@ public: // inconsistent) and always passing the size. hr is the region that // contains the object and it's passed optionally from callers who // might already have it (no point in recalculating it). - inline void grayRoot(oop obj, size_t word_size, - uint worker_id, HeapRegion* hr = NULL); + inline void grayRoot(oop obj, + size_t word_size, + uint worker_id, + HeapRegion* hr = NULL); // It iterates over the heap and for each object it comes across it // will dump the contents of its reference fields, as well as @@ -731,12 +738,14 @@ public: // AND MARKED : indicates that an object is both explicitly and // implicitly live (it should be one or the other, not both) void print_reachable(const char* str, - VerifyOption vo, bool all) PRODUCT_RETURN; + VerifyOption vo, + bool all) PRODUCT_RETURN; // Clear the next marking bitmap (will be called concurrently). void clearNextBitmap(); - // Return whether the next mark bitmap has no marks set. + // Return whether the next mark bitmap has no marks set. To be used for assertions + // only. Will not yield to pause requests. bool nextMarkBitmapIsClear(); // These two do the work that needs to be done before and after the @@ -767,12 +776,11 @@ public: // this carefully! inline void markPrev(oop p); - // Clears marks for all objects in the given range, for the prev, - // next, or both bitmaps. NB: the previous bitmap is usually + // Clears marks for all objects in the given range, for the prev or + // next bitmaps. NB: the previous bitmap is usually // read-only, so use this carefully! void clearRangePrevBitmap(MemRegion mr); void clearRangeNextBitmap(MemRegion mr); - void clearRangeBothBitmaps(MemRegion mr); // Notify data structures that a GC has started. void note_start_of_gc() { @@ -794,27 +802,6 @@ public: bool verify_thread_buffers, bool verify_fingers) PRODUCT_RETURN; - // It is called at the end of an evacuation pause during marking so - // that CM is notified of where the new end of the heap is. It - // doesn't do anything if concurrent_marking_in_progress() is false, - // unless the force parameter is true. - void update_g1_committed(bool force = false); - - bool isMarked(oop p) const { - assert(p != NULL && p->is_oop(), "expected an oop"); - HeapWord* addr = (HeapWord*)p; - assert(addr >= _nextMarkBitMap->startWord() || - addr < _nextMarkBitMap->endWord(), "in a region"); - - return _nextMarkBitMap->isMarked(addr); - } - - inline bool not_yet_marked(oop p) const; - - // XXX Debug code - bool containing_card_is_marked(void* p); - bool containing_cards_are_marked(void* start, void* last); - bool isPrevMarked(oop p) const { assert(p != NULL && p->is_oop(), "expected an oop"); HeapWord* addr = (HeapWord*)p; @@ -900,7 +887,8 @@ public: // marked_bytes array slot for the given HeapRegion. // Sets the bits in the given card bitmap that are associated with the // cards that are spanned by the memory region. - inline void count_region(MemRegion mr, HeapRegion* hr, + inline void count_region(MemRegion mr, + HeapRegion* hr, size_t* marked_bytes_array, BitMap* task_card_bm); @@ -908,56 +896,27 @@ public: // data structures for the given worker id. inline void count_region(MemRegion mr, HeapRegion* hr, uint worker_id); - // Counts the given memory region in the task/worker counting - // data structures for the given worker id. - inline void count_region(MemRegion mr, uint worker_id); - // Counts the given object in the given task/worker counting // data structures. - inline void count_object(oop obj, HeapRegion* hr, + inline void count_object(oop obj, + HeapRegion* hr, size_t* marked_bytes_array, BitMap* task_card_bm); - // Counts the given object in the task/worker counting data - // structures for the given worker id. - inline void count_object(oop obj, HeapRegion* hr, uint worker_id); - // Attempts to mark the given object and, if successful, counts // the object in the given task/worker counting structures. - inline bool par_mark_and_count(oop obj, HeapRegion* hr, + inline bool par_mark_and_count(oop obj, + HeapRegion* hr, size_t* marked_bytes_array, BitMap* task_card_bm); // Attempts to mark the given object and, if successful, counts // the object in the task/worker counting structures for the // given worker id. - inline bool par_mark_and_count(oop obj, size_t word_size, - HeapRegion* hr, uint worker_id); - - // Attempts to mark the given object and, if successful, counts - // the object in the task/worker counting structures for the - // given worker id. - inline bool par_mark_and_count(oop obj, HeapRegion* hr, uint worker_id); - - // Similar to the above routine but we don't know the heap region that - // contains the object to be marked/counted, which this routine looks up. - inline bool par_mark_and_count(oop obj, uint worker_id); - - // Similar to the above routine but there are times when we cannot - // safely calculate the size of obj due to races and we, therefore, - // pass the size in as a parameter. It is the caller's responsibility - // to ensure that the size passed in for obj is valid. - inline bool par_mark_and_count(oop obj, size_t word_size, uint worker_id); - - // Unconditionally mark the given object, and unconditionally count - // the object in the counting structures for worker id 0. - // Should *not* be called from parallel code. - inline bool mark_and_count(oop obj, HeapRegion* hr); - - // Similar to the above routine but we don't know the heap region that - // contains the object to be marked/counted, which this routine looks up. - // Should *not* be called from parallel code. - inline bool mark_and_count(oop obj); + inline bool par_mark_and_count(oop obj, + size_t word_size, + HeapRegion* hr, + uint worker_id); // Returns true if initialization was successfully completed. bool completed_initialization() const { @@ -1229,9 +1188,12 @@ public: _finger = new_finger; } - CMTask(uint worker_id, ConcurrentMark *cm, - size_t* marked_bytes, BitMap* card_bm, - CMTaskQueue* task_queue, CMTaskQueueSet* task_queues); + CMTask(uint worker_id, + ConcurrentMark *cm, + size_t* marked_bytes, + BitMap* card_bm, + CMTaskQueue* task_queue, + CMTaskQueueSet* task_queues); // it prints statistics associated with this task void print_stats(); diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp index 12ba5683ed8..45cc086b7f8 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp @@ -86,7 +86,7 @@ inline void ConcurrentMark::count_region(MemRegion mr, HeapRegion* hr, HeapWord* start = mr.start(); HeapWord* end = mr.end(); size_t region_size_bytes = mr.byte_size(); - uint index = hr->hrs_index(); + uint index = hr->hrm_index(); assert(!hr->continuesHumongous(), "should not be HC region"); assert(hr == g1h->heap_region_containing(start), "sanity"); @@ -125,14 +125,6 @@ inline void ConcurrentMark::count_region(MemRegion mr, count_region(mr, hr, marked_bytes_array, task_card_bm); } -// Counts the given memory region, which may be a single object, in the -// task/worker counting data structures for the given worker id. -inline void ConcurrentMark::count_region(MemRegion mr, uint worker_id) { - HeapWord* addr = mr.start(); - HeapRegion* hr = _g1h->heap_region_containing_raw(addr); - count_region(mr, hr, worker_id); -} - // Counts the given object in the given task/worker counting data structures. inline void ConcurrentMark::count_object(oop obj, HeapRegion* hr, @@ -142,17 +134,6 @@ inline void ConcurrentMark::count_object(oop obj, count_region(mr, hr, marked_bytes_array, task_card_bm); } -// Counts the given object in the task/worker counting data -// structures for the given worker id. -inline void ConcurrentMark::count_object(oop obj, - HeapRegion* hr, - uint worker_id) { - size_t* marked_bytes_array = count_marked_bytes_array_for(worker_id); - BitMap* task_card_bm = count_card_bitmap_for(worker_id); - HeapWord* addr = (HeapWord*) obj; - count_object(obj, hr, marked_bytes_array, task_card_bm); -} - // Attempts to mark the given object and, if successful, counts // the object in the given task/worker counting structures. inline bool ConcurrentMark::par_mark_and_count(oop obj, @@ -184,63 +165,6 @@ inline bool ConcurrentMark::par_mark_and_count(oop obj, return false; } -// Attempts to mark the given object and, if successful, counts -// the object in the task/worker counting structures for the -// given worker id. -inline bool ConcurrentMark::par_mark_and_count(oop obj, - HeapRegion* hr, - uint worker_id) { - HeapWord* addr = (HeapWord*)obj; - if (_nextMarkBitMap->parMark(addr)) { - // Update the task specific count data for the object. - count_object(obj, hr, worker_id); - return true; - } - return false; -} - -// As above - but we don't know the heap region containing the -// object and so have to supply it. -inline bool ConcurrentMark::par_mark_and_count(oop obj, uint worker_id) { - HeapWord* addr = (HeapWord*)obj; - HeapRegion* hr = _g1h->heap_region_containing_raw(addr); - return par_mark_and_count(obj, hr, worker_id); -} - -// Similar to the above routine but we already know the size, in words, of -// the object that we wish to mark/count -inline bool ConcurrentMark::par_mark_and_count(oop obj, - size_t word_size, - uint worker_id) { - HeapWord* addr = (HeapWord*)obj; - if (_nextMarkBitMap->parMark(addr)) { - // Update the task specific count data for the object. - MemRegion mr(addr, word_size); - count_region(mr, worker_id); - return true; - } - return false; -} - -// Unconditionally mark the given object, and unconditionally count -// the object in the counting structures for worker id 0. -// Should *not* be called from parallel code. -inline bool ConcurrentMark::mark_and_count(oop obj, HeapRegion* hr) { - HeapWord* addr = (HeapWord*)obj; - _nextMarkBitMap->mark(addr); - // Update the task specific count data for the object. - count_object(obj, hr, 0 /* worker_id */); - return true; -} - -// As above - but we don't have the heap region containing the -// object, so we have to supply it. -inline bool ConcurrentMark::mark_and_count(oop obj) { - HeapWord* addr = (HeapWord*)obj; - HeapRegion* hr = _g1h->heap_region_containing_raw(addr); - return mark_and_count(obj, hr); -} - inline bool CMBitMapRO::iterate(BitMapClosure* cl, MemRegion mr) { HeapWord* start_addr = MAX2(startWord(), mr.start()); HeapWord* end_addr = MIN2(endWord(), mr.end()); @@ -268,6 +192,36 @@ inline bool CMBitMapRO::iterate(BitMapClosure* cl) { return iterate(cl, mr); } +#define check_mark(addr) \ + assert(_bmStartWord <= (addr) && (addr) < (_bmStartWord + _bmWordSize), \ + "outside underlying space?"); \ + assert(G1CollectedHeap::heap()->is_in_exact(addr), \ + err_msg("Trying to access not available bitmap "PTR_FORMAT \ + " corresponding to "PTR_FORMAT" (%u)", \ + p2i(this), p2i(addr), G1CollectedHeap::heap()->addr_to_region(addr))); + +inline void CMBitMap::mark(HeapWord* addr) { + check_mark(addr); + _bm.set_bit(heapWordToOffset(addr)); +} + +inline void CMBitMap::clear(HeapWord* addr) { + check_mark(addr); + _bm.clear_bit(heapWordToOffset(addr)); +} + +inline bool CMBitMap::parMark(HeapWord* addr) { + check_mark(addr); + return _bm.par_set_bit(heapWordToOffset(addr)); +} + +inline bool CMBitMap::parClear(HeapWord* addr) { + check_mark(addr); + return _bm.par_clear_bit(heapWordToOffset(addr)); +} + +#undef check_mark + inline void CMTask::push(oop obj) { HeapWord* objAddr = (HeapWord*) obj; assert(_g1h->is_in_g1_reserved(objAddr), "invariant"); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1AllocRegion.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1AllocRegion.hpp index 050490dbbef..50cc9be07ea 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1AllocRegion.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1AllocRegion.hpp @@ -173,7 +173,7 @@ public: // Should be called when we want to release the active region which // is returned after it's been retired. - HeapRegion* release(); + virtual HeapRegion* release(); #if G1_ALLOC_REGION_TRACING void trace(const char* str, size_t word_size = 0, HeapWord* result = NULL); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp index c7d4ff3431a..9d2b7da758f 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp @@ -30,66 +30,32 @@ #include "runtime/java.hpp" #include "services/memTracker.hpp" -PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC + ////////////////////////////////////////////////////////////////////// // G1BlockOffsetSharedArray ////////////////////////////////////////////////////////////////////// -G1BlockOffsetSharedArray::G1BlockOffsetSharedArray(MemRegion reserved, - size_t init_word_size) : - _reserved(reserved), _end(NULL) -{ - size_t size = compute_size(reserved.word_size()); - ReservedSpace rs(ReservedSpace::allocation_align_size_up(size)); - if (!rs.is_reserved()) { - vm_exit_during_initialization("Could not reserve enough space for heap offset array"); - } - if (!_vs.initialize(rs, 0)) { - vm_exit_during_initialization("Could not reserve enough space for heap offset array"); - } +G1BlockOffsetSharedArray::G1BlockOffsetSharedArray(MemRegion heap, G1RegionToSpaceMapper* storage) : + _reserved(), _end(NULL), _listener(), _offset_array(NULL) { - MemTracker::record_virtual_memory_type((address)rs.base(), mtGC); + _reserved = heap; + _end = NULL; + + MemRegion bot_reserved = storage->reserved(); + + _offset_array = (u_char*)bot_reserved.start(); + _end = _reserved.end(); + + storage->set_mapping_changed_listener(&_listener); - _offset_array = (u_char*)_vs.low_boundary(); - resize(init_word_size); if (TraceBlockOffsetTable) { gclog_or_tty->print_cr("G1BlockOffsetSharedArray::G1BlockOffsetSharedArray: "); gclog_or_tty->print_cr(" " - " rs.base(): " INTPTR_FORMAT - " rs.size(): " INTPTR_FORMAT - " rs end(): " INTPTR_FORMAT, - rs.base(), rs.size(), rs.base() + rs.size()); - gclog_or_tty->print_cr(" " - " _vs.low_boundary(): " INTPTR_FORMAT - " _vs.high_boundary(): " INTPTR_FORMAT, - _vs.low_boundary(), - _vs.high_boundary()); - } -} - -void G1BlockOffsetSharedArray::resize(size_t new_word_size) { - assert(new_word_size <= _reserved.word_size(), "Resize larger than reserved"); - size_t new_size = compute_size(new_word_size); - size_t old_size = _vs.committed_size(); - size_t delta; - char* high = _vs.high(); - _end = _reserved.start() + new_word_size; - if (new_size > old_size) { - delta = ReservedSpace::page_align_size_up(new_size - old_size); - assert(delta > 0, "just checking"); - if (!_vs.expand_by(delta)) { - // Do better than this for Merlin - vm_exit_out_of_memory(delta, OOM_MMAP_ERROR, "offset table expansion"); - } - assert(_vs.high() == high + delta, "invalid expansion"); - // Initialization of the contents is left to the - // G1BlockOffsetArray that uses it. - } else { - delta = ReservedSpace::page_align_size_down(old_size - new_size); - if (delta == 0) return; - _vs.shrink_by(delta); - assert(_vs.high() == high - delta, "invalid expansion"); + " rs.base(): " PTR_FORMAT + " rs.size(): " SIZE_FORMAT + " rs end(): " PTR_FORMAT, + p2i(bot_reserved.start()), bot_reserved.byte_size(), p2i(bot_reserved.end())); } } @@ -99,37 +65,16 @@ bool G1BlockOffsetSharedArray::is_card_boundary(HeapWord* p) const { return (delta & right_n_bits(LogN_words)) == (size_t)NoBits; } -void G1BlockOffsetSharedArray::set_offset_array(HeapWord* left, HeapWord* right, u_char offset) { - check_index(index_for(right - 1), "right address out of range"); - assert(left < right, "Heap addresses out of order"); - size_t num_cards = pointer_delta(right, left) >> LogN_words; - if (UseMemSetInBOT) { - memset(&_offset_array[index_for(left)], offset, num_cards); - } else { - size_t i = index_for(left); - const size_t end = i + num_cards; - for (; i < end; i++) { - _offset_array[i] = offset; - } - } -} - ////////////////////////////////////////////////////////////////////// // G1BlockOffsetArray ////////////////////////////////////////////////////////////////////// G1BlockOffsetArray::G1BlockOffsetArray(G1BlockOffsetSharedArray* array, - MemRegion mr, bool init_to_zero) : + MemRegion mr) : G1BlockOffsetTable(mr.start(), mr.end()), _unallocated_block(_bottom), - _array(array), _gsp(NULL), - _init_to_zero(init_to_zero) { + _array(array), _gsp(NULL) { assert(_bottom <= _end, "arguments out of order"); - if (!_init_to_zero) { - // initialize cards to point back to mr.start() - set_remainder_to_point_to_start(mr.start() + N_words, mr.end()); - _array->set_offset_array(0, 0); // set first card to 0 - } } void G1BlockOffsetArray::set_space(G1OffsetTableContigSpace* sp) { @@ -219,93 +164,6 @@ G1BlockOffsetArray::set_remainder_to_point_to_start_incl(size_t start_card, size DEBUG_ONLY(check_all_cards(start_card, end_card);) } -// The block [blk_start, blk_end) has been allocated; -// adjust the block offset table to represent this information; -// right-open interval: [blk_start, blk_end) -void -G1BlockOffsetArray::alloc_block(HeapWord* blk_start, HeapWord* blk_end) { - mark_block(blk_start, blk_end); - allocated(blk_start, blk_end); -} - -// Adjust BOT to show that a previously whole block has been split -// into two. -void G1BlockOffsetArray::split_block(HeapWord* blk, size_t blk_size, - size_t left_blk_size) { - // Verify that the BOT shows [blk, blk + blk_size) to be one block. - verify_single_block(blk, blk_size); - // Update the BOT to indicate that [blk + left_blk_size, blk + blk_size) - // is one single block. - mark_block(blk + left_blk_size, blk + blk_size); -} - - -// Action_mark - update the BOT for the block [blk_start, blk_end). -// Current typical use is for splitting a block. -// Action_single - update the BOT for an allocation. -// Action_verify - BOT verification. -void G1BlockOffsetArray::do_block_internal(HeapWord* blk_start, - HeapWord* blk_end, - Action action) { - assert(Universe::heap()->is_in_reserved(blk_start), - "reference must be into the heap"); - assert(Universe::heap()->is_in_reserved(blk_end-1), - "limit must be within the heap"); - // This is optimized to make the test fast, assuming we only rarely - // cross boundaries. - uintptr_t end_ui = (uintptr_t)(blk_end - 1); - uintptr_t start_ui = (uintptr_t)blk_start; - // Calculate the last card boundary preceding end of blk - intptr_t boundary_before_end = (intptr_t)end_ui; - clear_bits(boundary_before_end, right_n_bits(LogN)); - if (start_ui <= (uintptr_t)boundary_before_end) { - // blk starts at or crosses a boundary - // Calculate index of card on which blk begins - size_t start_index = _array->index_for(blk_start); - // Index of card on which blk ends - size_t end_index = _array->index_for(blk_end - 1); - // Start address of card on which blk begins - HeapWord* boundary = _array->address_for_index(start_index); - assert(boundary <= blk_start, "blk should start at or after boundary"); - if (blk_start != boundary) { - // blk starts strictly after boundary - // adjust card boundary and start_index forward to next card - boundary += N_words; - start_index++; - } - assert(start_index <= end_index, "monotonicity of index_for()"); - assert(boundary <= (HeapWord*)boundary_before_end, "tautology"); - switch (action) { - case Action_mark: { - if (init_to_zero()) { - _array->set_offset_array(start_index, boundary, blk_start); - break; - } // Else fall through to the next case - } - case Action_single: { - _array->set_offset_array(start_index, boundary, blk_start); - // We have finished marking the "offset card". We need to now - // mark the subsequent cards that this blk spans. - if (start_index < end_index) { - HeapWord* rem_st = _array->address_for_index(start_index) + N_words; - HeapWord* rem_end = _array->address_for_index(end_index) + N_words; - set_remainder_to_point_to_start(rem_st, rem_end); - } - break; - } - case Action_check: { - _array->check_offset_array(start_index, boundary, blk_start); - // We have finished checking the "offset card". We need to now - // check the subsequent cards that this blk spans. - check_all_cards(start_index + 1, end_index); - break; - } - default: - ShouldNotReachHere(); - } - } -} - // The card-interval [start_card, end_card] is a closed interval; this // is an expensive check -- use with care and only under protection of // suitable flag. @@ -344,25 +202,6 @@ void G1BlockOffsetArray::check_all_cards(size_t start_card, size_t end_card) con } } -// The range [blk_start, blk_end) represents a single contiguous block -// of storage; modify the block offset table to represent this -// information; Right-open interval: [blk_start, blk_end) -// NOTE: this method does _not_ adjust _unallocated_block. -void -G1BlockOffsetArray::single_block(HeapWord* blk_start, HeapWord* blk_end) { - do_block_internal(blk_start, blk_end, Action_single); -} - -// Mark the BOT such that if [blk_start, blk_end) straddles a card -// boundary, the card following the first such boundary is marked -// with the appropriate offset. -// NOTE: this method does _not_ adjust _unallocated_block or -// any cards subsequent to the first one. -void -G1BlockOffsetArray::mark_block(HeapWord* blk_start, HeapWord* blk_end) { - do_block_internal(blk_start, blk_end, Action_mark); -} - HeapWord* G1BlockOffsetArray::block_start_unsafe(const void* addr) { assert(_bottom <= addr && addr < _end, "addr must be covered by this Array"); @@ -419,7 +258,7 @@ G1BlockOffsetArray::forward_to_block_containing_addr_slow(HeapWord* q, assert(next_boundary <= _array->_end, err_msg("next_boundary is beyond the end of the covered region " " next_boundary " PTR_FORMAT " _array->_end " PTR_FORMAT, - next_boundary, _array->_end)); + p2i(next_boundary), p2i(_array->_end))); if (addr >= gsp()->top()) return gsp()->top(); while (next_boundary < addr) { while (n <= next_boundary) { @@ -435,57 +274,13 @@ G1BlockOffsetArray::forward_to_block_containing_addr_slow(HeapWord* q, return forward_to_block_containing_addr_const(q, n, addr); } -HeapWord* G1BlockOffsetArray::block_start_careful(const void* addr) const { - assert(_array->offset_array(0) == 0, "objects can't cross covered areas"); - - assert(_bottom <= addr && addr < _end, - "addr must be covered by this Array"); - // Must read this exactly once because it can be modified by parallel - // allocation. - HeapWord* ub = _unallocated_block; - if (BlockOffsetArrayUseUnallocatedBlock && addr >= ub) { - assert(ub < _end, "tautology (see above)"); - return ub; - } - - // Otherwise, find the block start using the table, but taking - // care (cf block_start_unsafe() above) not to parse any objects/blocks - // on the cards themselves. - size_t index = _array->index_for(addr); - assert(_array->address_for_index(index) == addr, - "arg should be start of card"); - - HeapWord* q = (HeapWord*)addr; - uint offset; - do { - offset = _array->offset_array(index--); - q -= offset; - } while (offset == N_words); - assert(q <= addr, "block start should be to left of arg"); - return q; -} - // Note that the committed size of the covered space may have changed, // so the table size might also wish to change. void G1BlockOffsetArray::resize(size_t new_word_size) { HeapWord* new_end = _bottom + new_word_size; - if (_end < new_end && !init_to_zero()) { - // verify that the old and new boundaries are also card boundaries - assert(_array->is_card_boundary(_end), - "_end not a card boundary"); - assert(_array->is_card_boundary(new_end), - "new _end would not be a card boundary"); - // set all the newly added cards - _array->set_offset_array(_end, new_end, N_words); - } _end = new_end; // update _end } -void G1BlockOffsetArray::set_region(MemRegion mr) { - _bottom = mr.start(); - _end = mr.end(); -} - // // threshold_ // | _index_ @@ -560,7 +355,7 @@ void G1BlockOffsetArray::alloc_block_work2(HeapWord** threshold_, size_t* index_ "blk_start: " PTR_FORMAT ", " "boundary: " PTR_FORMAT, (uint)_array->offset_array(orig_index), - blk_start, boundary)); + p2i(blk_start), p2i(boundary))); for (size_t j = orig_index + 1; j <= end_index; j++) { assert(_array->offset_array(j) > 0 && _array->offset_array(j) <= @@ -594,9 +389,9 @@ G1BlockOffsetArray::verify_for_object(HeapWord* obj_start, "card addr: "PTR_FORMAT" BOT entry: %u " "obj: "PTR_FORMAT" word size: "SIZE_FORMAT" " "cards: ["SIZE_FORMAT","SIZE_FORMAT"]", - block_start, card, card_addr, + p2i(block_start), card, p2i(card_addr), _array->offset_array(card), - obj_start, word_size, first_card, last_card); + p2i(obj_start), word_size, first_card, last_card); return false; } } @@ -610,10 +405,10 @@ G1BlockOffsetArray::print_on(outputStream* out) { size_t to_index = _array->index_for(_end); out->print_cr(">> BOT for area ["PTR_FORMAT","PTR_FORMAT") " "cards ["SIZE_FORMAT","SIZE_FORMAT")", - _bottom, _end, from_index, to_index); + p2i(_bottom), p2i(_end), from_index, to_index); for (size_t i = from_index; i < to_index; ++i) { out->print_cr(" entry "SIZE_FORMAT_W(8)" | "PTR_FORMAT" : %3u", - i, _array->address_for_index(i), + i, p2i(_array->address_for_index(i)), (uint) _array->offset_array(i)); } } @@ -644,12 +439,31 @@ block_start_unsafe_const(const void* addr) const { G1BlockOffsetArrayContigSpace:: G1BlockOffsetArrayContigSpace(G1BlockOffsetSharedArray* array, MemRegion mr) : - G1BlockOffsetArray(array, mr, true) + G1BlockOffsetArray(array, mr) { _next_offset_threshold = NULL; _next_offset_index = 0; } +HeapWord* G1BlockOffsetArrayContigSpace::initialize_threshold_raw() { + assert(!Universe::heap()->is_in_reserved(_array->_offset_array), + "just checking"); + _next_offset_index = _array->index_for_raw(_bottom); + _next_offset_index++; + _next_offset_threshold = + _array->address_for_index_raw(_next_offset_index); + return _next_offset_threshold; +} + +void G1BlockOffsetArrayContigSpace::zero_bottom_entry_raw() { + assert(!Universe::heap()->is_in_reserved(_array->_offset_array), + "just checking"); + size_t bottom_index = _array->index_for_raw(_bottom); + assert(_array->address_for_index_raw(bottom_index) == _bottom, + "Precondition of call"); + _array->set_offset_array_raw(bottom_index, 0); +} + HeapWord* G1BlockOffsetArrayContigSpace::initialize_threshold() { assert(!Universe::heap()->is_in_reserved(_array->_offset_array), "just checking"); @@ -660,22 +474,12 @@ HeapWord* G1BlockOffsetArrayContigSpace::initialize_threshold() { return _next_offset_threshold; } -void G1BlockOffsetArrayContigSpace::zero_bottom_entry() { - assert(!Universe::heap()->is_in_reserved(_array->_offset_array), - "just checking"); - size_t bottom_index = _array->index_for(_bottom); - assert(_array->address_for_index(bottom_index) == _bottom, - "Precondition of call"); - _array->set_offset_array(bottom_index, 0); -} - void G1BlockOffsetArrayContigSpace::set_for_starts_humongous(HeapWord* new_top) { assert(new_top <= _end, "_end should have already been updated"); // The first BOT entry should have offset 0. - zero_bottom_entry(); - initialize_threshold(); + reset_bot(); alloc_block(_bottom, new_top); } @@ -683,7 +487,7 @@ G1BlockOffsetArrayContigSpace::set_for_starts_humongous(HeapWord* new_top) { void G1BlockOffsetArrayContigSpace::print_on(outputStream* out) { G1BlockOffsetArray::print_on(out); - out->print_cr(" next offset threshold: "PTR_FORMAT, _next_offset_threshold); + out->print_cr(" next offset threshold: "PTR_FORMAT, p2i(_next_offset_threshold)); out->print_cr(" next offset index: "SIZE_FORMAT, _next_offset_index); } #endif // !PRODUCT diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp index 5395899f143..47bcf656857 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1BLOCKOFFSETTABLE_HPP #define SHARE_VM_GC_IMPLEMENTATION_G1_G1BLOCKOFFSETTABLE_HPP +#include "gc_implementation/g1/g1RegionToSpaceMapper.hpp" #include "memory/memRegion.hpp" #include "runtime/virtualspace.hpp" #include "utilities/globalDefinitions.hpp" @@ -106,6 +107,16 @@ public: inline HeapWord* block_start_const(const void* addr) const; }; +class G1BlockOffsetSharedArrayMappingChangedListener : public G1MappingChangedListener { + public: + virtual void on_commit(uint start_idx, size_t num_regions) { + // Nothing to do. The BOT is hard-wired to be part of the HeapRegion, and we cannot + // retrieve it here since this would cause firing of several asserts. The code + // executed after commit of a region already needs to do some re-initialization of + // the HeapRegion, so we combine that. + } +}; + // This implementation of "G1BlockOffsetTable" divides the covered region // into "N"-word subregions (where "N" = 2^"LogN". An array with an entry // for each such subregion indicates how far back one must go to find the @@ -125,6 +136,7 @@ class G1BlockOffsetSharedArray: public CHeapObj { friend class VMStructs; private: + G1BlockOffsetSharedArrayMappingChangedListener _listener; // The reserved region covered by the shared array. MemRegion _reserved; @@ -133,16 +145,8 @@ private: // Array for keeping offsets for retrieving object start fast given an // address. - VirtualSpace _vs; u_char* _offset_array; // byte array keeping backwards offsets - void check_index(size_t index, const char* msg) const { - assert(index < _vs.committed_size(), - err_msg("%s - " - "index: " SIZE_FORMAT ", _vs.committed_size: " SIZE_FORMAT, - msg, index, _vs.committed_size())); - } - void check_offset(size_t offset, const char* msg) const { assert(offset <= N_words, err_msg("%s - " @@ -152,63 +156,29 @@ private: // Bounds checking accessors: // For performance these have to devolve to array accesses in product builds. - u_char offset_array(size_t index) const { - check_index(index, "index out of range"); - return _offset_array[index]; - } + inline u_char offset_array(size_t index) const; - void set_offset_array(HeapWord* left, HeapWord* right, u_char offset); - - void set_offset_array(size_t index, u_char offset) { - check_index(index, "index out of range"); - check_offset(offset, "offset too large"); + void set_offset_array_raw(size_t index, u_char offset) { _offset_array[index] = offset; } - void set_offset_array(size_t index, HeapWord* high, HeapWord* low) { - check_index(index, "index out of range"); - assert(high >= low, "addresses out of order"); - check_offset(pointer_delta(high, low), "offset too large"); - _offset_array[index] = (u_char) pointer_delta(high, low); - } + inline void set_offset_array(size_t index, u_char offset); - void set_offset_array(size_t left, size_t right, u_char offset) { - check_index(right, "right index out of range"); - assert(left <= right, "indexes out of order"); - size_t num_cards = right - left + 1; - if (UseMemSetInBOT) { - memset(&_offset_array[left], offset, num_cards); - } else { - size_t i = left; - const size_t end = i + num_cards; - for (; i < end; i++) { - _offset_array[i] = offset; - } - } - } + inline void set_offset_array(size_t index, HeapWord* high, HeapWord* low); - void check_offset_array(size_t index, HeapWord* high, HeapWord* low) const { - check_index(index, "index out of range"); - assert(high >= low, "addresses out of order"); - check_offset(pointer_delta(high, low), "offset too large"); - assert(_offset_array[index] == pointer_delta(high, low), "Wrong offset"); - } + inline void set_offset_array(size_t left, size_t right, u_char offset); bool is_card_boundary(HeapWord* p) const; +public: + // Return the number of slots needed for an offset array // that covers mem_region_words words. - // We always add an extra slot because if an object - // ends on a card boundary we put a 0 in the next - // offset array slot, so we want that slot always - // to be reserved. - - size_t compute_size(size_t mem_region_words) { - size_t number_of_slots = (mem_region_words / N_words) + 1; - return ReservedSpace::page_align_size_up(number_of_slots); + static size_t compute_size(size_t mem_region_words) { + size_t number_of_slots = (mem_region_words / N_words); + return ReservedSpace::allocation_align_size_up(number_of_slots); } -public: enum SomePublicConstants { LogN = 9, LogN_words = LogN - LogHeapWordSize, @@ -222,25 +192,19 @@ public: // least "init_word_size".) The contents of the initial table are // undefined; it is the responsibility of the constituent // G1BlockOffsetTable(s) to initialize cards. - G1BlockOffsetSharedArray(MemRegion reserved, size_t init_word_size); - - // Notes a change in the committed size of the region covered by the - // table. The "new_word_size" may not be larger than the size of the - // reserved region this table covers. - void resize(size_t new_word_size); - - void set_bottom(HeapWord* new_bottom); - - // Updates all the BlockOffsetArray's sharing this shared array to - // reflect the current "top"'s of their spaces. - void update_offset_arrays(); + G1BlockOffsetSharedArray(MemRegion heap, G1RegionToSpaceMapper* storage); // Return the appropriate index into "_offset_array" for "p". inline size_t index_for(const void* p) const; + inline size_t index_for_raw(const void* p) const; // Return the address indicating the start of the region corresponding to // "index" in "_offset_array". inline HeapWord* address_for_index(size_t index) const; + // Variant of address_for_index that does not check the index for validity. + inline HeapWord* address_for_index_raw(size_t index) const { + return _reserved.start() + (index << LogN_words); + } }; // And here is the G1BlockOffsetTable subtype that uses the array. @@ -255,14 +219,6 @@ private: LogN = G1BlockOffsetSharedArray::LogN }; - // The following enums are used by do_block_helper - enum Action { - Action_single, // BOT records a single block (see single_block()) - Action_mark, // BOT marks the start of a block (see mark_block()) - Action_check // Check that BOT records block correctly - // (see verify_single_block()). - }; - // This is the array, which can be shared by several BlockOffsetArray's // servicing different G1BlockOffsetSharedArray* _array; @@ -270,10 +226,6 @@ private: // The space that owns this subregion. G1OffsetTableContigSpace* _gsp; - // If true, array entries are initialized to 0; otherwise, they are - // initialized to point backwards to the beginning of the covered region. - bool _init_to_zero; - // The portion [_unallocated_block, _sp.end()) of the space that // is a single block known not to contain any objects. // NOTE: See BlockOffsetArrayUseUnallocatedBlock flag. @@ -288,9 +240,6 @@ private: // that is closed: [start_index, end_index] void set_remainder_to_point_to_start_incl(size_t start, size_t end); - // A helper function for BOT adjustment/verification work - void do_block_internal(HeapWord* blk_start, HeapWord* blk_end, Action action); - protected: G1OffsetTableContigSpace* gsp() const { return _gsp; } @@ -338,11 +287,9 @@ protected: public: // The space may not have it's bottom and top set yet, which is why the - // region is passed as a parameter. If "init_to_zero" is true, the - // elements of the array are initialized to zero. Otherwise, they are - // initialized to point backwards to the beginning. - G1BlockOffsetArray(G1BlockOffsetSharedArray* array, MemRegion mr, - bool init_to_zero); + // region is passed as a parameter. The elements of the array are + // initialized to zero. + G1BlockOffsetArray(G1BlockOffsetSharedArray* array, MemRegion mr); // Note: this ought to be part of the constructor, but that would require // "this" to be passed as a parameter to a member constructor for @@ -350,114 +297,19 @@ public: // This would be legal C++, but MS VC++ doesn't allow it. void set_space(G1OffsetTableContigSpace* sp); - // Resets the covered region to the given "mr". - void set_region(MemRegion mr); - // Resets the covered region to one with the same _bottom as before but // the "new_word_size". void resize(size_t new_word_size); - // These must be guaranteed to work properly (i.e., do nothing) - // when "blk_start" ("blk" for second version) is "NULL". - virtual void alloc_block(HeapWord* blk_start, HeapWord* blk_end); - virtual void alloc_block(HeapWord* blk, size_t size) { - alloc_block(blk, blk + size); - } - - // The following methods are useful and optimized for a - // general, non-contiguous space. - - // Given a block [blk_start, blk_start + full_blk_size), and - // a left_blk_size < full_blk_size, adjust the BOT to show two - // blocks [blk_start, blk_start + left_blk_size) and - // [blk_start + left_blk_size, blk_start + full_blk_size). - // It is assumed (and verified in the non-product VM) that the - // BOT was correct for the original block. - void split_block(HeapWord* blk_start, size_t full_blk_size, - size_t left_blk_size); - - // Adjust the BOT to show that it has a single block in the - // range [blk_start, blk_start + size). All necessary BOT - // cards are adjusted, but _unallocated_block isn't. - void single_block(HeapWord* blk_start, HeapWord* blk_end); - void single_block(HeapWord* blk, size_t size) { - single_block(blk, blk + size); - } - - // Adjust BOT to show that it has a block in the range - // [blk_start, blk_start + size). Only the first card - // of BOT is touched. It is assumed (and verified in the - // non-product VM) that the remaining cards of the block - // are correct. - void mark_block(HeapWord* blk_start, HeapWord* blk_end); - void mark_block(HeapWord* blk, size_t size) { - mark_block(blk, blk + size); - } - - // Adjust _unallocated_block to indicate that a particular - // block has been newly allocated or freed. It is assumed (and - // verified in the non-product VM) that the BOT is correct for - // the given block. - inline void allocated(HeapWord* blk_start, HeapWord* blk_end) { - // Verify that the BOT shows [blk, blk + blk_size) to be one block. - verify_single_block(blk_start, blk_end); - if (BlockOffsetArrayUseUnallocatedBlock) { - _unallocated_block = MAX2(_unallocated_block, blk_end); - } - } - - inline void allocated(HeapWord* blk, size_t size) { - allocated(blk, blk + size); - } - - inline void freed(HeapWord* blk_start, HeapWord* blk_end); - - inline void freed(HeapWord* blk, size_t size); - virtual HeapWord* block_start_unsafe(const void* addr); virtual HeapWord* block_start_unsafe_const(const void* addr) const; - // Requires "addr" to be the start of a card and returns the - // start of the block that contains the given address. - HeapWord* block_start_careful(const void* addr) const; - - // If true, initialize array slots with no allocated blocks to zero. - // Otherwise, make them point back to the front. - bool init_to_zero() { return _init_to_zero; } - - // Verification & debugging - ensure that the offset table reflects the fact - // that the block [blk_start, blk_end) or [blk, blk + size) is a - // single block of storage. NOTE: can;t const this because of - // call to non-const do_block_internal() below. - inline void verify_single_block(HeapWord* blk_start, HeapWord* blk_end) { - if (VerifyBlockOffsetArray) { - do_block_internal(blk_start, blk_end, Action_check); - } - } - - inline void verify_single_block(HeapWord* blk, size_t size) { - verify_single_block(blk, blk + size); - } - // Used by region verification. Checks that the contents of the // BOT reflect that there's a single object that spans the address // range [obj_start, obj_start + word_size); returns true if this is // the case, returns false if it's not. bool verify_for_object(HeapWord* obj_start, size_t word_size) const; - // Verify that the given block is before _unallocated_block - inline void verify_not_unallocated(HeapWord* blk_start, - HeapWord* blk_end) const { - if (BlockOffsetArrayUseUnallocatedBlock) { - assert(blk_start < blk_end, "Block inconsistency?"); - assert(blk_end <= _unallocated_block, "_unallocated_block problem"); - } - } - - inline void verify_not_unallocated(HeapWord* blk, size_t size) const { - verify_not_unallocated(blk, blk + size); - } - void check_all_cards(size_t left_card, size_t right_card) const; virtual void print_on(outputStream* out) PRODUCT_RETURN; @@ -480,6 +332,12 @@ class G1BlockOffsetArrayContigSpace: public G1BlockOffsetArray { blk_start, blk_end); } + // Zero out the entry for _bottom (offset will be zero). Does not check for availability of the + // memory first. + void zero_bottom_entry_raw(); + // Variant of initialize_threshold that does not check for availability of the + // memory first. + HeapWord* initialize_threshold_raw(); public: G1BlockOffsetArrayContigSpace(G1BlockOffsetSharedArray* array, MemRegion mr); @@ -487,8 +345,10 @@ class G1BlockOffsetArrayContigSpace: public G1BlockOffsetArray { // bottom of the covered region. HeapWord* initialize_threshold(); - // Zero out the entry for _bottom (offset will be zero). - void zero_bottom_entry(); + void reset_bot() { + zero_bottom_entry_raw(); + initialize_threshold_raw(); + } // Return the next threshold, the point at which the table should be // updated. diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.inline.hpp index 4cf0a06cd74..be5cf309a32 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.inline.hpp @@ -47,14 +47,62 @@ G1BlockOffsetTable::block_start_const(const void* addr) const { } } +#define check_index(index, msg) \ + assert((index) < (_reserved.word_size() >> LogN_words), \ + err_msg("%s - index: "SIZE_FORMAT", _vs.committed_size: "SIZE_FORMAT, \ + msg, (index), (_reserved.word_size() >> LogN_words))); \ + assert(G1CollectedHeap::heap()->is_in_exact(address_for_index_raw(index)), \ + err_msg("Index "SIZE_FORMAT" corresponding to "PTR_FORMAT \ + " (%u) is not in committed area.", \ + (index), \ + p2i(address_for_index_raw(index)), \ + G1CollectedHeap::heap()->addr_to_region(address_for_index_raw(index)))); + +u_char G1BlockOffsetSharedArray::offset_array(size_t index) const { + check_index(index, "index out of range"); + return _offset_array[index]; +} + +void G1BlockOffsetSharedArray::set_offset_array(size_t index, u_char offset) { + check_index(index, "index out of range"); + set_offset_array_raw(index, offset); +} + +void G1BlockOffsetSharedArray::set_offset_array(size_t index, HeapWord* high, HeapWord* low) { + check_index(index, "index out of range"); + assert(high >= low, "addresses out of order"); + size_t offset = pointer_delta(high, low); + check_offset(offset, "offset too large"); + set_offset_array(index, (u_char)offset); +} + +void G1BlockOffsetSharedArray::set_offset_array(size_t left, size_t right, u_char offset) { + check_index(right, "right index out of range"); + assert(left <= right, "indexes out of order"); + size_t num_cards = right - left + 1; + if (UseMemSetInBOT) { + memset(&_offset_array[left], offset, num_cards); + } else { + size_t i = left; + const size_t end = i + num_cards; + for (; i < end; i++) { + _offset_array[i] = offset; + } + } +} + +// Variant of index_for that does not check the index for validity. +inline size_t G1BlockOffsetSharedArray::index_for_raw(const void* p) const { + return pointer_delta((char*)p, _reserved.start(), sizeof(char)) >> LogN; +} + inline size_t G1BlockOffsetSharedArray::index_for(const void* p) const { char* pc = (char*)p; assert(pc >= (char*)_reserved.start() && pc < (char*)_reserved.end(), err_msg("p (" PTR_FORMAT ") not in reserved [" PTR_FORMAT ", " PTR_FORMAT ")", p2i(p), p2i(_reserved.start()), p2i(_reserved.end()))); - size_t delta = pointer_delta(pc, _reserved.start(), sizeof(char)); - size_t result = delta >> LogN; + size_t result = index_for_raw(p); check_index(result, "bad index from address"); return result; } @@ -62,7 +110,7 @@ inline size_t G1BlockOffsetSharedArray::index_for(const void* p) const { inline HeapWord* G1BlockOffsetSharedArray::address_for_index(size_t index) const { check_index(index, "index out of range"); - HeapWord* result = _reserved.start() + (index << LogN_words); + HeapWord* result = address_for_index_raw(index); assert(result >= _reserved.start() && result < _reserved.end(), err_msg("bad address from index result " PTR_FORMAT " _reserved.start() " PTR_FORMAT " _reserved.end() " @@ -71,6 +119,8 @@ G1BlockOffsetSharedArray::address_for_index(size_t index) const { return result; } +#undef check_index + inline size_t G1BlockOffsetArray::block_size(const HeapWord* p) const { return gsp()->block_size(p); @@ -136,28 +186,4 @@ G1BlockOffsetArray::forward_to_block_containing_addr(HeapWord* q, return q; } -////////////////////////////////////////////////////////////////////////// -// BlockOffsetArrayNonContigSpace inlines -////////////////////////////////////////////////////////////////////////// -inline void G1BlockOffsetArray::freed(HeapWord* blk_start, HeapWord* blk_end) { - // Verify that the BOT shows [blk_start, blk_end) to be one block. - verify_single_block(blk_start, blk_end); - // adjust _unallocated_block upward or downward - // as appropriate - if (BlockOffsetArrayUseUnallocatedBlock) { - assert(_unallocated_block <= _end, - "Inconsistent value for _unallocated_block"); - if (blk_end >= _unallocated_block && blk_start <= _unallocated_block) { - // CMS-specific note: a block abutting _unallocated_block to - // its left is being freed, a new block is being added or - // we are resetting following a compaction - _unallocated_block = blk_start; - } - } -} - -inline void G1BlockOffsetArray::freed(HeapWord* blk, size_t size) { - freed(blk, blk + size); -} - #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1BLOCKOFFSETTABLE_INLINE_HPP diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.cpp index 9fff9604ff2..f9fd47cfb64 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.cpp @@ -33,31 +33,26 @@ PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC +void G1CardCountsMappingChangedListener::on_commit(uint start_idx, size_t num_regions) { + MemRegion mr(G1CollectedHeap::heap()->bottom_addr_for_region(start_idx), num_regions * HeapRegion::GrainWords); + _counts->clear_range(mr); +} + void G1CardCounts::clear_range(size_t from_card_num, size_t to_card_num) { if (has_count_table()) { - assert(from_card_num >= 0 && from_card_num < _committed_max_card_num, - err_msg("from card num out of range: "SIZE_FORMAT, from_card_num)); assert(from_card_num < to_card_num, err_msg("Wrong order? from: " SIZE_FORMAT ", to: "SIZE_FORMAT, from_card_num, to_card_num)); - assert(to_card_num <= _committed_max_card_num, - err_msg("to card num out of range: " - "to: "SIZE_FORMAT ", " - "max: "SIZE_FORMAT, - to_card_num, _committed_max_card_num)); - - to_card_num = MIN2(_committed_max_card_num, to_card_num); - Copy::fill_to_bytes(&_card_counts[from_card_num], (to_card_num - from_card_num)); } } G1CardCounts::G1CardCounts(G1CollectedHeap *g1h): - _g1h(g1h), _card_counts(NULL), - _reserved_max_card_num(0), _committed_max_card_num(0), - _committed_size(0) {} + _listener(), _g1h(g1h), _card_counts(NULL), _reserved_max_card_num(0) { + _listener.set_cardcounts(this); +} -void G1CardCounts::initialize() { +void G1CardCounts::initialize(G1RegionToSpaceMapper* mapper) { assert(_g1h->max_capacity() > 0, "initialization order"); assert(_g1h->capacity() == 0, "initialization order"); @@ -70,70 +65,9 @@ void G1CardCounts::initialize() { _ct_bs = _g1h->g1_barrier_set(); _ct_bot = _ct_bs->byte_for_const(_g1h->reserved_region().start()); - // Allocate/Reserve the counts table - size_t reserved_bytes = _g1h->max_capacity(); - _reserved_max_card_num = reserved_bytes >> CardTableModRefBS::card_shift; - - size_t reserved_size = _reserved_max_card_num * sizeof(jbyte); - ReservedSpace rs(ReservedSpace::allocation_align_size_up(reserved_size)); - if (!rs.is_reserved()) { - warning("Could not reserve enough space for the card counts table"); - guarantee(!has_reserved_count_table(), "should be NULL"); - return; - } - - MemTracker::record_virtual_memory_type((address)rs.base(), mtGC); - - _card_counts_storage.initialize(rs, 0); - _card_counts = (jubyte*) _card_counts_storage.low(); - } -} - -void G1CardCounts::resize(size_t heap_capacity) { - // Expand the card counts table to handle a heap with the given capacity. - - if (!has_reserved_count_table()) { - // Don't expand if we failed to reserve the card counts table. - return; - } - - assert(_committed_size == - ReservedSpace::allocation_align_size_up(_committed_size), - err_msg("Unaligned? committed_size: " SIZE_FORMAT, _committed_size)); - - // Verify that the committed space for the card counts matches our - // committed max card num. Note for some allocation alignments, the - // amount of space actually committed for the counts table will be able - // to span more cards than the number spanned by the maximum heap. - size_t prev_committed_size = _committed_size; - size_t prev_committed_card_num = committed_to_card_num(prev_committed_size); - - assert(prev_committed_card_num == _committed_max_card_num, - err_msg("Card mismatch: " - "prev: " SIZE_FORMAT ", " - "committed: "SIZE_FORMAT", " - "reserved: "SIZE_FORMAT, - prev_committed_card_num, _committed_max_card_num, _reserved_max_card_num)); - - size_t new_size = (heap_capacity >> CardTableModRefBS::card_shift) * sizeof(jbyte); - size_t new_committed_size = ReservedSpace::allocation_align_size_up(new_size); - size_t new_committed_card_num = committed_to_card_num(new_committed_size); - - if (_committed_max_card_num < new_committed_card_num) { - // we need to expand the backing store for the card counts - size_t expand_size = new_committed_size - prev_committed_size; - - if (!_card_counts_storage.expand_by(expand_size)) { - warning("Card counts table backing store commit failure"); - return; - } - assert(_card_counts_storage.committed_size() == new_committed_size, - "expansion commit failure"); - - _committed_size = new_committed_size; - _committed_max_card_num = new_committed_card_num; - - clear_range(prev_committed_card_num, _committed_max_card_num); + _card_counts = (jubyte*) mapper->reserved().start(); + _reserved_max_card_num = mapper->reserved().byte_size(); + mapper->set_mapping_changed_listener(&_listener); } } @@ -149,12 +83,13 @@ uint G1CardCounts::add_card_count(jbyte* card_ptr) { uint count = 0; if (has_count_table()) { size_t card_num = ptr_2_card_num(card_ptr); - if (card_num < _committed_max_card_num) { - count = (uint) _card_counts[card_num]; - if (count < G1ConcRSHotCardLimit) { - _card_counts[card_num] = - (jubyte)(MIN2((uintx)(_card_counts[card_num] + 1), G1ConcRSHotCardLimit)); - } + assert(card_num < _reserved_max_card_num, + err_msg("Card "SIZE_FORMAT" outside of card counts table (max size "SIZE_FORMAT")", + card_num, _reserved_max_card_num)); + count = (uint) _card_counts[card_num]; + if (count < G1ConcRSHotCardLimit) { + _card_counts[card_num] = + (jubyte)(MIN2((uintx)(_card_counts[card_num] + 1), G1ConcRSHotCardLimit)); } } return count; @@ -165,31 +100,23 @@ bool G1CardCounts::is_hot(uint count) { } void G1CardCounts::clear_region(HeapRegion* hr) { - assert(!hr->isHumongous(), "Should have been cleared"); + MemRegion mr(hr->bottom(), hr->end()); + clear_range(mr); +} + +void G1CardCounts::clear_range(MemRegion mr) { if (has_count_table()) { - HeapWord* bottom = hr->bottom(); - - // We use the last address in hr as hr could be the - // last region in the heap. In which case trying to find - // the card for hr->end() will be an OOB access to the - // card table. - HeapWord* last = hr->end() - 1; - assert(_g1h->g1_committed().contains(last), - err_msg("last not in committed: " - "last: " PTR_FORMAT ", " - "committed: [" PTR_FORMAT ", " PTR_FORMAT ")", - last, - _g1h->g1_committed().start(), - _g1h->g1_committed().end())); - - const jbyte* from_card_ptr = _ct_bs->byte_for_const(bottom); - const jbyte* last_card_ptr = _ct_bs->byte_for_const(last); + const jbyte* from_card_ptr = _ct_bs->byte_for_const(mr.start()); + // We use the last address in the range as the range could represent the + // last region in the heap. In which case trying to find the card will be an + // OOB access to the card table. + const jbyte* last_card_ptr = _ct_bs->byte_for_const(mr.last()); #ifdef ASSERT HeapWord* start_addr = _ct_bs->addr_for(from_card_ptr); - assert(start_addr == hr->bottom(), "alignment"); + assert(start_addr == mr.start(), "MemRegion start must be aligned to a card."); HeapWord* last_addr = _ct_bs->addr_for(last_card_ptr); - assert((last_addr + CardTableModRefBS::card_size_in_words) == hr->end(), "alignment"); + assert((last_addr + CardTableModRefBS::card_size_in_words) == mr.end(), "MemRegion end must be aligned to a card."); #endif // ASSERT // Clear the counts for the (exclusive) card range. @@ -199,14 +126,22 @@ void G1CardCounts::clear_region(HeapRegion* hr) { } } +class G1CardCountsClearClosure : public HeapRegionClosure { + private: + G1CardCounts* _card_counts; + public: + G1CardCountsClearClosure(G1CardCounts* card_counts) : + HeapRegionClosure(), _card_counts(card_counts) { } + + + virtual bool doHeapRegion(HeapRegion* r) { + _card_counts->clear_region(r); + return false; + } +}; + void G1CardCounts::clear_all() { assert(SafepointSynchronize::is_at_safepoint(), "don't call this otherwise"); - clear_range((size_t)0, _committed_max_card_num); + G1CardCountsClearClosure cl(this); + _g1h->heap_region_iterate(&cl); } - -G1CardCounts::~G1CardCounts() { - if (has_reserved_count_table()) { - _card_counts_storage.release(); - } -} - diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.hpp index ef08479f60a..4252cba0937 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CardCounts.hpp @@ -25,14 +25,26 @@ #ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1CARDCOUNTS_HPP #define SHARE_VM_GC_IMPLEMENTATION_G1_G1CARDCOUNTS_HPP +#include "gc_implementation/g1/g1RegionToSpaceMapper.hpp" #include "memory/allocation.hpp" #include "runtime/virtualspace.hpp" #include "utilities/globalDefinitions.hpp" class CardTableModRefBS; +class G1CardCounts; class G1CollectedHeap; +class G1RegionToSpaceMapper; class HeapRegion; +class G1CardCountsMappingChangedListener : public G1MappingChangedListener { + private: + G1CardCounts* _counts; + public: + void set_cardcounts(G1CardCounts* counts) { _counts = counts; } + + virtual void on_commit(uint start_idx, size_t num_regions); +}; + // Table to track the number of times a card has been refined. Once // a card has been refined a certain number of times, it is // considered 'hot' and its refinement is delayed by inserting the @@ -41,6 +53,8 @@ class HeapRegion; // is 'drained' during the next evacuation pause. class G1CardCounts: public CHeapObj { + G1CardCountsMappingChangedListener _listener; + G1CollectedHeap* _g1h; // The table of counts @@ -49,27 +63,18 @@ class G1CardCounts: public CHeapObj { // Max capacity of the reserved space for the counts table size_t _reserved_max_card_num; - // Max capacity of the committed space for the counts table - size_t _committed_max_card_num; - - // Size of committed space for the counts table - size_t _committed_size; - // CardTable bottom. const jbyte* _ct_bot; // Barrier set CardTableModRefBS* _ct_bs; - // The virtual memory backing the counts table - VirtualSpace _card_counts_storage; - // Returns true if the card counts table has been reserved. bool has_reserved_count_table() { return _card_counts != NULL; } // Returns true if the card counts table has been reserved and committed. bool has_count_table() { - return has_reserved_count_table() && _committed_max_card_num > 0; + return has_reserved_count_table(); } size_t ptr_2_card_num(const jbyte* card_ptr) { @@ -79,37 +84,24 @@ class G1CardCounts: public CHeapObj { "_ct_bot: " PTR_FORMAT, p2i(card_ptr), p2i(_ct_bot))); size_t card_num = pointer_delta(card_ptr, _ct_bot, sizeof(jbyte)); - assert(card_num >= 0 && card_num < _committed_max_card_num, + assert(card_num >= 0 && card_num < _reserved_max_card_num, err_msg("card pointer out of range: " PTR_FORMAT, p2i(card_ptr))); return card_num; } jbyte* card_num_2_ptr(size_t card_num) { - assert(card_num >= 0 && card_num < _committed_max_card_num, + assert(card_num >= 0 && card_num < _reserved_max_card_num, err_msg("card num out of range: "SIZE_FORMAT, card_num)); return (jbyte*) (_ct_bot + card_num); } - // Helper routine. - // Returns the number of cards that can be counted by the given committed - // table size, with a maximum of the number of cards spanned by the max - // capacity of the heap. - size_t committed_to_card_num(size_t committed_size) { - return MIN2(_reserved_max_card_num, committed_size / sizeof(jbyte)); - } - // Clear the counts table for the given (exclusive) index range. void clear_range(size_t from_card_num, size_t to_card_num); public: G1CardCounts(G1CollectedHeap* g1h); - ~G1CardCounts(); - void initialize(); - - // Resize the committed space for the card counts table in - // response to a resize of the committed space for the heap. - void resize(size_t heap_capacity); + void initialize(G1RegionToSpaceMapper* mapper); // Increments the refinement count for the given card. // Returns the pre-increment count value. @@ -122,8 +114,10 @@ class G1CardCounts: public CHeapObj { // Clears the card counts for the cards spanned by the region void clear_region(HeapRegion* hr); + // Clears the card counts for the cards spanned by the MemRegion + void clear_range(MemRegion mr); + // Clear the entire card counts table during GC. - // Updates the policy stats with the duration. void clear_all(); }; diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp index 159a445b325..1e75d669d5c 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp @@ -22,372 +22,375 @@ * */ - #include "precompiled.hpp" +#include "code/codeCache.hpp" #include "code/nmethod.hpp" #include "gc_implementation/g1/g1CodeCacheRemSet.hpp" +#include "gc_implementation/g1/heapRegion.hpp" +#include "memory/heap.hpp" #include "memory/iterator.hpp" +#include "oops/oop.inline.hpp" +#include "utilities/hashtable.inline.hpp" +#include "utilities/stack.inline.hpp" PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC -G1CodeRootChunk::G1CodeRootChunk() : _top(NULL), _next(NULL), _prev(NULL), _free(NULL) { - _top = bottom(); +class CodeRootSetTable : public Hashtable { + friend class G1CodeRootSetTest; + typedef HashtableEntry Entry; + + static CodeRootSetTable* volatile _purge_list; + + CodeRootSetTable* _purge_next; + + unsigned int compute_hash(nmethod* nm) { + uintptr_t hash = (uintptr_t)nm; + return hash ^ (hash >> 7); // code heap blocks are 128byte aligned + } + + Entry* new_entry(nmethod* nm); + + public: + CodeRootSetTable(int size) : Hashtable(size, sizeof(Entry)), _purge_next(NULL) {} + ~CodeRootSetTable(); + + // Needs to be protected locks + bool add(nmethod* nm); + bool remove(nmethod* nm); + + // Can be called without locking + bool contains(nmethod* nm); + + int entry_size() const { return BasicHashtable::entry_size(); } + + void copy_to(CodeRootSetTable* new_table); + void nmethods_do(CodeBlobClosure* blk); + + template + void remove_if(CB& should_remove); + + static void purge_list_append(CodeRootSetTable* tbl); + static void purge(); + + static size_t static_mem_size() { + return sizeof(_purge_list); + } +}; + +CodeRootSetTable* volatile CodeRootSetTable::_purge_list = NULL; + +CodeRootSetTable::Entry* CodeRootSetTable::new_entry(nmethod* nm) { + unsigned int hash = compute_hash(nm); + Entry* entry = (Entry*) new_entry_free_list(); + if (entry == NULL) { + entry = (Entry*) NEW_C_HEAP_ARRAY2(char, entry_size(), mtGC, CURRENT_PC); + } + entry->set_next(NULL); + entry->set_hash(hash); + entry->set_literal(nm); + return entry; } -void G1CodeRootChunk::reset() { - _next = _prev = NULL; - _free = NULL; - _top = bottom(); -} - -void G1CodeRootChunk::nmethods_do(CodeBlobClosure* cl) { - NmethodOrLink* cur = bottom(); - while (cur != _top) { - if (is_nmethod(cur)) { - cl->do_code_blob(cur->_nmethod); +CodeRootSetTable::~CodeRootSetTable() { + for (int index = 0; index < table_size(); ++index) { + for (Entry* e = bucket(index); e != NULL; ) { + Entry* to_remove = e; + // read next before freeing. + e = e->next(); + unlink_entry(to_remove); + FREE_C_HEAP_ARRAY(char, to_remove, mtGC); } - cur++; + } + assert(number_of_entries() == 0, "should have removed all entries"); + free_buckets(); + for (BasicHashtableEntry* e = new_entry_free_list(); e != NULL; e = new_entry_free_list()) { + FREE_C_HEAP_ARRAY(char, e, mtGC); } } -bool G1CodeRootChunk::remove_lock_free(nmethod* method) { - NmethodOrLink* cur = bottom(); - - for (NmethodOrLink* cur = bottom(); cur != _top; cur++) { - if (cur->_nmethod == method) { - bool result = Atomic::cmpxchg_ptr(NULL, &cur->_nmethod, method) == method; - - if (!result) { - // Someone else cleared out this entry. - return false; - } - - // The method was cleared. Time to link it into the free list. - NmethodOrLink* prev_free; - do { - prev_free = (NmethodOrLink*)_free; - cur->_link = prev_free; - } while (Atomic::cmpxchg_ptr(cur, &_free, prev_free) != prev_free); - - return true; - } +bool CodeRootSetTable::add(nmethod* nm) { + if (!contains(nm)) { + Entry* e = new_entry(nm); + int index = hash_to_index(e->hash()); + add_entry(index, e); + return true; } - return false; } -G1CodeRootChunkManager::G1CodeRootChunkManager() : _free_list(), _num_chunks_handed_out(0) { - _free_list.initialize(); - _free_list.set_size(G1CodeRootChunk::word_size()); -} - -size_t G1CodeRootChunkManager::fl_mem_size() { - return _free_list.count() * _free_list.size(); -} - -void G1CodeRootChunkManager::free_all_chunks(FreeList* list) { - _num_chunks_handed_out -= list->count(); - _free_list.prepend(list); -} - -void G1CodeRootChunkManager::free_chunk(G1CodeRootChunk* chunk) { - _free_list.return_chunk_at_head(chunk); - _num_chunks_handed_out--; -} - -void G1CodeRootChunkManager::purge_chunks(size_t keep_ratio) { - size_t keep = _num_chunks_handed_out * keep_ratio / 100; - if (keep >= (size_t)_free_list.count()) { - return; +bool CodeRootSetTable::contains(nmethod* nm) { + int index = hash_to_index(compute_hash(nm)); + for (Entry* e = bucket(index); e != NULL; e = e->next()) { + if (e->literal() == nm) { + return true; + } } + return false; +} - FreeList temp; - temp.initialize(); - temp.set_size(G1CodeRootChunk::word_size()); +bool CodeRootSetTable::remove(nmethod* nm) { + int index = hash_to_index(compute_hash(nm)); + Entry* previous = NULL; + for (Entry* e = bucket(index); e != NULL; previous = e, e = e->next()) { + if (e->literal() == nm) { + if (previous != NULL) { + previous->set_next(e->next()); + } else { + set_entry(index, e->next()); + } + free_entry(e); + return true; + } + } + return false; +} - _free_list.getFirstNChunksFromList((size_t)_free_list.count() - keep, &temp); +void CodeRootSetTable::copy_to(CodeRootSetTable* new_table) { + for (int index = 0; index < table_size(); ++index) { + for (Entry* e = bucket(index); e != NULL; e = e->next()) { + new_table->add(e->literal()); + } + } + new_table->copy_freelist(this); +} - G1CodeRootChunk* cur = temp.get_chunk_at_head(); - while (cur != NULL) { - delete cur; - cur = temp.get_chunk_at_head(); +void CodeRootSetTable::nmethods_do(CodeBlobClosure* blk) { + for (int index = 0; index < table_size(); ++index) { + for (Entry* e = bucket(index); e != NULL; e = e->next()) { + blk->do_code_blob(e->literal()); + } } } -size_t G1CodeRootChunkManager::static_mem_size() { - return sizeof(G1CodeRootChunkManager); -} - - -G1CodeRootChunk* G1CodeRootChunkManager::new_chunk() { - G1CodeRootChunk* result = _free_list.get_chunk_at_head(); - if (result == NULL) { - result = new G1CodeRootChunk(); +template +void CodeRootSetTable::remove_if(CB& should_remove) { + for (int index = 0; index < table_size(); ++index) { + Entry* previous = NULL; + Entry* e = bucket(index); + while (e != NULL) { + Entry* next = e->next(); + if (should_remove(e->literal())) { + if (previous != NULL) { + previous->set_next(next); + } else { + set_entry(index, next); + } + free_entry(e); + } else { + previous = e; + } + e = next; + } } - _num_chunks_handed_out++; - result->reset(); - return result; -} - -#ifndef PRODUCT - -size_t G1CodeRootChunkManager::num_chunks_handed_out() const { - return _num_chunks_handed_out; -} - -size_t G1CodeRootChunkManager::num_free_chunks() const { - return (size_t)_free_list.count(); -} - -#endif - -G1CodeRootChunkManager G1CodeRootSet::_default_chunk_manager; - -void G1CodeRootSet::purge_chunks(size_t keep_ratio) { - _default_chunk_manager.purge_chunks(keep_ratio); -} - -size_t G1CodeRootSet::free_chunks_static_mem_size() { - return _default_chunk_manager.static_mem_size(); -} - -size_t G1CodeRootSet::free_chunks_mem_size() { - return _default_chunk_manager.fl_mem_size(); -} - -G1CodeRootSet::G1CodeRootSet(G1CodeRootChunkManager* manager) : _manager(manager), _list(), _length(0) { - if (_manager == NULL) { - _manager = &_default_chunk_manager; - } - _list.initialize(); - _list.set_size(G1CodeRootChunk::word_size()); } G1CodeRootSet::~G1CodeRootSet() { - clear(); + delete _table; } -void G1CodeRootSet::add(nmethod* method) { - if (!contains(method)) { - // Find the first chunk that isn't full. - G1CodeRootChunk* cur = _list.head(); - while (cur != NULL) { - if (!cur->is_full()) { - break; - } - cur = cur->next(); - } - - // All chunks are full, get a new chunk. - if (cur == NULL) { - cur = new_chunk(); - _list.return_chunk_at_head(cur); - } - - // Add the nmethod. - bool result = cur->add(method); - - guarantee(result, err_msg("Not able to add nmethod "PTR_FORMAT" to newly allocated chunk.", method)); - - _length++; - } +CodeRootSetTable* G1CodeRootSet::load_acquire_table() { + return (CodeRootSetTable*) OrderAccess::load_ptr_acquire(&_table); } -void G1CodeRootSet::remove_lock_free(nmethod* method) { - G1CodeRootChunk* found = find(method); - if (found != NULL) { - bool result = found->remove_lock_free(method); - if (result) { - Atomic::dec_ptr((volatile intptr_t*)&_length); - } - } - assert(!contains(method), err_msg(PTR_FORMAT" still contains nmethod "PTR_FORMAT, this, method)); +void G1CodeRootSet::allocate_small_table() { + _table = new CodeRootSetTable(SmallSize); } -nmethod* G1CodeRootSet::pop() { - while (true) { - G1CodeRootChunk* cur = _list.head(); - if (cur == NULL) { - assert(_length == 0, "when there are no chunks, there should be no elements"); - return NULL; - } - nmethod* result = cur->pop(); - if (result != NULL) { - _length--; - return result; - } else { - free(_list.get_chunk_at_head()); +void CodeRootSetTable::purge_list_append(CodeRootSetTable* table) { + for (;;) { + table->_purge_next = _purge_list; + CodeRootSetTable* old = (CodeRootSetTable*) Atomic::cmpxchg_ptr(table, &_purge_list, table->_purge_next); + if (old == table->_purge_next) { + break; } } } -G1CodeRootChunk* G1CodeRootSet::find(nmethod* method) { - G1CodeRootChunk* cur = _list.head(); - while (cur != NULL) { - if (cur->contains(method)) { - return cur; - } - cur = (G1CodeRootChunk*)cur->next(); +void CodeRootSetTable::purge() { + CodeRootSetTable* table = _purge_list; + _purge_list = NULL; + while (table != NULL) { + CodeRootSetTable* to_purge = table; + table = table->_purge_next; + delete to_purge; } - return NULL; } -void G1CodeRootSet::free(G1CodeRootChunk* chunk) { - free_chunk(chunk); +void G1CodeRootSet::move_to_large() { + CodeRootSetTable* temp = new CodeRootSetTable(LargeSize); + + _table->copy_to(temp); + + CodeRootSetTable::purge_list_append(_table); + + OrderAccess::release_store_ptr(&_table, temp); } -bool G1CodeRootSet::contains(nmethod* method) { - return find(method) != NULL; -} -void G1CodeRootSet::clear() { - free_all_chunks(&_list); - _length = 0; -} - -void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const { - G1CodeRootChunk* cur = _list.head(); - while (cur != NULL) { - cur->nmethods_do(blk); - cur = (G1CodeRootChunk*)cur->next(); - } +void G1CodeRootSet::purge() { + CodeRootSetTable::purge(); } size_t G1CodeRootSet::static_mem_size() { - return sizeof(G1CodeRootSet); + return CodeRootSetTable::static_mem_size(); +} + +void G1CodeRootSet::add(nmethod* method) { + bool added = false; + if (is_empty()) { + allocate_small_table(); + } + added = _table->add(method); + if (_length == Threshold) { + move_to_large(); + } + if (added) { + ++_length; + } +} + +bool G1CodeRootSet::remove(nmethod* method) { + bool removed = false; + if (_table != NULL) { + removed = _table->remove(method); + } + if (removed) { + _length--; + if (_length == 0) { + clear(); + } + } + return removed; +} + +bool G1CodeRootSet::contains(nmethod* method) { + CodeRootSetTable* table = load_acquire_table(); + if (table != NULL) { + return table->contains(method); + } + return false; +} + +void G1CodeRootSet::clear() { + delete _table; + _table = NULL; + _length = 0; } size_t G1CodeRootSet::mem_size() { - return G1CodeRootSet::static_mem_size() + _list.count() * _list.size(); + return sizeof(*this) + + (_table != NULL ? sizeof(CodeRootSetTable) + _table->entry_size() * _length : 0); +} + +void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const { + if (_table != NULL) { + _table->nmethods_do(blk); + } +} + +class CleanCallback : public StackObj { + class PointsIntoHRDetectionClosure : public OopClosure { + HeapRegion* _hr; + public: + bool _points_into; + PointsIntoHRDetectionClosure(HeapRegion* hr) : _hr(hr), _points_into(false) {} + + void do_oop(narrowOop* o) { + do_oop_work(o); + } + + void do_oop(oop* o) { + do_oop_work(o); + } + + template + void do_oop_work(T* p) { + if (_hr->is_in(oopDesc::load_decode_heap_oop(p))) { + _points_into = true; + } + } + }; + + PointsIntoHRDetectionClosure _detector; + CodeBlobToOopClosure _blobs; + + public: + CleanCallback(HeapRegion* hr) : _detector(hr), _blobs(&_detector, !CodeBlobToOopClosure::FixRelocations) {} + + bool operator() (nmethod* nm) { + _detector._points_into = false; + _blobs.do_code_blob(nm); + return _detector._points_into; + } +}; + +void G1CodeRootSet::clean(HeapRegion* owner) { + CleanCallback should_clean(owner); + if (_table != NULL) { + _table->remove_if(should_clean); + } } #ifndef PRODUCT -void G1CodeRootSet::test() { - G1CodeRootChunkManager mgr; - - assert(mgr.num_chunks_handed_out() == 0, "Must not have handed out chunks yet"); - - assert(G1CodeRootChunkManager::static_mem_size() > sizeof(void*), - err_msg("The chunk manager's static memory usage seems too small, is only "SIZE_FORMAT" bytes.", G1CodeRootChunkManager::static_mem_size())); - - // The number of chunks that we allocate for purge testing. - size_t const num_chunks = 10; - - { - G1CodeRootSet set1(&mgr); - assert(set1.is_empty(), "Code root set must be initially empty but is not."); - - assert(G1CodeRootSet::static_mem_size() > sizeof(void*), - err_msg("The code root set's static memory usage seems too small, is only "SIZE_FORMAT" bytes", G1CodeRootSet::static_mem_size())); - - set1.add((nmethod*)1); - assert(mgr.num_chunks_handed_out() == 1, - err_msg("Must have allocated and handed out one chunk, but handed out " - SIZE_FORMAT" chunks", mgr.num_chunks_handed_out())); - assert(set1.length() == 1, err_msg("Added exactly one element, but set contains " - SIZE_FORMAT" elements", set1.length())); - - // G1CodeRootChunk::word_size() is larger than G1CodeRootChunk::num_entries which - // we cannot access. - for (uint i = 0; i < G1CodeRootChunk::word_size() + 1; i++) { - set1.add((nmethod*)1); - } - assert(mgr.num_chunks_handed_out() == 1, - err_msg("Duplicate detection must have prevented allocation of further " - "chunks but allocated "SIZE_FORMAT, mgr.num_chunks_handed_out())); - assert(set1.length() == 1, - err_msg("Duplicate detection should not have increased the set size but " - "is "SIZE_FORMAT, set1.length())); - - size_t num_total_after_add = G1CodeRootChunk::word_size() + 1; - for (size_t i = 0; i < num_total_after_add - 1; i++) { - set1.add((nmethod*)(uintptr_t)(2 + i)); - } - assert(mgr.num_chunks_handed_out() > 1, - "After adding more code roots, more than one additional chunk should have been handed out"); - assert(set1.length() == num_total_after_add, - err_msg("After adding in total "SIZE_FORMAT" distinct code roots, they " - "need to be in the set, but there are only "SIZE_FORMAT, - num_total_after_add, set1.length())); - - size_t num_popped = 0; - while (set1.pop() != NULL) { - num_popped++; - } - assert(num_popped == num_total_after_add, - err_msg("Managed to pop "SIZE_FORMAT" code roots, but only "SIZE_FORMAT" " - "were added", num_popped, num_total_after_add)); - assert(mgr.num_chunks_handed_out() == 0, - err_msg("After popping all elements, all chunks must have been returned " - "but there are still "SIZE_FORMAT" additional", mgr.num_chunks_handed_out())); - - mgr.purge_chunks(0); - assert(mgr.num_free_chunks() == 0, - err_msg("After purging everything, the free list must be empty but still " - "contains "SIZE_FORMAT" chunks", mgr.num_free_chunks())); - - // Add some more handed out chunks. - size_t i = 0; - while (mgr.num_chunks_handed_out() < num_chunks) { - set1.add((nmethod*)i); - i++; - } - +class G1CodeRootSetTest { + public: + static void test() { { - // Generate chunks on the free list. - G1CodeRootSet set2(&mgr); - size_t i = 0; - while (mgr.num_chunks_handed_out() < (num_chunks * 2)) { - set2.add((nmethod*)i); - i++; + G1CodeRootSet set1; + assert(set1.is_empty(), "Code root set must be initially empty but is not."); + + assert(G1CodeRootSet::static_mem_size() == sizeof(void*), + err_msg("The code root set's static memory usage is incorrect, "SIZE_FORMAT" bytes", G1CodeRootSet::static_mem_size())); + + set1.add((nmethod*)1); + assert(set1.length() == 1, err_msg("Added exactly one element, but set contains " + SIZE_FORMAT" elements", set1.length())); + + const size_t num_to_add = (size_t)G1CodeRootSet::Threshold + 1; + + for (size_t i = 1; i <= num_to_add; i++) { + set1.add((nmethod*)1); } - // Exit of the scope of the set2 object will call the destructor that generates - // num_chunks elements on the free list. + assert(set1.length() == 1, + err_msg("Duplicate detection should not have increased the set size but " + "is "SIZE_FORMAT, set1.length())); + + for (size_t i = 2; i <= num_to_add; i++) { + set1.add((nmethod*)(uintptr_t)(i)); + } + assert(set1.length() == num_to_add, + err_msg("After adding in total "SIZE_FORMAT" distinct code roots, they " + "need to be in the set, but there are only "SIZE_FORMAT, + num_to_add, set1.length())); + + assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable"); + + size_t num_popped = 0; + for (size_t i = 1; i <= num_to_add; i++) { + bool removed = set1.remove((nmethod*)i); + if (removed) { + num_popped += 1; + } else { + break; + } + } + assert(num_popped == num_to_add, + err_msg("Managed to pop "SIZE_FORMAT" code roots, but only "SIZE_FORMAT" " + "were added", num_popped, num_to_add)); + assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable"); + + G1CodeRootSet::purge(); + + assert(CodeRootSetTable::_purge_list == NULL, "should have purged old small tables"); + } - assert(mgr.num_chunks_handed_out() == num_chunks, - err_msg("Deletion of the second set must have resulted in giving back " - "those, but there are still "SIZE_FORMAT" additional handed out, expecting " - SIZE_FORMAT, mgr.num_chunks_handed_out(), num_chunks)); - assert(mgr.num_free_chunks() == num_chunks, - err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list " - "but there are only "SIZE_FORMAT, num_chunks, mgr.num_free_chunks())); - - size_t const test_percentage = 50; - mgr.purge_chunks(test_percentage); - assert(mgr.num_chunks_handed_out() == num_chunks, - err_msg("Purging must not hand out chunks but there are "SIZE_FORMAT, - mgr.num_chunks_handed_out())); - assert(mgr.num_free_chunks() == (size_t)(mgr.num_chunks_handed_out() * test_percentage / 100), - err_msg("Must have purged "SIZE_FORMAT" percent of "SIZE_FORMAT" chunks" - "but there are "SIZE_FORMAT, test_percentage, num_chunks, - mgr.num_free_chunks())); - // Purge the remainder of the chunks on the free list. - mgr.purge_chunks(0); - assert(mgr.num_free_chunks() == 0, "Free List must be empty"); - assert(mgr.num_chunks_handed_out() == num_chunks, - err_msg("Expected to be "SIZE_FORMAT" chunks handed out from the first set " - "but there are "SIZE_FORMAT, num_chunks, mgr.num_chunks_handed_out())); - - // Exit of the scope of the set1 object will call the destructor that generates - // num_chunks additional elements on the free list. - } - - assert(mgr.num_chunks_handed_out() == 0, - err_msg("Deletion of the only set must have resulted in no chunks handed " - "out, but there is still "SIZE_FORMAT" handed out", mgr.num_chunks_handed_out())); - assert(mgr.num_free_chunks() == num_chunks, - err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list " - "but there are only "SIZE_FORMAT, num_chunks, mgr.num_free_chunks())); - - // Restore initial state. - mgr.purge_chunks(0); - assert(mgr.num_free_chunks() == 0, "Free List must be empty"); - assert(mgr.num_chunks_handed_out() == 0, "No additional elements must have been handed out yet"); -} + } +}; void TestCodeCacheRemSet_test() { - G1CodeRootSet::test(); + G1CodeRootSetTest::test(); } + #endif diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.hpp index c351330f12f..87eb52a5b64 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.hpp @@ -26,222 +26,64 @@ #define SHARE_VM_GC_IMPLEMENTATION_G1_G1CODECACHEREMSET_HPP #include "memory/allocation.hpp" -#include "memory/freeList.hpp" -#include "runtime/globals.hpp" class CodeBlobClosure; - -// The elements of the G1CodeRootChunk is either: -// 1) nmethod pointers -// 2) nodes in an internally chained free list -typedef union { - nmethod* _nmethod; - void* _link; -} NmethodOrLink; - -class G1CodeRootChunk : public CHeapObj { - private: - static const int NUM_ENTRIES = 32; - public: - G1CodeRootChunk* _next; - G1CodeRootChunk* _prev; - - NmethodOrLink* _top; - // First free position within the chunk. - volatile NmethodOrLink* _free; - - NmethodOrLink _data[NUM_ENTRIES]; - - NmethodOrLink* bottom() const { - return (NmethodOrLink*) &(_data[0]); - } - - NmethodOrLink* end() const { - return (NmethodOrLink*) &(_data[NUM_ENTRIES]); - } - - bool is_link(NmethodOrLink* nmethod_or_link) { - return nmethod_or_link->_link == NULL || - (bottom() <= nmethod_or_link->_link - && nmethod_or_link->_link < end()); - } - - bool is_nmethod(NmethodOrLink* nmethod_or_link) { - return !is_link(nmethod_or_link); - } - - public: - G1CodeRootChunk(); - ~G1CodeRootChunk() {} - - static size_t word_size() { return (size_t)(align_size_up_(sizeof(G1CodeRootChunk), HeapWordSize) / HeapWordSize); } - - // FreeList "interface" methods - - G1CodeRootChunk* next() const { return _next; } - G1CodeRootChunk* prev() const { return _prev; } - void set_next(G1CodeRootChunk* v) { _next = v; assert(v != this, "Boom");} - void set_prev(G1CodeRootChunk* v) { _prev = v; assert(v != this, "Boom");} - void clear_next() { set_next(NULL); } - void clear_prev() { set_prev(NULL); } - - size_t size() const { return word_size(); } - - void link_next(G1CodeRootChunk* ptr) { set_next(ptr); } - void link_prev(G1CodeRootChunk* ptr) { set_prev(ptr); } - void link_after(G1CodeRootChunk* ptr) { - link_next(ptr); - if (ptr != NULL) ptr->link_prev((G1CodeRootChunk*)this); - } - - bool is_free() { return true; } - - // New G1CodeRootChunk routines - - void reset(); - - bool is_empty() const { - return _top == bottom(); - } - - bool is_full() const { - return _top == end() && _free == NULL; - } - - bool contains(nmethod* method) { - NmethodOrLink* cur = bottom(); - while (cur != _top) { - if (cur->_nmethod == method) return true; - cur++; - } - return false; - } - - bool add(nmethod* method) { - if (is_full()) { - return false; - } - - if (_free != NULL) { - // Take from internally chained free list - NmethodOrLink* first_free = (NmethodOrLink*)_free; - _free = (NmethodOrLink*)_free->_link; - first_free->_nmethod = method; - } else { - // Take from top. - _top->_nmethod = method; - _top++; - } - - return true; - } - - bool remove_lock_free(nmethod* method); - - void nmethods_do(CodeBlobClosure* blk); - - nmethod* pop() { - if (_free != NULL) { - // Kill the free list. - _free = NULL; - } - - while (!is_empty()) { - _top--; - if (is_nmethod(_top)) { - return _top->_nmethod; - } - } - - return NULL; - } -}; - -// Manages free chunks. -class G1CodeRootChunkManager VALUE_OBJ_CLASS_SPEC { - private: - // Global free chunk list management - FreeList _free_list; - // Total number of chunks handed out - size_t _num_chunks_handed_out; - - public: - G1CodeRootChunkManager(); - - G1CodeRootChunk* new_chunk(); - void free_chunk(G1CodeRootChunk* chunk); - // Free all elements of the given list. - void free_all_chunks(FreeList* list); - - void initialize(); - void purge_chunks(size_t keep_ratio); - - static size_t static_mem_size(); - size_t fl_mem_size(); - -#ifndef PRODUCT - size_t num_chunks_handed_out() const; - size_t num_free_chunks() const; -#endif -}; +class CodeRootSetTable; +class HeapRegion; +class nmethod; // Implements storage for a set of code roots. // All methods that modify the set are not thread-safe except if otherwise noted. class G1CodeRootSet VALUE_OBJ_CLASS_SPEC { + friend class G1CodeRootSetTest; private: - // Global default free chunk manager instance. - static G1CodeRootChunkManager _default_chunk_manager; - G1CodeRootChunk* new_chunk() { return _manager->new_chunk(); } - void free_chunk(G1CodeRootChunk* chunk) { _manager->free_chunk(chunk); } - // Free all elements of the given list. - void free_all_chunks(FreeList* list) { _manager->free_all_chunks(list); } + const static size_t SmallSize = 32; + const static size_t Threshold = 24; + const static size_t LargeSize = 512; - // Return the chunk that contains the given nmethod, NULL otherwise. - // Scans the list of chunks backwards, as this method is used to add new - // entries, which are typically added in bulk for a single nmethod. - G1CodeRootChunk* find(nmethod* method); - void free(G1CodeRootChunk* chunk); + CodeRootSetTable* _table; + CodeRootSetTable* load_acquire_table(); size_t _length; - FreeList _list; - G1CodeRootChunkManager* _manager; + + void move_to_large(); + void allocate_small_table(); public: - // If an instance is initialized with a chunk manager of NULL, use the global - // default one. - G1CodeRootSet(G1CodeRootChunkManager* manager = NULL); + G1CodeRootSet() : _table(NULL), _length(0) {} ~G1CodeRootSet(); - static void purge_chunks(size_t keep_ratio); + static void purge(); - static size_t free_chunks_static_mem_size(); - static size_t free_chunks_mem_size(); + static size_t static_mem_size(); - // Search for the code blob from the recently allocated ones to find duplicates more quickly, as this - // method is likely to be repeatedly called with the same nmethod. void add(nmethod* method); - void remove_lock_free(nmethod* method); - nmethod* pop(); + bool remove(nmethod* method); + // Safe to call without synchronization, but may return false negatives. bool contains(nmethod* method); void clear(); void nmethods_do(CodeBlobClosure* blk) const; - bool is_empty() { return length() == 0; } + // Remove all nmethods which no longer contain pointers into our "owner" region + void clean(HeapRegion* owner); + + bool is_empty() { + bool empty = length() == 0; + assert(empty == (_table == NULL), "is empty only if table is deallocated"); + return empty; + } // Length in elements size_t length() const { return _length; } - // Static data memory size in bytes of this set. - static size_t static_mem_size(); // Memory size in bytes taken by this set. size_t mem_size(); - static void test() PRODUCT_RETURN; }; #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1CODECACHEREMSET_HPP diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index db56c33b91a..b7bf1ffdb9c 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -45,12 +45,13 @@ #include "gc_implementation/g1/g1MarkSweep.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1ParScanThreadState.inline.hpp" +#include "gc_implementation/g1/g1RegionToSpaceMapper.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" #include "gc_implementation/g1/g1StringDedup.hpp" #include "gc_implementation/g1/g1YCTypes.hpp" #include "gc_implementation/g1/heapRegion.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionSet.inline.hpp" #include "gc_implementation/g1/vm_operations_g1.hpp" #include "gc_implementation/shared/gcHeapSummary.hpp" #include "gc_implementation/shared/gcTimer.hpp" @@ -381,6 +382,14 @@ void YoungList::print() { gclog_or_tty->cr(); } +void G1RegionMappingChangedListener::reset_from_card_cache(uint start_idx, size_t num_regions) { + OtherRegionsTable::invalidate(start_idx, num_regions); +} + +void G1RegionMappingChangedListener::on_commit(uint start_idx, size_t num_regions) { + reset_from_card_cache(start_idx, num_regions); +} + void G1CollectedHeap::push_dirty_cards_region(HeapRegion* hr) { // Claim the right to put the region on the dirty cards region list @@ -523,9 +532,9 @@ G1CollectedHeap::new_region_try_secondary_free_list(bool is_old) { // again to allocate from it. append_secondary_free_list(); - assert(!_free_list.is_empty(), "if the secondary_free_list was not " + assert(_hrm.num_free_regions() > 0, "if the secondary_free_list was not " "empty we should have moved at least one entry to the free_list"); - HeapRegion* res = _free_list.remove_region(is_old); + HeapRegion* res = _hrm.allocate_free_region(is_old); if (G1ConcRegionFreeingVerbose) { gclog_or_tty->print_cr("G1ConcRegionFreeing [region alloc] : " "allocated "HR_FORMAT" from secondary_free_list", @@ -566,7 +575,7 @@ HeapRegion* G1CollectedHeap::new_region(size_t word_size, bool is_old, bool do_e } } - res = _free_list.remove_region(is_old); + res = _hrm.allocate_free_region(is_old); if (res == NULL) { if (G1ConcRegionFreeingVerbose) { @@ -591,8 +600,8 @@ HeapRegion* G1CollectedHeap::new_region(size_t word_size, bool is_old, bool do_e // Given that expand() succeeded in expanding the heap, and we // always expand the heap by an amount aligned to the heap // region size, the free list should in theory not be empty. - // In either case remove_region() will check for NULL. - res = _free_list.remove_region(is_old); + // In either case allocate_free_region() will check for NULL. + res = _hrm.allocate_free_region(is_old); } else { _expand_heap_after_alloc_failure = false; } @@ -600,55 +609,11 @@ HeapRegion* G1CollectedHeap::new_region(size_t word_size, bool is_old, bool do_e return res; } -uint G1CollectedHeap::humongous_obj_allocate_find_first(uint num_regions, - size_t word_size) { - assert(isHumongous(word_size), "word_size should be humongous"); - assert(num_regions * HeapRegion::GrainWords >= word_size, "pre-condition"); - - uint first = G1_NULL_HRS_INDEX; - if (num_regions == 1) { - // Only one region to allocate, no need to go through the slower - // path. The caller will attempt the expansion if this fails, so - // let's not try to expand here too. - HeapRegion* hr = new_region(word_size, true /* is_old */, false /* do_expand */); - if (hr != NULL) { - first = hr->hrs_index(); - } else { - first = G1_NULL_HRS_INDEX; - } - } else { - // We can't allocate humongous regions while cleanupComplete() is - // running, since some of the regions we find to be empty might not - // yet be added to the free list and it is not straightforward to - // know which list they are on so that we can remove them. Note - // that we only need to do this if we need to allocate more than - // one region to satisfy the current humongous allocation - // request. If we are only allocating one region we use the common - // region allocation code (see above). - wait_while_free_regions_coming(); - append_secondary_free_list_if_not_empty_with_lock(); - - if (free_regions() >= num_regions) { - first = _hrs.find_contiguous(num_regions); - if (first != G1_NULL_HRS_INDEX) { - for (uint i = first; i < first + num_regions; ++i) { - HeapRegion* hr = region_at(i); - assert(hr->is_empty(), "sanity"); - assert(is_on_master_free_list(hr), "sanity"); - hr->set_pending_removal(true); - } - _free_list.remove_all_pending(num_regions); - } - } - } - return first; -} - HeapWord* G1CollectedHeap::humongous_obj_allocate_initialize_regions(uint first, uint num_regions, size_t word_size) { - assert(first != G1_NULL_HRS_INDEX, "pre-condition"); + assert(first != G1_NO_HRM_INDEX, "pre-condition"); assert(isHumongous(word_size), "word_size should be humongous"); assert(num_regions * HeapRegion::GrainWords >= word_size, "pre-condition"); @@ -786,42 +751,70 @@ HeapWord* G1CollectedHeap::humongous_obj_allocate(size_t word_size) { verify_region_sets_optional(); - size_t word_size_rounded = round_to(word_size, HeapRegion::GrainWords); - uint num_regions = (uint) (word_size_rounded / HeapRegion::GrainWords); - uint x_num = expansion_regions(); - uint fs = _hrs.free_suffix(); - uint first = humongous_obj_allocate_find_first(num_regions, word_size); - if (first == G1_NULL_HRS_INDEX) { - // The only thing we can do now is attempt expansion. - if (fs + x_num >= num_regions) { - // If the number of regions we're trying to allocate for this - // object is at most the number of regions in the free suffix, - // then the call to humongous_obj_allocate_find_first() above - // should have succeeded and we wouldn't be here. - // - // We should only be trying to expand when the free suffix is - // not sufficient for the object _and_ we have some expansion - // room available. - assert(num_regions > fs, "earlier allocation should have succeeded"); + uint first = G1_NO_HRM_INDEX; + uint obj_regions = (uint)(align_size_up_(word_size, HeapRegion::GrainWords) / HeapRegion::GrainWords); + if (obj_regions == 1) { + // Only one region to allocate, try to use a fast path by directly allocating + // from the free lists. Do not try to expand here, we will potentially do that + // later. + HeapRegion* hr = new_region(word_size, true /* is_old */, false /* do_expand */); + if (hr != NULL) { + first = hr->hrm_index(); + } + } else { + // We can't allocate humongous regions spanning more than one region while + // cleanupComplete() is running, since some of the regions we find to be + // empty might not yet be added to the free list. It is not straightforward + // to know in which list they are on so that we can remove them. We only + // need to do this if we need to allocate more than one region to satisfy the + // current humongous allocation request. If we are only allocating one region + // we use the one-region region allocation code (see above), that already + // potentially waits for regions from the secondary free list. + wait_while_free_regions_coming(); + append_secondary_free_list_if_not_empty_with_lock(); + + // Policy: Try only empty regions (i.e. already committed first). Maybe we + // are lucky enough to find some. + first = _hrm.find_contiguous_only_empty(obj_regions); + if (first != G1_NO_HRM_INDEX) { + _hrm.allocate_free_regions_starting_at(first, obj_regions); + } + } + + if (first == G1_NO_HRM_INDEX) { + // Policy: We could not find enough regions for the humongous object in the + // free list. Look through the heap to find a mix of free and uncommitted regions. + // If so, try expansion. + first = _hrm.find_contiguous_empty_or_unavailable(obj_regions); + if (first != G1_NO_HRM_INDEX) { + // We found something. Make sure these regions are committed, i.e. expand + // the heap. Alternatively we could do a defragmentation GC. ergo_verbose1(ErgoHeapSizing, "attempt heap expansion", ergo_format_reason("humongous allocation request failed") ergo_format_byte("allocation request"), word_size * HeapWordSize); - if (expand((num_regions - fs) * HeapRegion::GrainBytes)) { - // Even though the heap was expanded, it might not have - // reached the desired size. So, we cannot assume that the - // allocation will succeed. - first = humongous_obj_allocate_find_first(num_regions, word_size); + + _hrm.expand_at(first, obj_regions); + g1_policy()->record_new_heap_size(num_regions()); + +#ifdef ASSERT + for (uint i = first; i < first + obj_regions; ++i) { + HeapRegion* hr = region_at(i); + assert(hr->is_empty(), "sanity"); + assert(is_on_master_free_list(hr), "sanity"); } +#endif + _hrm.allocate_free_regions_starting_at(first, obj_regions); + } else { + // Policy: Potentially trigger a defragmentation GC. } } HeapWord* result = NULL; - if (first != G1_NULL_HRS_INDEX) { - result = - humongous_obj_allocate_initialize_regions(first, num_regions, word_size); + if (first != G1_NO_HRM_INDEX) { + result = humongous_obj_allocate_initialize_regions(first, obj_regions, word_size); assert(result != NULL, "it should always return a valid result"); // A successful humongous object allocation changes the used space @@ -1255,7 +1248,7 @@ public: : _hr_printer(hr_printer) { } }; -void G1CollectedHeap::print_hrs_post_compaction() { +void G1CollectedHeap::print_hrm_post_compaction() { PostCompactionPrinterClosure cl(hr_printer()); heap_region_iterate(&cl); } @@ -1384,7 +1377,7 @@ bool G1CollectedHeap::do_collection(bool explicit_gc, G1MarkSweep::invoke_at_safepoint(ref_processor_stw(), do_clear_all_soft_refs); } - assert(free_regions() == 0, "we should not have added any free regions"); + assert(num_free_regions() == 0, "we should not have added any free regions"); rebuild_region_sets(false /* free_list_only */); // Enqueue any discovered reference objects that have @@ -1424,7 +1417,7 @@ bool G1CollectedHeap::do_collection(bool explicit_gc, // that all the COMMIT / UNCOMMIT events are generated before // the end GC event. - print_hrs_post_compaction(); + print_hrm_post_compaction(); _hr_printer.end_gc(true /* full */, (size_t) total_collections()); } @@ -1497,7 +1490,7 @@ bool G1CollectedHeap::do_collection(bool explicit_gc, // Update the number of full collections that have been completed. increment_old_marking_cycles_completed(false /* concurrent */); - _hrs.verify_optional(); + _hrm.verify_optional(); verify_region_sets_optional(); verify_after_gc(); @@ -1741,7 +1734,7 @@ HeapWord* G1CollectedHeap::expand_and_allocate(size_t word_size) { ergo_format_byte("allocation request"), word_size * HeapWordSize); if (expand(expand_bytes)) { - _hrs.verify_optional(); + _hrm.verify_optional(); verify_region_sets_optional(); return attempt_allocation_at_safepoint(word_size, false /* expect_null_mutator_alloc_region */); @@ -1749,21 +1742,6 @@ HeapWord* G1CollectedHeap::expand_and_allocate(size_t word_size) { return NULL; } -void G1CollectedHeap::update_committed_space(HeapWord* old_end, - HeapWord* new_end) { - assert(old_end != new_end, "don't call this otherwise"); - assert((HeapWord*) _g1_storage.high() == new_end, "invariant"); - - // Update the committed mem region. - _g1_committed.set_end(new_end); - // Tell the card table about the update. - Universe::heap()->barrier_set()->resize_covered_region(_g1_committed); - // Tell the BOT about the update. - _bot_shared->resize(_g1_committed.word_size()); - // Tell the hot card cache about the update - _cg1r->hot_card_cache()->resize_card_counts(capacity()); -} - bool G1CollectedHeap::expand(size_t expand_bytes) { size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes); aligned_expand_bytes = align_size_up(aligned_expand_bytes, @@ -1774,55 +1752,22 @@ bool G1CollectedHeap::expand(size_t expand_bytes) { ergo_format_byte("attempted expansion amount"), expand_bytes, aligned_expand_bytes); - if (_g1_storage.uncommitted_size() == 0) { + if (is_maximal_no_gc()) { ergo_verbose0(ErgoHeapSizing, "did not expand the heap", ergo_format_reason("heap already fully expanded")); return false; } - // First commit the memory. - HeapWord* old_end = (HeapWord*) _g1_storage.high(); - bool successful = _g1_storage.expand_by(aligned_expand_bytes); - if (successful) { - // Then propagate this update to the necessary data structures. - HeapWord* new_end = (HeapWord*) _g1_storage.high(); - update_committed_space(old_end, new_end); + uint regions_to_expand = (uint)(aligned_expand_bytes / HeapRegion::GrainBytes); + assert(regions_to_expand > 0, "Must expand by at least one region"); - FreeRegionList expansion_list("Local Expansion List"); - MemRegion mr = _hrs.expand_by(old_end, new_end, &expansion_list); - assert(mr.start() == old_end, "post-condition"); - // mr might be a smaller region than what was requested if - // expand_by() was unable to allocate the HeapRegion instances - assert(mr.end() <= new_end, "post-condition"); + uint expanded_by = _hrm.expand_by(regions_to_expand); - size_t actual_expand_bytes = mr.byte_size(); + if (expanded_by > 0) { + size_t actual_expand_bytes = expanded_by * HeapRegion::GrainBytes; assert(actual_expand_bytes <= aligned_expand_bytes, "post-condition"); - assert(actual_expand_bytes == expansion_list.total_capacity_bytes(), - "post-condition"); - if (actual_expand_bytes < aligned_expand_bytes) { - // We could not expand _hrs to the desired size. In this case we - // need to shrink the committed space accordingly. - assert(mr.end() < new_end, "invariant"); - - size_t diff_bytes = aligned_expand_bytes - actual_expand_bytes; - // First uncommit the memory. - _g1_storage.shrink_by(diff_bytes); - // Then propagate this update to the necessary data structures. - update_committed_space(new_end, mr.end()); - } - _free_list.add_as_tail(&expansion_list); - - if (_hr_printer.is_active()) { - HeapWord* curr = mr.start(); - while (curr < mr.end()) { - HeapWord* curr_end = curr + HeapRegion::GrainWords; - _hr_printer.commit(curr, curr_end); - curr = curr_end; - } - assert(curr == mr.end(), "post-condition"); - } - g1_policy()->record_new_heap_size(n_regions()); + g1_policy()->record_new_heap_size(num_regions()); } else { ergo_verbose0(ErgoHeapSizing, "did not expand the heap", @@ -1830,12 +1775,12 @@ bool G1CollectedHeap::expand(size_t expand_bytes) { // The expansion of the virtual storage space was unsuccessful. // Let's see if it was because we ran out of swap. if (G1ExitOnExpansionFailure && - _g1_storage.uncommitted_size() >= aligned_expand_bytes) { + _hrm.available() >= regions_to_expand) { // We had head room... vm_exit_out_of_memory(aligned_expand_bytes, OOM_MMAP_ERROR, "G1 heap expansion"); } } - return successful; + return regions_to_expand > 0; } void G1CollectedHeap::shrink_helper(size_t shrink_bytes) { @@ -1845,8 +1790,7 @@ void G1CollectedHeap::shrink_helper(size_t shrink_bytes) { HeapRegion::GrainBytes); uint num_regions_to_remove = (uint)(shrink_bytes / HeapRegion::GrainBytes); - uint num_regions_removed = _hrs.shrink_by(num_regions_to_remove); - HeapWord* old_end = (HeapWord*) _g1_storage.high(); + uint num_regions_removed = _hrm.shrink_by(num_regions_to_remove); size_t shrunk_bytes = num_regions_removed * HeapRegion::GrainBytes; ergo_verbose3(ErgoHeapSizing, @@ -1856,22 +1800,7 @@ void G1CollectedHeap::shrink_helper(size_t shrink_bytes) { ergo_format_byte("attempted shrinking amount"), shrink_bytes, aligned_shrink_bytes, shrunk_bytes); if (num_regions_removed > 0) { - _g1_storage.shrink_by(shrunk_bytes); - HeapWord* new_end = (HeapWord*) _g1_storage.high(); - - if (_hr_printer.is_active()) { - HeapWord* curr = old_end; - while (curr > new_end) { - HeapWord* curr_end = curr; - curr -= HeapRegion::GrainWords; - _hr_printer.uncommit(curr, curr_end); - } - } - - _expansion_regions += num_regions_removed; - update_committed_space(old_end, new_end); - HeapRegionRemSet::shrink_heap(n_regions()); - g1_policy()->record_new_heap_size(n_regions()); + g1_policy()->record_new_heap_size(num_regions()); } else { ergo_verbose0(ErgoHeapSizing, "did not shrink the heap", @@ -1894,7 +1823,7 @@ void G1CollectedHeap::shrink(size_t shrink_bytes) { shrink_helper(shrink_bytes); rebuild_region_sets(true /* free_list_only */); - _hrs.verify_optional(); + _hrm.verify_optional(); verify_region_sets_optional(); } @@ -1922,10 +1851,11 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) : _g1mm(NULL), _refine_cte_cl(NULL), _full_collection(false), - _free_list("Master Free List", new MasterFreeRegionListMtSafeChecker()), _secondary_free_list("Secondary Free List", new SecondaryFreeRegionListMtSafeChecker()), _old_set("Old Set", false /* humongous */, new OldRegionSetMtSafeChecker()), _humongous_set("Master Humongous Set", true /* humongous */, new HumongousRegionSetMtSafeChecker()), + _humongous_is_live(), + _has_humongous_reclaim_candidates(false), _free_regions_coming(false), _young_list(new YoungList(this)), _gc_time_stamp(0), @@ -1937,6 +1867,7 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) : _old_marking_cycles_started(0), _old_marking_cycles_completed(0), _concurrent_cycle_started(false), + _heap_summary_sent(false), _in_cset_fast_test(), _dirty_cards_region_list(NULL), _worker_cset_start_region(NULL), @@ -2034,8 +1965,6 @@ jint G1CollectedHeap::initialize() { _reserved.set_start((HeapWord*)heap_rs.base()); _reserved.set_end((HeapWord*)(heap_rs.base() + heap_rs.size())); - _expansion_regions = (uint) (max_byte_size / HeapRegion::GrainBytes); - // Create the gen rem set (and barrier set) for the entire reserved region. _rem_set = collector_policy()->create_rem_set(_reserved, 2); set_barrier_set(rem_set()->bs()); @@ -2049,20 +1978,65 @@ jint G1CollectedHeap::initialize() { // Carve out the G1 part of the heap. - ReservedSpace g1_rs = heap_rs.first_part(max_byte_size); - _g1_reserved = MemRegion((HeapWord*)g1_rs.base(), - g1_rs.size()/HeapWordSize); + ReservedSpace g1_rs = heap_rs.first_part(max_byte_size); + G1RegionToSpaceMapper* heap_storage = + G1RegionToSpaceMapper::create_mapper(g1_rs, + UseLargePages ? os::large_page_size() : os::vm_page_size(), + HeapRegion::GrainBytes, + 1, + mtJavaHeap); + heap_storage->set_mapping_changed_listener(&_listener); - _g1_storage.initialize(g1_rs, 0); - _g1_committed = MemRegion((HeapWord*)_g1_storage.low(), (size_t) 0); - _hrs.initialize((HeapWord*) _g1_reserved.start(), - (HeapWord*) _g1_reserved.end()); - assert(_hrs.max_length() == _expansion_regions, - err_msg("max length: %u expansion regions: %u", - _hrs.max_length(), _expansion_regions)); + // Reserve space for the block offset table. We do not support automatic uncommit + // for the card table at this time. BOT only. + ReservedSpace bot_rs(G1BlockOffsetSharedArray::compute_size(g1_rs.size() / HeapWordSize)); + G1RegionToSpaceMapper* bot_storage = + G1RegionToSpaceMapper::create_mapper(bot_rs, + os::vm_page_size(), + HeapRegion::GrainBytes, + G1BlockOffsetSharedArray::N_bytes, + mtGC); - // Do later initialization work for concurrent refinement. - _cg1r->init(); + ReservedSpace cardtable_rs(G1SATBCardTableLoggingModRefBS::compute_size(g1_rs.size() / HeapWordSize)); + G1RegionToSpaceMapper* cardtable_storage = + G1RegionToSpaceMapper::create_mapper(cardtable_rs, + os::vm_page_size(), + HeapRegion::GrainBytes, + G1BlockOffsetSharedArray::N_bytes, + mtGC); + + // Reserve space for the card counts table. + ReservedSpace card_counts_rs(G1BlockOffsetSharedArray::compute_size(g1_rs.size() / HeapWordSize)); + G1RegionToSpaceMapper* card_counts_storage = + G1RegionToSpaceMapper::create_mapper(card_counts_rs, + os::vm_page_size(), + HeapRegion::GrainBytes, + G1BlockOffsetSharedArray::N_bytes, + mtGC); + + // Reserve space for prev and next bitmap. + size_t bitmap_size = CMBitMap::compute_size(g1_rs.size()); + + ReservedSpace prev_bitmap_rs(ReservedSpace::allocation_align_size_up(bitmap_size)); + G1RegionToSpaceMapper* prev_bitmap_storage = + G1RegionToSpaceMapper::create_mapper(prev_bitmap_rs, + os::vm_page_size(), + HeapRegion::GrainBytes, + CMBitMap::mark_distance(), + mtGC); + + ReservedSpace next_bitmap_rs(ReservedSpace::allocation_align_size_up(bitmap_size)); + G1RegionToSpaceMapper* next_bitmap_storage = + G1RegionToSpaceMapper::create_mapper(next_bitmap_rs, + os::vm_page_size(), + HeapRegion::GrainBytes, + CMBitMap::mark_distance(), + mtGC); + + _hrm.initialize(heap_storage, prev_bitmap_storage, next_bitmap_storage, bot_storage, cardtable_storage, card_counts_storage); + g1_barrier_set()->initialize(cardtable_storage); + // Do later initialization work for concurrent refinement. + _cg1r->init(card_counts_storage); // 6843694 - ensure that the maximum region index can fit // in the remembered set structures. @@ -2076,16 +2050,16 @@ jint G1CollectedHeap::initialize() { FreeRegionList::set_unrealistically_long_length(max_regions() + 1); - _bot_shared = new G1BlockOffsetSharedArray(_reserved, - heap_word_size(init_byte_size)); + _bot_shared = new G1BlockOffsetSharedArray(_reserved, bot_storage); _g1h = this; - _in_cset_fast_test.initialize(_g1_reserved.start(), _g1_reserved.end(), HeapRegion::GrainBytes); + _in_cset_fast_test.initialize(_hrm.reserved().start(), _hrm.reserved().end(), HeapRegion::GrainBytes); + _humongous_is_live.initialize(_hrm.reserved().start(), _hrm.reserved().end(), HeapRegion::GrainBytes); // Create the ConcurrentMark data structure and thread. // (Must do this late, so that "max_regions" is defined.) - _cm = new ConcurrentMark(this, heap_rs); + _cm = new ConcurrentMark(this, prev_bitmap_storage, next_bitmap_storage); if (_cm == NULL || !_cm->completed_initialization()) { vm_shutdown_during_initialization("Could not create/initialize ConcurrentMark"); return JNI_ENOMEM; @@ -2140,12 +2114,10 @@ jint G1CollectedHeap::initialize() { // counts and that mechanism. SpecializationStats::clear(); - // Here we allocate the dummy full region that is required by the - // G1AllocRegion class. If we don't pass an address in the reserved - // space here, lots of asserts fire. + // Here we allocate the dummy HeapRegion that is required by the + // G1AllocRegion class. + HeapRegion* dummy_region = _hrm.get_dummy_region(); - HeapRegion* dummy_region = new_heap_region(0 /* index of bottom region */, - _g1_reserved.start()); // We'll re-use the same region whether the alloc region will // require BOT updates or not and, if it doesn't, then a non-young // region will complain that it cannot support allocations without @@ -2177,6 +2149,11 @@ void G1CollectedHeap::stop() { } } +void G1CollectedHeap::clear_humongous_is_live_table() { + guarantee(G1ReclaimDeadHumongousObjectsAtYoungGC, "Should only be called if true"); + _humongous_is_live.clear(); +} + size_t G1CollectedHeap::conservative_max_heap_alignment() { return HeapRegion::max_region_size(); } @@ -2256,14 +2233,14 @@ void G1CollectedHeap::ref_processing_init() { } size_t G1CollectedHeap::capacity() const { - return _g1_committed.byte_size(); + return _hrm.length() * HeapRegion::GrainBytes; } void G1CollectedHeap::reset_gc_time_stamps(HeapRegion* hr) { assert(!hr->continuesHumongous(), "pre-condition"); hr->reset_gc_time_stamp(); if (hr->startsHumongous()) { - uint first_index = hr->hrs_index() + 1; + uint first_index = hr->hrm_index() + 1; uint last_index = hr->last_hc_index(); for (uint i = first_index; i < last_index; i += 1) { HeapRegion* chr = region_at(i); @@ -2469,13 +2446,24 @@ void G1CollectedHeap::register_concurrent_cycle_end() { _gc_timer_cm->register_gc_end(); _gc_tracer_cm->report_gc_end(_gc_timer_cm->gc_end(), _gc_timer_cm->time_partitions()); + // Clear state variables to prepare for the next concurrent cycle. _concurrent_cycle_started = false; + _heap_summary_sent = false; } } void G1CollectedHeap::trace_heap_after_concurrent_cycle() { if (_concurrent_cycle_started) { - trace_heap_after_gc(_gc_tracer_cm); + // This function can be called when: + // the cleanup pause is run + // the concurrent cycle is aborted before the cleanup pause. + // the concurrent cycle is aborted after the cleanup pause, + // but before the concurrent cycle end has been registered. + // Make sure that we only send the heap information once. + if (!_heap_summary_sent) { + trace_heap_after_gc(_gc_tracer_cm); + _heap_summary_sent = true; + } } } @@ -2540,7 +2528,7 @@ void G1CollectedHeap::collect(GCCause::Cause cause) { } } } else { - if (cause == GCCause::_gc_locker + if (cause == GCCause::_gc_locker || cause == GCCause::_wb_young_gc DEBUG_ONLY(|| cause == GCCause::_scavenge_alot)) { // Schedule a standard evacuation pause. We're setting word_size @@ -2561,8 +2549,8 @@ void G1CollectedHeap::collect(GCCause::Cause cause) { } bool G1CollectedHeap::is_in(const void* p) const { - if (_g1_committed.contains(p)) { - // Given that we know that p is in the committed space, + if (_hrm.reserved().contains(p)) { + // Given that we know that p is in the reserved space, // heap_region_containing_raw() should successfully // return the containing region. HeapRegion* hr = heap_region_containing_raw(p); @@ -2572,17 +2560,26 @@ bool G1CollectedHeap::is_in(const void* p) const { } } +#ifdef ASSERT +bool G1CollectedHeap::is_in_exact(const void* p) const { + bool contains = reserved_region().contains(p); + bool available = _hrm.is_available(addr_to_region((HeapWord*)p)); + if (contains && available) { + return true; + } else { + return false; + } +} +#endif + // Iteration functions. -// Iterates an OopClosure over all ref-containing fields of objects -// within a HeapRegion. +// Applies an ExtendedOopClosure onto all references of objects within a HeapRegion. class IterateOopClosureRegionClosure: public HeapRegionClosure { - MemRegion _mr; ExtendedOopClosure* _cl; public: - IterateOopClosureRegionClosure(MemRegion mr, ExtendedOopClosure* cl) - : _mr(mr), _cl(cl) {} + IterateOopClosureRegionClosure(ExtendedOopClosure* cl) : _cl(cl) {} bool doHeapRegion(HeapRegion* r) { if (!r->continuesHumongous()) { r->oop_iterate(_cl); @@ -2592,12 +2589,7 @@ public: }; void G1CollectedHeap::oop_iterate(ExtendedOopClosure* cl) { - IterateOopClosureRegionClosure blk(_g1_committed, cl); - heap_region_iterate(&blk); -} - -void G1CollectedHeap::oop_iterate(MemRegion mr, ExtendedOopClosure* cl) { - IterateOopClosureRegionClosure blk(mr, cl); + IterateOopClosureRegionClosure blk(cl); heap_region_iterate(&blk); } @@ -2638,89 +2630,15 @@ void G1CollectedHeap::space_iterate(SpaceClosure* cl) { } void G1CollectedHeap::heap_region_iterate(HeapRegionClosure* cl) const { - _hrs.iterate(cl); + _hrm.iterate(cl); } void G1CollectedHeap::heap_region_par_iterate_chunked(HeapRegionClosure* cl, uint worker_id, - uint no_of_par_workers, - jint claim_value) { - const uint regions = n_regions(); - const uint max_workers = (G1CollectedHeap::use_parallel_gc_threads() ? - no_of_par_workers : - 1); - assert(UseDynamicNumberOfGCThreads || - no_of_par_workers == workers()->total_workers(), - "Non dynamic should use fixed number of workers"); - // try to spread out the starting points of the workers - const HeapRegion* start_hr = - start_region_for_worker(worker_id, no_of_par_workers); - const uint start_index = start_hr->hrs_index(); - - // each worker will actually look at all regions - for (uint count = 0; count < regions; ++count) { - const uint index = (start_index + count) % regions; - assert(0 <= index && index < regions, "sanity"); - HeapRegion* r = region_at(index); - // we'll ignore "continues humongous" regions (we'll process them - // when we come across their corresponding "start humongous" - // region) and regions already claimed - if (r->claim_value() == claim_value || r->continuesHumongous()) { - continue; - } - // OK, try to claim it - if (r->claimHeapRegion(claim_value)) { - // success! - assert(!r->continuesHumongous(), "sanity"); - if (r->startsHumongous()) { - // If the region is "starts humongous" we'll iterate over its - // "continues humongous" first; in fact we'll do them - // first. The order is important. In on case, calling the - // closure on the "starts humongous" region might de-allocate - // and clear all its "continues humongous" regions and, as a - // result, we might end up processing them twice. So, we'll do - // them first (notice: most closures will ignore them anyway) and - // then we'll do the "starts humongous" region. - for (uint ch_index = index + 1; ch_index < regions; ++ch_index) { - HeapRegion* chr = region_at(ch_index); - - // if the region has already been claimed or it's not - // "continues humongous" we're done - if (chr->claim_value() == claim_value || - !chr->continuesHumongous()) { - break; - } - - // No one should have claimed it directly. We can given - // that we claimed its "starts humongous" region. - assert(chr->claim_value() != claim_value, "sanity"); - assert(chr->humongous_start_region() == r, "sanity"); - - if (chr->claimHeapRegion(claim_value)) { - // we should always be able to claim it; no one else should - // be trying to claim this region - - bool res2 = cl->doHeapRegion(chr); - assert(!res2, "Should not abort"); - - // Right now, this holds (i.e., no closure that actually - // does something with "continues humongous" regions - // clears them). We might have to weaken it in the future, - // but let's leave these two asserts here for extra safety. - assert(chr->continuesHumongous(), "should still be the case"); - assert(chr->humongous_start_region() == r, "sanity"); - } else { - guarantee(false, "we should not reach here"); - } - } - } - - assert(!r->continuesHumongous(), "sanity"); - bool res = cl->doHeapRegion(r); - assert(!res, "Should not abort"); - } - } + uint num_workers, + jint claim_value) const { + _hrm.par_iterate(cl, worker_id, num_workers, claim_value); } class ResetClaimValuesClosure: public HeapRegionClosure { @@ -2898,17 +2816,6 @@ HeapRegion* G1CollectedHeap::start_cset_region_for_worker(uint worker_i) { return result; } -HeapRegion* G1CollectedHeap::start_region_for_worker(uint worker_i, - uint no_of_par_workers) { - uint worker_num = - G1CollectedHeap::use_parallel_gc_threads() ? no_of_par_workers : 1U; - assert(UseDynamicNumberOfGCThreads || - no_of_par_workers == workers()->total_workers(), - "Non dynamic should use fixed number of workers"); - const uint start_index = n_regions() * worker_i / worker_num; - return region_at(start_index); -} - void G1CollectedHeap::collection_set_iterate(HeapRegionClosure* cl) { HeapRegion* r = g1_policy()->collection_set(); while (r != NULL) { @@ -2951,15 +2858,11 @@ void G1CollectedHeap::collection_set_iterate_from(HeapRegion* r, } HeapRegion* G1CollectedHeap::next_compaction_region(const HeapRegion* from) const { - // We're not using an iterator given that it will wrap around when - // it reaches the last region and this is not what we want here. - for (uint index = from->hrs_index() + 1; index < n_regions(); index++) { - HeapRegion* hr = region_at(index); - if (!hr->isHumongous()) { - return hr; - } + HeapRegion* result = _hrm.next_region_in_heap(from); + while (result != NULL && result->isHumongous()) { + result = _hrm.next_region_in_heap(result); } - return NULL; + return result; } Space* G1CollectedHeap::space_containing(const void* addr) const { @@ -3017,7 +2920,7 @@ size_t G1CollectedHeap::unsafe_max_tlab_alloc(Thread* ignored) const { } size_t G1CollectedHeap::max_capacity() const { - return _g1_reserved.byte_size(); + return _hrm.reserved().byte_size(); } jlong G1CollectedHeap::millis_since_last_gc() { @@ -3546,9 +3449,9 @@ void G1CollectedHeap::print_on(outputStream* st) const { st->print(" total " SIZE_FORMAT "K, used " SIZE_FORMAT "K", capacity()/K, used_unlocked()/K); st->print(" [" INTPTR_FORMAT ", " INTPTR_FORMAT ", " INTPTR_FORMAT ")", - _g1_storage.low_boundary(), - _g1_storage.high(), - _g1_storage.high_boundary()); + _hrm.reserved().start(), + _hrm.reserved().start() + _hrm.length() + HeapRegion::GrainWords, + _hrm.reserved().end()); st->cr(); st->print(" region size " SIZE_FORMAT "K, ", HeapRegion::GrainBytes / K); uint young_regions = _young_list->length(); @@ -3771,6 +3674,61 @@ size_t G1CollectedHeap::cards_scanned() { return g1_rem_set()->cardsScanned(); } +bool G1CollectedHeap::humongous_region_is_always_live(uint index) { + HeapRegion* region = region_at(index); + assert(region->startsHumongous(), "Must start a humongous object"); + return oop(region->bottom())->is_objArray() || !region->rem_set()->is_empty(); +} + +class RegisterHumongousWithInCSetFastTestClosure : public HeapRegionClosure { + private: + size_t _total_humongous; + size_t _candidate_humongous; + public: + RegisterHumongousWithInCSetFastTestClosure() : _total_humongous(0), _candidate_humongous(0) { + } + + virtual bool doHeapRegion(HeapRegion* r) { + if (!r->startsHumongous()) { + return false; + } + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + + uint region_idx = r->hrm_index(); + bool is_candidate = !g1h->humongous_region_is_always_live(region_idx); + // Is_candidate already filters out humongous regions with some remembered set. + // This will not lead to humongous object that we mistakenly keep alive because + // during young collection the remembered sets will only be added to. + if (is_candidate) { + g1h->register_humongous_region_with_in_cset_fast_test(region_idx); + _candidate_humongous++; + } + _total_humongous++; + + return false; + } + + size_t total_humongous() const { return _total_humongous; } + size_t candidate_humongous() const { return _candidate_humongous; } +}; + +void G1CollectedHeap::register_humongous_regions_with_in_cset_fast_test() { + if (!G1ReclaimDeadHumongousObjectsAtYoungGC) { + g1_policy()->phase_times()->record_fast_reclaim_humongous_stats(0, 0); + return; + } + + RegisterHumongousWithInCSetFastTestClosure cl; + heap_region_iterate(&cl); + g1_policy()->phase_times()->record_fast_reclaim_humongous_stats(cl.total_humongous(), + cl.candidate_humongous()); + _has_humongous_reclaim_candidates = cl.candidate_humongous() > 0; + + if (_has_humongous_reclaim_candidates) { + clear_humongous_is_live_table(); + } +} + void G1CollectedHeap::setup_surviving_young_words() { assert(_surviving_young_words == NULL, "pre-condition"); @@ -4058,6 +4016,8 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { g1_policy()->finalize_cset(target_pause_time_ms, evacuation_info); + register_humongous_regions_with_in_cset_fast_test(); + _cm->note_start_of_gc(); // We should not verify the per-thread SATB buffers given that // we have not filtered them yet (we'll do so during the @@ -4108,6 +4068,9 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { true /* verify_fingers */); free_collection_set(g1_policy()->collection_set(), evacuation_info); + + eagerly_reclaim_humongous_regions(); + g1_policy()->clear_collection_set(); cleanup_surviving_young_words(); @@ -4179,10 +4142,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { // No need for an ergo verbose message here, // expansion_amount() does this when it returns a value > 0. if (!expand(expand_bytes)) { - // We failed to expand the heap so let's verify that - // committed/uncommitted amount match the backing store - assert(capacity() == _g1_storage.committed_size(), "committed size mismatch"); - assert(max_capacity() == _g1_storage.reserved_size(), "reserved size mismatch"); + // We failed to expand the heap. Cannot do anything about it. } } } @@ -4242,10 +4202,6 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { // RETIRE events are generated before the end GC event. _hr_printer.end_gc(false /* full */, (size_t) total_collections()); - if (mark_in_progress()) { - concurrent_mark()->update_g1_committed(); - } - #ifdef TRACESPINNING ParallelTaskTerminator::print_termination_counts(); #endif @@ -4261,7 +4217,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { // output from the concurrent mark thread interfering with this // logging output either. - _hrs.verify_optional(); + _hrm.verify_optional(); verify_region_sets_optional(); TASKQUEUE_STATS_ONLY(if (ParallelGCVerbose) print_taskqueue_stats()); @@ -4608,7 +4564,9 @@ void G1ParCopyClosure::do_oop_work(T* p) { assert(_worker_id == _par_scan_state->queue_num(), "sanity"); - if (_g1->in_cset_fast_test(obj)) { + G1CollectedHeap::in_cset_state_t state = _g1->in_cset_state(obj); + + if (state == G1CollectedHeap::InCSet) { oop forwardee; if (obj->is_forwarded()) { forwardee = obj->forwardee(); @@ -4627,6 +4585,9 @@ void G1ParCopyClosure::do_oop_work(T* p) { do_klass_barrier(p, forwardee); } } else { + if (state == G1CollectedHeap::IsHumongous) { + _g1->set_humongous_is_live(obj); + } // The object is not in collection set. If we're a root scanning // closure during an initial mark pause then attempt to mark the object. if (do_mark_object == G1MarkFromRoot) { @@ -4709,6 +4670,56 @@ class G1KlassScanClosure : public KlassClosure { } }; +class G1CodeBlobClosure : public CodeBlobClosure { + class HeapRegionGatheringOopClosure : public OopClosure { + G1CollectedHeap* _g1h; + OopClosure* _work; + nmethod* _nm; + + template + void do_oop_work(T* p) { + _work->do_oop(p); + T oop_or_narrowoop = oopDesc::load_heap_oop(p); + if (!oopDesc::is_null(oop_or_narrowoop)) { + oop o = oopDesc::decode_heap_oop_not_null(oop_or_narrowoop); + HeapRegion* hr = _g1h->heap_region_containing_raw(o); + assert(!_g1h->obj_in_cs(o) || hr->rem_set()->strong_code_roots_list_contains(_nm), "if o still in CS then evacuation failed and nm must already be in the remset"); + hr->add_strong_code_root(_nm); + } + } + + public: + HeapRegionGatheringOopClosure(OopClosure* oc) : _g1h(G1CollectedHeap::heap()), _work(oc), _nm(NULL) {} + + void do_oop(oop* o) { + do_oop_work(o); + } + + void do_oop(narrowOop* o) { + do_oop_work(o); + } + + void set_nm(nmethod* nm) { + _nm = nm; + } + }; + + HeapRegionGatheringOopClosure _oc; +public: + G1CodeBlobClosure(OopClosure* oc) : _oc(oc) {} + + void do_code_blob(CodeBlob* cb) { + nmethod* nm = cb->as_nmethod_or_null(); + if (nm != NULL) { + if (!nm->test_set_oops_do_mark()) { + _oc.set_nm(nm); + nm->oops_do(&_oc); + nm->fix_oop_relocations(); + } + } + } +}; + class G1ParTask : public AbstractGangTask { protected: G1CollectedHeap* _g1h; @@ -4719,11 +4730,6 @@ protected: Mutex _stats_lock; Mutex* stats_lock() { return &_stats_lock; } - size_t getNCards() { - return (_g1h->capacity() + G1BlockOffsetSharedArray::N_bytes - 1) - / G1BlockOffsetSharedArray::N_bytes; - } - public: G1ParTask(G1CollectedHeap* g1h, RefToScanQueueSet *task_queues) : AbstractGangTask("G1 collection"), @@ -4782,22 +4788,6 @@ public: } }; - class G1CodeBlobClosure: public CodeBlobClosure { - OopClosure* _f; - - public: - G1CodeBlobClosure(OopClosure* f) : _f(f) {} - void do_code_blob(CodeBlob* blob) { - nmethod* that = blob->as_nmethod_or_null(); - if (that != NULL) { - if (!that->test_set_oops_do_mark()) { - that->oops_do(_f); - that->fix_oop_relocations(); - } - } - } - }; - void work(uint worker_id) { if (worker_id >= _n_workers) return; // no work needed this round @@ -4847,10 +4837,15 @@ public: if (_g1h->g1_policy()->during_initial_mark_pause()) { // We also need to mark copied objects. strong_root_cl = &scan_mark_root_cl; - weak_root_cl = &scan_mark_weak_root_cl; strong_cld_cl = &scan_mark_cld_cl; - weak_cld_cl = &scan_mark_weak_cld_cl; strong_code_cl = &scan_mark_code_cl; + if (ClassUnloadingWithConcurrentMark) { + weak_root_cl = &scan_mark_weak_root_cl; + weak_cld_cl = &scan_mark_weak_cld_cl; + } else { + weak_root_cl = &scan_mark_root_cl; + weak_cld_cl = &scan_mark_cld_cl; + } } else { strong_root_cl = &scan_only_root_cl; weak_root_cl = &scan_only_root_cl; @@ -4921,6 +4916,7 @@ g1_process_roots(OopClosure* scan_non_heap_roots, double closure_app_time_sec = 0.0; bool during_im = _g1h->g1_policy()->during_initial_mark_pause(); + bool trace_metadata = during_im && ClassUnloadingWithConcurrentMark; BufferingOopClosure buf_scan_non_heap_roots(scan_non_heap_roots); BufferingOopClosure buf_scan_non_heap_weak_roots(scan_non_heap_weak_roots); @@ -4930,8 +4926,8 @@ g1_process_roots(OopClosure* scan_non_heap_roots, &buf_scan_non_heap_roots, &buf_scan_non_heap_weak_roots, scan_strong_clds, - // Initial Mark handles the weak CLDs separately. - (during_im ? NULL : scan_weak_clds), + // Unloading Initial Marks handle the weak CLDs separately. + (trace_metadata ? NULL : scan_weak_clds), scan_strong_code); // Now the CM ref_processor roots. @@ -4943,7 +4939,7 @@ g1_process_roots(OopClosure* scan_non_heap_roots, ref_processor_cm()->weak_oops_do(&buf_scan_non_heap_roots); } - if (during_im) { + if (trace_metadata) { // Barrier to make sure all workers passed // the strong CLD and strong nmethods phases. active_strong_roots_scope()->wait_until_all_workers_done_with_threads(n_par_threads()); @@ -4982,7 +4978,7 @@ g1_process_roots(OopClosure* scan_non_heap_roots, g1_policy()->phase_times()->record_satb_filtering_time(worker_i, satb_filtering_ms); // Now scan the complement of the collection set. - MarkingCodeBlobClosure scavenge_cs_nmethods(scan_non_heap_weak_roots, CodeBlobToOopClosure::FixRelocations); + G1CodeBlobClosure scavenge_cs_nmethods(scan_non_heap_weak_roots); g1_rem_set()->oops_into_collection_set_do(scan_rs, &scavenge_cs_nmethods, worker_i); @@ -5450,12 +5446,21 @@ class G1KeepAliveClosure: public OopClosure { public: G1KeepAliveClosure(G1CollectedHeap* g1) : _g1(g1) {} void do_oop(narrowOop* p) { guarantee(false, "Not needed"); } - void do_oop( oop* p) { + void do_oop(oop* p) { oop obj = *p; - if (_g1->obj_in_cs(obj)) { + G1CollectedHeap::in_cset_state_t cset_state = _g1->in_cset_state(obj); + if (obj == NULL || cset_state == G1CollectedHeap::InNeither) { + return; + } + if (cset_state == G1CollectedHeap::InCSet) { assert( obj->is_forwarded(), "invariant" ); *p = obj->forwardee(); + } else { + assert(!obj->is_forwarded(), "invariant" ); + assert(cset_state == G1CollectedHeap::IsHumongous, + err_msg("Only allowed InCSet state is IsHumongous, but is %d", cset_state)); + _g1->set_humongous_is_live(obj); } } }; @@ -5485,7 +5490,7 @@ public: template void do_oop_work(T* p) { oop obj = oopDesc::load_decode_heap_oop(p); - if (_g1h->obj_in_cs(obj)) { + if (_g1h->is_in_cset_or_humongous(obj)) { // If the referent object has been forwarded (either copied // to a new location or to itself in the event of an // evacuation failure) then we need to update the reference @@ -5510,10 +5515,10 @@ public: assert(!Metaspace::contains((const void*)p), err_msg("Unexpectedly found a pointer from metadata: " PTR_FORMAT, p)); - _copy_non_heap_obj_cl->do_oop(p); - } + _copy_non_heap_obj_cl->do_oop(p); } } + } }; // Serial drain queue closure. Called as the 'complete_gc' @@ -6020,12 +6025,6 @@ void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info) { hot_card_cache->reset_hot_cache(); hot_card_cache->set_use_cache(true); - // Migrate the strong code roots attached to each region in - // the collection set. Ideally we would like to do this - // after we have finished the scanning/evacuation of the - // strong code roots for a particular heap region. - migrate_strong_code_roots(); - purge_code_root_memory(); if (g1_policy()->during_initial_mark_pause()) { @@ -6065,6 +6064,7 @@ void G1CollectedHeap::free_region(HeapRegion* hr, bool locked) { assert(!hr->isHumongous(), "this is only for non-humongous regions"); assert(!hr->is_empty(), "the region should not be empty"); + assert(_hrm.is_available(hr->hrm_index()), "region should be committed"); assert(free_list != NULL, "pre-condition"); if (G1VerifyBitmaps) { @@ -6095,7 +6095,7 @@ void G1CollectedHeap::free_humongous_region(HeapRegion* hr, hr->set_notHumongous(); free_region(hr, free_list, par); - uint i = hr->hrs_index() + 1; + uint i = hr->hrm_index() + 1; while (i < last_index) { HeapRegion* curr_hr = region_at(i); assert(curr_hr->continuesHumongous(), "invariant"); @@ -6119,7 +6119,7 @@ void G1CollectedHeap::prepend_to_freelist(FreeRegionList* list) { assert(list != NULL, "list can't be null"); if (!list->is_empty()) { MutexLockerEx x(FreeList_lock, Mutex::_no_safepoint_check_flag); - _free_list.add_ordered(list); + _hrm.insert_list_into_free_list(list); } } @@ -6435,6 +6435,154 @@ void G1CollectedHeap::free_collection_set(HeapRegion* cs_head, EvacuationInfo& e policy->phase_times()->record_non_young_free_cset_time_ms(non_young_time_ms); } +class G1FreeHumongousRegionClosure : public HeapRegionClosure { + private: + FreeRegionList* _free_region_list; + HeapRegionSet* _proxy_set; + HeapRegionSetCount _humongous_regions_removed; + size_t _freed_bytes; + public: + + G1FreeHumongousRegionClosure(FreeRegionList* free_region_list) : + _free_region_list(free_region_list), _humongous_regions_removed(), _freed_bytes(0) { + } + + virtual bool doHeapRegion(HeapRegion* r) { + if (!r->startsHumongous()) { + return false; + } + + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + + oop obj = (oop)r->bottom(); + CMBitMap* next_bitmap = g1h->concurrent_mark()->nextMarkBitMap(); + + // The following checks whether the humongous object is live are sufficient. + // The main additional check (in addition to having a reference from the roots + // or the young gen) is whether the humongous object has a remembered set entry. + // + // A humongous object cannot be live if there is no remembered set for it + // because: + // - there can be no references from within humongous starts regions referencing + // the object because we never allocate other objects into them. + // (I.e. there are no intra-region references that may be missed by the + // remembered set) + // - as soon there is a remembered set entry to the humongous starts region + // (i.e. it has "escaped" to an old object) this remembered set entry will stay + // until the end of a concurrent mark. + // + // It is not required to check whether the object has been found dead by marking + // or not, in fact it would prevent reclamation within a concurrent cycle, as + // all objects allocated during that time are considered live. + // SATB marking is even more conservative than the remembered set. + // So if at this point in the collection there is no remembered set entry, + // nobody has a reference to it. + // At the start of collection we flush all refinement logs, and remembered sets + // are completely up-to-date wrt to references to the humongous object. + // + // Other implementation considerations: + // - never consider object arrays: while they are a valid target, they have not + // been observed to be used as temporary objects. + // - they would also pose considerable effort for cleaning up the the remembered + // sets. + // While this cleanup is not strictly necessary to be done (or done instantly), + // given that their occurrence is very low, this saves us this additional + // complexity. + uint region_idx = r->hrm_index(); + if (g1h->humongous_is_live(region_idx) || + g1h->humongous_region_is_always_live(region_idx)) { + + if (G1TraceReclaimDeadHumongousObjectsAtYoungGC) { + gclog_or_tty->print_cr("Live humongous %d region %d with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is marked %d live-other %d obj array %d", + r->isHumongous(), + region_idx, + r->rem_set()->occupied(), + r->rem_set()->strong_code_roots_list_length(), + next_bitmap->isMarked(r->bottom()), + g1h->humongous_is_live(region_idx), + obj->is_objArray() + ); + } + + return false; + } + + guarantee(!obj->is_objArray(), + err_msg("Eagerly reclaiming object arrays is not supported, but the object "PTR_FORMAT" is.", + r->bottom())); + + if (G1TraceReclaimDeadHumongousObjectsAtYoungGC) { + gclog_or_tty->print_cr("Reclaim humongous region %d start "PTR_FORMAT" region %d length "UINT32_FORMAT" with remset "SIZE_FORMAT" code roots "SIZE_FORMAT" is marked %d live-other %d obj array %d", + r->isHumongous(), + r->bottom(), + region_idx, + r->region_num(), + r->rem_set()->occupied(), + r->rem_set()->strong_code_roots_list_length(), + next_bitmap->isMarked(r->bottom()), + g1h->humongous_is_live(region_idx), + obj->is_objArray() + ); + } + // Need to clear mark bit of the humongous object if already set. + if (next_bitmap->isMarked(r->bottom())) { + next_bitmap->clear(r->bottom()); + } + _freed_bytes += r->used(); + r->set_containing_set(NULL); + _humongous_regions_removed.increment(1u, r->capacity()); + g1h->free_humongous_region(r, _free_region_list, false); + + return false; + } + + HeapRegionSetCount& humongous_free_count() { + return _humongous_regions_removed; + } + + size_t bytes_freed() const { + return _freed_bytes; + } + + size_t humongous_reclaimed() const { + return _humongous_regions_removed.length(); + } +}; + +void G1CollectedHeap::eagerly_reclaim_humongous_regions() { + assert_at_safepoint(true); + + if (!G1ReclaimDeadHumongousObjectsAtYoungGC || !_has_humongous_reclaim_candidates) { + g1_policy()->phase_times()->record_fast_reclaim_humongous_time_ms(0.0, 0); + return; + } + + double start_time = os::elapsedTime(); + + FreeRegionList local_cleanup_list("Local Humongous Cleanup List"); + + G1FreeHumongousRegionClosure cl(&local_cleanup_list); + heap_region_iterate(&cl); + + HeapRegionSetCount empty_set; + remove_from_old_sets(empty_set, cl.humongous_free_count()); + + G1HRPrinter* hr_printer = _g1h->hr_printer(); + if (hr_printer->is_active()) { + FreeRegionListIterator iter(&local_cleanup_list); + while (iter.more_available()) { + HeapRegion* hr = iter.get_next(); + hr_printer->cleanup(hr); + } + } + + prepend_to_freelist(&local_cleanup_list); + decrement_summary_bytes(cl.bytes_freed()); + + g1_policy()->phase_times()->record_fast_reclaim_humongous_time_ms((os::elapsedTime() - start_time) * 1000.0, + cl.humongous_reclaimed()); +} + // This routine is similar to the above but does not record // any policy statistics or update free lists; we are abandoning // the current incremental collection set in preparation of a @@ -6579,22 +6727,22 @@ void G1CollectedHeap::tear_down_region_sets(bool free_list_only) { // this is that during a full GC string deduplication needs to know if // a collected region was young or old when the full GC was initiated. } - _free_list.remove_all(); + _hrm.remove_all_free_regions(); } class RebuildRegionSetsClosure : public HeapRegionClosure { private: bool _free_list_only; HeapRegionSet* _old_set; - FreeRegionList* _free_list; + HeapRegionManager* _hrm; size_t _total_used; public: RebuildRegionSetsClosure(bool free_list_only, - HeapRegionSet* old_set, FreeRegionList* free_list) : + HeapRegionSet* old_set, HeapRegionManager* hrm) : _free_list_only(free_list_only), - _old_set(old_set), _free_list(free_list), _total_used(0) { - assert(_free_list->is_empty(), "pre-condition"); + _old_set(old_set), _hrm(hrm), _total_used(0) { + assert(_hrm->num_free_regions() == 0, "pre-condition"); if (!free_list_only) { assert(_old_set->is_empty(), "pre-condition"); } @@ -6607,7 +6755,7 @@ public: if (r->is_empty()) { // Add free regions to the free list - _free_list->add_as_tail(r); + _hrm->insert_into_free_list(r); } else if (!_free_list_only) { assert(!r->is_young(), "we should not come across young regions"); @@ -6635,7 +6783,7 @@ void G1CollectedHeap::rebuild_region_sets(bool free_list_only) { _young_list->empty_list(); } - RebuildRegionSetsClosure cl(free_list_only, &_old_set, &_free_list); + RebuildRegionSetsClosure cl(free_list_only, &_old_set, &_hrm); heap_region_iterate(&cl); if (!free_list_only) { @@ -6790,13 +6938,42 @@ void OldGCAllocRegion::retire_region(HeapRegion* alloc_region, _g1h->retire_gc_alloc_region(alloc_region, allocated_bytes, GCAllocForTenured); } + +HeapRegion* OldGCAllocRegion::release() { + HeapRegion* cur = get(); + if (cur != NULL) { + // Determine how far we are from the next card boundary. If it is smaller than + // the minimum object size we can allocate into, expand into the next card. + HeapWord* top = cur->top(); + HeapWord* aligned_top = (HeapWord*)align_ptr_up(top, G1BlockOffsetSharedArray::N_bytes); + + size_t to_allocate_words = pointer_delta(aligned_top, top, HeapWordSize); + + if (to_allocate_words != 0) { + // We are not at a card boundary. Fill up, possibly into the next, taking the + // end of the region and the minimum object size into account. + to_allocate_words = MIN2(pointer_delta(cur->end(), cur->top(), HeapWordSize), + MAX2(to_allocate_words, G1CollectedHeap::min_fill_size())); + + // Skip allocation if there is not enough space to allocate even the smallest + // possible object. In this case this region will not be retained, so the + // original problem cannot occur. + if (to_allocate_words >= G1CollectedHeap::min_fill_size()) { + HeapWord* dummy = attempt_allocation(to_allocate_words, true /* bot_updates */); + CollectedHeap::fill_with_object(dummy, to_allocate_words); + } + } + } + return G1AllocRegion::release(); +} + // Heap region set verification class VerifyRegionListsClosure : public HeapRegionClosure { private: HeapRegionSet* _old_set; HeapRegionSet* _humongous_set; - FreeRegionList* _free_list; + HeapRegionManager* _hrm; public: HeapRegionSetCount _old_count; @@ -6805,8 +6982,8 @@ public: VerifyRegionListsClosure(HeapRegionSet* old_set, HeapRegionSet* humongous_set, - FreeRegionList* free_list) : - _old_set(old_set), _humongous_set(humongous_set), _free_list(free_list), + HeapRegionManager* hrm) : + _old_set(old_set), _humongous_set(humongous_set), _hrm(hrm), _old_count(), _humongous_count(), _free_count(){ } bool doHeapRegion(HeapRegion* hr) { @@ -6817,19 +6994,19 @@ public: if (hr->is_young()) { // TODO } else if (hr->startsHumongous()) { - assert(hr->containing_set() == _humongous_set, err_msg("Heap region %u is starts humongous but not in humongous set.", hr->hrs_index())); + assert(hr->containing_set() == _humongous_set, err_msg("Heap region %u is starts humongous but not in humongous set.", hr->hrm_index())); _humongous_count.increment(1u, hr->capacity()); } else if (hr->is_empty()) { - assert(hr->containing_set() == _free_list, err_msg("Heap region %u is empty but not on the free list.", hr->hrs_index())); + assert(_hrm->is_free(hr), err_msg("Heap region %u is empty but not on the free list.", hr->hrm_index())); _free_count.increment(1u, hr->capacity()); } else { - assert(hr->containing_set() == _old_set, err_msg("Heap region %u is old but not in the old set.", hr->hrs_index())); + assert(hr->containing_set() == _old_set, err_msg("Heap region %u is old but not in the old set.", hr->hrm_index())); _old_count.increment(1u, hr->capacity()); } return false; } - void verify_counts(HeapRegionSet* old_set, HeapRegionSet* humongous_set, FreeRegionList* free_list) { + void verify_counts(HeapRegionSet* old_set, HeapRegionSet* humongous_set, HeapRegionManager* free_list) { guarantee(old_set->length() == _old_count.length(), err_msg("Old set count mismatch. Expected %u, actual %u.", old_set->length(), _old_count.length())); guarantee(old_set->total_capacity_bytes() == _old_count.capacity(), err_msg("Old set capacity mismatch. Expected " SIZE_FORMAT ", actual " SIZE_FORMAT, old_set->total_capacity_bytes(), _old_count.capacity())); @@ -6838,26 +7015,17 @@ public: guarantee(humongous_set->total_capacity_bytes() == _humongous_count.capacity(), err_msg("Hum set capacity mismatch. Expected " SIZE_FORMAT ", actual " SIZE_FORMAT, humongous_set->total_capacity_bytes(), _humongous_count.capacity())); - guarantee(free_list->length() == _free_count.length(), err_msg("Free list count mismatch. Expected %u, actual %u.", free_list->length(), _free_count.length())); + guarantee(free_list->num_free_regions() == _free_count.length(), err_msg("Free list count mismatch. Expected %u, actual %u.", free_list->num_free_regions(), _free_count.length())); guarantee(free_list->total_capacity_bytes() == _free_count.capacity(), err_msg("Free list capacity mismatch. Expected " SIZE_FORMAT ", actual " SIZE_FORMAT, free_list->total_capacity_bytes(), _free_count.capacity())); } }; -HeapRegion* G1CollectedHeap::new_heap_region(uint hrs_index, - HeapWord* bottom) { - HeapWord* end = bottom + HeapRegion::GrainWords; - MemRegion mr(bottom, end); - assert(_g1_reserved.contains(mr), "invariant"); - // This might return NULL if the allocation fails - return new HeapRegion(hrs_index, _bot_shared, mr); -} - void G1CollectedHeap::verify_region_sets() { assert_heap_locked_or_at_safepoint(true /* should_be_vm_thread */); // First, check the explicit lists. - _free_list.verify_list(); + _hrm.verify(); { // Given that a concurrent operation might be adding regions to // the secondary free list we have to take the lock before @@ -6888,9 +7056,9 @@ void G1CollectedHeap::verify_region_sets() { // Finally, make sure that the region accounting in the lists is // consistent with what we see in the heap. - VerifyRegionListsClosure cl(&_old_set, &_humongous_set, &_free_list); + VerifyRegionListsClosure cl(&_old_set, &_humongous_set, &_hrm); heap_region_iterate(&cl); - cl.verify_counts(&_old_set, &_humongous_set, &_free_list); + cl.verify_counts(&_old_set, &_humongous_set, &_hrm); } // Optimized nmethod scanning @@ -6909,13 +7077,8 @@ class RegisterNMethodOopClosure: public OopClosure { " starting at "HR_FORMAT, _nm, HR_FORMAT_PARAMS(hr), HR_FORMAT_PARAMS(hr->humongous_start_region()))); - // HeapRegion::add_strong_code_root() avoids adding duplicate - // entries but having duplicates is OK since we "mark" nmethods - // as visited when we scan the strong code root lists during the GC. - hr->add_strong_code_root(_nm); - assert(hr->rem_set()->strong_code_roots_list_contains(_nm), - err_msg("failed to add code root "PTR_FORMAT" to remembered set of region "HR_FORMAT, - _nm, HR_FORMAT_PARAMS(hr))); + // HeapRegion::add_strong_code_root_locked() avoids adding duplicate entries. + hr->add_strong_code_root_locked(_nm); } } @@ -6942,9 +7105,6 @@ class UnregisterNMethodOopClosure: public OopClosure { _nm, HR_FORMAT_PARAMS(hr), HR_FORMAT_PARAMS(hr->humongous_start_region()))); hr->remove_strong_code_root(_nm); - assert(!hr->rem_set()->strong_code_roots_list_contains(_nm), - err_msg("failed to remove code root "PTR_FORMAT" of region "HR_FORMAT, - _nm, HR_FORMAT_PARAMS(hr))); } } @@ -6972,28 +7132,9 @@ void G1CollectedHeap::unregister_nmethod(nmethod* nm) { nm->oops_do(®_cl, true); } -class MigrateCodeRootsHeapRegionClosure: public HeapRegionClosure { -public: - bool doHeapRegion(HeapRegion *hr) { - assert(!hr->isHumongous(), - err_msg("humongous region "HR_FORMAT" should not have been added to collection set", - HR_FORMAT_PARAMS(hr))); - hr->migrate_strong_code_roots(); - return false; - } -}; - -void G1CollectedHeap::migrate_strong_code_roots() { - MigrateCodeRootsHeapRegionClosure cl; - double migrate_start = os::elapsedTime(); - collection_set_iterate(&cl); - double migration_time_ms = (os::elapsedTime() - migrate_start) * 1000.0; - g1_policy()->phase_times()->record_strong_code_root_migration_time(migration_time_ms); -} - void G1CollectedHeap::purge_code_root_memory() { double purge_start = os::elapsedTime(); - G1CodeRootSet::purge_chunks(G1CodeRootsChunkCacheKeepPercent); + G1CodeRootSet::purge(); double purge_time_ms = (os::elapsedTime() - purge_start) * 1000.0; g1_policy()->phase_times()->record_strong_code_root_purge_time(purge_time_ms); } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index 9f51eafa1ed..d8611470ffb 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -33,7 +33,7 @@ #include "gc_implementation/g1/g1MonitoringSupport.hpp" #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" #include "gc_implementation/g1/g1YCTypes.hpp" -#include "gc_implementation/g1/heapRegionSeq.hpp" +#include "gc_implementation/g1/heapRegionManager.hpp" #include "gc_implementation/g1/heapRegionSet.hpp" #include "gc_implementation/shared/hSpaceCounters.hpp" #include "gc_implementation/shared/parGCAllocBuffer.hpp" @@ -183,6 +183,13 @@ protected: public: OldGCAllocRegion() : G1AllocRegion("Old GC Alloc Region", true /* bot_updates */) { } + + // This specialization of release() makes sure that the last card that has been + // allocated into has been completely filled by a dummy object. + // This avoids races when remembered set scanning wants to update the BOT of the + // last card in the retained old gc alloc region, and allocation threads + // allocating into that card at the same time. + virtual HeapRegion* release(); }; // The G1 STW is alive closure. @@ -197,18 +204,15 @@ public: bool do_object_b(oop p); }; -// Instances of this class are used for quick tests on whether a reference points -// into the collection set. Each of the array's elements denotes whether the -// corresponding region is in the collection set. -class G1FastCSetBiasedMappedArray : public G1BiasedMappedArray { - protected: - bool default_value() const { return false; } - public: - void clear() { G1BiasedMappedArray::clear(); } -}; - class RefineCardTableEntryClosure; +class G1RegionMappingChangedListener : public G1MappingChangedListener { + private: + void reset_from_card_cache(uint start_idx, size_t num_regions); + public: + virtual void on_commit(uint start_idx, size_t num_regions); +}; + class G1CollectedHeap : public SharedHeap { friend class VM_CollectForMetadataAllocation; friend class VM_G1CollectForAllocation; @@ -237,6 +241,7 @@ class G1CollectedHeap : public SharedHeap { friend class EvacPopObjClosure; friend class G1ParCleanupCTTask; + friend class G1FreeHumongousRegionClosure; // Other related classes. friend class G1MarkSweep; @@ -246,19 +251,9 @@ private: static size_t _humongous_object_threshold_in_words; - // Storage for the G1 heap. - VirtualSpace _g1_storage; - MemRegion _g1_reserved; - - // The part of _g1_storage that is currently committed. - MemRegion _g1_committed; - - // The master free list. It will satisfy all new region allocations. - FreeRegionList _free_list; - // The secondary free list which contains regions that have been - // freed up during the cleanup process. This will be appended to the - // master free list when appropriate. + // freed up during the cleanup process. This will be appended to + // the master free list when appropriate. FreeRegionList _secondary_free_list; // It keeps track of the old regions. @@ -267,6 +262,9 @@ private: // It keeps track of the humongous regions. HeapRegionSet _humongous_set; + void clear_humongous_is_live_table(); + void eagerly_reclaim_humongous_regions(); + // The number of regions we could create by expansion. uint _expansion_regions; @@ -289,8 +287,11 @@ private: // after heap shrinking (free_list_only == true). void rebuild_region_sets(bool free_list_only); + // Callback for region mapping changed events. + G1RegionMappingChangedListener _listener; + // The sequence of all heap regions in the heap. - HeapRegionSeq _hrs; + HeapRegionManager _hrm; // Alloc region used to satisfy mutator allocation requests. MutatorAllocRegion _mutator_alloc_region; @@ -367,10 +368,25 @@ private: // than the current allocation region. size_t _summary_bytes_used; - // This array is used for a quick test on whether a reference points into - // the collection set or not. Each of the array's elements denotes whether the - // corresponding region is in the collection set or not. - G1FastCSetBiasedMappedArray _in_cset_fast_test; + // Records whether the region at the given index is kept live by roots or + // references from the young generation. + class HumongousIsLiveBiasedMappedArray : public G1BiasedMappedArray { + protected: + bool default_value() const { return false; } + public: + void clear() { G1BiasedMappedArray::clear(); } + void set_live(uint region) { + set_by_index(region, true); + } + bool is_live(uint region) { + return get_by_index(region); + } + }; + + HumongousIsLiveBiasedMappedArray _humongous_is_live; + // Stores whether during humongous object registration we found candidate regions. + // If not, we can skip a few steps. + bool _has_humongous_reclaim_candidates; volatile unsigned _gc_time_stamp; @@ -399,6 +415,7 @@ private: volatile unsigned int _old_marking_cycles_completed; bool _concurrent_cycle_started; + bool _heap_summary_sent; // This is a non-product method that is helpful for testing. It is // called at the end of a GC and artificially expands the heap by @@ -413,7 +430,7 @@ private: // If the HR printer is active, dump the state of the regions in the // heap after a compaction. - void print_hrs_post_compaction(); + void print_hrm_post_compaction(); double verify(bool guard, const char* msg); void verify_before_gc(); @@ -504,14 +521,6 @@ protected: // humongous object, set is_old to true. If not, to false. HeapRegion* new_region(size_t word_size, bool is_old, bool do_expand); - // Attempt to satisfy a humongous allocation request of the given - // size by finding a contiguous set of free regions of num_regions - // length and remove them from the master free list. Return the - // index of the first region or G1_NULL_HRS_INDEX if the search - // was unsuccessful. - uint humongous_obj_allocate_find_first(uint num_regions, - size_t word_size); - // Initialize a contiguous set of free regions of length num_regions // and starting at index first so that they appear as a single // humongous region. @@ -690,10 +699,24 @@ public: virtual void gc_prologue(bool full); virtual void gc_epilogue(bool full); + inline void set_humongous_is_live(oop obj); + + bool humongous_is_live(uint region) { + return _humongous_is_live.is_live(region); + } + + // Returns whether the given region (which must be a humongous (start) region) + // is to be considered conservatively live regardless of any other conditions. + bool humongous_region_is_always_live(uint index); + // Register the given region to be part of the collection set. + inline void register_humongous_region_with_in_cset_fast_test(uint index); + // Register regions with humongous objects (actually on the start region) in + // the in_cset_fast_test table. + void register_humongous_regions_with_in_cset_fast_test(); // We register a region with the fast "in collection set" test. We // simply set to true the array slot corresponding to this region. void register_region_with_in_cset_fast_test(HeapRegion* r) { - _in_cset_fast_test.set_by_index(r->hrs_index(), true); + _in_cset_fast_test.set_in_cset(r->hrm_index()); } // This is a fast test on whether a reference points into the @@ -839,11 +862,6 @@ protected: CodeBlobClosure* scan_strong_code, uint worker_i); - // Notifies all the necessary spaces that the committed space has - // been updated (either expanded or shrunk). It should be called - // after _g1_storage is updated. - void update_committed_space(HeapWord* old_end, HeapWord* new_end); - // The concurrent marker (and the thread it runs in.) ConcurrentMark* _cm; ConcurrentMarkThread* _cmThread; @@ -1154,27 +1172,20 @@ public: // But G1CollectedHeap doesn't yet support this. virtual bool is_maximal_no_gc() const { - return _g1_storage.uncommitted_size() == 0; + return _hrm.available() == 0; } - // The total number of regions in the heap. - uint n_regions() const { return _hrs.length(); } + // The current number of regions in the heap. + uint num_regions() const { return _hrm.length(); } // The max number of regions in the heap. - uint max_regions() const { return _hrs.max_length(); } + uint max_regions() const { return _hrm.max_length(); } // The number of regions that are completely free. - uint free_regions() const { return _free_list.length(); } + uint num_free_regions() const { return _hrm.num_free_regions(); } // The number of regions that are not completely free. - uint used_regions() const { return n_regions() - free_regions(); } - - // The number of regions available for "regular" expansion. - uint expansion_regions() const { return _expansion_regions; } - - // Factory method for HeapRegion instances. It will return NULL if - // the allocation fails. - HeapRegion* new_heap_region(uint hrs_index, HeapWord* bottom); + uint num_used_regions() const { return num_regions() - num_free_regions(); } void verify_not_dirty_region(HeapRegion* hr) PRODUCT_RETURN; void verify_dirty_region(HeapRegion* hr) PRODUCT_RETURN; @@ -1223,7 +1234,7 @@ public: #ifdef ASSERT bool is_on_master_free_list(HeapRegion* hr) { - return hr->containing_set() == &_free_list; + return _hrm.is_free(hr); } #endif // ASSERT @@ -1235,7 +1246,7 @@ public: } void append_secondary_free_list() { - _free_list.add_ordered(&_secondary_free_list); + _hrm.insert_list_into_free_list(&_secondary_free_list); } void append_secondary_free_list_if_not_empty_with_lock() { @@ -1281,33 +1292,84 @@ public: // Returns "TRUE" iff "p" points into the committed areas of the heap. virtual bool is_in(const void* p) const; +#ifdef ASSERT + // Returns whether p is in one of the available areas of the heap. Slow but + // extensive version. + bool is_in_exact(const void* p) const; +#endif // Return "TRUE" iff the given object address is within the collection - // set. + // set. Slow implementation. inline bool obj_in_cs(oop obj); + inline bool is_in_cset(oop obj); + + inline bool is_in_cset_or_humongous(const oop obj); + + enum in_cset_state_t { + InNeither, // neither in collection set nor humongous + InCSet, // region is in collection set only + IsHumongous // region is a humongous start region + }; + private: + // Instances of this class are used for quick tests on whether a reference points + // into the collection set or is a humongous object (points into a humongous + // object). + // Each of the array's elements denotes whether the corresponding region is in + // the collection set or a humongous region. + // We use this to quickly reclaim humongous objects: by making a humongous region + // succeed this test, we sort-of add it to the collection set. During the reference + // iteration closures, when we see a humongous region, we simply mark it as + // referenced, i.e. live. + class G1FastCSetBiasedMappedArray : public G1BiasedMappedArray { + protected: + char default_value() const { return G1CollectedHeap::InNeither; } + public: + void set_humongous(uintptr_t index) { + assert(get_by_index(index) != InCSet, "Should not overwrite InCSet values"); + set_by_index(index, G1CollectedHeap::IsHumongous); + } + + void clear_humongous(uintptr_t index) { + set_by_index(index, G1CollectedHeap::InNeither); + } + + void set_in_cset(uintptr_t index) { + assert(get_by_index(index) != G1CollectedHeap::IsHumongous, "Should not overwrite IsHumongous value"); + set_by_index(index, G1CollectedHeap::InCSet); + } + + bool is_in_cset_or_humongous(HeapWord* addr) const { return get_by_address(addr) != G1CollectedHeap::InNeither; } + bool is_in_cset(HeapWord* addr) const { return get_by_address(addr) == G1CollectedHeap::InCSet; } + G1CollectedHeap::in_cset_state_t at(HeapWord* addr) const { return (G1CollectedHeap::in_cset_state_t)get_by_address(addr); } + void clear() { G1BiasedMappedArray::clear(); } + }; + + // This array is used for a quick test on whether a reference points into + // the collection set or not. Each of the array's elements denotes whether the + // corresponding region is in the collection set or not. + G1FastCSetBiasedMappedArray _in_cset_fast_test; + + public: + + inline in_cset_state_t in_cset_state(const oop obj); + // Return "TRUE" iff the given object address is in the reserved // region of g1. bool is_in_g1_reserved(const void* p) const { - return _g1_reserved.contains(p); + return _hrm.reserved().contains(p); } // Returns a MemRegion that corresponds to the space that has been // reserved for the heap - MemRegion g1_reserved() { - return _g1_reserved; - } - - // Returns a MemRegion that corresponds to the space that has been - // committed in the heap - MemRegion g1_committed() { - return _g1_committed; + MemRegion g1_reserved() const { + return _hrm.reserved(); } virtual bool is_in_closed_subset(const void* p) const; - G1SATBCardTableModRefBS* g1_barrier_set() { - return (G1SATBCardTableModRefBS*) barrier_set(); + G1SATBCardTableLoggingModRefBS* g1_barrier_set() { + return (G1SATBCardTableLoggingModRefBS*) barrier_set(); } // This resets the card table to all zeros. It is used after @@ -1320,9 +1382,6 @@ public: // "cl.do_oop" on each. virtual void oop_iterate(ExtendedOopClosure* cl); - // Same as above, restricted to a memory region. - void oop_iterate(MemRegion mr, ExtendedOopClosure* cl); - // Iterate over all objects, calling "cl.do_object" on each. virtual void object_iterate(ObjectClosure* cl); @@ -1340,6 +1399,12 @@ public: // Return the region with the given index. It assumes the index is valid. inline HeapRegion* region_at(uint index) const; + // Calculate the region index of the given address. Given address must be + // within the heap. + inline uint addr_to_region(HeapWord* addr) const; + + inline HeapWord* bottom_addr_for_region(uint index) const; + // Divide the heap region sequence into "chunks" of some size (the number // of regions divided by the number of parallel threads times some // overpartition factor, currently 4). Assumes that this will be called @@ -1353,10 +1418,10 @@ public: // setting the claim value of the second and subsequent regions of the // chunk.) For now requires that "doHeapRegion" always returns "false", // i.e., that a closure never attempt to abort a traversal. - void heap_region_par_iterate_chunked(HeapRegionClosure* blk, - uint worker, - uint no_of_par_workers, - jint claim_value); + void heap_region_par_iterate_chunked(HeapRegionClosure* cl, + uint worker_id, + uint num_workers, + jint claim_value) const; // It resets all the region claim values to the default. void reset_heap_region_claim_values(); @@ -1381,11 +1446,6 @@ public: // starting region for iterating over the current collection set. HeapRegion* start_cset_region_for_worker(uint worker_i); - // This is a convenience method that is used by the - // HeapRegionIterator classes to calculate the starting region for - // each worker so that they do not all start from the same region. - HeapRegion* start_region_for_worker(uint worker_i, uint no_of_par_workers); - // Iterate over the regions (if any) in the current collection set. void collection_set_iterate(HeapRegionClosure* blk); @@ -1602,12 +1662,6 @@ public: // Unregister the given nmethod from the G1 heap. virtual void unregister_nmethod(nmethod* nm); - // Migrate the nmethods in the code root lists of the regions - // in the collection set to regions in to-space. In the event - // of an evacuation failure, nmethods that reference objects - // that were not successfully evacuated are not migrated. - void migrate_strong_code_roots(); - // Free up superfluous code root memory. void purge_code_root_memory(); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp index 7d5e530c497..78bd016a89d 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp @@ -30,29 +30,38 @@ #include "gc_implementation/g1/g1AllocRegion.inline.hpp" #include "gc_implementation/g1/g1CollectorPolicy.hpp" #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/g1/heapRegionSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" #include "runtime/orderAccess.inline.hpp" #include "utilities/taskqueue.hpp" // Inline functions for G1CollectedHeap // Return the region with the given index. It assumes the index is valid. -inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrs.at(index); } +inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrm.at(index); } -template -inline HeapRegion* -G1CollectedHeap::heap_region_containing_raw(const T addr) const { - assert(addr != NULL, "invariant"); - assert(_g1_reserved.contains((const void*) addr), - err_msg("Address "PTR_FORMAT" is outside of the heap ranging from ["PTR_FORMAT" to "PTR_FORMAT")", - p2i((void*)addr), p2i(_g1_reserved.start()), p2i(_g1_reserved.end()))); - return _hrs.addr_to_region((HeapWord*) addr); +inline uint G1CollectedHeap::addr_to_region(HeapWord* addr) const { + assert(is_in_reserved(addr), + err_msg("Cannot calculate region index for address "PTR_FORMAT" that is outside of the heap ["PTR_FORMAT", "PTR_FORMAT")", + p2i(addr), p2i(_reserved.start()), p2i(_reserved.end()))); + return (uint)(pointer_delta(addr, _reserved.start(), sizeof(uint8_t)) >> HeapRegion::LogOfHRGrainBytes); +} + +inline HeapWord* G1CollectedHeap::bottom_addr_for_region(uint index) const { + return _hrm.reserved().start() + index * HeapRegion::GrainWords; } template -inline HeapRegion* -G1CollectedHeap::heap_region_containing(const T addr) const { +inline HeapRegion* G1CollectedHeap::heap_region_containing_raw(const T addr) const { + assert(addr != NULL, "invariant"); + assert(is_in_g1_reserved((const void*) addr), + err_msg("Address "PTR_FORMAT" is outside of the heap ranging from ["PTR_FORMAT" to "PTR_FORMAT")", + p2i((void*)addr), p2i(g1_reserved().start()), p2i(g1_reserved().end()))); + return _hrm.addr_to_region((HeapWord*) addr); +} + +template +inline HeapRegion* G1CollectedHeap::heap_region_containing(const T addr) const { HeapRegion* hr = heap_region_containing_raw(addr); if (hr->continuesHumongous()) { return hr->humongous_start_region(); @@ -78,14 +87,13 @@ inline void G1CollectedHeap::old_set_remove(HeapRegion* hr) { } inline bool G1CollectedHeap::obj_in_cs(oop obj) { - HeapRegion* r = _hrs.addr_to_region((HeapWord*) obj); + HeapRegion* r = _hrm.addr_to_region((HeapWord*) obj); return r != NULL && r->in_collection_set(); } -inline HeapWord* -G1CollectedHeap::attempt_allocation(size_t word_size, - unsigned int* gc_count_before_ret, - int* gclocker_retry_count_ret) { +inline HeapWord* G1CollectedHeap::attempt_allocation(size_t word_size, + unsigned int* gc_count_before_ret, + int* gclocker_retry_count_ret) { assert_heap_not_locked_and_not_at_safepoint(); assert(!isHumongous(word_size), "attempt_allocation() should not " "be called for humongous allocation requests"); @@ -172,12 +180,11 @@ inline bool G1CollectedHeap::isMarkedNext(oop obj) const { return _cm->nextMarkBitMap()->isMarked((HeapWord *)obj); } - // This is a fast test on whether a reference points into the // collection set or not. Assume that the reference // points into the heap. -inline bool G1CollectedHeap::in_cset_fast_test(oop obj) { - bool ret = _in_cset_fast_test.get_by_address((HeapWord*)obj); +inline bool G1CollectedHeap::is_in_cset(oop obj) { + bool ret = _in_cset_fast_test.is_in_cset((HeapWord*)obj); // let's make sure the result is consistent with what the slower // test returns assert( ret || !obj_in_cs(obj), "sanity"); @@ -185,6 +192,18 @@ inline bool G1CollectedHeap::in_cset_fast_test(oop obj) { return ret; } +bool G1CollectedHeap::is_in_cset_or_humongous(const oop obj) { + return _in_cset_fast_test.is_in_cset_or_humongous((HeapWord*)obj); +} + +G1CollectedHeap::in_cset_state_t G1CollectedHeap::in_cset_state(const oop obj) { + return _in_cset_fast_test.at((HeapWord*)obj); +} + +void G1CollectedHeap::register_humongous_region_with_in_cset_fast_test(uint index) { + _in_cset_fast_test.set_humongous(index); +} + #ifndef PRODUCT // Support for G1EvacuationFailureALot @@ -234,8 +253,7 @@ G1CollectedHeap::set_evacuation_failure_alot_for_current_gc() { } } -inline bool -G1CollectedHeap::evacuation_should_fail() { +inline bool G1CollectedHeap::evacuation_should_fail() { if (!G1EvacuationFailureALot || !_evacuation_failure_alot_for_current_gc) { return false; } @@ -288,4 +306,22 @@ inline bool G1CollectedHeap::is_obj_ill(const oop obj) const { return is_obj_ill(obj, heap_region_containing(obj)); } +inline void G1CollectedHeap::set_humongous_is_live(oop obj) { + uint region = addr_to_region((HeapWord*)obj); + // We not only set the "live" flag in the humongous_is_live table, but also + // reset the entry in the _in_cset_fast_test table so that subsequent references + // to the same humongous object do not go into the slow path again. + // This is racy, as multiple threads may at the same time enter here, but this + // is benign. + // During collection we only ever set the "live" flag, and only ever clear the + // entry in the in_cset_fast_table. + // We only ever evaluate the contents of these tables (in the VM thread) after + // having synchronized the worker threads with the VM thread, or in the same + // thread (i.e. within the VM thread). + if (!_humongous_is_live.is_live(region)) { + _humongous_is_live.set_live(region); + _in_cset_fast_test.clear_humongous(region); + } +} + #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1COLLECTEDHEAP_INLINE_HPP diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp index eba482046fd..931c4e3d25e 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp @@ -456,7 +456,7 @@ void G1CollectorPolicy::init() { } else { _young_list_fixed_length = _young_gen_sizer->min_desired_young_length(); } - _free_regions_at_end_of_collection = _g1->free_regions(); + _free_regions_at_end_of_collection = _g1->num_free_regions(); update_young_list_target_length(); // We may immediately start allocating regions and placing them on the @@ -829,7 +829,7 @@ void G1CollectorPolicy::record_full_collection_end() { record_survivor_regions(0, NULL, NULL); - _free_regions_at_end_of_collection = _g1->free_regions(); + _free_regions_at_end_of_collection = _g1->num_free_regions(); // Reset survivors SurvRateGroup. _survivor_surv_rate_group->reset(); update_young_list_target_length(); @@ -1181,7 +1181,7 @@ void G1CollectorPolicy::record_collection_pause_end(double pause_time_ms, Evacua _in_marking_window = new_in_marking_window; _in_marking_window_im = new_in_marking_window_im; - _free_regions_at_end_of_collection = _g1->free_regions(); + _free_regions_at_end_of_collection = _g1->num_free_regions(); update_young_list_target_length(); // Note that _mmu_tracker->max_gc_time() returns the time in seconds. @@ -1203,7 +1203,7 @@ void G1CollectorPolicy::record_heap_size_info_at_start(bool full) { _survivor_used_bytes_before_gc = young_list->survivor_used_bytes(); _heap_capacity_bytes_before_gc = _g1->capacity(); _heap_used_bytes_before_gc = _g1->used(); - _cur_collection_pause_used_regions_at_start = _g1->used_regions(); + _cur_collection_pause_used_regions_at_start = _g1->num_used_regions(); _eden_capacity_bytes_before_gc = (_young_list_target_length * HeapRegion::GrainBytes) - _survivor_used_bytes_before_gc; @@ -1618,7 +1618,7 @@ void G1CollectorPolicy::record_concurrent_mark_cleanup_end(int no_of_gc_threads) { _collectionSetChooser->clear(); - uint region_num = _g1->n_regions(); + uint region_num = _g1->num_regions(); if (G1CollectedHeap::use_parallel_gc_threads()) { const uint OverpartitionFactor = 4; uint WorkUnit; @@ -1639,7 +1639,7 @@ G1CollectorPolicy::record_concurrent_mark_cleanup_end(int no_of_gc_threads) { MAX2(region_num / (uint) (ParallelGCThreads * OverpartitionFactor), MinWorkUnit); } - _collectionSetChooser->prepare_for_par_region_addition(_g1->n_regions(), + _collectionSetChooser->prepare_for_par_region_addition(_g1->num_regions(), WorkUnit); ParKnownGarbageTask parKnownGarbageTask(_collectionSetChooser, (int) WorkUnit); @@ -1936,7 +1936,7 @@ uint G1CollectorPolicy::calc_max_old_cset_length() { // of them are available. G1CollectedHeap* g1h = G1CollectedHeap::heap(); - const size_t region_num = g1h->n_regions(); + const size_t region_num = g1h->num_regions(); const size_t perc = (size_t) G1OldCSetRegionThresholdPercent; size_t result = region_num * perc / 100; // emulate ceiling diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1EvacFailure.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1EvacFailure.hpp index 4b25d904063..72d1ca179f8 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1EvacFailure.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1EvacFailure.hpp @@ -217,6 +217,8 @@ public: _update_rset_cl->set_region(hr); hr->object_iterate(&rspc); + hr->rem_set()->clean_strong_code_roots(hr); + hr->note_self_forwarding_removal_end(during_initial_mark, during_conc_mark, rspc.marked_bytes()); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp index 49232d60728..bf7e3533b71 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp @@ -237,8 +237,10 @@ void G1GCPhaseTimes::note_gc_end() { _last_gc_worker_times_ms.verify(); _last_gc_worker_other_times_ms.verify(); - _last_redirty_logged_cards_time_ms.verify(); - _last_redirty_logged_cards_processed_cards.verify(); + if (G1DeferredRSUpdate) { + _last_redirty_logged_cards_time_ms.verify(); + _last_redirty_logged_cards_processed_cards.verify(); + } } void G1GCPhaseTimes::note_string_dedup_fixup_start() { @@ -255,6 +257,10 @@ void G1GCPhaseTimes::print_stats(int level, const char* str, double value) { LineBuffer(level).append_and_print_cr("[%s: %.1lf ms]", str, value); } +void G1GCPhaseTimes::print_stats(int level, const char* str, size_t value) { + LineBuffer(level).append_and_print_cr("[%s: "SIZE_FORMAT"]", str, value); +} + void G1GCPhaseTimes::print_stats(int level, const char* str, double value, uint workers) { LineBuffer(level).append_and_print_cr("[%s: %.1lf ms, GC Workers: %u]", str, value, workers); } @@ -269,9 +275,6 @@ double G1GCPhaseTimes::accounted_time_ms() { // Now subtract the time taken to fix up roots in generated code misc_time_ms += _cur_collection_code_root_fixup_time_ms; - // Strong code root migration time - misc_time_ms += _cur_strong_code_root_migration_time_ms; - // Strong code root purge time misc_time_ms += _cur_strong_code_root_purge_time_ms; @@ -322,7 +325,6 @@ void G1GCPhaseTimes::print(double pause_time_sec) { _last_obj_copy_times_ms.print(1, "Object Copy (ms)"); } print_stats(1, "Code Root Fixup", _cur_collection_code_root_fixup_time_ms); - print_stats(1, "Code Root Migration", _cur_strong_code_root_migration_time_ms); print_stats(1, "Code Root Purge", _cur_strong_code_root_purge_time_ms); if (G1StringDedup::is_enabled()) { print_stats(1, "String Dedup Fixup", _cur_string_dedup_fixup_time_ms, _active_gc_threads); @@ -357,6 +359,14 @@ void G1GCPhaseTimes::print(double pause_time_sec) { _last_redirty_logged_cards_processed_cards.print(3, "Redirtied Cards"); } } + if (G1ReclaimDeadHumongousObjectsAtYoungGC) { + print_stats(2, "Humongous Reclaim", _cur_fast_reclaim_humongous_time_ms); + if (G1Log::finest()) { + print_stats(3, "Humongous Total", _cur_fast_reclaim_humongous_total); + print_stats(3, "Humongous Candidate", _cur_fast_reclaim_humongous_candidates); + print_stats(3, "Humongous Reclaimed", _cur_fast_reclaim_humongous_reclaimed); + } + } print_stats(2, "Free CSet", (_recorded_young_free_cset_time_ms + _recorded_non_young_free_cset_time_ms)); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp index 12a3a7dd21b..8421eb07b6a 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp @@ -129,7 +129,6 @@ class G1GCPhaseTimes : public CHeapObj { double _cur_collection_par_time_ms; double _cur_collection_code_root_fixup_time_ms; - double _cur_strong_code_root_migration_time_ms; double _cur_strong_code_root_purge_time_ms; double _cur_evac_fail_recalc_used; @@ -157,11 +156,17 @@ class G1GCPhaseTimes : public CHeapObj { double _recorded_young_free_cset_time_ms; double _recorded_non_young_free_cset_time_ms; + double _cur_fast_reclaim_humongous_time_ms; + size_t _cur_fast_reclaim_humongous_total; + size_t _cur_fast_reclaim_humongous_candidates; + size_t _cur_fast_reclaim_humongous_reclaimed; + double _cur_verify_before_time_ms; double _cur_verify_after_time_ms; // Helper methods for detailed logging void print_stats(int level, const char* str, double value); + void print_stats(int level, const char* str, size_t value); void print_stats(int level, const char* str, double value, uint workers); public: @@ -227,10 +232,6 @@ class G1GCPhaseTimes : public CHeapObj { _cur_collection_code_root_fixup_time_ms = ms; } - void record_strong_code_root_migration_time(double ms) { - _cur_strong_code_root_migration_time_ms = ms; - } - void record_strong_code_root_purge_time(double ms) { _cur_strong_code_root_purge_time_ms = ms; } @@ -282,6 +283,16 @@ class G1GCPhaseTimes : public CHeapObj { _recorded_non_young_free_cset_time_ms = time_ms; } + void record_fast_reclaim_humongous_stats(size_t total, size_t candidates) { + _cur_fast_reclaim_humongous_total = total; + _cur_fast_reclaim_humongous_candidates = candidates; + } + + void record_fast_reclaim_humongous_time_ms(double value, size_t reclaimed) { + _cur_fast_reclaim_humongous_time_ms = value; + _cur_fast_reclaim_humongous_reclaimed = reclaimed; + } + void record_young_cset_choice_time_ms(double time_ms) { _recorded_young_cset_choice_time_ms = time_ms; } @@ -348,6 +359,10 @@ class G1GCPhaseTimes : public CHeapObj { return _recorded_non_young_free_cset_time_ms; } + double fast_reclaim_humongous_time_ms() { + return _cur_fast_reclaim_humongous_time_ms; + } + double average_last_update_rs_time() { return _last_update_rs_times_ms.average(); } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1HotCardCache.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1HotCardCache.cpp index a75ba66e06f..79655933da7 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1HotCardCache.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1HotCardCache.cpp @@ -33,7 +33,7 @@ G1HotCardCache::G1HotCardCache(G1CollectedHeap *g1h): _g1h(g1h), _hot_cache(NULL), _use_cache(false), _card_counts(g1h) {} -void G1HotCardCache::initialize() { +void G1HotCardCache::initialize(G1RegionToSpaceMapper* card_counts_storage) { if (default_use_cache()) { _use_cache = true; @@ -49,7 +49,7 @@ void G1HotCardCache::initialize() { _hot_cache_par_chunk_size = MAX2(1, _hot_cache_size / (int)n_workers); _hot_cache_par_claimed_idx = 0; - _card_counts.initialize(); + _card_counts.initialize(card_counts_storage); } } @@ -135,11 +135,8 @@ void G1HotCardCache::drain(uint worker_i, // above, are discarded prior to re-enabling the cache near the end of the GC. } -void G1HotCardCache::resize_card_counts(size_t heap_capacity) { - _card_counts.resize(heap_capacity); -} - void G1HotCardCache::reset_card_counts(HeapRegion* hr) { + assert(!hr->isHumongous(), "Should have been cleared"); _card_counts.clear_region(hr); } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1HotCardCache.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1HotCardCache.hpp index 5dc929c25e2..becd6832769 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1HotCardCache.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1HotCardCache.hpp @@ -78,7 +78,7 @@ class G1HotCardCache: public CHeapObj { G1HotCardCache(G1CollectedHeap* g1h); ~G1HotCardCache(); - void initialize(); + void initialize(G1RegionToSpaceMapper* card_counts_storage); bool use_cache() { return _use_cache; } @@ -115,9 +115,6 @@ class G1HotCardCache: public CHeapObj { bool hot_cache_is_empty() { return _n_hot == 0; } - // Resizes the card counts table to match the given capacity - void resize_card_counts(size_t heap_capacity); - // Zeros the values in the card counts table for entire committed heap void reset_card_counts(); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp index 2db5b9bfa1a..ecdb19440fa 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp @@ -44,7 +44,7 @@ template inline void FilterIntoCSClosure::do_oop_nv(T* p) { T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop) && - _g1->obj_in_cs(oopDesc::decode_heap_oop_not_null(heap_oop))) { + _g1->is_in_cset_or_humongous(oopDesc::decode_heap_oop_not_null(heap_oop))) { _oc->do_oop(p); } } @@ -67,7 +67,8 @@ inline void G1ParScanClosure::do_oop_nv(T* p) { if (!oopDesc::is_null(heap_oop)) { oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); - if (_g1->in_cset_fast_test(obj)) { + G1CollectedHeap::in_cset_state_t state = _g1->in_cset_state(obj); + if (state == G1CollectedHeap::InCSet) { // We're not going to even bother checking whether the object is // already forwarded or not, as this usually causes an immediate // stall. We'll try to prefetch the object (for write, given that @@ -86,6 +87,9 @@ inline void G1ParScanClosure::do_oop_nv(T* p) { _par_scan_state->push_on_queue(p); } else { + if (state == G1CollectedHeap::IsHumongous) { + _g1->set_humongous_is_live(obj); + } _par_scan_state->update_rs(_from, p, _worker_id); } } @@ -97,12 +101,14 @@ inline void G1ParPushHeapRSClosure::do_oop_nv(T* p) { if (!oopDesc::is_null(heap_oop)) { oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); - if (_g1->in_cset_fast_test(obj)) { + if (_g1->is_in_cset_or_humongous(obj)) { Prefetch::write(obj->mark_addr(), 0); Prefetch::read(obj->mark_addr(), (HeapWordSize*2)); // Place on the references queue _par_scan_state->push_on_queue(p); + } else { + assert(!_g1->obj_in_cs(obj), "checking"); } } } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1PageBasedVirtualSpace.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1PageBasedVirtualSpace.cpp new file mode 100644 index 00000000000..1b294edffed --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/g1/g1PageBasedVirtualSpace.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "gc_implementation/g1/g1PageBasedVirtualSpace.hpp" +#include "oops/markOop.hpp" +#include "oops/oop.inline.hpp" +#include "services/memTracker.hpp" +#ifdef TARGET_OS_FAMILY_linux +# include "os_linux.inline.hpp" +#endif +#ifdef TARGET_OS_FAMILY_solaris +# include "os_solaris.inline.hpp" +#endif +#ifdef TARGET_OS_FAMILY_windows +# include "os_windows.inline.hpp" +#endif +#ifdef TARGET_OS_FAMILY_aix +# include "os_aix.inline.hpp" +#endif +#ifdef TARGET_OS_FAMILY_bsd +# include "os_bsd.inline.hpp" +#endif +#include "utilities/bitMap.inline.hpp" + +G1PageBasedVirtualSpace::G1PageBasedVirtualSpace() : _low_boundary(NULL), + _high_boundary(NULL), _committed(), _page_size(0), _special(false), _executable(false) { +} + +bool G1PageBasedVirtualSpace::initialize_with_granularity(ReservedSpace rs, size_t page_size) { + if (!rs.is_reserved()) { + return false; // Allocation failed. + } + assert(_low_boundary == NULL, "VirtualSpace already initialized"); + assert(page_size > 0, "Granularity must be non-zero."); + + _low_boundary = rs.base(); + _high_boundary = _low_boundary + rs.size(); + + _special = rs.special(); + _executable = rs.executable(); + + _page_size = page_size; + + assert(_committed.size() == 0, "virtual space initialized more than once"); + uintx size_in_bits = rs.size() / page_size; + _committed.resize(size_in_bits, /* in_resource_area */ false); + + return true; +} + + +G1PageBasedVirtualSpace::~G1PageBasedVirtualSpace() { + release(); +} + +void G1PageBasedVirtualSpace::release() { + // This does not release memory it never reserved. + // Caller must release via rs.release(); + _low_boundary = NULL; + _high_boundary = NULL; + _special = false; + _executable = false; + _page_size = 0; + _committed.resize(0, false); +} + +size_t G1PageBasedVirtualSpace::committed_size() const { + return _committed.count_one_bits() * _page_size; +} + +size_t G1PageBasedVirtualSpace::reserved_size() const { + return pointer_delta(_high_boundary, _low_boundary, sizeof(char)); +} + +size_t G1PageBasedVirtualSpace::uncommitted_size() const { + return reserved_size() - committed_size(); +} + +uintptr_t G1PageBasedVirtualSpace::addr_to_page_index(char* addr) const { + return (addr - _low_boundary) / _page_size; +} + +bool G1PageBasedVirtualSpace::is_area_committed(uintptr_t start, size_t size_in_pages) const { + uintptr_t end = start + size_in_pages; + return _committed.get_next_zero_offset(start, end) >= end; +} + +bool G1PageBasedVirtualSpace::is_area_uncommitted(uintptr_t start, size_t size_in_pages) const { + uintptr_t end = start + size_in_pages; + return _committed.get_next_one_offset(start, end) >= end; +} + +char* G1PageBasedVirtualSpace::page_start(uintptr_t index) { + return _low_boundary + index * _page_size; +} + +size_t G1PageBasedVirtualSpace::byte_size_for_pages(size_t num) { + return num * _page_size; +} + +MemRegion G1PageBasedVirtualSpace::commit(uintptr_t start, size_t size_in_pages) { + // We need to make sure to commit all pages covered by the given area. + guarantee(is_area_uncommitted(start, size_in_pages), "Specified area is not uncommitted"); + + if (!_special) { + os::commit_memory_or_exit(page_start(start), byte_size_for_pages(size_in_pages), _executable, + err_msg("Failed to commit pages from "SIZE_FORMAT" of length "SIZE_FORMAT, start, size_in_pages)); + } + _committed.set_range(start, start + size_in_pages); + + MemRegion result((HeapWord*)page_start(start), byte_size_for_pages(size_in_pages) / HeapWordSize); + return result; +} + +MemRegion G1PageBasedVirtualSpace::uncommit(uintptr_t start, size_t size_in_pages) { + guarantee(is_area_committed(start, size_in_pages), "checking"); + + if (!_special) { + os::uncommit_memory(page_start(start), byte_size_for_pages(size_in_pages)); + } + + _committed.clear_range(start, start + size_in_pages); + + MemRegion result((HeapWord*)page_start(start), byte_size_for_pages(size_in_pages) / HeapWordSize); + return result; +} + +bool G1PageBasedVirtualSpace::contains(const void* p) const { + return _low_boundary <= (const char*) p && (const char*) p < _high_boundary; +} + +#ifndef PRODUCT +void G1PageBasedVirtualSpace::print_on(outputStream* out) { + out->print ("Virtual space:"); + if (special()) out->print(" (pinned in memory)"); + out->cr(); + out->print_cr(" - committed: " SIZE_FORMAT, committed_size()); + out->print_cr(" - reserved: " SIZE_FORMAT, reserved_size()); + out->print_cr(" - [low_b, high_b]: [" INTPTR_FORMAT ", " INTPTR_FORMAT "]", p2i(_low_boundary), p2i(_high_boundary)); +} + +void G1PageBasedVirtualSpace::print() { + print_on(tty); +} +#endif diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1PageBasedVirtualSpace.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1PageBasedVirtualSpace.hpp new file mode 100644 index 00000000000..972a69b2e8f --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/g1/g1PageBasedVirtualSpace.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1PAGEBASEDVIRTUALSPACE_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1PAGEBASEDVIRTUALSPACE_HPP + +#include "memory/allocation.hpp" +#include "memory/memRegion.hpp" +#include "runtime/virtualspace.hpp" +#include "utilities/bitMap.hpp" + +// Virtual space management helper for a virtual space with an OS page allocation +// granularity. +// (De-)Allocation requests are always OS page aligned by passing a page index +// and multiples of pages. +// The implementation gives an error when trying to commit or uncommit pages that +// have already been committed or uncommitted. +class G1PageBasedVirtualSpace VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + // Reserved area addresses. + char* _low_boundary; + char* _high_boundary; + + // The commit/uncommit granularity in bytes. + size_t _page_size; + + // Bitmap used for verification of commit/uncommit operations. + BitMap _committed; + + // Indicates that the entire space has been committed and pinned in memory, + // os::commit_memory() or os::uncommit_memory() have no function. + bool _special; + + // Indicates whether the committed space should be executable. + bool _executable; + + // Returns the index of the page which contains the given address. + uintptr_t addr_to_page_index(char* addr) const; + // Returns the address of the given page index. + char* page_start(uintptr_t index); + // Returns the byte size of the given number of pages. + size_t byte_size_for_pages(size_t num); + + // Returns true if the entire area is backed by committed memory. + bool is_area_committed(uintptr_t start, size_t size_in_pages) const; + // Returns true if the entire area is not backed by committed memory. + bool is_area_uncommitted(uintptr_t start, size_t size_in_pages) const; + + public: + + // Commit the given area of pages starting at start being size_in_pages large. + MemRegion commit(uintptr_t start, size_t size_in_pages); + + // Uncommit the given area of pages starting at start being size_in_pages large. + MemRegion uncommit(uintptr_t start, size_t size_in_pages); + + bool special() const { return _special; } + + // Initialization + G1PageBasedVirtualSpace(); + bool initialize_with_granularity(ReservedSpace rs, size_t page_size); + + // Destruction + ~G1PageBasedVirtualSpace(); + + // Amount of reserved memory. + size_t reserved_size() const; + // Memory used in this virtual space. + size_t committed_size() const; + // Memory left to use/expand in this virtual space. + size_t uncommitted_size() const; + + bool contains(const void* p) const; + + MemRegion reserved() { + MemRegion x((HeapWord*)_low_boundary, reserved_size() / HeapWordSize); + return x; + } + + void release(); + + void check_for_contiguity() PRODUCT_RETURN; + + // Debugging + void print_on(outputStream* out) PRODUCT_RETURN; + void print(); +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1PAGEBASEDVIRTUALSPACE_HPP diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1ParScanThreadState.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1ParScanThreadState.cpp index 89e3e813b98..bf5824f1ed2 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1ParScanThreadState.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1ParScanThreadState.cpp @@ -288,7 +288,12 @@ void G1ParScanThreadState::undo_allocation(GCAllocPurpose purpose, HeapWord* obj } HeapWord* G1ParScanThreadState::allocate(GCAllocPurpose purpose, size_t word_sz) { - HeapWord* obj = alloc_buffer(purpose)->allocate(word_sz); + HeapWord* obj = NULL; + if (purpose == GCAllocForSurvived) { + obj = alloc_buffer(GCAllocForSurvived)->allocate_aligned(word_sz, SurvivorAlignmentInBytes); + } else { + obj = alloc_buffer(GCAllocForTenured)->allocate(word_sz); + } if (obj != NULL) { return obj; } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1ParScanThreadState.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1ParScanThreadState.inline.hpp index fc2fbe3ff45..75517fb3f9d 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1ParScanThreadState.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1ParScanThreadState.inline.hpp @@ -52,15 +52,20 @@ template void G1ParScanThreadState::do_oop_evac(T* p, HeapRegion* from // set, due to (benign) races in the claim mechanism during RSet scanning more // than one thread might claim the same card. So the same card may be // processed multiple times. So redo this check. - if (_g1h->in_cset_fast_test(obj)) { + G1CollectedHeap::in_cset_state_t in_cset_state = _g1h->in_cset_state(obj); + if (in_cset_state == G1CollectedHeap::InCSet) { oop forwardee; if (obj->is_forwarded()) { forwardee = obj->forwardee(); } else { forwardee = copy_to_survivor_space(obj); } - assert(forwardee != NULL, "forwardee should not be NULL"); oopDesc::encode_store_heap_oop(p, forwardee); + } else if (in_cset_state == G1CollectedHeap::IsHumongous) { + _g1h->set_humongous_is_live(obj); + } else { + assert(in_cset_state == G1CollectedHeap::InNeither, + err_msg("In_cset_state must be InNeither here, but is %d", in_cset_state)); } assert(obj != NULL, "Must be"); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RegionToSpaceMapper.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1RegionToSpaceMapper.cpp new file mode 100644 index 00000000000..da0976ca99b --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RegionToSpaceMapper.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2001, 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. + * + */ + +#include "precompiled.hpp" +#include "gc_implementation/g1/g1BiasedArray.hpp" +#include "gc_implementation/g1/g1RegionToSpaceMapper.hpp" +#include "memory/allocation.inline.hpp" +#include "runtime/virtualspace.hpp" +#include "services/memTracker.hpp" +#include "utilities/bitMap.inline.hpp" + +G1RegionToSpaceMapper::G1RegionToSpaceMapper(ReservedSpace rs, + size_t commit_granularity, + size_t region_granularity, + MemoryType type) : + _storage(), + _commit_granularity(commit_granularity), + _region_granularity(region_granularity), + _listener(NULL), + _commit_map() { + guarantee(is_power_of_2(commit_granularity), "must be"); + guarantee(is_power_of_2(region_granularity), "must be"); + _storage.initialize_with_granularity(rs, commit_granularity); + + MemTracker::record_virtual_memory_type((address)rs.base(), type); +} + +// G1RegionToSpaceMapper implementation where the region granularity is larger than +// or the same as the commit granularity. +// Basically, the space corresponding to one region region spans several OS pages. +class G1RegionsLargerThanCommitSizeMapper : public G1RegionToSpaceMapper { + private: + size_t _pages_per_region; + + public: + G1RegionsLargerThanCommitSizeMapper(ReservedSpace rs, + size_t os_commit_granularity, + size_t alloc_granularity, + size_t commit_factor, + MemoryType type) : + G1RegionToSpaceMapper(rs, os_commit_granularity, alloc_granularity, type), + _pages_per_region(alloc_granularity / (os_commit_granularity * commit_factor)) { + + guarantee(alloc_granularity >= os_commit_granularity, "allocation granularity smaller than commit granularity"); + _commit_map.resize(rs.size() * commit_factor / alloc_granularity, /* in_resource_area */ false); + } + + virtual void commit_regions(uintptr_t start_idx, size_t num_regions) { + _storage.commit(start_idx * _pages_per_region, num_regions * _pages_per_region); + _commit_map.set_range(start_idx, start_idx + num_regions); + fire_on_commit(start_idx, num_regions); + } + + virtual void uncommit_regions(uintptr_t start_idx, size_t num_regions) { + _storage.uncommit(start_idx * _pages_per_region, num_regions * _pages_per_region); + _commit_map.clear_range(start_idx, start_idx + num_regions); + } +}; + +// G1RegionToSpaceMapper implementation where the region granularity is smaller +// than the commit granularity. +// Basically, the contents of one OS page span several regions. +class G1RegionsSmallerThanCommitSizeMapper : public G1RegionToSpaceMapper { + private: + class CommitRefcountArray : public G1BiasedMappedArray { + protected: + virtual uint default_value() const { return 0; } + }; + + size_t _regions_per_page; + + CommitRefcountArray _refcounts; + + uintptr_t region_idx_to_page_idx(uint region) const { + return region / _regions_per_page; + } + + public: + G1RegionsSmallerThanCommitSizeMapper(ReservedSpace rs, + size_t os_commit_granularity, + size_t alloc_granularity, + size_t commit_factor, + MemoryType type) : + G1RegionToSpaceMapper(rs, os_commit_granularity, alloc_granularity, type), + _regions_per_page((os_commit_granularity * commit_factor) / alloc_granularity), _refcounts() { + + guarantee((os_commit_granularity * commit_factor) >= alloc_granularity, "allocation granularity smaller than commit granularity"); + _refcounts.initialize((HeapWord*)rs.base(), (HeapWord*)(rs.base() + rs.size()), os_commit_granularity); + _commit_map.resize(rs.size() * commit_factor / alloc_granularity, /* in_resource_area */ false); + } + + virtual void commit_regions(uintptr_t start_idx, size_t num_regions) { + for (uintptr_t i = start_idx; i < start_idx + num_regions; i++) { + assert(!_commit_map.at(i), err_msg("Trying to commit storage at region "INTPTR_FORMAT" that is already committed", i)); + uintptr_t idx = region_idx_to_page_idx(i); + uint old_refcount = _refcounts.get_by_index(idx); + if (old_refcount == 0) { + _storage.commit(idx, 1); + } + _refcounts.set_by_index(idx, old_refcount + 1); + _commit_map.set_bit(i); + fire_on_commit(i, 1); + } + } + + virtual void uncommit_regions(uintptr_t start_idx, size_t num_regions) { + for (uintptr_t i = start_idx; i < start_idx + num_regions; i++) { + assert(_commit_map.at(i), err_msg("Trying to uncommit storage at region "INTPTR_FORMAT" that is not committed", i)); + uintptr_t idx = region_idx_to_page_idx(i); + uint old_refcount = _refcounts.get_by_index(idx); + assert(old_refcount > 0, "must be"); + if (old_refcount == 1) { + _storage.uncommit(idx, 1); + } + _refcounts.set_by_index(idx, old_refcount - 1); + _commit_map.clear_bit(i); + } + } +}; + +void G1RegionToSpaceMapper::fire_on_commit(uint start_idx, size_t num_regions) { + if (_listener != NULL) { + _listener->on_commit(start_idx, num_regions); + } +} + +G1RegionToSpaceMapper* G1RegionToSpaceMapper::create_mapper(ReservedSpace rs, + size_t os_commit_granularity, + size_t region_granularity, + size_t commit_factor, + MemoryType type) { + + if (region_granularity >= (os_commit_granularity * commit_factor)) { + return new G1RegionsLargerThanCommitSizeMapper(rs, os_commit_granularity, region_granularity, commit_factor, type); + } else { + return new G1RegionsSmallerThanCommitSizeMapper(rs, os_commit_granularity, region_granularity, commit_factor, type); + } +} diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RegionToSpaceMapper.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1RegionToSpaceMapper.hpp new file mode 100644 index 00000000000..5f614b7a866 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RegionToSpaceMapper.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1REGIONTOSPACEMAPPER_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_G1REGIONTOSPACEMAPPER_HPP + +#include "gc_implementation/g1/g1PageBasedVirtualSpace.hpp" +#include "memory/allocation.hpp" +#include "utilities/debug.hpp" + +class G1MappingChangedListener VALUE_OBJ_CLASS_SPEC { + public: + // Fired after commit of the memory, i.e. the memory this listener is registered + // for can be accessed. + virtual void on_commit(uint start_idx, size_t num_regions) = 0; +}; + +// Maps region based commit/uncommit requests to the underlying page sized virtual +// space. +class G1RegionToSpaceMapper : public CHeapObj { + private: + G1MappingChangedListener* _listener; + protected: + // Backing storage. + G1PageBasedVirtualSpace _storage; + size_t _commit_granularity; + size_t _region_granularity; + // Mapping management + BitMap _commit_map; + + G1RegionToSpaceMapper(ReservedSpace rs, size_t commit_granularity, size_t region_granularity, MemoryType type); + + void fire_on_commit(uint start_idx, size_t num_regions); + public: + MemRegion reserved() { return _storage.reserved(); } + + void set_mapping_changed_listener(G1MappingChangedListener* listener) { _listener = listener; } + + virtual ~G1RegionToSpaceMapper() { + _commit_map.resize(0, /* in_resource_area */ false); + } + + bool is_committed(uintptr_t idx) const { + return _commit_map.at(idx); + } + + virtual void commit_regions(uintptr_t start_idx, size_t num_regions = 1) = 0; + virtual void uncommit_regions(uintptr_t start_idx, size_t num_regions = 1) = 0; + + // Creates an appropriate G1RegionToSpaceMapper for the given parameters. + // The byte_translation_factor defines how many bytes in a region correspond to + // a single byte in the data structure this mapper is for. + // Eg. in the card table, this value corresponds to the size a single card + // table entry corresponds to. + static G1RegionToSpaceMapper* create_mapper(ReservedSpace rs, + size_t os_commit_granularity, + size_t region_granularity, + size_t byte_translation_factor, + MemoryType type); +}; + +#endif /* SHARE_VM_GC_IMPLEMENTATION_G1_G1REGIONTOSPACEMAPPER_HPP */ diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp index 69b17c56702..f1628f63968 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp @@ -32,7 +32,7 @@ #include "gc_implementation/g1/g1GCPhaseTimes.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" #include "memory/iterator.hpp" #include "oops/oop.inline.hpp" @@ -110,7 +110,7 @@ class ScanRSClosure : public HeapRegionClosure { G1CollectedHeap* _g1h; OopsInHeapRegionClosure* _oc; - CodeBlobToOopClosure* _code_root_cl; + CodeBlobClosure* _code_root_cl; G1BlockOffsetSharedArray* _bot_shared; G1SATBCardTableModRefBS *_ct_bs; @@ -122,7 +122,7 @@ class ScanRSClosure : public HeapRegionClosure { public: ScanRSClosure(OopsInHeapRegionClosure* oc, - CodeBlobToOopClosure* code_root_cl, + CodeBlobClosure* code_root_cl, uint worker_i) : _oc(oc), _code_root_cl(code_root_cl), @@ -242,7 +242,7 @@ public: }; void G1RemSet::scanRS(OopsInHeapRegionClosure* oc, - CodeBlobToOopClosure* code_root_cl, + CodeBlobClosure* code_root_cl, uint worker_i) { double rs_time_start = os::elapsedTime(); HeapRegion *startRegion = _g1->start_cset_region_for_worker(worker_i); @@ -321,7 +321,7 @@ void G1RemSet::cleanupHRRS() { } void G1RemSet::oops_into_collection_set_do(OopsInHeapRegionClosure* oc, - CodeBlobToOopClosure* code_root_cl, + CodeBlobClosure* code_root_cl, uint worker_i) { #if CARD_REPEAT_HISTO ct_freq_update_histo_and_reset(); @@ -349,23 +349,8 @@ void G1RemSet::oops_into_collection_set_do(OopsInHeapRegionClosure* oc, assert((ParallelGCThreads > 0) || worker_i == 0, "invariant"); - // The two flags below were introduced temporarily to serialize - // the updating and scanning of remembered sets. There are some - // race conditions when these two operations are done in parallel - // and they are causing failures. When we resolve said race - // conditions, we'll revert back to parallel remembered set - // updating and scanning. See CRs 6677707 and 6677708. - if (G1UseParallelRSetUpdating || (worker_i == 0)) { - updateRS(&into_cset_dcq, worker_i); - } else { - _g1p->phase_times()->record_update_rs_processed_buffers(worker_i, 0); - _g1p->phase_times()->record_update_rs_time(worker_i, 0.0); - } - if (G1UseParallelRSetScanning || (worker_i == 0)) { - scanRS(oc, code_root_cl, worker_i); - } else { - _g1p->phase_times()->record_scan_rs_time(worker_i, 0.0); - } + updateRS(&into_cset_dcq, worker_i); + scanRS(oc, code_root_cl, worker_i); // We now clear the cached values of _cset_rs_update_cl for this worker _cset_rs_update_cl[worker_i] = NULL; @@ -555,6 +540,12 @@ G1UpdateRSOrPushRefOopClosure(G1CollectedHeap* g1h, bool G1RemSet::refine_card(jbyte* card_ptr, uint worker_i, bool check_for_refs_into_cset) { + assert(_g1->is_in_exact(_ct_bs->addr_for(card_ptr)), + err_msg("Card at "PTR_FORMAT" index "SIZE_FORMAT" representing heap at "PTR_FORMAT" (%u) must be in committed heap", + p2i(card_ptr), + _ct_bs->index_for(_ct_bs->addr_for(card_ptr)), + _ct_bs->addr_for(card_ptr), + _g1->addr_to_region(_ct_bs->addr_for(card_ptr)))); // If the card is no longer dirty, nothing to do. if (*card_ptr != CardTableModRefBS::dirty_card_val()) { diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp index 81e85593509..35279a52e20 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp @@ -96,7 +96,7 @@ public: // the "i" passed to the calling thread's work(i) function. // In the sequential case this param will be ignored. void oops_into_collection_set_do(OopsInHeapRegionClosure* blk, - CodeBlobToOopClosure* code_root_cl, + CodeBlobClosure* code_root_cl, uint worker_i); // Prepare for and cleanup after an oops_into_collection_set_do @@ -108,7 +108,7 @@ public: void cleanup_after_oops_into_collection_set_do(); void scanRS(OopsInHeapRegionClosure* oc, - CodeBlobToOopClosure* code_root_cl, + CodeBlobClosure* code_root_cl, uint worker_i); void updateRS(DirtyCardQueue* into_cset_dcq, uint worker_i); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSetSummary.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSetSummary.cpp index cdc13041a5d..69b1c1f8707 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSetSummary.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSetSummary.cpp @@ -253,6 +253,7 @@ public: size_t occupied_cards = hrrs->occupied(); size_t code_root_mem_sz = hrrs->strong_code_roots_mem_size(); if (code_root_mem_sz > max_code_root_mem_sz()) { + _max_code_root_mem_sz = code_root_mem_sz; _max_code_root_mem_sz_region = r; } size_t code_root_elems = hrrs->strong_code_roots_list_length(); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp index bcabcdd9bfa..2156577ea92 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" #include "gc_implementation/g1/heapRegion.hpp" #include "gc_implementation/g1/satbQueue.hpp" @@ -38,7 +39,6 @@ G1SATBCardTableModRefBS::G1SATBCardTableModRefBS(MemRegion whole_heap, _kind = G1SATBCT; } - void G1SATBCardTableModRefBS::enqueue(oop pre_val) { // Nulls should have been already filtered. assert(pre_val->is_oop(true), "Error"); @@ -125,13 +125,52 @@ void G1SATBCardTableModRefBS::verify_g1_young_region(MemRegion mr) { } #endif +void G1SATBCardTableLoggingModRefBSChangedListener::on_commit(uint start_idx, size_t num_regions) { + MemRegion mr(G1CollectedHeap::heap()->bottom_addr_for_region(start_idx), num_regions * HeapRegion::GrainWords); + _card_table->clear(mr); +} + G1SATBCardTableLoggingModRefBS:: G1SATBCardTableLoggingModRefBS(MemRegion whole_heap, int max_covered_regions) : G1SATBCardTableModRefBS(whole_heap, max_covered_regions), - _dcqs(JavaThread::dirty_card_queue_set()) + _dcqs(JavaThread::dirty_card_queue_set()), + _listener() { _kind = G1SATBCTLogging; + _listener.set_card_table(this); +} + +void G1SATBCardTableLoggingModRefBS::initialize(G1RegionToSpaceMapper* mapper) { + mapper->set_mapping_changed_listener(&_listener); + + _byte_map_size = mapper->reserved().byte_size(); + + _guard_index = cards_required(_whole_heap.word_size()) - 1; + _last_valid_index = _guard_index - 1; + + HeapWord* low_bound = _whole_heap.start(); + HeapWord* high_bound = _whole_heap.end(); + + _cur_covered_regions = 1; + _covered[0] = _whole_heap; + + _byte_map = (jbyte*) mapper->reserved().start(); + byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift); + assert(byte_for(low_bound) == &_byte_map[0], "Checking start of map"); + assert(byte_for(high_bound-1) <= &_byte_map[_last_valid_index], "Checking end of map"); + + if (TraceCardTableModRefBS) { + gclog_or_tty->print_cr("G1SATBCardTableModRefBS::G1SATBCardTableModRefBS: "); + gclog_or_tty->print_cr(" " + " &_byte_map[0]: " INTPTR_FORMAT + " &_byte_map[_last_valid_index]: " INTPTR_FORMAT, + p2i(&_byte_map[0]), + p2i(&_byte_map[_last_valid_index])); + gclog_or_tty->print_cr(" " + " byte_map_base: " INTPTR_FORMAT, + p2i(byte_map_base)); + } } void diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp index 3e612605c02..f02c28c227b 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1SATBCARDTABLEMODREFBS_HPP #define SHARE_VM_GC_IMPLEMENTATION_G1_G1SATBCARDTABLEMODREFBS_HPP +#include "gc_implementation/g1/g1RegionToSpaceMapper.hpp" #include "memory/cardTableModRefBS.hpp" #include "memory/memRegion.hpp" #include "oops/oop.inline.hpp" @@ -33,6 +34,7 @@ #if INCLUDE_ALL_GCS class DirtyCardQueueSet; +class G1SATBCardTableLoggingModRefBS; // This barrier is specialized to use a logging barrier to support // snapshot-at-the-beginning marking. @@ -126,18 +128,40 @@ public: jbyte val = _byte_map[card_index]; return (val & (clean_card_mask_val() | deferred_card_val())) == deferred_card_val(); } +}; +class G1SATBCardTableLoggingModRefBSChangedListener : public G1MappingChangedListener { + private: + G1SATBCardTableLoggingModRefBS* _card_table; + public: + G1SATBCardTableLoggingModRefBSChangedListener() : _card_table(NULL) { } + + void set_card_table(G1SATBCardTableLoggingModRefBS* card_table) { _card_table = card_table; } + + virtual void on_commit(uint start_idx, size_t num_regions); }; // Adds card-table logging to the post-barrier. // Usual invariant: all dirty cards are logged in the DirtyCardQueueSet. class G1SATBCardTableLoggingModRefBS: public G1SATBCardTableModRefBS { + friend class G1SATBCardTableLoggingModRefBSChangedListener; private: + G1SATBCardTableLoggingModRefBSChangedListener _listener; DirtyCardQueueSet& _dcqs; public: + static size_t compute_size(size_t mem_region_size_in_words) { + size_t number_of_slots = (mem_region_size_in_words / card_size_in_words); + return ReservedSpace::allocation_align_size_up(number_of_slots); + } + G1SATBCardTableLoggingModRefBS(MemRegion whole_heap, int max_covered_regions); + virtual void initialize() { } + virtual void initialize(G1RegionToSpaceMapper* mapper); + + virtual void resize_covered_region(MemRegion new_region) { ShouldNotReachHere(); } + bool is_a(BarrierSet::Name bsn) { return bsn == BarrierSet::G1SATBCTLogging || G1SATBCardTableModRefBS::is_a(bsn); @@ -154,8 +178,6 @@ class G1SATBCardTableLoggingModRefBS: public G1SATBCardTableModRefBS { void write_region_work(MemRegion mr) { invalidate(mr); } void write_ref_array_work(MemRegion mr) { invalidate(mr); } - - }; diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp index f50eaa1001e..1913327309c 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp @@ -220,14 +220,6 @@ product(uintx, G1HeapRegionSize, 0, \ "Size of the G1 regions.") \ \ - experimental(bool, G1UseParallelRSetUpdating, true, \ - "Enables the parallelization of remembered set updating " \ - "during evacuation pauses") \ - \ - experimental(bool, G1UseParallelRSetScanning, true, \ - "Enables the parallelization of remembered set scanning " \ - "during evacuation pauses") \ - \ product(uintx, G1ConcRefinementThreads, 0, \ "If non-0 is the number of parallel rem set update threads, " \ "otherwise the value is determined ergonomically.") \ @@ -285,9 +277,12 @@ product(uintx, G1MixedGCCountTarget, 8, \ "The target number of mixed GCs after a marking cycle.") \ \ - experimental(uintx, G1CodeRootsChunkCacheKeepPercent, 10, \ - "The amount of code root chunks that should be kept at most " \ - "as percentage of already allocated.") \ + experimental(bool, G1ReclaimDeadHumongousObjectsAtYoungGC, true, \ + "Try to reclaim dead large objects at every young GC.") \ + \ + experimental(bool, G1TraceReclaimDeadHumongousObjectsAtYoungGC, false, \ + "Print some information about large object liveness " \ + "at every young GC.") \ \ experimental(uintx, G1OldCSetRegionThresholdPercent, 10, \ "An upper bound for the number of old CSet regions expressed " \ diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp index 524f2f315fb..aa6697a742c 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp @@ -29,7 +29,7 @@ #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/heapRegion.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/shared/liveRange.hpp" #include "memory/genOopClosures.inline.hpp" #include "memory/iterator.hpp" @@ -322,46 +322,18 @@ bool HeapRegion::claimHeapRegion(jint claimValue) { return false; } -HeapWord* HeapRegion::next_block_start_careful(HeapWord* addr) { - HeapWord* low = addr; - HeapWord* high = end(); - while (low < high) { - size_t diff = pointer_delta(high, low); - // Must add one below to bias toward the high amount. Otherwise, if - // "high" were at the desired value, and "low" were one less, we - // would not converge on "high". This is not symmetric, because - // we set "high" to a block start, which might be the right one, - // which we don't do for "low". - HeapWord* middle = low + (diff+1)/2; - if (middle == high) return high; - HeapWord* mid_bs = block_start_careful(middle); - if (mid_bs < addr) { - low = middle; - } else { - high = mid_bs; - } - } - assert(low == high && low >= addr, "Didn't work."); - return low; -} - -#ifdef _MSC_VER // the use of 'this' below gets a warning, make it go away -#pragma warning( disable:4355 ) // 'this' : used in base member initializer list -#endif // _MSC_VER - - -HeapRegion::HeapRegion(uint hrs_index, +HeapRegion::HeapRegion(uint hrm_index, G1BlockOffsetSharedArray* sharedOffsetArray, MemRegion mr) : G1OffsetTableContigSpace(sharedOffsetArray, mr), - _hrs_index(hrs_index), + _hrm_index(hrm_index), _humongous_type(NotHumongous), _humongous_start_region(NULL), _in_collection_set(false), _next_in_special_set(NULL), _orig_end(NULL), _claimed(InitialClaimValue), _evacuation_failed(false), _prev_marked_bytes(0), _next_marked_bytes(0), _gc_efficiency(0.0), _young_type(NotYoung), _next_young_region(NULL), - _next_dirty_cards_region(NULL), _next(NULL), _prev(NULL), _pending_removal(false), + _next_dirty_cards_region(NULL), _next(NULL), _prev(NULL), #ifdef ASSERT _containing_set(NULL), #endif // ASSERT @@ -370,14 +342,20 @@ HeapRegion::HeapRegion(uint hrs_index, _predicted_bytes_to_copy(0) { _rem_set = new HeapRegionRemSet(sharedOffsetArray, this); + assert(HeapRegionRemSet::num_par_rem_sets() > 0, "Invariant."); + + initialize(mr); +} + +void HeapRegion::initialize(MemRegion mr, bool clear_space, bool mangle_space) { + assert(_rem_set->is_empty(), "Remembered set must be empty"); + + G1OffsetTableContigSpace::initialize(mr, clear_space, mangle_space); + _orig_end = mr.end(); - // Note that initialize() will set the start of the unmarked area of the - // region. hr_clear(false /*par*/, false /*clear_space*/); set_top(bottom()); record_top_and_timestamp(); - - assert(HeapRegionRemSet::num_par_rem_sets() > 0, "Invariant."); } CompactibleSpace* HeapRegion::next_compaction_space() const { @@ -562,21 +540,17 @@ void HeapRegion::add_strong_code_root(nmethod* nm) { hrrs->add_strong_code_root(nm); } +void HeapRegion::add_strong_code_root_locked(nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + HeapRegionRemSet* hrrs = rem_set(); + hrrs->add_strong_code_root_locked(nm); +} + void HeapRegion::remove_strong_code_root(nmethod* nm) { HeapRegionRemSet* hrrs = rem_set(); hrrs->remove_strong_code_root(nm); } -void HeapRegion::migrate_strong_code_roots() { - assert(in_collection_set(), "only collection set regions"); - assert(!isHumongous(), - err_msg("humongous region "HR_FORMAT" should not have been added to collection set", - HR_FORMAT_PARAMS(this))); - - HeapRegionRemSet* hrrs = rem_set(); - hrrs->migrate_strong_code_roots(); -} - void HeapRegion::strong_code_roots_do(CodeBlobClosure* blk) const { HeapRegionRemSet* hrrs = rem_set(); hrrs->strong_code_roots_do(blk); @@ -905,7 +879,7 @@ void HeapRegion::verify(VerifyOption vo, } // If it returns false, verify_for_object() will output the - // appropriate messasge. + // appropriate message. if (do_bot_verify && !g1->is_obj_dead(obj, this) && !_offsets.verify_for_object(p, obj_size)) { @@ -1036,8 +1010,7 @@ void G1OffsetTableContigSpace::clear(bool mangle_space) { set_top(bottom()); set_saved_mark_word(bottom()); CompactibleSpace::clear(mangle_space); - _offsets.zero_bottom_entry(); - _offsets.initialize_threshold(); + reset_bot(); } void G1OffsetTableContigSpace::set_bottom(HeapWord* new_bottom) { @@ -1127,9 +1100,11 @@ G1OffsetTableContigSpace(G1BlockOffsetSharedArray* sharedOffsetArray, _gc_time_stamp(0) { _offsets.set_space(this); - // false ==> we'll do the clearing if there's clearing to be done. - CompactibleSpace::initialize(mr, false, SpaceDecorator::Mangle); - _top = bottom(); - _offsets.zero_bottom_entry(); - _offsets.initialize_threshold(); } + +void G1OffsetTableContigSpace::initialize(MemRegion mr, bool clear_space, bool mangle_space) { + CompactibleSpace::initialize(mr, clear_space, mangle_space); + _top = bottom(); + reset_bot(); +} + diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp index 42c9eac5431..a311fd5e92d 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp @@ -54,15 +54,15 @@ class nmethod; #define HR_FORMAT "%u:(%s)["PTR_FORMAT","PTR_FORMAT","PTR_FORMAT"]" #define HR_FORMAT_PARAMS(_hr_) \ - (_hr_)->hrs_index(), \ + (_hr_)->hrm_index(), \ (_hr_)->is_survivor() ? "S" : (_hr_)->is_young() ? "E" : \ (_hr_)->startsHumongous() ? "HS" : \ (_hr_)->continuesHumongous() ? "HC" : \ !(_hr_)->is_empty() ? "O" : "F", \ p2i((_hr_)->bottom()), p2i((_hr_)->top()), p2i((_hr_)->end()) -// sentinel value for hrs_index -#define G1_NULL_HRS_INDEX ((uint) -1) +// sentinel value for hrm_index +#define G1_NO_HRM_INDEX ((uint) -1) // A dirty card to oop closure for heap regions. It // knows how to get the G1 heap and how to use the bitmap @@ -146,6 +146,9 @@ class G1OffsetTableContigSpace: public CompactibleSpace { HeapWord* top() const { return _top; } protected: + // Reset the G1OffsetTableContigSpace. + virtual void initialize(MemRegion mr, bool clear_space, bool mangle_space); + HeapWord** top_addr() { return &_top; } // Allocation helpers (return NULL if full). inline HeapWord* allocate_impl(size_t word_size, HeapWord* end_value); @@ -200,12 +203,7 @@ class G1OffsetTableContigSpace: public CompactibleSpace { virtual void print() const; void reset_bot() { - _offsets.zero_bottom_entry(); - _offsets.initialize_threshold(); - } - - void update_bot_for_object(HeapWord* start, size_t word_size) { - _offsets.alloc_block(start, word_size); + _offsets.reset_bot(); } void print_bot_on(outputStream* out) { @@ -232,7 +230,7 @@ class HeapRegion: public G1OffsetTableContigSpace { protected: // The index of this region in the heap region sequence. - uint _hrs_index; + uint _hrm_index; HumongousType _humongous_type; // For a humongous region, region in which it starts. @@ -264,7 +262,6 @@ class HeapRegion: public G1OffsetTableContigSpace { #ifdef ASSERT HeapRegionSetBase* _containing_set; #endif // ASSERT - bool _pending_removal; // For parallel heapRegion traversal. jint _claimed; @@ -329,10 +326,16 @@ class HeapRegion: public G1OffsetTableContigSpace { size_t _predicted_bytes_to_copy; public: - HeapRegion(uint hrs_index, + HeapRegion(uint hrm_index, G1BlockOffsetSharedArray* sharedOffsetArray, MemRegion mr); + // Initializing the HeapRegion not only resets the data structure, but also + // resets the BOT for that heap region. + // The default values for clear_space means that we will do the clearing if + // there's clearing to be done ourselves. We also always mangle the space. + virtual void initialize(MemRegion mr, bool clear_space = false, bool mangle_space = SpaceDecorator::Mangle); + static int LogOfHRGrainBytes; static int LogOfHRGrainWords; @@ -378,9 +381,9 @@ class HeapRegion: public G1OffsetTableContigSpace { inline HeapWord* par_allocate_no_bot_updates(size_t word_size); inline HeapWord* allocate_no_bot_updates(size_t word_size); - // If this region is a member of a HeapRegionSeq, the index in that + // If this region is a member of a HeapRegionManager, the index in that // sequence, otherwise -1. - uint hrs_index() const { return _hrs_index; } + uint hrm_index() const { return _hrm_index; } // The number of bytes marked live in the region in the last marking phase. size_t marked_bytes() { return _prev_marked_bytes; } @@ -451,7 +454,7 @@ class HeapRegion: public G1OffsetTableContigSpace { // with this HS region. uint last_hc_index() const { assert(startsHumongous(), "don't call this otherwise"); - return hrs_index() + region_num(); + return hrm_index() + region_num(); } // Same as Space::is_in_reserved, but will use the original size of the region. @@ -553,26 +556,6 @@ class HeapRegion: public G1OffsetTableContigSpace { // to provide a dummy version of it. #endif // ASSERT - // If we want to remove regions from a list in bulk we can simply tag - // them with the pending_removal tag and call the - // remove_all_pending() method on the list. - - bool pending_removal() { return _pending_removal; } - - void set_pending_removal(bool pending_removal) { - if (pending_removal) { - assert(!_pending_removal && containing_set() != NULL, - "can only set pending removal to true if it's false and " - "the region belongs to a region set"); - } else { - assert( _pending_removal && containing_set() == NULL, - "can only set pending removal to false if it's true and " - "the region does not belong to a region set"); - } - - _pending_removal = pending_removal; - } - HeapRegion* get_next_young_region() { return _next_young_region; } void set_next_young_region(HeapRegion* hr) { _next_young_region = hr; @@ -583,7 +566,7 @@ class HeapRegion: public G1OffsetTableContigSpace { void set_next_dirty_cards_region(HeapRegion* hr) { _next_dirty_cards_region = hr; } bool is_on_dirty_cards_region_list() const { return get_next_dirty_cards_region() != NULL; } - HeapWord* orig_end() { return _orig_end; } + HeapWord* orig_end() const { return _orig_end; } // Reset HR stuff to default values. void hr_clear(bool par, bool clear_space, bool locked = false); @@ -750,18 +733,6 @@ class HeapRegion: public G1OffsetTableContigSpace { bool filter_young, jbyte* card_ptr); - // A version of block start that is guaranteed to find *some* block - // boundary at or before "p", but does not object iteration, and may - // therefore be used safely when the heap is unparseable. - HeapWord* block_start_careful(const void* p) const { - return _offsets.block_start_careful(p); - } - - // Requires that "addr" is within the region. Returns the start of the - // first ("careful") block that starts at or after "addr", or else the - // "end" of the region if there is no such block. - HeapWord* next_block_start_careful(HeapWord* addr); - size_t recorded_rs_length() const { return _recorded_rs_length; } double predicted_elapsed_time_ms() const { return _predicted_elapsed_time_ms; } size_t predicted_bytes_to_copy() const { return _predicted_bytes_to_copy; } @@ -785,14 +756,9 @@ class HeapRegion: public G1OffsetTableContigSpace { // Routines for managing a list of code roots (attached to the // this region's RSet) that point into this heap region. void add_strong_code_root(nmethod* nm); + void add_strong_code_root_locked(nmethod* nm); void remove_strong_code_root(nmethod* nm); - // During a collection, migrate the successfully evacuated - // strong code roots that referenced into this region to the - // new regions that they now point into. Unsuccessfully - // evacuated code roots are not migrated. - void migrate_strong_code_roots(); - // Applies blk->do_code_blob() to each of the entries in // the strong code roots list for this region void strong_code_roots_do(CodeBlobClosure* blk) const; @@ -826,7 +792,7 @@ class HeapRegion: public G1OffsetTableContigSpace { // HeapRegionClosure is used for iterating over regions. // Terminates the iteration when the "doHeapRegion" method returns "true". class HeapRegionClosure : public StackObj { - friend class HeapRegionSeq; + friend class HeapRegionManager; friend class G1CollectedHeap; bool _complete; diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp index ebe4c067b9d..62ae230f1a3 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp @@ -94,26 +94,37 @@ G1OffsetTableContigSpace::block_start_const(const void* p) const { inline bool HeapRegion::block_is_obj(const HeapWord* p) const { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - return !g1h->is_obj_dead(oop(p), this); + if (ClassUnloadingWithConcurrentMark) { + return !g1h->is_obj_dead(oop(p), this); + } + return p < top(); } inline size_t HeapRegion::block_size(const HeapWord *addr) const { + if (addr == top()) { + return pointer_delta(end(), addr); + } + + if (block_is_obj(addr)) { + return oop(addr)->size(); + } + + assert(ClassUnloadingWithConcurrentMark, + err_msg("All blocks should be objects if G1 Class Unloading isn't used. " + "HR: ["PTR_FORMAT", "PTR_FORMAT", "PTR_FORMAT") " + "addr: " PTR_FORMAT, + p2i(bottom()), p2i(top()), p2i(end()), p2i(addr))); + // Old regions' dead objects may have dead classes // We need to find the next live object in some other // manner than getting the oop size G1CollectedHeap* g1h = G1CollectedHeap::heap(); - if (g1h->is_obj_dead(oop(addr), this)) { - HeapWord* next = g1h->concurrent_mark()->prevMarkBitMap()-> - getNextMarkedWordAddress(addr, prev_top_at_mark_start()); + HeapWord* next = g1h->concurrent_mark()->prevMarkBitMap()-> + getNextMarkedWordAddress(addr, prev_top_at_mark_start()); - assert(next > addr, "must get the next live object"); - - return pointer_delta(next, addr); - } else if (addr == top()) { - return pointer_delta(end(), addr); - } - return oop(addr)->size(); + assert(next > addr, "must get the next live object"); + return pointer_delta(next, addr); } inline HeapWord* HeapRegion::par_allocate_no_bot_updates(size_t word_size) { diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.cpp new file mode 100644 index 00000000000..29f46b31f18 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.cpp @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "gc_implementation/g1/heapRegion.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" +#include "gc_implementation/g1/heapRegionSet.inline.hpp" +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/g1/concurrentG1Refine.hpp" +#include "memory/allocation.hpp" + +void HeapRegionManager::initialize(G1RegionToSpaceMapper* heap_storage, + G1RegionToSpaceMapper* prev_bitmap, + G1RegionToSpaceMapper* next_bitmap, + G1RegionToSpaceMapper* bot, + G1RegionToSpaceMapper* cardtable, + G1RegionToSpaceMapper* card_counts) { + _allocated_heapregions_length = 0; + + _heap_mapper = heap_storage; + + _prev_bitmap_mapper = prev_bitmap; + _next_bitmap_mapper = next_bitmap; + + _bot_mapper = bot; + _cardtable_mapper = cardtable; + + _card_counts_mapper = card_counts; + + MemRegion reserved = heap_storage->reserved(); + _regions.initialize(reserved.start(), reserved.end(), HeapRegion::GrainBytes); + + _available_map.resize(_regions.length(), false); + _available_map.clear(); +} + +bool HeapRegionManager::is_available(uint region) const { + return _available_map.at(region); +} + +#ifdef ASSERT +bool HeapRegionManager::is_free(HeapRegion* hr) const { + return _free_list.contains(hr); +} +#endif + +HeapRegion* HeapRegionManager::new_heap_region(uint hrm_index) { + HeapWord* bottom = G1CollectedHeap::heap()->bottom_addr_for_region(hrm_index); + MemRegion mr(bottom, bottom + HeapRegion::GrainWords); + assert(reserved().contains(mr), "invariant"); + return new HeapRegion(hrm_index, G1CollectedHeap::heap()->bot_shared(), mr); +} + +void HeapRegionManager::commit_regions(uint index, size_t num_regions) { + guarantee(num_regions > 0, "Must commit more than zero regions"); + guarantee(_num_committed + num_regions <= max_length(), "Cannot commit more than the maximum amount of regions"); + + _num_committed += (uint)num_regions; + + _heap_mapper->commit_regions(index, num_regions); + + // Also commit auxiliary data + _prev_bitmap_mapper->commit_regions(index, num_regions); + _next_bitmap_mapper->commit_regions(index, num_regions); + + _bot_mapper->commit_regions(index, num_regions); + _cardtable_mapper->commit_regions(index, num_regions); + + _card_counts_mapper->commit_regions(index, num_regions); +} + +void HeapRegionManager::uncommit_regions(uint start, size_t num_regions) { + guarantee(num_regions >= 1, err_msg("Need to specify at least one region to uncommit, tried to uncommit zero regions at %u", start)); + guarantee(_num_committed >= num_regions, "pre-condition"); + + // Print before uncommitting. + if (G1CollectedHeap::heap()->hr_printer()->is_active()) { + for (uint i = start; i < start + num_regions; i++) { + HeapRegion* hr = at(i); + G1CollectedHeap::heap()->hr_printer()->uncommit(hr->bottom(), hr->end()); + } + } + + _num_committed -= (uint)num_regions; + + _available_map.par_clear_range(start, start + num_regions, BitMap::unknown_range); + _heap_mapper->uncommit_regions(start, num_regions); + + // Also uncommit auxiliary data + _prev_bitmap_mapper->uncommit_regions(start, num_regions); + _next_bitmap_mapper->uncommit_regions(start, num_regions); + + _bot_mapper->uncommit_regions(start, num_regions); + _cardtable_mapper->uncommit_regions(start, num_regions); + + _card_counts_mapper->uncommit_regions(start, num_regions); +} + +void HeapRegionManager::make_regions_available(uint start, uint num_regions) { + guarantee(num_regions > 0, "No point in calling this for zero regions"); + commit_regions(start, num_regions); + for (uint i = start; i < start + num_regions; i++) { + if (_regions.get_by_index(i) == NULL) { + HeapRegion* new_hr = new_heap_region(i); + _regions.set_by_index(i, new_hr); + _allocated_heapregions_length = MAX2(_allocated_heapregions_length, i + 1); + } + } + + _available_map.par_set_range(start, start + num_regions, BitMap::unknown_range); + + for (uint i = start; i < start + num_regions; i++) { + assert(is_available(i), err_msg("Just made region %u available but is apparently not.", i)); + HeapRegion* hr = at(i); + if (G1CollectedHeap::heap()->hr_printer()->is_active()) { + G1CollectedHeap::heap()->hr_printer()->commit(hr->bottom(), hr->end()); + } + HeapWord* bottom = G1CollectedHeap::heap()->bottom_addr_for_region(i); + MemRegion mr(bottom, bottom + HeapRegion::GrainWords); + + hr->initialize(mr); + insert_into_free_list(at(i)); + } +} + +uint HeapRegionManager::expand_by(uint num_regions) { + return expand_at(0, num_regions); +} + +uint HeapRegionManager::expand_at(uint start, uint num_regions) { + if (num_regions == 0) { + return 0; + } + + uint cur = start; + uint idx_last_found = 0; + uint num_last_found = 0; + + uint expanded = 0; + + while (expanded < num_regions && + (num_last_found = find_unavailable_from_idx(cur, &idx_last_found)) > 0) { + uint to_expand = MIN2(num_regions - expanded, num_last_found); + make_regions_available(idx_last_found, to_expand); + expanded += to_expand; + cur = idx_last_found + num_last_found + 1; + } + + verify_optional(); + return expanded; +} + +uint HeapRegionManager::find_contiguous(size_t num, bool empty_only) { + uint found = 0; + size_t length_found = 0; + uint cur = 0; + + while (length_found < num && cur < max_length()) { + HeapRegion* hr = _regions.get_by_index(cur); + if ((!empty_only && !is_available(cur)) || (is_available(cur) && hr != NULL && hr->is_empty())) { + // This region is a potential candidate for allocation into. + length_found++; + } else { + // This region is not a candidate. The next region is the next possible one. + found = cur + 1; + length_found = 0; + } + cur++; + } + + if (length_found == num) { + for (uint i = found; i < (found + num); i++) { + HeapRegion* hr = _regions.get_by_index(i); + // sanity check + guarantee((!empty_only && !is_available(i)) || (is_available(i) && hr != NULL && hr->is_empty()), + err_msg("Found region sequence starting at " UINT32_FORMAT ", length " SIZE_FORMAT + " that is not empty at " UINT32_FORMAT ". Hr is " PTR_FORMAT, found, num, i, p2i(hr))); + } + return found; + } else { + return G1_NO_HRM_INDEX; + } +} + +HeapRegion* HeapRegionManager::next_region_in_heap(const HeapRegion* r) const { + guarantee(r != NULL, "Start region must be a valid region"); + guarantee(is_available(r->hrm_index()), err_msg("Trying to iterate starting from region %u which is not in the heap", r->hrm_index())); + for (uint i = r->hrm_index() + 1; i < _allocated_heapregions_length; i++) { + HeapRegion* hr = _regions.get_by_index(i); + if (is_available(i)) { + return hr; + } + } + return NULL; +} + +void HeapRegionManager::iterate(HeapRegionClosure* blk) const { + uint len = max_length(); + + for (uint i = 0; i < len; i++) { + if (!is_available(i)) { + continue; + } + guarantee(at(i) != NULL, err_msg("Tried to access region %u that has a NULL HeapRegion*", i)); + bool res = blk->doHeapRegion(at(i)); + if (res) { + blk->incomplete(); + return; + } + } +} + +uint HeapRegionManager::find_unavailable_from_idx(uint start_idx, uint* res_idx) const { + guarantee(res_idx != NULL, "checking"); + guarantee(start_idx <= (max_length() + 1), "checking"); + + uint num_regions = 0; + + uint cur = start_idx; + while (cur < max_length() && is_available(cur)) { + cur++; + } + if (cur == max_length()) { + return num_regions; + } + *res_idx = cur; + while (cur < max_length() && !is_available(cur)) { + cur++; + } + num_regions = cur - *res_idx; +#ifdef ASSERT + for (uint i = *res_idx; i < (*res_idx + num_regions); i++) { + assert(!is_available(i), "just checking"); + } + assert(cur == max_length() || num_regions == 0 || is_available(cur), + err_msg("The region at the current position %u must be available or at the end of the heap.", cur)); +#endif + return num_regions; +} + +uint HeapRegionManager::start_region_for_worker(uint worker_i, uint num_workers, uint num_regions) const { + return num_regions * worker_i / num_workers; +} + +void HeapRegionManager::par_iterate(HeapRegionClosure* blk, uint worker_id, uint num_workers, jint claim_value) const { + const uint start_index = start_region_for_worker(worker_id, num_workers, _allocated_heapregions_length); + + // Every worker will actually look at all regions, skipping over regions that + // are currently not committed. + // This also (potentially) iterates over regions newly allocated during GC. This + // is no problem except for some extra work. + for (uint count = 0; count < _allocated_heapregions_length; count++) { + const uint index = (start_index + count) % _allocated_heapregions_length; + assert(0 <= index && index < _allocated_heapregions_length, "sanity"); + // Skip over unavailable regions + if (!is_available(index)) { + continue; + } + HeapRegion* r = _regions.get_by_index(index); + // We'll ignore "continues humongous" regions (we'll process them + // when we come across their corresponding "start humongous" + // region) and regions already claimed. + if (r->claim_value() == claim_value || r->continuesHumongous()) { + continue; + } + // OK, try to claim it + if (!r->claimHeapRegion(claim_value)) { + continue; + } + // Success! + if (r->startsHumongous()) { + // If the region is "starts humongous" we'll iterate over its + // "continues humongous" first; in fact we'll do them + // first. The order is important. In one case, calling the + // closure on the "starts humongous" region might de-allocate + // and clear all its "continues humongous" regions and, as a + // result, we might end up processing them twice. So, we'll do + // them first (note: most closures will ignore them anyway) and + // then we'll do the "starts humongous" region. + for (uint ch_index = index + 1; ch_index < index + r->region_num(); ch_index++) { + HeapRegion* chr = _regions.get_by_index(ch_index); + + assert(chr->continuesHumongous(), "Must be humongous region"); + assert(chr->humongous_start_region() == r, + err_msg("Must work on humongous continuation of the original start region " + PTR_FORMAT ", but is " PTR_FORMAT, p2i(r), p2i(chr))); + assert(chr->claim_value() != claim_value, + "Must not have been claimed yet because claiming of humongous continuation first claims the start region"); + + bool claim_result = chr->claimHeapRegion(claim_value); + // We should always be able to claim it; no one else should + // be trying to claim this region. + guarantee(claim_result, "We should always be able to claim the continuesHumongous part of the humongous object"); + + bool res2 = blk->doHeapRegion(chr); + if (res2) { + return; + } + + // Right now, this holds (i.e., no closure that actually + // does something with "continues humongous" regions + // clears them). We might have to weaken it in the future, + // but let's leave these two asserts here for extra safety. + assert(chr->continuesHumongous(), "should still be the case"); + assert(chr->humongous_start_region() == r, "sanity"); + } + } + + bool res = blk->doHeapRegion(r); + if (res) { + return; + } + } +} + +uint HeapRegionManager::shrink_by(uint num_regions_to_remove) { + assert(length() > 0, "the region sequence should not be empty"); + assert(length() <= _allocated_heapregions_length, "invariant"); + assert(_allocated_heapregions_length > 0, "we should have at least one region committed"); + assert(num_regions_to_remove < length(), "We should never remove all regions"); + + if (num_regions_to_remove == 0) { + return 0; + } + + uint removed = 0; + uint cur = _allocated_heapregions_length - 1; + uint idx_last_found = 0; + uint num_last_found = 0; + + while ((removed < num_regions_to_remove) && + (num_last_found = find_empty_from_idx_reverse(cur, &idx_last_found)) > 0) { + uint to_remove = MIN2(num_regions_to_remove - removed, num_last_found); + + uncommit_regions(idx_last_found + num_last_found - to_remove, to_remove); + + cur -= num_last_found; + removed += to_remove; + } + + verify_optional(); + + return removed; +} + +uint HeapRegionManager::find_empty_from_idx_reverse(uint start_idx, uint* res_idx) const { + guarantee(start_idx < _allocated_heapregions_length, "checking"); + guarantee(res_idx != NULL, "checking"); + + uint num_regions_found = 0; + + jlong cur = start_idx; + while (cur != -1 && !(is_available(cur) && at(cur)->is_empty())) { + cur--; + } + if (cur == -1) { + return num_regions_found; + } + jlong old_cur = cur; + // cur indexes the first empty region + while (cur != -1 && is_available(cur) && at(cur)->is_empty()) { + cur--; + } + *res_idx = cur + 1; + num_regions_found = old_cur - cur; + +#ifdef ASSERT + for (uint i = *res_idx; i < (*res_idx + num_regions_found); i++) { + assert(at(i)->is_empty(), "just checking"); + } +#endif + return num_regions_found; +} + +void HeapRegionManager::verify() { + guarantee(length() <= _allocated_heapregions_length, + err_msg("invariant: _length: %u _allocated_length: %u", + length(), _allocated_heapregions_length)); + guarantee(_allocated_heapregions_length <= max_length(), + err_msg("invariant: _allocated_length: %u _max_length: %u", + _allocated_heapregions_length, max_length())); + + bool prev_committed = true; + uint num_committed = 0; + HeapWord* prev_end = heap_bottom(); + for (uint i = 0; i < _allocated_heapregions_length; i++) { + if (!is_available(i)) { + prev_committed = false; + continue; + } + num_committed++; + HeapRegion* hr = _regions.get_by_index(i); + guarantee(hr != NULL, err_msg("invariant: i: %u", i)); + guarantee(!prev_committed || hr->bottom() == prev_end, + err_msg("invariant i: %u "HR_FORMAT" prev_end: "PTR_FORMAT, + i, HR_FORMAT_PARAMS(hr), p2i(prev_end))); + guarantee(hr->hrm_index() == i, + err_msg("invariant: i: %u hrm_index(): %u", i, hr->hrm_index())); + // Asserts will fire if i is >= _length + HeapWord* addr = hr->bottom(); + guarantee(addr_to_region(addr) == hr, "sanity"); + // We cannot check whether the region is part of a particular set: at the time + // this method may be called, we have only completed allocation of the regions, + // but not put into a region set. + prev_committed = true; + if (hr->startsHumongous()) { + prev_end = hr->orig_end(); + } else { + prev_end = hr->end(); + } + } + for (uint i = _allocated_heapregions_length; i < max_length(); i++) { + guarantee(_regions.get_by_index(i) == NULL, err_msg("invariant i: %u", i)); + } + + guarantee(num_committed == _num_committed, err_msg("Found %u committed regions, but should be %u", num_committed, _num_committed)); + _free_list.verify(); +} + +#ifndef PRODUCT +void HeapRegionManager::verify_optional() { + verify(); +} +#endif // PRODUCT + diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.hpp new file mode 100644 index 00000000000..7f96df4b2b2 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.hpp @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2001, 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. + * + */ + +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_HPP + +#include "gc_implementation/g1/g1BiasedArray.hpp" +#include "gc_implementation/g1/g1RegionToSpaceMapper.hpp" +#include "gc_implementation/g1/heapRegionSet.hpp" + +class HeapRegion; +class HeapRegionClosure; +class FreeRegionList; + +class G1HeapRegionTable : public G1BiasedMappedArray { + protected: + virtual HeapRegion* default_value() const { return NULL; } +}; + +// This class keeps track of the actual heap memory, auxiliary data +// and its metadata (i.e., HeapRegion instances) and the list of free regions. +// +// This allows maximum flexibility for deciding what to commit or uncommit given +// a request from outside. +// +// HeapRegions are kept in the _regions array in address order. A region's +// index in the array corresponds to its index in the heap (i.e., 0 is the +// region at the bottom of the heap, 1 is the one after it, etc.). Two +// regions that are consecutive in the array should also be adjacent in the +// address space (i.e., region(i).end() == region(i+1).bottom(). +// +// We create a HeapRegion when we commit the region's address space +// for the first time. When we uncommit the address space of a +// region we retain the HeapRegion to be able to re-use it in the +// future (in case we recommit it). +// +// We keep track of three lengths: +// +// * _num_committed (returned by length()) is the number of currently +// committed regions. These may not be contiguous. +// * _allocated_heapregions_length (not exposed outside this class) is the +// number of regions+1 for which we have HeapRegions. +// * max_length() returns the maximum number of regions the heap can have. +// + +class HeapRegionManager: public CHeapObj { + friend class VMStructs; + + G1HeapRegionTable _regions; + + G1RegionToSpaceMapper* _heap_mapper; + G1RegionToSpaceMapper* _prev_bitmap_mapper; + G1RegionToSpaceMapper* _next_bitmap_mapper; + G1RegionToSpaceMapper* _bot_mapper; + G1RegionToSpaceMapper* _cardtable_mapper; + G1RegionToSpaceMapper* _card_counts_mapper; + + FreeRegionList _free_list; + + // Each bit in this bitmap indicates that the corresponding region is available + // for allocation. + BitMap _available_map; + + // The number of regions committed in the heap. + uint _num_committed; + + // Internal only. The highest heap region +1 we allocated a HeapRegion instance for. + uint _allocated_heapregions_length; + + HeapWord* heap_bottom() const { return _regions.bottom_address_mapped(); } + HeapWord* heap_end() const {return _regions.end_address_mapped(); } + + void make_regions_available(uint index, uint num_regions = 1); + + // Pass down commit calls to the VirtualSpace. + void commit_regions(uint index, size_t num_regions = 1); + void uncommit_regions(uint index, size_t num_regions = 1); + + // Notify other data structures about change in the heap layout. + void update_committed_space(HeapWord* old_end, HeapWord* new_end); + // Calculate the starting region for each worker during parallel iteration so + // that they do not all start from the same region. + uint start_region_for_worker(uint worker_i, uint num_workers, uint num_regions) const; + + // Find a contiguous set of empty or uncommitted regions of length num and return + // the index of the first region or G1_NO_HRM_INDEX if the search was unsuccessful. + // If only_empty is true, only empty regions are considered. + // Searches from bottom to top of the heap, doing a first-fit. + uint find_contiguous(size_t num, bool only_empty); + // Finds the next sequence of unavailable regions starting from start_idx. Returns the + // length of the sequence found. If this result is zero, no such sequence could be found, + // otherwise res_idx indicates the start index of these regions. + uint find_unavailable_from_idx(uint start_idx, uint* res_idx) const; + // Finds the next sequence of empty regions starting from start_idx, going backwards in + // the heap. Returns the length of the sequence found. If this value is zero, no + // sequence could be found, otherwise res_idx contains the start index of this range. + uint find_empty_from_idx_reverse(uint start_idx, uint* res_idx) const; + // Allocate a new HeapRegion for the given index. + HeapRegion* new_heap_region(uint hrm_index); +#ifdef ASSERT +public: + bool is_free(HeapRegion* hr) const; +#endif + // Returns whether the given region is available for allocation. + bool is_available(uint region) const; + + public: + // Empty constructor, we'll initialize it with the initialize() method. + HeapRegionManager() : _regions(), _heap_mapper(NULL), _num_committed(0), + _next_bitmap_mapper(NULL), _prev_bitmap_mapper(NULL), _bot_mapper(NULL), + _allocated_heapregions_length(0), _available_map(), + _free_list("Free list", new MasterFreeRegionListMtSafeChecker()) + { } + + void initialize(G1RegionToSpaceMapper* heap_storage, + G1RegionToSpaceMapper* prev_bitmap, + G1RegionToSpaceMapper* next_bitmap, + G1RegionToSpaceMapper* bot, + G1RegionToSpaceMapper* cardtable, + G1RegionToSpaceMapper* card_counts); + + // Return the "dummy" region used for G1AllocRegion. This is currently a hardwired + // new HeapRegion that owns HeapRegion at index 0. Since at the moment we commit + // the heap from the lowest address, this region (and its associated data + // structures) are available and we do not need to check further. + HeapRegion* get_dummy_region() { return new_heap_region(0); } + + // Return the HeapRegion at the given index. Assume that the index + // is valid. + inline HeapRegion* at(uint index) const; + + // If addr is within the committed space return its corresponding + // HeapRegion, otherwise return NULL. + inline HeapRegion* addr_to_region(HeapWord* addr) const; + + // Insert the given region into the free region list. + inline void insert_into_free_list(HeapRegion* hr); + + // Insert the given region list into the global free region list. + void insert_list_into_free_list(FreeRegionList* list) { + _free_list.add_ordered(list); + } + + HeapRegion* allocate_free_region(bool is_old) { + HeapRegion* hr = _free_list.remove_region(is_old); + + if (hr != NULL) { + assert(hr->next() == NULL, "Single region should not have next"); + assert(is_available(hr->hrm_index()), "Must be committed"); + } + return hr; + } + + inline void allocate_free_regions_starting_at(uint first, uint num_regions); + + // Remove all regions from the free list. + void remove_all_free_regions() { + _free_list.remove_all(); + } + + // Return the number of committed free regions in the heap. + uint num_free_regions() const { + return _free_list.length(); + } + + size_t total_capacity_bytes() const { + return num_free_regions() * HeapRegion::GrainBytes; + } + + // Return the number of available (uncommitted) regions. + uint available() const { return max_length() - length(); } + + // Return the number of regions that have been committed in the heap. + uint length() const { return _num_committed; } + + // Return the maximum number of regions in the heap. + uint max_length() const { return (uint)_regions.length(); } + + MemRegion reserved() const { return MemRegion(heap_bottom(), heap_end()); } + + // Expand the sequence to reflect that the heap has grown. Either create new + // HeapRegions, or re-use existing ones. Returns the number of regions the + // sequence was expanded by. If a HeapRegion allocation fails, the resulting + // number of regions might be smaller than what's desired. + uint expand_by(uint num_regions); + + // Makes sure that the regions from start to start+num_regions-1 are available + // for allocation. Returns the number of regions that were committed to achieve + // this. + uint expand_at(uint start, uint num_regions); + + // Find a contiguous set of empty regions of length num. Returns the start index of + // that set, or G1_NO_HRM_INDEX. + uint find_contiguous_only_empty(size_t num) { return find_contiguous(num, true); } + // Find a contiguous set of empty or unavailable regions of length num. Returns the + // start index of that set, or G1_NO_HRM_INDEX. + uint find_contiguous_empty_or_unavailable(size_t num) { return find_contiguous(num, false); } + + HeapRegion* next_region_in_heap(const HeapRegion* r) const; + + // Apply blk->doHeapRegion() on all committed regions in address order, + // terminating the iteration early if doHeapRegion() returns true. + void iterate(HeapRegionClosure* blk) const; + + void par_iterate(HeapRegionClosure* blk, uint worker_id, uint no_of_par_workers, jint claim_value) const; + + // Uncommit up to num_regions_to_remove regions that are completely free. + // Return the actual number of uncommitted regions. + uint shrink_by(uint num_regions_to_remove); + + void verify(); + + // Do some sanity checking. + void verify_optional() PRODUCT_RETURN; +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_HPP + diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.inline.hpp similarity index 65% rename from hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.inline.hpp rename to hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.inline.hpp index 4028b7a4a5d..96bc92fcec2 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.inline.hpp @@ -22,29 +22,37 @@ * */ -#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_INLINE_HPP -#define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_INLINE_HPP +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_INLINE_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_INLINE_HPP #include "gc_implementation/g1/heapRegion.hpp" -#include "gc_implementation/g1/heapRegionSeq.hpp" +#include "gc_implementation/g1/heapRegionManager.hpp" +#include "gc_implementation/g1/heapRegionSet.inline.hpp" -inline HeapRegion* HeapRegionSeq::addr_to_region(HeapWord* addr) const { +inline HeapRegion* HeapRegionManager::addr_to_region(HeapWord* addr) const { assert(addr < heap_end(), err_msg("addr: "PTR_FORMAT" end: "PTR_FORMAT, p2i(addr), p2i(heap_end()))); assert(addr >= heap_bottom(), err_msg("addr: "PTR_FORMAT" bottom: "PTR_FORMAT, p2i(addr), p2i(heap_bottom()))); HeapRegion* hr = _regions.get_by_address(addr); - assert(hr != NULL, "invariant"); return hr; } -inline HeapRegion* HeapRegionSeq::at(uint index) const { - assert(index < length(), "pre-condition"); +inline HeapRegion* HeapRegionManager::at(uint index) const { + assert(is_available(index), "pre-condition"); HeapRegion* hr = _regions.get_by_index(index); assert(hr != NULL, "sanity"); - assert(hr->hrs_index() == index, "sanity"); + assert(hr->hrm_index() == index, "sanity"); return hr; } -#endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_INLINE_HPP +inline void HeapRegionManager::insert_into_free_list(HeapRegion* hr) { + _free_list.add_ordered(hr); +} + +inline void HeapRegionManager::allocate_free_regions_starting_at(uint first, uint num_regions) { + _free_list.remove_starting_at(at(first), num_regions); +} + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_INLINE_HPP diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp index df65940100d..419976c11dd 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp @@ -27,7 +27,7 @@ #include "gc_implementation/g1/g1BlockOffsetTable.inline.hpp" #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "memory/allocation.hpp" #include "memory/padded.inline.hpp" #include "memory/space.inline.hpp" @@ -289,7 +289,7 @@ OtherRegionsTable::OtherRegionsTable(HeapRegion* hr, Mutex* m) : } _fine_grain_regions = NEW_C_HEAP_ARRAY3(PerRegionTablePtr, _max_fine_entries, - mtGC, 0, AllocFailStrategy::RETURN_NULL); + mtGC, CURRENT_PC, AllocFailStrategy::RETURN_NULL); if (_fine_grain_regions == NULL) { vm_exit_out_of_memory(sizeof(void*)*_max_fine_entries, OOM_MALLOC_ERROR, @@ -373,17 +373,17 @@ void FromCardCache::initialize(uint n_par_rs, uint max_num_regions) { _max_regions, &_static_mem_size); - for (uint i = 0; i < n_par_rs; i++) { - for (uint j = 0; j < _max_regions; j++) { - set(i, j, InvalidCard); - } - } + invalidate(0, _max_regions); } -void FromCardCache::shrink(uint new_num_regions) { +void FromCardCache::invalidate(uint start_idx, size_t new_num_regions) { + guarantee((size_t)start_idx + new_num_regions <= max_uintx, + err_msg("Trying to invalidate beyond maximum region, from %u size "SIZE_FORMAT, + start_idx, new_num_regions)); for (uint i = 0; i < HeapRegionRemSet::num_par_rem_sets(); i++) { - assert(new_num_regions <= _max_regions, "Must be within max."); - for (uint j = new_num_regions; j < _max_regions; j++) { + uint end_idx = (start_idx + (uint)new_num_regions); + assert(end_idx <= _max_regions, "Must be within max."); + for (uint j = start_idx; j < end_idx; j++) { set(i, j, InvalidCard); } } @@ -407,12 +407,12 @@ void FromCardCache::clear(uint region_idx) { } } -void OtherRegionsTable::init_from_card_cache(uint max_regions) { +void OtherRegionsTable::initialize(uint max_regions) { FromCardCache::initialize(HeapRegionRemSet::num_par_rem_sets(), max_regions); } -void OtherRegionsTable::shrink_from_card_cache(uint new_num_regions) { - FromCardCache::shrink(new_num_regions); +void OtherRegionsTable::invalidate(uint start_idx, size_t num_regions) { + FromCardCache::invalidate(start_idx, num_regions); } void OtherRegionsTable::print_from_card_cache() { @@ -420,7 +420,7 @@ void OtherRegionsTable::print_from_card_cache() { } void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, int tid) { - uint cur_hrs_ind = hr()->hrs_index(); + uint cur_hrm_ind = hr()->hrm_index(); if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr("ORT::add_reference_work(" PTR_FORMAT "->" PTR_FORMAT ").", @@ -435,10 +435,10 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, int tid) { if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr("Table for [" PTR_FORMAT "...): card %d (cache = %d)", hr()->bottom(), from_card, - FromCardCache::at((uint)tid, cur_hrs_ind)); + FromCardCache::at((uint)tid, cur_hrm_ind)); } - if (FromCardCache::contains_or_replace((uint)tid, cur_hrs_ind, from_card)) { + if (FromCardCache::contains_or_replace((uint)tid, cur_hrm_ind, from_card)) { if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr(" from-card cache hit."); } @@ -448,10 +448,10 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, int tid) { // Note that this may be a continued H region. HeapRegion* from_hr = _g1h->heap_region_containing_raw(from); - RegionIdx_t from_hrs_ind = (RegionIdx_t) from_hr->hrs_index(); + RegionIdx_t from_hrm_ind = (RegionIdx_t) from_hr->hrm_index(); // If the region is already coarsened, return. - if (_coarse_map.at(from_hrs_ind)) { + if (_coarse_map.at(from_hrm_ind)) { if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr(" coarse map hit."); } @@ -460,7 +460,7 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, int tid) { } // Otherwise find a per-region table to add it to. - size_t ind = from_hrs_ind & _mod_max_fine_entries_mask; + size_t ind = from_hrm_ind & _mod_max_fine_entries_mask; PerRegionTable* prt = find_region_table(ind, from_hr); if (prt == NULL) { MutexLockerEx x(_m, Mutex::_no_safepoint_check_flag); @@ -475,7 +475,7 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, int tid) { assert(0 <= card_index && (size_t)card_index < HeapRegion::CardsPerRegion, "Must be in range."); if (G1HRRSUseSparseTable && - _sparse_table.add_card(from_hrs_ind, card_index)) { + _sparse_table.add_card(from_hrm_ind, card_index)) { if (G1RecordHRRSOops) { HeapRegionRemSet::record(hr(), from); if (G1TraceHeapRegionRememberedSet) { @@ -495,7 +495,7 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, int tid) { if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr(" [tid %d] sparse table entry " "overflow(f: %d, t: %u)", - tid, from_hrs_ind, cur_hrs_ind); + tid, from_hrm_ind, cur_hrm_ind); } } @@ -516,7 +516,7 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, int tid) { if (G1HRRSUseSparseTable) { // Transfer from sparse to fine-grain. - SparsePRTEntry *sprt_entry = _sparse_table.get_entry(from_hrs_ind); + SparsePRTEntry *sprt_entry = _sparse_table.get_entry(from_hrm_ind); assert(sprt_entry != NULL, "There should have been an entry"); for (int i = 0; i < SparsePRTEntry::cards_num(); i++) { CardIdx_t c = sprt_entry->card(i); @@ -525,7 +525,7 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, int tid) { } } // Now we can delete the sparse entry. - bool res = _sparse_table.delete_entry(from_hrs_ind); + bool res = _sparse_table.delete_entry(from_hrm_ind); assert(res, "It should have been there."); } } @@ -607,9 +607,9 @@ PerRegionTable* OtherRegionsTable::delete_region_table() { guarantee(max != NULL, "Since _n_fine_entries > 0"); // Set the corresponding coarse bit. - size_t max_hrs_index = (size_t) max->hr()->hrs_index(); - if (!_coarse_map.at(max_hrs_index)) { - _coarse_map.at_put(max_hrs_index, true); + size_t max_hrm_index = (size_t) max->hr()->hrm_index(); + if (!_coarse_map.at(max_hrm_index)) { + _coarse_map.at_put(max_hrm_index, true); _n_coarse_entries++; if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print("Coarsened entry in region [" PTR_FORMAT "...] " @@ -633,7 +633,7 @@ void OtherRegionsTable::scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm) { // First eliminated garbage regions from the coarse map. if (G1RSScrubVerbose) { - gclog_or_tty->print_cr("Scrubbing region %u:", hr()->hrs_index()); + gclog_or_tty->print_cr("Scrubbing region %u:", hr()->hrm_index()); } assert(_coarse_map.size() == region_bm->size(), "Precondition"); @@ -656,9 +656,9 @@ void OtherRegionsTable::scrub(CardTableModRefBS* ctbs, // If the entire region is dead, eliminate. if (G1RSScrubVerbose) { gclog_or_tty->print_cr(" For other region %u:", - cur->hr()->hrs_index()); + cur->hr()->hrm_index()); } - if (!region_bm->at((size_t) cur->hr()->hrs_index())) { + if (!region_bm->at((size_t) cur->hr()->hrm_index())) { *prev = nxt; cur->set_collision_list_next(NULL); _n_fine_entries--; @@ -695,6 +695,9 @@ void OtherRegionsTable::scrub(CardTableModRefBS* ctbs, clear_fcc(); } +bool OtherRegionsTable::is_empty() const { + return occ_sparse() == 0 && occ_coarse() == 0 && _first_all_fine_prts == NULL; +} size_t OtherRegionsTable::occupied() const { size_t sum = occ_fine(); @@ -749,7 +752,7 @@ size_t OtherRegionsTable::fl_mem_size() { } void OtherRegionsTable::clear_fcc() { - FromCardCache::clear(hr()->hrs_index()); + FromCardCache::clear(hr()->hrm_index()); } void OtherRegionsTable::clear() { @@ -800,7 +803,7 @@ bool OtherRegionsTable::contains_reference(OopOrNarrowOopStar from) const { bool OtherRegionsTable::contains_reference_locked(OopOrNarrowOopStar from) const { HeapRegion* hr = _g1h->heap_region_containing_raw(from); - RegionIdx_t hr_ind = (RegionIdx_t) hr->hrs_index(); + RegionIdx_t hr_ind = (RegionIdx_t) hr->hrm_index(); // Is this region in the coarse map? if (_coarse_map.at(hr_ind)) return true; @@ -837,8 +840,8 @@ uint HeapRegionRemSet::num_par_rem_sets() { HeapRegionRemSet::HeapRegionRemSet(G1BlockOffsetSharedArray* bosa, HeapRegion* hr) : _bosa(bosa), - _m(Mutex::leaf, FormatBuffer<128>("HeapRegionRemSet lock #%u", hr->hrs_index()), true), - _code_roots(), _other_regions(hr, &_m) { + _m(Mutex::leaf, FormatBuffer<128>("HeapRegionRemSet lock #%u", hr->hrm_index()), true), + _code_roots(), _other_regions(hr, &_m), _iter_state(Unclaimed), _iter_claimed(0) { reset_for_par_iteration(); } @@ -923,8 +926,24 @@ void HeapRegionRemSet::scrub(CardTableModRefBS* ctbs, } // Code roots support +// +// The code root set is protected by two separate locking schemes +// When at safepoint the per-hrrs lock must be held during modifications +// except when doing a full gc. +// When not at safepoint the CodeCache_lock must be held during modifications. +// When concurrent readers access the contains() function +// (during the evacuation phase) no removals are allowed. void HeapRegionRemSet::add_strong_code_root(nmethod* nm) { + assert(nm != NULL, "sanity"); + // Optimistic unlocked contains-check + if (!_code_roots.contains(nm)) { + MutexLockerEx ml(&_m, Mutex::_no_safepoint_check_flag); + add_strong_code_root_locked(nm); + } +} + +void HeapRegionRemSet::add_strong_code_root_locked(nmethod* nm) { assert(nm != NULL, "sanity"); _code_roots.add(nm); } @@ -933,98 +952,21 @@ void HeapRegionRemSet::remove_strong_code_root(nmethod* nm) { assert(nm != NULL, "sanity"); assert_locked_or_safepoint(CodeCache_lock); - _code_roots.remove_lock_free(nm); + MutexLockerEx ml(CodeCache_lock->owned_by_self() ? NULL : &_m, Mutex::_no_safepoint_check_flag); + _code_roots.remove(nm); // Check that there were no duplicates guarantee(!_code_roots.contains(nm), "duplicate entry found"); } -class NMethodMigrationOopClosure : public OopClosure { - G1CollectedHeap* _g1h; - HeapRegion* _from; - nmethod* _nm; - - uint _num_self_forwarded; - - template void do_oop_work(T* p) { - T heap_oop = oopDesc::load_heap_oop(p); - if (!oopDesc::is_null(heap_oop)) { - oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); - if (_from->is_in(obj)) { - // Reference still points into the source region. - // Since roots are immediately evacuated this means that - // we must have self forwarded the object - assert(obj->is_forwarded(), - err_msg("code roots should be immediately evacuated. " - "Ref: "PTR_FORMAT", " - "Obj: "PTR_FORMAT", " - "Region: "HR_FORMAT, - p, (void*) obj, HR_FORMAT_PARAMS(_from))); - assert(obj->forwardee() == obj, - err_msg("not self forwarded? obj = "PTR_FORMAT, (void*)obj)); - - // The object has been self forwarded. - // Note, if we're during an initial mark pause, there is - // no need to explicitly mark object. It will be marked - // during the regular evacuation failure handling code. - _num_self_forwarded++; - } else { - // The reference points into a promotion or to-space region - HeapRegion* to = _g1h->heap_region_containing(obj); - to->rem_set()->add_strong_code_root(_nm); - } - } - } - -public: - NMethodMigrationOopClosure(G1CollectedHeap* g1h, HeapRegion* from, nmethod* nm): - _g1h(g1h), _from(from), _nm(nm), _num_self_forwarded(0) {} - - void do_oop(narrowOop* p) { do_oop_work(p); } - void do_oop(oop* p) { do_oop_work(p); } - - uint retain() { return _num_self_forwarded > 0; } -}; - -void HeapRegionRemSet::migrate_strong_code_roots() { - assert(hr()->in_collection_set(), "only collection set regions"); - assert(!hr()->isHumongous(), - err_msg("humongous region "HR_FORMAT" should not have been added to the collection set", - HR_FORMAT_PARAMS(hr()))); - - ResourceMark rm; - - // List of code blobs to retain for this region - GrowableArray to_be_retained(10); - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - - while (!_code_roots.is_empty()) { - nmethod *nm = _code_roots.pop(); - if (nm != NULL) { - NMethodMigrationOopClosure oop_cl(g1h, hr(), nm); - nm->oops_do(&oop_cl); - if (oop_cl.retain()) { - to_be_retained.push(nm); - } - } - } - - // Now push any code roots we need to retain - assert(to_be_retained.is_empty() || hr()->evacuation_failed(), - "Retained nmethod list must be empty or " - "evacuation of this region failed"); - - while (to_be_retained.is_nonempty()) { - nmethod* nm = to_be_retained.pop(); - assert(nm != NULL, "sanity"); - add_strong_code_root(nm); - } -} - void HeapRegionRemSet::strong_code_roots_do(CodeBlobClosure* blk) const { _code_roots.nmethods_do(blk); } +void HeapRegionRemSet::clean_strong_code_roots(HeapRegion* hr) { + _code_roots.clean(hr); +} + size_t HeapRegionRemSet::strong_code_roots_mem_size() { return _code_roots.mem_size(); } diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp index 64a02ead2ea..687f1fb0a68 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp @@ -84,7 +84,7 @@ class FromCardCache : public AllStatic { static void initialize(uint n_par_rs, uint max_num_regions); - static void shrink(uint new_num_regions); + static void invalidate(uint start_idx, size_t num_regions); static void print(outputStream* out = gclog_or_tty) PRODUCT_RETURN; @@ -185,6 +185,9 @@ public: // objects. void scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm); + // Returns whether this remembered set (and all sub-sets) contain no entries. + bool is_empty() const; + size_t occupied() const; size_t occ_fine() const; size_t occ_coarse() const; @@ -210,11 +213,11 @@ public: // Declare the heap size (in # of regions) to the OtherRegionsTable. // (Uses it to initialize from_card_cache). - static void init_from_card_cache(uint max_regions); + static void initialize(uint max_regions); - // Declares that only regions i s.t. 0 <= i < new_n_regs are in use. - // Make sure any entries for higher regions are invalid. - static void shrink_from_card_cache(uint new_num_regions); + // Declares that regions between start_idx <= i < start_idx + num_regions are + // not in use. Make sure that any entries for these regions are invalid. + static void invalidate(uint start_idx, size_t num_regions); static void print_from_card_cache(); }; @@ -269,6 +272,10 @@ public: return _other_regions.hr(); } + bool is_empty() const { + return (strong_code_roots_list_length() == 0) && _other_regions.is_empty(); + } + size_t occupied() { MutexLockerEx x(&_m, Mutex::_no_safepoint_check_flag); return occupied_locked(); @@ -342,13 +349,13 @@ public: // Returns the memory occupancy of all static data structures associated // with remembered sets. static size_t static_mem_size() { - return OtherRegionsTable::static_mem_size() + G1CodeRootSet::free_chunks_static_mem_size(); + return OtherRegionsTable::static_mem_size() + G1CodeRootSet::static_mem_size(); } // Returns the memory occupancy of all free_list data structures associated // with remembered sets. static size_t fl_mem_size() { - return OtherRegionsTable::fl_mem_size() + G1CodeRootSet::free_chunks_mem_size(); + return OtherRegionsTable::fl_mem_size(); } bool contains_reference(OopOrNarrowOopStar from) const { @@ -358,20 +365,17 @@ public: // Routines for managing the list of code roots that point into // the heap region that owns this RSet. void add_strong_code_root(nmethod* nm); + void add_strong_code_root_locked(nmethod* nm); void remove_strong_code_root(nmethod* nm); - // During a collection, migrate the successfully evacuated strong - // code roots that referenced into the region that owns this RSet - // to the RSets of the new regions that they now point into. - // Unsuccessfully evacuated code roots are not migrated. - void migrate_strong_code_roots(); - // Applies blk->do_code_blob() to each of the entries in // the strong code roots list void strong_code_roots_do(CodeBlobClosure* blk) const; + void clean_strong_code_roots(HeapRegion* hr); + // Returns the number of elements in the strong code roots list - size_t strong_code_roots_list_length() { + size_t strong_code_roots_list_length() const { return _code_roots.length(); } @@ -393,12 +397,11 @@ public: // Declare the heap size (in # of regions) to the HeapRegionRemSet(s). // (Uses it to initialize from_card_cache). static void init_heap(uint max_regions) { - OtherRegionsTable::init_from_card_cache(max_regions); + OtherRegionsTable::initialize(max_regions); } - // Declares that only regions i s.t. 0 <= i < new_n_regs are in use. - static void shrink_heap(uint new_n_regs) { - OtherRegionsTable::shrink_from_card_cache(new_n_regs); + static void invalidate(uint start_idx, uint num_regions) { + OtherRegionsTable::invalidate(start_idx, num_regions); } #ifndef PRODUCT diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp deleted file mode 100644 index 04f48e60550..00000000000 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "gc_implementation/g1/heapRegion.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" -#include "gc_implementation/g1/heapRegionSet.hpp" -#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" -#include "memory/allocation.hpp" - -// Private - -uint HeapRegionSeq::find_contiguous_from(uint from, uint num) { - uint len = length(); - assert(num > 1, "use this only for sequences of length 2 or greater"); - assert(from <= len, - err_msg("from: %u should be valid and <= than %u", from, len)); - - uint curr = from; - uint first = G1_NULL_HRS_INDEX; - uint num_so_far = 0; - while (curr < len && num_so_far < num) { - if (at(curr)->is_empty()) { - if (first == G1_NULL_HRS_INDEX) { - first = curr; - num_so_far = 1; - } else { - num_so_far += 1; - } - } else { - first = G1_NULL_HRS_INDEX; - num_so_far = 0; - } - curr += 1; - } - assert(num_so_far <= num, "post-condition"); - if (num_so_far == num) { - // we found enough space for the humongous object - assert(from <= first && first < len, "post-condition"); - assert(first < curr && (curr - first) == num, "post-condition"); - for (uint i = first; i < first + num; ++i) { - assert(at(i)->is_empty(), "post-condition"); - } - return first; - } else { - // we failed to find enough space for the humongous object - return G1_NULL_HRS_INDEX; - } -} - -// Public - -void HeapRegionSeq::initialize(HeapWord* bottom, HeapWord* end) { - assert((uintptr_t) bottom % HeapRegion::GrainBytes == 0, - "bottom should be heap region aligned"); - assert((uintptr_t) end % HeapRegion::GrainBytes == 0, - "end should be heap region aligned"); - - _next_search_index = 0; - _allocated_length = 0; - - _regions.initialize(bottom, end, HeapRegion::GrainBytes); -} - -MemRegion HeapRegionSeq::expand_by(HeapWord* old_end, - HeapWord* new_end, - FreeRegionList* list) { - assert(old_end < new_end, "don't call it otherwise"); - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - - HeapWord* next_bottom = old_end; - assert(heap_bottom() <= next_bottom, "invariant"); - while (next_bottom < new_end) { - assert(next_bottom < heap_end(), "invariant"); - uint index = length(); - - assert(index < max_length(), "otherwise we cannot expand further"); - if (index == 0) { - // We have not allocated any regions so far - assert(next_bottom == heap_bottom(), "invariant"); - } else { - // next_bottom should match the end of the last/previous region - assert(next_bottom == at(index - 1)->end(), "invariant"); - } - - if (index == _allocated_length) { - // We have to allocate a new HeapRegion. - HeapRegion* new_hr = g1h->new_heap_region(index, next_bottom); - if (new_hr == NULL) { - // allocation failed, we bail out and return what we have done so far - return MemRegion(old_end, next_bottom); - } - assert(_regions.get_by_index(index) == NULL, "invariant"); - _regions.set_by_index(index, new_hr); - increment_allocated_length(); - } - // Have to increment the length first, otherwise we will get an - // assert failure at(index) below. - increment_length(); - HeapRegion* hr = at(index); - list->add_as_tail(hr); - - next_bottom = hr->end(); - } - assert(next_bottom == new_end, "post-condition"); - return MemRegion(old_end, next_bottom); -} - -uint HeapRegionSeq::free_suffix() { - uint res = 0; - uint index = length(); - while (index > 0) { - index -= 1; - if (!at(index)->is_empty()) { - break; - } - res += 1; - } - return res; -} - -uint HeapRegionSeq::find_contiguous(uint num) { - assert(num > 1, "use this only for sequences of length 2 or greater"); - assert(_next_search_index <= length(), - err_msg("_next_search_index: %u should be valid and <= than %u", - _next_search_index, length())); - - uint start = _next_search_index; - uint res = find_contiguous_from(start, num); - if (res == G1_NULL_HRS_INDEX && start > 0) { - // Try starting from the beginning. If _next_search_index was 0, - // no point in doing this again. - res = find_contiguous_from(0, num); - } - if (res != G1_NULL_HRS_INDEX) { - assert(res < length(), err_msg("res: %u should be valid", res)); - _next_search_index = res + num; - assert(_next_search_index <= length(), - err_msg("_next_search_index: %u should be valid and <= than %u", - _next_search_index, length())); - } - return res; -} - -void HeapRegionSeq::iterate(HeapRegionClosure* blk) const { - iterate_from((HeapRegion*) NULL, blk); -} - -void HeapRegionSeq::iterate_from(HeapRegion* hr, HeapRegionClosure* blk) const { - uint hr_index = 0; - if (hr != NULL) { - hr_index = hr->hrs_index(); - } - - uint len = length(); - for (uint i = hr_index; i < len; i += 1) { - bool res = blk->doHeapRegion(at(i)); - if (res) { - blk->incomplete(); - return; - } - } - for (uint i = 0; i < hr_index; i += 1) { - bool res = blk->doHeapRegion(at(i)); - if (res) { - blk->incomplete(); - return; - } - } -} - -uint HeapRegionSeq::shrink_by(uint num_regions_to_remove) { - // Reset this in case it's currently pointing into the regions that - // we just removed. - _next_search_index = 0; - - assert(length() > 0, "the region sequence should not be empty"); - assert(length() <= _allocated_length, "invariant"); - assert(_allocated_length > 0, "we should have at least one region committed"); - assert(num_regions_to_remove < length(), "We should never remove all regions"); - - uint i = 0; - for (; i < num_regions_to_remove; i++) { - HeapRegion* cur = at(length() - 1); - - if (!cur->is_empty()) { - // We have to give up if the region can not be moved - break; - } - assert(!cur->isHumongous(), "Humongous regions should not be empty"); - - decrement_length(); - } - return i; -} - -#ifndef PRODUCT -void HeapRegionSeq::verify_optional() { - guarantee(length() <= _allocated_length, - err_msg("invariant: _length: %u _allocated_length: %u", - length(), _allocated_length)); - guarantee(_allocated_length <= max_length(), - err_msg("invariant: _allocated_length: %u _max_length: %u", - _allocated_length, max_length())); - guarantee(_next_search_index <= length(), - err_msg("invariant: _next_search_index: %u _length: %u", - _next_search_index, length())); - - HeapWord* prev_end = heap_bottom(); - for (uint i = 0; i < _allocated_length; i += 1) { - HeapRegion* hr = _regions.get_by_index(i); - guarantee(hr != NULL, err_msg("invariant: i: %u", i)); - guarantee(hr->bottom() == prev_end, - err_msg("invariant i: %u "HR_FORMAT" prev_end: "PTR_FORMAT, - i, HR_FORMAT_PARAMS(hr), p2i(prev_end))); - guarantee(hr->hrs_index() == i, - err_msg("invariant: i: %u hrs_index(): %u", i, hr->hrs_index())); - if (i < length()) { - // Asserts will fire if i is >= _length - HeapWord* addr = hr->bottom(); - guarantee(addr_to_region(addr) == hr, "sanity"); - } else { - guarantee(hr->is_empty(), "sanity"); - guarantee(!hr->isHumongous(), "sanity"); - // using assert instead of guarantee here since containing_set() - // is only available in non-product builds. - assert(hr->containing_set() == NULL, "sanity"); - } - if (hr->startsHumongous()) { - prev_end = hr->orig_end(); - } else { - prev_end = hr->end(); - } - } - for (uint i = _allocated_length; i < max_length(); i += 1) { - guarantee(_regions.get_by_index(i) == NULL, err_msg("invariant i: %u", i)); - } -} -#endif // PRODUCT diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.hpp deleted file mode 100644 index 888b2ce5289..00000000000 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.hpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2001, 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. - * - */ - -#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_HPP -#define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_HPP - -#include "gc_implementation/g1/g1BiasedArray.hpp" - -class HeapRegion; -class HeapRegionClosure; -class FreeRegionList; - -class G1HeapRegionTable : public G1BiasedMappedArray { - protected: - virtual HeapRegion* default_value() const { return NULL; } -}; - -// This class keeps track of the region metadata (i.e., HeapRegion -// instances). They are kept in the _regions array in address -// order. A region's index in the array corresponds to its index in -// the heap (i.e., 0 is the region at the bottom of the heap, 1 is -// the one after it, etc.). Two regions that are consecutive in the -// array should also be adjacent in the address space (i.e., -// region(i).end() == region(i+1).bottom(). -// -// We create a HeapRegion when we commit the region's address space -// for the first time. When we uncommit the address space of a -// region we retain the HeapRegion to be able to re-use it in the -// future (in case we recommit it). -// -// We keep track of three lengths: -// -// * _committed_length (returned by length()) is the number of currently -// committed regions. -// * _allocated_length (not exposed outside this class) is the -// number of regions for which we have HeapRegions. -// * max_length() returns the maximum number of regions the heap can have. -// -// and maintain that: _committed_length <= _allocated_length <= max_length() - -class HeapRegionSeq: public CHeapObj { - friend class VMStructs; - - G1HeapRegionTable _regions; - - // The number of regions committed in the heap. - uint _committed_length; - - // A hint for which index to start searching from for humongous - // allocations. - uint _next_search_index; - - // The number of regions for which we have allocated HeapRegions for. - uint _allocated_length; - - // Find a contiguous set of empty regions of length num, starting - // from the given index. - uint find_contiguous_from(uint from, uint num); - - void increment_allocated_length() { - assert(_allocated_length < max_length(), "pre-condition"); - _allocated_length++; - } - - void increment_length() { - assert(length() < max_length(), "pre-condition"); - _committed_length++; - } - - void decrement_length() { - assert(length() > 0, "pre-condition"); - _committed_length--; - } - - HeapWord* heap_bottom() const { return _regions.bottom_address_mapped(); } - HeapWord* heap_end() const {return _regions.end_address_mapped(); } - - public: - // Empty constructor, we'll initialize it with the initialize() method. - HeapRegionSeq() : _regions(), _committed_length(0), _next_search_index(0), _allocated_length(0) { } - - void initialize(HeapWord* bottom, HeapWord* end); - - // Return the HeapRegion at the given index. Assume that the index - // is valid. - inline HeapRegion* at(uint index) const; - - // If addr is within the committed space return its corresponding - // HeapRegion, otherwise return NULL. - inline HeapRegion* addr_to_region(HeapWord* addr) const; - - // Return the number of regions that have been committed in the heap. - uint length() const { return _committed_length; } - - // Return the maximum number of regions in the heap. - uint max_length() const { return (uint)_regions.length(); } - - // Expand the sequence to reflect that the heap has grown from - // old_end to new_end. Either create new HeapRegions, or re-use - // existing ones, and return them in the given list. Returns the - // memory region that covers the newly-created regions. If a - // HeapRegion allocation fails, the result memory region might be - // smaller than the desired one. - MemRegion expand_by(HeapWord* old_end, HeapWord* new_end, - FreeRegionList* list); - - // Return the number of contiguous regions at the end of the sequence - // that are available for allocation. - uint free_suffix(); - - // Find a contiguous set of empty regions of length num and return - // the index of the first region or G1_NULL_HRS_INDEX if the - // search was unsuccessful. - uint find_contiguous(uint num); - - // Apply blk->doHeapRegion() on all committed regions in address order, - // terminating the iteration early if doHeapRegion() returns true. - void iterate(HeapRegionClosure* blk) const; - - // As above, but start the iteration from hr and loop around. If hr - // is NULL, we start from the first region in the heap. - void iterate_from(HeapRegion* hr, HeapRegionClosure* blk) const; - - // Tag as uncommitted as many regions that are completely free as - // possible, up to num_regions_to_remove, from the suffix of the committed - // sequence. Return the actual number of removed regions. - uint shrink_by(uint num_regions_to_remove); - - // Do some sanity checking. - void verify_optional() PRODUCT_RETURN; -}; - -#endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_HPP diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.cpp index fa5ab14287e..7f3e76ad15c 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" #include "gc_implementation/g1/heapRegionSet.inline.hpp" @@ -38,11 +39,11 @@ void HeapRegionSetBase::fill_in_ext_msg(hrs_ext_msg* msg, const char* message) { #ifndef PRODUCT void HeapRegionSetBase::verify_region(HeapRegion* hr) { - assert(hr->containing_set() == this, err_msg("Inconsistent containing set for %u", hr->hrs_index())); - assert(!hr->is_young(), err_msg("Adding young region %u", hr->hrs_index())); // currently we don't use these sets for young regions - assert(hr->isHumongous() == regions_humongous(), err_msg("Wrong humongous state for region %u and set %s", hr->hrs_index(), name())); - assert(hr->is_empty() == regions_empty(), err_msg("Wrong empty state for region %u and set %s", hr->hrs_index(), name())); - assert(hr->rem_set()->verify_ready_for_par_iteration(), err_msg("Wrong iteration state %u", hr->hrs_index())); + assert(hr->containing_set() == this, err_msg("Inconsistent containing set for %u", hr->hrm_index())); + assert(!hr->is_young(), err_msg("Adding young region %u", hr->hrm_index())); // currently we don't use these sets for young regions + assert(hr->isHumongous() == regions_humongous(), err_msg("Wrong humongous state for region %u and set %s", hr->hrm_index(), name())); + assert(hr->is_empty() == regions_empty(), err_msg("Wrong empty state for region %u and set %s", hr->hrm_index(), name())); + assert(hr->rem_set()->verify_ready_for_par_iteration(), err_msg("Wrong iteration state %u", hr->hrm_index())); } #endif @@ -67,7 +68,7 @@ void HeapRegionSetBase::verify_start() { // Do the basic verification first before we do the checks over the regions. HeapRegionSetBase::verify(); - _verify_in_progress = true; + _verify_in_progress = true; } void HeapRegionSetBase::verify_end() { @@ -103,62 +104,7 @@ void FreeRegionList::set_unrealistically_long_length(uint len) { } void FreeRegionList::fill_in_ext_msg_extra(hrs_ext_msg* msg) { - msg->append(" hd: "PTR_FORMAT" tl: "PTR_FORMAT, head(), tail()); -} - -void FreeRegionList::add_as_head_or_tail(FreeRegionList* from_list, bool as_head) { - check_mt_safety(); - from_list->check_mt_safety(); - - verify_optional(); - from_list->verify_optional(); - - if (from_list->is_empty()) { - return; - } - -#ifdef ASSERT - FreeRegionListIterator iter(from_list); - while (iter.more_available()) { - HeapRegion* hr = iter.get_next(); - // In set_containing_set() we check that we either set the value - // from NULL to non-NULL or vice versa to catch bugs. So, we have - // to NULL it first before setting it to the value. - hr->set_containing_set(NULL); - hr->set_containing_set(this); - } -#endif // ASSERT - - if (_head == NULL) { - assert(length() == 0 && _tail == NULL, hrs_ext_msg(this, "invariant")); - _head = from_list->_head; - _tail = from_list->_tail; - } else { - assert(length() > 0 && _tail != NULL, hrs_ext_msg(this, "invariant")); - if (as_head) { - from_list->_tail->set_next(_head); - _head->set_prev(from_list->_tail); - _head = from_list->_head; - } else { - _tail->set_next(from_list->_head); - from_list->_head->set_prev(_tail); - _tail = from_list->_tail; - } - } - - _count.increment(from_list->length(), from_list->total_capacity_bytes()); - from_list->clear(); - - verify_optional(); - from_list->verify_optional(); -} - -void FreeRegionList::add_as_head(FreeRegionList* from_list) { - add_as_head_or_tail(from_list, true /* as_head */); -} - -void FreeRegionList::add_as_tail(FreeRegionList* from_list) { - add_as_head_or_tail(from_list, false /* as_head */); + msg->append(" hd: "PTR_FORMAT" tl: "PTR_FORMAT, _head, _tail); } void FreeRegionList::remove_all() { @@ -191,11 +137,6 @@ void FreeRegionList::add_ordered(FreeRegionList* from_list) { return; } - if (is_empty()) { - add_as_head(from_list); - return; - } - #ifdef ASSERT FreeRegionListIterator iter(from_list); while (iter.more_available()) { @@ -208,37 +149,43 @@ void FreeRegionList::add_ordered(FreeRegionList* from_list) { } #endif // ASSERT - HeapRegion* curr_to = _head; - HeapRegion* curr_from = from_list->_head; - - while (curr_from != NULL) { - while (curr_to != NULL && curr_to->hrs_index() < curr_from->hrs_index()) { - curr_to = curr_to->next(); - } - - if (curr_to == NULL) { - // The rest of the from list should be added as tail - _tail->set_next(curr_from); - curr_from->set_prev(_tail); - curr_from = NULL; - } else { - HeapRegion* next_from = curr_from->next(); - - curr_from->set_next(curr_to); - curr_from->set_prev(curr_to->prev()); - if (curr_to->prev() == NULL) { - _head = curr_from; - } else { - curr_to->prev()->set_next(curr_from); - } - curr_to->set_prev(curr_from); - - curr_from = next_from; - } - } - - if (_tail->hrs_index() < from_list->_tail->hrs_index()) { + if (is_empty()) { + assert(length() == 0 && _tail == NULL, hrs_ext_msg(this, "invariant")); + _head = from_list->_head; _tail = from_list->_tail; + } else { + HeapRegion* curr_to = _head; + HeapRegion* curr_from = from_list->_head; + + while (curr_from != NULL) { + while (curr_to != NULL && curr_to->hrm_index() < curr_from->hrm_index()) { + curr_to = curr_to->next(); + } + + if (curr_to == NULL) { + // The rest of the from list should be added as tail + _tail->set_next(curr_from); + curr_from->set_prev(_tail); + curr_from = NULL; + } else { + HeapRegion* next_from = curr_from->next(); + + curr_from->set_next(curr_to); + curr_from->set_prev(curr_to->prev()); + if (curr_to->prev() == NULL) { + _head = curr_from; + } else { + curr_to->prev()->set_next(curr_from); + } + curr_to->set_prev(curr_from); + + curr_from = next_from; + } + } + + if (_tail->hrm_index() < from_list->_tail->hrm_index()) { + _tail = from_list->_tail; + } } _count.increment(from_list->length(), from_list->total_capacity_bytes()); @@ -248,68 +195,59 @@ void FreeRegionList::add_ordered(FreeRegionList* from_list) { from_list->verify_optional(); } -void FreeRegionList::remove_all_pending(uint target_count) { +void FreeRegionList::remove_starting_at(HeapRegion* first, uint num_regions) { check_mt_safety(); - assert(target_count > 1, hrs_ext_msg(this, "pre-condition")); + assert(num_regions >= 1, hrs_ext_msg(this, "pre-condition")); assert(!is_empty(), hrs_ext_msg(this, "pre-condition")); verify_optional(); DEBUG_ONLY(uint old_length = length();) - HeapRegion* curr = _head; + HeapRegion* curr = first; uint count = 0; - while (curr != NULL) { + while (count < num_regions) { verify_region(curr); HeapRegion* next = curr->next(); HeapRegion* prev = curr->prev(); - if (curr->pending_removal()) { - assert(count < target_count, - hrs_err_msg("[%s] should not come across more regions " - "pending for removal than target_count: %u", - name(), target_count)); + assert(count < num_regions, + hrs_err_msg("[%s] should not come across more regions " + "pending for removal than num_regions: %u", + name(), num_regions)); - if (prev == NULL) { - assert(_head == curr, hrs_ext_msg(this, "invariant")); - _head = next; - } else { - assert(_head != curr, hrs_ext_msg(this, "invariant")); - prev->set_next(next); - } - if (next == NULL) { - assert(_tail == curr, hrs_ext_msg(this, "invariant")); - _tail = prev; - } else { - assert(_tail != curr, hrs_ext_msg(this, "invariant")); - next->set_prev(prev); - } - if (_last = curr) { - _last = NULL; - } - - curr->set_next(NULL); - curr->set_prev(NULL); - remove(curr); - curr->set_pending_removal(false); - - count += 1; - - // If we have come across the target number of regions we can - // just bail out. However, for debugging purposes, we can just - // carry on iterating to make sure there are not more regions - // tagged with pending removal. - DEBUG_ONLY(if (count == target_count) break;) + if (prev == NULL) { + assert(_head == curr, hrs_ext_msg(this, "invariant")); + _head = next; + } else { + assert(_head != curr, hrs_ext_msg(this, "invariant")); + prev->set_next(next); } + if (next == NULL) { + assert(_tail == curr, hrs_ext_msg(this, "invariant")); + _tail = prev; + } else { + assert(_tail != curr, hrs_ext_msg(this, "invariant")); + next->set_prev(prev); + } + if (_last = curr) { + _last = NULL; + } + + curr->set_next(NULL); + curr->set_prev(NULL); + remove(curr); + + count++; curr = next; } - assert(count == target_count, - hrs_err_msg("[%s] count: %u should be == target_count: %u", - name(), count, target_count)); - assert(length() + target_count == old_length, + assert(count == num_regions, + hrs_err_msg("[%s] count: %u should be == num_regions: %u", + name(), count, num_regions)); + assert(length() + num_regions == old_length, hrs_err_msg("[%s] new length should be consistent " - "new length: %u old length: %u target_count: %u", - name(), length(), old_length, target_count)); + "new length: %u old length: %u num_regions: %u", + name(), length(), old_length, num_regions)); verify_optional(); } @@ -348,10 +286,12 @@ void FreeRegionList::print_on(outputStream* out, bool print_contents) { hr->print_on(out); } } + + out->cr(); } void FreeRegionList::verify_list() { - HeapRegion* curr = head(); + HeapRegion* curr = _head; HeapRegion* prev1 = NULL; HeapRegion* prev0 = NULL; uint count = 0; @@ -369,8 +309,8 @@ void FreeRegionList::verify_list() { if (curr->next() != NULL) { guarantee(curr->next()->prev() == curr, "Next or prev pointers messed up"); } - guarantee(curr->hrs_index() == 0 || curr->hrs_index() > last_index, "List should be sorted"); - last_index = curr->hrs_index(); + guarantee(curr->hrm_index() == 0 || curr->hrm_index() > last_index, "List should be sorted"); + last_index = curr->hrm_index(); capacity += curr->capacity(); @@ -379,7 +319,7 @@ void FreeRegionList::verify_list() { curr = curr->next(); } - guarantee(tail() == prev0, err_msg("Expected %s to end with %u but it ended with %u.", name(), tail()->hrs_index(), prev0->hrs_index())); + guarantee(_tail == prev0, err_msg("Expected %s to end with %u but it ended with %u.", name(), _tail->hrm_index(), prev0->hrm_index())); guarantee(_tail == NULL || _tail->next() == NULL, "_tail should not have a next"); guarantee(length() == count, err_msg("%s count mismatch. Expected %u, actual %u.", name(), length(), count)); guarantee(total_capacity_bytes() == capacity, err_msg("%s capacity mismatch. Expected " SIZE_FORMAT ", actual " SIZE_FORMAT, @@ -463,3 +403,41 @@ void HumongousRegionSetMtSafeChecker::check() { "master humongous set MT safety protocol outside a safepoint"); } } + +void FreeRegionList_test() { + FreeRegionList l("test"); + + const uint num_regions_in_test = 5; + // Create a fake heap. It does not need to be valid, as the HeapRegion constructor + // does not access it. + MemRegion heap(NULL, num_regions_in_test * HeapRegion::GrainWords); + // Allocate a fake BOT because the HeapRegion constructor initializes + // the BOT. + size_t bot_size = G1BlockOffsetSharedArray::compute_size(heap.word_size()); + HeapWord* bot_data = NEW_C_HEAP_ARRAY(HeapWord, bot_size, mtGC); + ReservedSpace bot_rs(G1BlockOffsetSharedArray::compute_size(heap.word_size())); + G1RegionToSpaceMapper* bot_storage = + G1RegionToSpaceMapper::create_mapper(bot_rs, + os::vm_page_size(), + HeapRegion::GrainBytes, + G1BlockOffsetSharedArray::N_bytes, + mtGC); + G1BlockOffsetSharedArray oa(heap, bot_storage); + bot_storage->commit_regions(0, num_regions_in_test); + HeapRegion hr0(0, &oa, heap); + HeapRegion hr1(1, &oa, heap); + HeapRegion hr2(2, &oa, heap); + HeapRegion hr3(3, &oa, heap); + HeapRegion hr4(4, &oa, heap); + l.add_ordered(&hr1); + l.add_ordered(&hr0); + l.add_ordered(&hr3); + l.add_ordered(&hr4); + l.add_ordered(&hr2); + assert(l.length() == num_regions_in_test, "wrong length"); + l.verify_list(); + + bot_storage->uncommit_regions(0, num_regions_in_test); + delete bot_storage; + FREE_C_HEAP_ARRAY(HeapWord, bot_data, mtGC); +} diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.hpp index 8502763ccf1..db501993685 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.hpp @@ -162,7 +162,7 @@ public: // diagnosing failures. class hrs_ext_msg : public hrs_err_msg { public: - hrs_ext_msg(HeapRegionSetBase* set, const char* message) : hrs_err_msg("%s","") { + hrs_ext_msg(HeapRegionSetBase* set, const char* message) : hrs_err_msg("%s", "") { set->fill_in_ext_msg(this, message); } }; @@ -192,13 +192,9 @@ public: }; // A set that links all the regions added to it in a doubly-linked -// list. We should try to avoid doing operations that iterate over +// sorted list. We should try to avoid doing operations that iterate over // such lists in performance critical paths. Typically we should -// add / remove one region at a time or concatenate two lists. There are -// two ways to treat your lists, ordered and un-ordered. All un-ordered -// operations are done in constant time. To keep a list ordered only use -// add_ordered() to add elements to the list. If a list is not ordered -// from start, there is no way to sort it later. +// add / remove one region at a time or concatenate two lists. class FreeRegionListIterator; @@ -210,13 +206,13 @@ private: HeapRegion* _tail; // _last is used to keep track of where we added an element the last - // time in ordered lists. It helps to improve performance when adding - // several ordered items in a row. + // time. It helps to improve performance when adding several ordered items in a row. HeapRegion* _last; static uint _unrealistically_long_length; - void add_as_head_or_tail(FreeRegionList* from_list, bool as_head); + inline HeapRegion* remove_from_head_impl(); + inline HeapRegion* remove_from_tail_impl(); protected: virtual void fill_in_ext_msg_extra(hrs_ext_msg* msg); @@ -232,65 +228,33 @@ public: void verify_list(); - HeapRegion* head() { return _head; } - HeapRegion* tail() { return _tail; } +#ifdef ASSERT + bool contains(HeapRegion* hr) const { + return hr->containing_set() == this; + } +#endif static void set_unrealistically_long_length(uint len); // Add hr to the list. The region should not be a member of another set. // Assumes that the list is ordered and will preserve that order. The order - // is determined by hrs_index. + // is determined by hrm_index. inline void add_ordered(HeapRegion* hr); - // It adds hr to the list as the new head. The region should not be - // a member of another set. - inline void add_as_head(HeapRegion* hr); - - // It adds hr to the list as the new tail. The region should not be - // a member of another set. - inline void add_as_tail(HeapRegion* hr); - - // It removes and returns the head of the list. It assumes that the - // list is not empty so it will return a non-NULL value. - inline HeapRegion* remove_head(); - - // Convenience method. - inline HeapRegion* remove_head_or_null(); - - // Removes and returns the last element (_tail) of the list. It assumes - // that the list isn't empty so that it can return a non-NULL value. - inline HeapRegion* remove_tail(); - - // Convenience method - inline HeapRegion* remove_tail_or_null(); - // Removes from head or tail based on the given argument. - inline HeapRegion* remove_region(bool from_head); + HeapRegion* remove_region(bool from_head); // Merge two ordered lists. The result is also ordered. The order is - // determined by hrs_index. + // determined by hrm_index. void add_ordered(FreeRegionList* from_list); - // It moves the regions from from_list to this list and empties - // from_list. The new regions will appear in the same order as they - // were in from_list and be linked in the beginning of this list. - void add_as_head(FreeRegionList* from_list); - - // It moves the regions from from_list to this list and empties - // from_list. The new regions will appear in the same order as they - // were in from_list and be linked in the end of this list. - void add_as_tail(FreeRegionList* from_list); - // It empties the list by removing all regions from it. void remove_all(); - // It removes all regions in the list that are pending for removal - // (i.e., they have been tagged with "pending_removal"). The list - // must not be empty, target_count should reflect the exact number - // of regions that are pending for removal in the list, and - // target_count should be > 1 (currently, we never need to remove a - // single region using this). - void remove_all_pending(uint target_count); + // Remove all (contiguous) regions from first to first + num_regions -1 from + // this list. + // Num_regions must be > 1. + void remove_starting_at(HeapRegion* first, uint num_regions); virtual void verify(); @@ -298,7 +262,7 @@ public: }; // Iterator class that provides a convenient way to iterate over the -// regions of a HeapRegionLinkedList instance. +// regions of a FreeRegionList. class FreeRegionListIterator : public StackObj { private: @@ -324,7 +288,7 @@ public: } FreeRegionListIterator(FreeRegionList* list) : _curr(NULL), _list(list) { - _curr = list->head(); + _curr = list->_head; } }; diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.inline.hpp index 08f74a776ac..f1fce751a2e 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.inline.hpp @@ -30,7 +30,8 @@ inline void HeapRegionSetBase::add(HeapRegion* hr) { check_mt_safety(); assert(hr->containing_set() == NULL, hrs_ext_msg(this, "should not already have a containing set %u")); - assert(hr->next() == NULL && hr->prev() == NULL, hrs_ext_msg(this, "should not already be linked")); + assert(hr->next() == NULL, hrs_ext_msg(this, "should not already be linked")); + assert(hr->prev() == NULL, hrs_ext_msg(this, "should not already be linked")); _count.increment(1u, hr->capacity()); hr->set_containing_set(this); @@ -40,7 +41,8 @@ inline void HeapRegionSetBase::add(HeapRegion* hr) { inline void HeapRegionSetBase::remove(HeapRegion* hr) { check_mt_safety(); verify_region(hr); - assert(hr->next() == NULL && hr->prev() == NULL, hrs_ext_msg(this, "should already be unlinked")); + assert(hr->next() == NULL, hrs_ext_msg(this, "should already be unlinked")); + assert(hr->prev() == NULL, hrs_ext_msg(this, "should already be unlinked")); hr->set_containing_set(NULL); assert(_count.length() > 0, hrs_ext_msg(this, "pre-condition")); @@ -48,8 +50,7 @@ inline void HeapRegionSetBase::remove(HeapRegion* hr) { } inline void FreeRegionList::add_ordered(HeapRegion* hr) { - check_mt_safety(); - assert((length() == 0 && _head == NULL && _tail == NULL) || + assert((length() == 0 && _head == NULL && _tail == NULL && _last == NULL) || (length() > 0 && _head != NULL && _tail != NULL), hrs_ext_msg(this, "invariant")); // add() will verify the region and check mt safety. @@ -59,14 +60,14 @@ inline void FreeRegionList::add_ordered(HeapRegion* hr) { if (_head != NULL) { HeapRegion* curr; - if (_last != NULL && _last->hrs_index() < hr->hrs_index()) { + if (_last != NULL && _last->hrm_index() < hr->hrm_index()) { curr = _last; } else { curr = _head; } // Find first entry with a Region Index larger than entry to insert. - while (curr != NULL && curr->hrs_index() < hr->hrs_index()) { + while (curr != NULL && curr->hrm_index() < hr->hrm_index()) { curr = curr->next(); } @@ -95,89 +96,48 @@ inline void FreeRegionList::add_ordered(HeapRegion* hr) { _last = hr; } -inline void FreeRegionList::add_as_head(HeapRegion* hr) { - assert((length() == 0 && _head == NULL && _tail == NULL) || - (length() > 0 && _head != NULL && _tail != NULL), - hrs_ext_msg(this, "invariant")); - // add() will verify the region and check mt safety. - add(hr); - - // Now link the region. - if (_head != NULL) { - hr->set_next(_head); - _head->set_prev(hr); - } else { - _tail = hr; - } - _head = hr; -} - -inline void FreeRegionList::add_as_tail(HeapRegion* hr) { - check_mt_safety(); - assert((length() == 0 && _head == NULL && _tail == NULL) || - (length() > 0 && _head != NULL && _tail != NULL), - hrs_ext_msg(this, "invariant")); - // add() will verify the region and check mt safety. - add(hr); - - // Now link the region. - if (_tail != NULL) { - _tail->set_next(hr); - hr->set_prev(_tail); - } else { - _head = hr; - } - _tail = hr; -} - -inline HeapRegion* FreeRegionList::remove_head() { - assert(!is_empty(), hrs_ext_msg(this, "the list should not be empty")); - assert(length() > 0 && _head != NULL && _tail != NULL, - hrs_ext_msg(this, "invariant")); - - // We need to unlink it first. - HeapRegion* hr = _head; - _head = hr->next(); +inline HeapRegion* FreeRegionList::remove_from_head_impl() { + HeapRegion* result = _head; + _head = result->next(); if (_head == NULL) { _tail = NULL; } else { _head->set_prev(NULL); } - hr->set_next(NULL); - - if (_last == hr) { - _last = NULL; - } - - // remove() will verify the region and check mt safety. - remove(hr); - return hr; + result->set_next(NULL); + return result; } -inline HeapRegion* FreeRegionList::remove_head_or_null() { - check_mt_safety(); - if (!is_empty()) { - return remove_head(); - } else { - return NULL; - } -} +inline HeapRegion* FreeRegionList::remove_from_tail_impl() { + HeapRegion* result = _tail; -inline HeapRegion* FreeRegionList::remove_tail() { - assert(!is_empty(), hrs_ext_msg(this, "The list should not be empty")); - assert(length() > 0 && _head != NULL && _tail != NULL, - hrs_ext_msg(this, "invariant")); - - // We need to unlink it first - HeapRegion* hr = _tail; - - _tail = hr->prev(); + _tail = result->prev(); if (_tail == NULL) { _head = NULL; } else { _tail->set_next(NULL); } - hr->set_prev(NULL); + result->set_prev(NULL); + return result; +} + +inline HeapRegion* FreeRegionList::remove_region(bool from_head) { + check_mt_safety(); + verify_optional(); + + if (is_empty()) { + return NULL; + } + assert(length() > 0 && _head != NULL && _tail != NULL, + hrs_ext_msg(this, "invariant")); + + HeapRegion* hr; + + if (from_head) { + hr = remove_from_head_impl(); + } else { + hr = remove_from_tail_impl(); + } if (_last == hr) { _last = NULL; @@ -188,22 +148,5 @@ inline HeapRegion* FreeRegionList::remove_tail() { return hr; } -inline HeapRegion* FreeRegionList::remove_tail_or_null() { - check_mt_safety(); - - if (!is_empty()) { - return remove_tail(); - } else { - return NULL; - } -} - -inline HeapRegion* FreeRegionList::remove_region(bool from_head) { - if (from_head) { - return remove_head_or_null(); - } else { - return remove_tail_or_null(); - } -} - #endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSET_INLINE_HPP + diff --git a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp index bdb5899c928..9fb2a116f43 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp @@ -453,7 +453,7 @@ size_t SparsePRT::mem_size() const { bool SparsePRT::add_card(RegionIdx_t region_id, CardIdx_t card_index) { #if SPARSE_PRT_VERBOSE gclog_or_tty->print_cr(" Adding card %d from region %d to region %u sparse.", - card_index, region_id, _hr->hrs_index()); + card_index, region_id, _hr->hrm_index()); #endif if (_next->occupied_entries() * 2 > _next->capacity()) { expand(); @@ -505,7 +505,7 @@ void SparsePRT::expand() { #if SPARSE_PRT_VERBOSE gclog_or_tty->print_cr(" Expanded sparse table for %u to %d.", - _hr->hrs_index(), _next->capacity()); + _hr->hrm_index(), _next->capacity()); #endif for (size_t i = 0; i < last->capacity(); i++) { SparsePRTEntry* e = last->entry((int)i); diff --git a/hotspot/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp b/hotspot/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp index f32bfb0e3a3..64cb0d1cbb9 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp @@ -26,7 +26,7 @@ #define SHARE_VM_GC_IMPLEMENTATION_G1_VMSTRUCTS_G1_HPP #include "gc_implementation/g1/heapRegion.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #define VM_STRUCTS_G1(nonstatic_field, static_field) \ @@ -42,11 +42,10 @@ nonstatic_field(G1HeapRegionTable, _bias, size_t) \ nonstatic_field(G1HeapRegionTable, _shift_by, uint) \ \ - nonstatic_field(HeapRegionSeq, _regions, G1HeapRegionTable) \ - nonstatic_field(HeapRegionSeq, _committed_length, uint) \ + nonstatic_field(HeapRegionManager, _regions, G1HeapRegionTable) \ + nonstatic_field(HeapRegionManager, _num_committed, uint) \ \ - nonstatic_field(G1CollectedHeap, _hrs, HeapRegionSeq) \ - nonstatic_field(G1CollectedHeap, _g1_committed, MemRegion) \ + nonstatic_field(G1CollectedHeap, _hrm, HeapRegionManager) \ nonstatic_field(G1CollectedHeap, _summary_bytes_used, size_t) \ nonstatic_field(G1CollectedHeap, _g1mm, G1MonitoringSupport*) \ nonstatic_field(G1CollectedHeap, _old_set, HeapRegionSetBase) \ @@ -73,7 +72,7 @@ \ declare_type(G1OffsetTableContigSpace, CompactibleSpace) \ declare_type(HeapRegion, G1OffsetTableContigSpace) \ - declare_toplevel_type(HeapRegionSeq) \ + declare_toplevel_type(HeapRegionManager) \ declare_toplevel_type(HeapRegionSetBase) \ declare_toplevel_type(HeapRegionSetCount) \ declare_toplevel_type(G1MonitoringSupport) \ diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp index bddc6f678a3..cc86629281e 100644 --- a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp @@ -28,12 +28,12 @@ #include "gc_implementation/parNew/parOopClosures.inline.hpp" #include "gc_implementation/shared/adaptiveSizePolicy.hpp" #include "gc_implementation/shared/ageTable.hpp" -#include "gc_implementation/shared/parGCAllocBuffer.hpp" +#include "gc_implementation/shared/copyFailedInfo.hpp" #include "gc_implementation/shared/gcHeapSummary.hpp" #include "gc_implementation/shared/gcTimer.hpp" #include "gc_implementation/shared/gcTrace.hpp" #include "gc_implementation/shared/gcTraceTime.hpp" -#include "gc_implementation/shared/copyFailedInfo.hpp" +#include "gc_implementation/shared/parGCAllocBuffer.inline.hpp" #include "gc_implementation/shared/spaceDecorator.hpp" #include "memory/defNewGeneration.inline.hpp" #include "memory/genCollectedHeap.hpp" @@ -252,7 +252,7 @@ HeapWord* ParScanThreadState::alloc_in_to_space_slow(size_t word_sz) { plab->set_word_size(buf_size); plab->set_buf(buf_space); record_survivor_plab(buf_space, buf_size); - obj = plab->allocate(word_sz); + obj = plab->allocate_aligned(word_sz, SurvivorAlignmentInBytes); // Note that we cannot compare buf_size < word_sz below // because of AlignmentReserve (see ParGCAllocBuffer::allocate()). assert(obj != NULL || plab->words_remaining() < word_sz, diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp index bde15f7b8f8..7685353ed1e 100644 --- a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp +++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp @@ -168,7 +168,7 @@ class ParScanThreadState { HeapWord* alloc_in_to_space_slow(size_t word_sz); HeapWord* alloc_in_to_space(size_t word_sz) { - HeapWord* obj = to_space_alloc_buffer()->allocate(word_sz); + HeapWord* obj = to_space_alloc_buffer()->allocate_aligned(word_sz, SurvivorAlignmentInBytes); if (obj != NULL) return obj; else return alloc_in_to_space_slow(word_sz); } diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp index b7d521a4884..5262c6bc55e 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp @@ -78,6 +78,7 @@ jint ParallelScavengeHeap::initialize() { (HeapWord*)(heap_rs.base() + heap_rs.size())); CardTableExtension* const barrier_set = new CardTableExtension(_reserved, 3); + barrier_set->initialize(); _barrier_set = barrier_set; oopDesc::set_bs(_barrier_set); if (_barrier_set == NULL) { diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.cpp index 2bf6a0df94a..8e9b2a594ab 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.cpp @@ -30,6 +30,8 @@ PSGenerationCounters::PSGenerationCounters(const char* name, int ordinal, int spaces, + size_t min_capacity, + size_t max_capacity, PSVirtualSpace* v): _ps_virtual_space(v) { @@ -52,11 +54,11 @@ PSGenerationCounters::PSGenerationCounters(const char* name, cname = PerfDataManager::counter_name(_name_space, "minCapacity"); PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, - _ps_virtual_space->committed_size(), CHECK); + min_capacity, CHECK); cname = PerfDataManager::counter_name(_name_space, "maxCapacity"); PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, - _ps_virtual_space->reserved_size(), CHECK); + max_capacity, CHECK); cname = PerfDataManager::counter_name(_name_space, "capacity"); _current_size = PerfDataManager::create_variable(SUN_GC, cname, diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.hpp index a86677956a8..7028f1b1986 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.hpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.hpp @@ -41,7 +41,7 @@ class PSGenerationCounters: public GenerationCounters { public: PSGenerationCounters(const char* name, int ordinal, int spaces, - PSVirtualSpace* v); + size_t min_capacity, size_t max_capacity, PSVirtualSpace* v); void update_all() { assert(_virtual_space == NULL, "Only one should be in use"); diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp index 8ef5abefdef..a578d75f902 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp @@ -149,8 +149,8 @@ void PSOldGen::initialize_work(const char* perf_data_name, int level) { void PSOldGen::initialize_performance_counters(const char* perf_data_name, int level) { // Generation Counters, generation 'level', 1 subspace - _gen_counters = new PSGenerationCounters(perf_data_name, level, 1, - virtual_space()); + _gen_counters = new PSGenerationCounters(perf_data_name, level, 1, _min_gen_size, + _max_gen_size, virtual_space()); _space_counters = new SpaceCounters(perf_data_name, 0, virtual_space()->reserved_size(), _object_space, _gen_counters); diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.hpp index e87529c734d..05c27a3f3da 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.hpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.hpp @@ -26,6 +26,7 @@ #define SHARE_VM_GC_IMPLEMENTATION_PARALLELSCAVENGE_PSPROMOTIONLAB_HPP #include "gc_implementation/parallelScavenge/objectStartArray.hpp" +#include "gc_interface/collectedHeap.inline.hpp" #include "memory/allocation.hpp" // @@ -94,23 +95,9 @@ class PSYoungPromotionLAB : public PSPromotionLAB { PSYoungPromotionLAB() { } // Not MT safe - HeapWord* allocate(size_t size) { - // Can't assert this, when young fills, we keep the LAB around, but flushed. - // assert(_state != flushed, "Sanity"); - HeapWord* obj = top(); - HeapWord* new_top = obj + size; - // The 'new_top>obj' check is needed to detect overflow of obj+size. - if (new_top > obj && new_top <= end()) { - set_top(new_top); - assert(is_object_aligned((intptr_t)obj) && is_object_aligned((intptr_t)new_top), - "checking alignment"); - return obj; - } + inline HeapWord* allocate(size_t size); - return NULL; - } - - debug_only(virtual bool lab_is_valid(MemRegion lab)); + debug_only(virtual bool lab_is_valid(MemRegion lab);) }; class PSOldPromotionLAB : public PSPromotionLAB { diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.inline.hpp new file mode 100644 index 00000000000..0e5d7e7f7f2 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionLAB.inline.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_IMPLEMENTATION_PARALLELSCAVENGE_PSPROMOTIONLAB_INLINE_HPP +#define SHARE_VM_GC_IMPLEMENTATION_PARALLELSCAVENGE_PSPROMOTIONLAB_INLINE_HPP + +#include "gc_implementation/parallelScavenge/psPromotionLAB.hpp" +#include "gc_interface/collectedHeap.inline.hpp" + +HeapWord* PSYoungPromotionLAB::allocate(size_t size) { + // Can't assert this, when young fills, we keep the LAB around, but flushed. + // assert(_state != flushed, "Sanity"); + HeapWord* obj = CollectedHeap::align_allocation_or_fail(top(), end(), SurvivorAlignmentInBytes); + if (obj == NULL) { + return NULL; + } + + HeapWord* new_top = obj + size; + // The 'new_top>obj' check is needed to detect overflow of obj+size. + if (new_top > obj && new_top <= end()) { + set_top(new_top); + assert(is_ptr_aligned(obj, SurvivorAlignmentInBytes) && is_object_aligned((intptr_t)new_top), + "checking alignment"); + return obj; + } else { + set_top(obj); + return NULL; + } +} + +#endif // SHARE_VM_GC_IMPLEMENTATION_PARALLELSCAVENGE_PSPROMOTIONLAB_INLINE_HPP diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp index 356c2585168..b2de74d4175 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp @@ -27,6 +27,7 @@ #include "gc_implementation/parallelScavenge/psOldGen.hpp" #include "gc_implementation/parallelScavenge/psPromotionManager.hpp" +#include "gc_implementation/parallelScavenge/psPromotionLAB.inline.hpp" #include "gc_implementation/parallelScavenge/psScavenge.hpp" #include "oops/oop.psgc.inline.hpp" diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp index b5902a23381..77c0937eb01 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp @@ -101,7 +101,8 @@ void PSYoungGen::initialize_work() { } // Generation Counters - generation 0, 3 subspaces - _gen_counters = new PSGenerationCounters("new", 0, 3, _virtual_space); + _gen_counters = new PSGenerationCounters("new", 0, 3, _min_gen_size, + _max_gen_size, _virtual_space); // Compute maximum space sizes for performance counters ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp index aede4e5b641..3a8f347bc8d 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp @@ -70,7 +70,7 @@ void VM_ParallelGCSystemGC::doit() { "must be a ParallelScavengeHeap"); GCCauseSetter gccs(heap, _gc_cause); - if (_gc_cause == GCCause::_gc_locker + if (_gc_cause == GCCause::_gc_locker || _gc_cause == GCCause::_wb_young_gc DEBUG_ONLY(|| _gc_cause == GCCause::_scavenge_alot)) { // If (and only if) the scavenge fails, this will invoke a full gc. heap->invoke_scavenge(); diff --git a/hotspot/src/share/vm/gc_implementation/shared/generationCounters.cpp b/hotspot/src/share/vm/gc_implementation/shared/generationCounters.cpp index 6dd07a3a938..333678b225b 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/generationCounters.cpp +++ b/hotspot/src/share/vm/gc_implementation/shared/generationCounters.cpp @@ -62,11 +62,12 @@ void GenerationCounters::initialize(const char* name, int ordinal, int spaces, GenerationCounters::GenerationCounters(const char* name, int ordinal, int spaces, + size_t min_capacity, size_t max_capacity, VirtualSpace* v) : _virtual_space(v) { assert(v != NULL, "don't call this constructor if v == NULL"); initialize(name, ordinal, spaces, - v->committed_size(), v->reserved_size(), v->committed_size()); + min_capacity, max_capacity, v->committed_size()); } GenerationCounters::GenerationCounters(const char* name, diff --git a/hotspot/src/share/vm/gc_implementation/shared/generationCounters.hpp b/hotspot/src/share/vm/gc_implementation/shared/generationCounters.hpp index b716fe8bd11..f2776cee1f3 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/generationCounters.hpp +++ b/hotspot/src/share/vm/gc_implementation/shared/generationCounters.hpp @@ -66,7 +66,7 @@ private: public: GenerationCounters(const char* name, int ordinal, int spaces, - VirtualSpace* v); + size_t min_capacity, size_t max_capacity, VirtualSpace* v); ~GenerationCounters() { if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space, mtGC); diff --git a/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.hpp b/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.hpp index 3677ee26e28..6f1c5eb8eba 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.hpp +++ b/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.hpp @@ -24,7 +24,7 @@ #ifndef SHARE_VM_GC_IMPLEMENTATION_PARNEW_PARGCALLOCBUFFER_HPP #define SHARE_VM_GC_IMPLEMENTATION_PARNEW_PARGCALLOCBUFFER_HPP - +#include "gc_interface/collectedHeap.hpp" #include "memory/allocation.hpp" #include "memory/blockOffsetTable.hpp" #include "memory/threadLocalAllocBuffer.hpp" @@ -84,6 +84,9 @@ public: } } + // Allocate the object aligned to "alignment_in_bytes". + HeapWord* allocate_aligned(size_t word_sz, unsigned short alignment_in_bytes); + // Undo the last allocation in the buffer, which is required to be of the // "obj" of the given "word_sz". void undo_allocation(HeapWord* obj, size_t word_sz) { diff --git a/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.inline.hpp b/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.inline.hpp new file mode 100644 index 00000000000..352ce05a3e5 --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/parGCAllocBuffer.inline.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_IMPLEMENTATION_SHARED_PARGCALLOCBUFFER_INLINE_HPP +#define SHARE_VM_GC_IMPLEMENTATION_SHARED_PARGCALLOCBUFFER_INLINE_HPP + +#include "gc_implementation/shared/parGCAllocBuffer.hpp" +#include "gc_interface/collectedHeap.inline.hpp" + +HeapWord* ParGCAllocBuffer::allocate_aligned(size_t word_sz, unsigned short alignment_in_bytes) { + + HeapWord* res = CollectedHeap::align_allocation_or_fail(_top, _end, alignment_in_bytes); + if (res == NULL) { + return NULL; + } + + // Set _top so that allocate(), which expects _top to be correctly set, + // can be used below. + _top = res; + return allocate(word_sz); +} + +#endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_PARGCALLOCBUFFER_INLINE_HPP diff --git a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp index bb7028ec9c1..b80332b2ab1 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp +++ b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp @@ -195,6 +195,7 @@ void VM_GenCollectFull::doit() { gch->do_full_collection(gch->must_clear_all_soft_refs(), _max_level); } +// Returns true iff concurrent GCs unloads metadata. bool VM_CollectForMetadataAllocation::initiate_concurrent_GC() { #if INCLUDE_ALL_GCS if (UseConcMarkSweepGC && CMSClassUnloadingEnabled) { @@ -202,7 +203,7 @@ bool VM_CollectForMetadataAllocation::initiate_concurrent_GC() { return true; } - if (UseG1GC) { + if (UseG1GC && ClassUnloadingWithConcurrentMark) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); g1h->g1_policy()->set_initiate_conc_mark_if_possible(); diff --git a/hotspot/src/share/vm/gc_interface/collectedHeap.hpp b/hotspot/src/share/vm/gc_interface/collectedHeap.hpp index d3c2fba9276..d4fa7ffc0ec 100644 --- a/hotspot/src/share/vm/gc_interface/collectedHeap.hpp +++ b/hotspot/src/share/vm/gc_interface/collectedHeap.hpp @@ -351,6 +351,12 @@ class CollectedHeap : public CHeapObj { fill_with_object(start, pointer_delta(end, start), zap); } + // Return the address "addr" aligned by "alignment_in_bytes" if such + // an address is below "end". Return NULL otherwise. + inline static HeapWord* align_allocation_or_fail(HeapWord* addr, + HeapWord* end, + unsigned short alignment_in_bytes); + // Some heaps may offer a contiguous region for shared non-blocking // allocation, via inlined code (by exporting the address of the top and // end fields defining the extent of the contiguous allocation region.) diff --git a/hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp b/hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp index 89315a9424f..302d0c7cb3a 100644 --- a/hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp +++ b/hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp @@ -241,6 +241,44 @@ inline void CollectedHeap::oop_iterate_no_header(OopClosure* cl) { oop_iterate(&no_header_cl); } + +inline HeapWord* CollectedHeap::align_allocation_or_fail(HeapWord* addr, + HeapWord* end, + unsigned short alignment_in_bytes) { + if (alignment_in_bytes <= ObjectAlignmentInBytes) { + return addr; + } + + assert(is_ptr_aligned(addr, HeapWordSize), + err_msg("Address " PTR_FORMAT " is not properly aligned.", p2i(addr))); + assert(is_size_aligned(alignment_in_bytes, HeapWordSize), + err_msg("Alignment size %u is incorrect.", alignment_in_bytes)); + + HeapWord* new_addr = (HeapWord*) align_pointer_up(addr, alignment_in_bytes); + size_t padding = pointer_delta(new_addr, addr); + + if (padding == 0) { + return addr; + } + + if (padding < CollectedHeap::min_fill_size()) { + padding += alignment_in_bytes / HeapWordSize; + assert(padding >= CollectedHeap::min_fill_size(), + err_msg("alignment_in_bytes %u is expect to be larger " + "than the minimum object size", alignment_in_bytes)); + new_addr = addr + padding; + } + + assert(new_addr > addr, err_msg("Unexpected arithmetic overflow " + PTR_FORMAT " not greater than " PTR_FORMAT, p2i(new_addr), p2i(addr))); + if(new_addr < end) { + CollectedHeap::fill_with_object(addr, padding); + return new_addr; + } else { + return NULL; + } +} + #ifndef PRODUCT inline bool diff --git a/hotspot/src/share/vm/gc_interface/gcCause.cpp b/hotspot/src/share/vm/gc_interface/gcCause.cpp index 2588621aa6b..879bce11304 100644 --- a/hotspot/src/share/vm/gc_interface/gcCause.cpp +++ b/hotspot/src/share/vm/gc_interface/gcCause.cpp @@ -51,6 +51,9 @@ const char* GCCause::to_string(GCCause::Cause cause) { case _heap_dump: return "Heap Dump Initiated GC"; + case _wb_young_gc: + return "WhiteBox Initiated Young GC"; + case _no_gc: return "No GC"; diff --git a/hotspot/src/share/vm/gc_interface/gcCause.hpp b/hotspot/src/share/vm/gc_interface/gcCause.hpp index 1df82dba700..31a7a9d3827 100644 --- a/hotspot/src/share/vm/gc_interface/gcCause.hpp +++ b/hotspot/src/share/vm/gc_interface/gcCause.hpp @@ -46,6 +46,7 @@ class GCCause : public AllStatic { _gc_locker, _heap_inspection, _heap_dump, + _wb_young_gc, /* implementation independent, but reserved for GC use */ _no_gc, diff --git a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp index 783066e0adb..de54137168e 100644 --- a/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp +++ b/hotspot/src/share/vm/interpreter/abstractInterpreter.hpp @@ -280,9 +280,6 @@ class AbstractInterpreterGenerator: public StackObj { address generate_result_handler_for(BasicType type); address generate_slow_signature_handler(); - // entry point generator - address generate_method_entry(AbstractInterpreter::MethodKind kind); - void bang_stack_shadow_pages(bool native_call); void generate_all(); diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp index 323f67fc1f6..dc2a782ea05 100644 --- a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp +++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp @@ -3402,7 +3402,7 @@ BytecodeInterpreter::print() { tty->print_cr("osr._osr_buf: " INTPTR_FORMAT, (uintptr_t) this->_result._osr._osr_buf); tty->print_cr("osr._osr_entry: " INTPTR_FORMAT, (uintptr_t) this->_result._osr._osr_entry); tty->print_cr("prev_link: " INTPTR_FORMAT, (uintptr_t) this->_prev_link); - tty->print_cr("native_mirror: " INTPTR_FORMAT, (uintptr_t) this->_oop_temp); + tty->print_cr("native_mirror: " INTPTR_FORMAT, (uintptr_t) p2i(this->_oop_temp)); tty->print_cr("stack_base: " INTPTR_FORMAT, (uintptr_t) this->_stack_base); tty->print_cr("stack_limit: " INTPTR_FORMAT, (uintptr_t) this->_stack_limit); tty->print_cr("monitor_base: " INTPTR_FORMAT, (uintptr_t) this->_monitor_base); diff --git a/hotspot/src/share/vm/interpreter/cppInterpreter.cpp b/hotspot/src/share/vm/interpreter/cppInterpreter.cpp index 0007aa8be25..c154a746f4d 100644 --- a/hotspot/src/share/vm/interpreter/cppInterpreter.cpp +++ b/hotspot/src/share/vm/interpreter/cppInterpreter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -108,7 +108,7 @@ void CppInterpreterGenerator::generate_all() { } -#define method_entry(kind) Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind) +#define method_entry(kind) Interpreter::_entry_table[Interpreter::kind] = ((InterpreterGenerator*)this)->generate_method_entry(Interpreter::kind) { CodeletMark cm(_masm, "(kind = frame_manager)"); // all non-native method kinds diff --git a/hotspot/src/share/vm/interpreter/interpreter.cpp b/hotspot/src/share/vm/interpreter/interpreter.cpp index 4807cd08398..00c116234da 100644 --- a/hotspot/src/share/vm/interpreter/interpreter.cpp +++ b/hotspot/src/share/vm/interpreter/interpreter.cpp @@ -29,6 +29,7 @@ #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/bytecodeInterpreter.hpp" #include "interpreter/interpreter.hpp" +#include "interpreter/interpreterGenerator.hpp" #include "interpreter/interpreterRuntime.hpp" #include "interpreter/interp_masm.hpp" #include "interpreter/templateTable.hpp" @@ -261,7 +262,7 @@ AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(methodHandle m) // Special intrinsic method? // Note: This test must come _after_ the test for native methods, // otherwise we will run into problems with JDK 1.2, see also - // AbstractInterpreterGenerator::generate_method_entry() for + // InterpreterGenerator::generate_method_entry() for // for details. switch (m->intrinsic_id()) { case vmIntrinsics::_dsin : return java_lang_math_sin ; @@ -521,3 +522,50 @@ void AbstractInterpreterGenerator::initialize_method_handle_entries() { Interpreter::_entry_table[kind] = Interpreter::_entry_table[Interpreter::abstract]; } } + +// Generate method entries +address InterpreterGenerator::generate_method_entry( + AbstractInterpreter::MethodKind kind) { + // determine code generation flags + bool synchronized = false; + address entry_point = NULL; + + switch (kind) { + case Interpreter::zerolocals : break; + case Interpreter::zerolocals_synchronized: synchronized = true; break; + case Interpreter::native : entry_point = generate_native_entry(false); break; + case Interpreter::native_synchronized : entry_point = generate_native_entry(true); break; + case Interpreter::empty : entry_point = generate_empty_entry(); break; + case Interpreter::accessor : entry_point = generate_accessor_entry(); break; + case Interpreter::abstract : entry_point = generate_abstract_entry(); break; + + case Interpreter::java_lang_math_sin : // fall thru + case Interpreter::java_lang_math_cos : // fall thru + case Interpreter::java_lang_math_tan : // fall thru + case Interpreter::java_lang_math_abs : // fall thru + case Interpreter::java_lang_math_log : // fall thru + case Interpreter::java_lang_math_log10 : // fall thru + case Interpreter::java_lang_math_sqrt : // fall thru + case Interpreter::java_lang_math_pow : // fall thru + case Interpreter::java_lang_math_exp : entry_point = generate_math_entry(kind); break; + case Interpreter::java_lang_ref_reference_get + : entry_point = generate_Reference_get_entry(); break; +#ifndef CC_INTERP + case Interpreter::java_util_zip_CRC32_update + : entry_point = generate_CRC32_update_entry(); break; + case Interpreter::java_util_zip_CRC32_updateBytes + : // fall thru + case Interpreter::java_util_zip_CRC32_updateByteBuffer + : entry_point = generate_CRC32_updateBytes_entry(kind); break; +#endif // CC_INTERP + default: + fatal(err_msg("unexpected method kind: %d", kind)); + break; + } + + if (entry_point) { + return entry_point; + } + + return generate_normal_entry(synchronized); +} diff --git a/hotspot/src/share/vm/interpreter/interpreter.hpp b/hotspot/src/share/vm/interpreter/interpreter.hpp index 2a81daf205d..96c480e4385 100644 --- a/hotspot/src/share/vm/interpreter/interpreter.hpp +++ b/hotspot/src/share/vm/interpreter/interpreter.hpp @@ -55,7 +55,9 @@ class InterpreterCodelet: public Stub { public: // Initialization/finalization void initialize(int size, - CodeStrings& strings) { _size = size; DEBUG_ONLY(_strings.assign(strings);) } + CodeStrings& strings) { _size = size; + DEBUG_ONLY(::new(&_strings) CodeStrings();) + DEBUG_ONLY(_strings.assign(strings);) } void finalize() { ShouldNotCallThis(); } // General info/converters diff --git a/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp b/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp index 7bc43eccb21..5c4242df884 100644 --- a/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp +++ b/hotspot/src/share/vm/interpreter/interpreterGenerator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,9 +37,11 @@ class InterpreterGenerator: public CC_INTERP_ONLY(CppInterpreterGenerator) NOT_CC_INTERP(TemplateInterpreterGenerator) { -public: + public: -InterpreterGenerator(StubQueue* _code); + InterpreterGenerator(StubQueue* _code); + // entry point generator + address generate_method_entry(AbstractInterpreter::MethodKind kind); #ifdef TARGET_ARCH_x86 # include "interpreterGenerator_x86.hpp" diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp index 23d4bddca1b..ee7486041b9 100644 --- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp @@ -987,17 +987,6 @@ ConstantPoolCacheEntry *cp_entry)) int index = cp_entry->field_index(); if ((ik->field_access_flags(index) & JVM_ACC_FIELD_ACCESS_WATCHED) == 0) return; - switch(cp_entry->flag_state()) { - case btos: // fall through - case ctos: // fall through - case stos: // fall through - case itos: // fall through - case ftos: // fall through - case ltos: // fall through - case dtos: // fall through - case atos: break; - default: ShouldNotReachHere(); return; - } bool is_static = (obj == NULL); HandleMark hm(thread); diff --git a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp index 9f28e20f9b7..d42da317345 100644 --- a/hotspot/src/share/vm/interpreter/templateInterpreter.cpp +++ b/hotspot/src/share/vm/interpreter/templateInterpreter.cpp @@ -364,7 +364,7 @@ void TemplateInterpreterGenerator::generate_all() { #define method_entry(kind) \ { CodeletMark cm(_masm, "method entry point (kind = " #kind ")"); \ - Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind); \ + Interpreter::_entry_table[Interpreter::kind] = ((InterpreterGenerator*)this)->generate_method_entry(Interpreter::kind); \ } // all non-native method kinds diff --git a/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp b/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp index ee1b0f5acc5..e3740c47c4e 100644 --- a/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp +++ b/hotspot/src/share/vm/interpreter/templateInterpreterGenerator.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -59,9 +59,6 @@ class TemplateInterpreterGenerator: public AbstractInterpreterGenerator { address generate_safept_entry_for(TosState state, address runtime_entry); void generate_throw_exception(); - // entry point generator -// address generate_method_entry(AbstractInterpreter::MethodKind kind); - // Instruction generation void generate_and_dispatch (Template* t, TosState tos_out = ilgl); void set_vtos_entry_points (Template* t, address& bep, address& cep, address& sep, address& aep, address& iep, address& lep, address& fep, address& dep, address& vep); diff --git a/hotspot/src/share/vm/memory/allocation.cpp b/hotspot/src/share/vm/memory/allocation.cpp index ac9ff11bb65..ec9a8497836 100644 --- a/hotspot/src/share/vm/memory/allocation.cpp +++ b/hotspot/src/share/vm/memory/allocation.cpp @@ -422,26 +422,23 @@ void Chunk::start_chunk_pool_cleaner_task() { } //------------------------------Arena------------------------------------------ -NOT_PRODUCT(volatile jint Arena::_instance_count = 0;) -Arena::Arena(size_t init_size) { +Arena::Arena(MEMFLAGS flag, size_t init_size) : _flags(flag), _size_in_bytes(0) { size_t round_size = (sizeof (char *)) - 1; init_size = (init_size+round_size) & ~round_size; _first = _chunk = new (AllocFailStrategy::EXIT_OOM, init_size) Chunk(init_size); _hwm = _chunk->bottom(); // Save the cached hwm, max _max = _chunk->top(); - _size_in_bytes = 0; + MemTracker::record_new_arena(flag); set_size_in_bytes(init_size); - NOT_PRODUCT(Atomic::inc(&_instance_count);) } -Arena::Arena() { +Arena::Arena(MEMFLAGS flag) : _flags(flag), _size_in_bytes(0) { _first = _chunk = new (AllocFailStrategy::EXIT_OOM, Chunk::init_size) Chunk(Chunk::init_size); _hwm = _chunk->bottom(); // Save the cached hwm, max _max = _chunk->top(); - _size_in_bytes = 0; + MemTracker::record_new_arena(flag); set_size_in_bytes(Chunk::init_size); - NOT_PRODUCT(Atomic::inc(&_instance_count);) } Arena *Arena::move_contents(Arena *copy) { @@ -463,7 +460,7 @@ Arena *Arena::move_contents(Arena *copy) { Arena::~Arena() { destruct_contents(); - NOT_PRODUCT(Atomic::dec(&_instance_count);) + MemTracker::record_arena_free(_flags); } void* Arena::operator new(size_t size) throw() { @@ -479,21 +476,21 @@ void* Arena::operator new (size_t size, const std::nothrow_t& nothrow_constant) // dynamic memory type binding void* Arena::operator new(size_t size, MEMFLAGS flags) throw() { #ifdef ASSERT - void* p = (void*)AllocateHeap(size, flags|otArena, CALLER_PC); + 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|otArena, CALLER_PC); + 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|otArena, CALLER_PC); + 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|otArena, CALLER_PC); + return os::malloc(size, flags, CALLER_PC); #endif } @@ -518,8 +515,9 @@ void Arena::destruct_contents() { // change the size void Arena::set_size_in_bytes(size_t size) { if (_size_in_bytes != size) { + long delta = (long)(size - size_in_bytes()); _size_in_bytes = size; - MemTracker::record_arena_size((address)this, size); + MemTracker::record_arena_size_change(delta, _flags); } } diff --git a/hotspot/src/share/vm/memory/allocation.hpp b/hotspot/src/share/vm/memory/allocation.hpp index fbfff65dad4..a1faa70871a 100644 --- a/hotspot/src/share/vm/memory/allocation.hpp +++ b/hotspot/src/share/vm/memory/allocation.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -133,51 +133,34 @@ class AllocatedObj { /* - * MemoryType bitmap layout: - * | 16 15 14 13 12 11 10 09 | 08 07 06 05 | 04 03 02 01 | - * | memory type | object | reserved | - * | | type | | + * Memory types */ enum MemoryType { // Memory type by sub systems. It occupies lower byte. - mtNone = 0x0000, // undefined - mtClass = 0x0100, // memory class for Java classes - mtThread = 0x0200, // memory for thread objects - mtThreadStack = 0x0300, - mtCode = 0x0400, // memory for generated code - mtGC = 0x0500, // memory for GC - mtCompiler = 0x0600, // memory for compiler - mtInternal = 0x0700, // memory used by VM, but does not belong to + mtJavaHeap = 0x00, // Java heap + mtClass = 0x01, // memory class for Java classes + mtThread = 0x02, // memory for thread objects + mtThreadStack = 0x03, + mtCode = 0x04, // memory for generated code + mtGC = 0x05, // memory for GC + mtCompiler = 0x06, // memory for compiler + mtInternal = 0x07, // memory used by VM, but does not belong to // any of above categories, and not used for // native memory tracking - mtOther = 0x0800, // memory not used by VM - mtSymbol = 0x0900, // symbol - mtNMT = 0x0A00, // memory used by native memory tracking - mtChunk = 0x0B00, // chunk that holds content of arenas - mtJavaHeap = 0x0C00, // Java heap - mtClassShared = 0x0D00, // class data sharing - mtTest = 0x0E00, // Test type for verifying NMT - mtTracing = 0x0F00, // memory used for Tracing - mt_number_of_types = 0x000F, // number of memory types (mtDontTrack + mtOther = 0x08, // memory not used by VM + mtSymbol = 0x09, // symbol + mtNMT = 0x0A, // memory used by native memory tracking + mtClassShared = 0x0B, // class data sharing + mtChunk = 0x0C, // chunk that holds content of arenas + mtTest = 0x0D, // Test type for verifying NMT + mtTracing = 0x0E, // memory used for Tracing + mtNone = 0x0F, // undefined + mt_number_of_types = 0x10 // number of memory types (mtDontTrack // is not included as validate type) - mtDontTrack = 0x0F00, // memory we do not or cannot track - mt_masks = 0x7F00, - - // object type mask - otArena = 0x0010, // an arena object - otNMTRecorder = 0x0020, // memory recorder object - ot_masks = 0x00F0 }; -#define IS_MEMORY_TYPE(flags, type) ((flags & mt_masks) == type) -#define HAS_VALID_MEMORY_TYPE(flags)((flags & mt_masks) != mtNone) -#define FLAGS_TO_MEMORY_TYPE(flags) (flags & mt_masks) +typedef MemoryType MEMFLAGS; -#define IS_ARENA_OBJ(flags) ((flags & ot_masks) == otArena) -#define IS_NMT_RECORDER(flags) ((flags & ot_masks) == otNMTRecorder) -#define NMT_CAN_TRACK(flags) (!IS_NMT_RECORDER(flags) && !(IS_MEMORY_TYPE(flags, mtDontTrack))) - -typedef unsigned short MEMFLAGS; #if INCLUDE_NMT @@ -189,27 +172,23 @@ const bool NMT_track_callsite = false; #endif // INCLUDE_NMT -// debug build does not inline -#if defined(_NMT_NOINLINE_) - #define CURRENT_PC (NMT_track_callsite ? os::get_caller_pc(1) : 0) - #define CALLER_PC (NMT_track_callsite ? os::get_caller_pc(2) : 0) - #define CALLER_CALLER_PC (NMT_track_callsite ? os::get_caller_pc(3) : 0) -#else - #define CURRENT_PC (NMT_track_callsite? os::get_caller_pc(0) : 0) - #define CALLER_PC (NMT_track_callsite ? os::get_caller_pc(1) : 0) - #define CALLER_CALLER_PC (NMT_track_callsite ? os::get_caller_pc(2) : 0) -#endif - +class NativeCallStack; template class CHeapObj ALLOCATION_SUPER_CLASS_SPEC { public: - _NOINLINE_ void* operator new(size_t size, address caller_pc = 0) throw(); + _NOINLINE_ void* operator new(size_t size, const NativeCallStack& stack) throw(); + _NOINLINE_ void* operator new(size_t size) throw(); _NOINLINE_ void* operator new (size_t size, const std::nothrow_t& nothrow_constant, - address caller_pc = 0) throw(); - _NOINLINE_ void* operator new [](size_t size, address caller_pc = 0) throw(); + const NativeCallStack& stack) throw(); + _NOINLINE_ void* operator new (size_t size, const std::nothrow_t& nothrow_constant) + throw(); + _NOINLINE_ void* operator new [](size_t size, const NativeCallStack& stack) throw(); + _NOINLINE_ void* operator new [](size_t size) throw(); _NOINLINE_ void* operator new [](size_t size, const std::nothrow_t& nothrow_constant, - address caller_pc = 0) throw(); + const NativeCallStack& stack) throw(); + _NOINLINE_ void* operator new [](size_t size, const std::nothrow_t& nothrow_constant) + throw(); void operator delete(void* p); void operator delete [] (void* p); }; @@ -286,7 +265,8 @@ class MetaspaceObj { f(ConstantPool) \ f(ConstantPoolCache) \ f(Annotation) \ - f(MethodCounters) + f(MethodCounters) \ + f(Deallocated) #define METASPACE_OBJ_TYPE_DECLARE(name) name ## Type, #define METASPACE_OBJ_TYPE_NAME_CASE(name) case name ## Type: return #name; @@ -384,13 +364,15 @@ class Chunk: CHeapObj { //------------------------------Arena------------------------------------------ // Fast allocation of memory -class Arena : public CHeapObj { +class Arena : public CHeapObj { protected: friend class ResourceMark; friend class HandleMark; friend class NoHandleMark; friend class VMStructs; + MEMFLAGS _flags; // Memory tracking flags + Chunk *_first; // First chunk Chunk *_chunk; // current chunk char *_hwm, *_max; // High water mark and max in current chunk @@ -418,8 +400,8 @@ protected: } public: - Arena(); - Arena(size_t init_size); + Arena(MEMFLAGS memflag); + Arena(MEMFLAGS memflag, size_t init_size); ~Arena(); void destruct_contents(); char* hwm() const { return _hwm; } @@ -518,8 +500,6 @@ protected: static void free_malloced_objects(Chunk* chunk, char* hwm, char* max, char* hwm2) PRODUCT_RETURN; static void free_all(char** start, char** end) PRODUCT_RETURN; - // how many arena instances - NOT_PRODUCT(static volatile jint _instance_count;) private: // Reset this Arena to empty, access will trigger grow if necessary void reset(void) { @@ -681,7 +661,7 @@ class ResourceObj ALLOCATION_SUPER_CLASS_SPEC { NEW_C_HEAP_ARRAY3(type, (size), memflags, pc, AllocFailStrategy::RETURN_NULL) #define NEW_C_HEAP_ARRAY_RETURN_NULL(type, size, memflags)\ - NEW_C_HEAP_ARRAY3(type, (size), memflags, (address)0, AllocFailStrategy::RETURN_NULL) + NEW_C_HEAP_ARRAY3(type, (size), memflags, CURRENT_PC, AllocFailStrategy::RETURN_NULL) #define REALLOC_C_HEAP_ARRAY(type, old, size, memflags)\ (type*) (ReallocateHeap((char*)(old), (size) * sizeof(type), memflags)) diff --git a/hotspot/src/share/vm/memory/allocation.inline.hpp b/hotspot/src/share/vm/memory/allocation.inline.hpp index 806088b9bae..ddce9f5909b 100644 --- a/hotspot/src/share/vm/memory/allocation.inline.hpp +++ b/hotspot/src/share/vm/memory/allocation.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "runtime/atomic.inline.hpp" #include "runtime/os.hpp" +#include "services/memTracker.hpp" // Explicit C-heap memory management @@ -49,12 +50,10 @@ inline void inc_stat_counter(volatile julong* dest, julong add_value) { #endif // allocate using malloc; will fail if no memory available -inline char* AllocateHeap(size_t size, MEMFLAGS flags, address pc = 0, +inline char* AllocateHeap(size_t size, MEMFLAGS flags, + const NativeCallStack& stack, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) { - if (pc == 0) { - pc = CURRENT_PC; - } - char* p = (char*) os::malloc(size, flags, pc); + char* p = (char*) os::malloc(size, flags, stack); #ifdef ASSERT if (PrintMallocFree) trace_heap_malloc(size, "AllocateHeap", p); #endif @@ -63,10 +62,14 @@ inline char* AllocateHeap(size_t size, MEMFLAGS flags, address pc = 0, } return p; } - -inline char* ReallocateHeap(char *old, size_t size, MEMFLAGS flags, +inline char* AllocateHeap(size_t size, MEMFLAGS flags, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) { - char* p = (char*) os::realloc(old, size, flags, CURRENT_PC); + return AllocateHeap(size, flags, CURRENT_PC, alloc_failmode); +} + +inline 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 @@ -85,8 +88,22 @@ inline void FreeHeap(void* p, MEMFLAGS memflags = mtInternal) { template void* CHeapObj::operator new(size_t size, - address caller_pc) throw() { - void* p = (void*)AllocateHeap(size, F, (caller_pc != 0 ? caller_pc : CALLER_PC)); + 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; +} + +template void* CHeapObj::operator new(size_t size) throw() { + return CHeapObj::operator new(size, CALLER_PC); +} + +template void* CHeapObj::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 @@ -94,23 +111,28 @@ template void* CHeapObj::operator new(size_t size, } template void* CHeapObj::operator new (size_t size, - const std::nothrow_t& nothrow_constant, address caller_pc) throw() { - void* p = (void*)AllocateHeap(size, F, (caller_pc != 0 ? caller_pc : CALLER_PC), - AllocFailStrategy::RETURN_NULL); -#ifdef ASSERT - if (PrintMallocFree) trace_heap_malloc(size, "CHeapObj-new", p); -#endif - return p; + const std::nothrow_t& nothrow_constant) throw() { + return CHeapObj::operator new(size, nothrow_constant, CALLER_PC); } template void* CHeapObj::operator new [](size_t size, - address caller_pc) throw() { - return CHeapObj::operator new(size, caller_pc); + const NativeCallStack& stack) throw() { + return CHeapObj::operator new(size, stack); +} + +template void* CHeapObj::operator new [](size_t size) + throw() { + return CHeapObj::operator new(size, CALLER_PC); } template void* CHeapObj::operator new [](size_t size, - const std::nothrow_t& nothrow_constant, address caller_pc) throw() { - return CHeapObj::operator new(size, nothrow_constant, caller_pc); + const std::nothrow_t& nothrow_constant, const NativeCallStack& stack) throw() { + return CHeapObj::operator new(size, nothrow_constant, stack); +} + +template void* CHeapObj::operator new [](size_t size, + const std::nothrow_t& nothrow_constant) throw() { + return CHeapObj::operator new(size, nothrow_constant, CALLER_PC); } template void CHeapObj::operator delete(void* p){ diff --git a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp index 1f56344c95d..30e8618d597 100644 --- a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp +++ b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp @@ -44,13 +44,6 @@ // enumerate ref fields that have been modified (since the last // enumeration.) -size_t CardTableModRefBS::cards_required(size_t covered_words) -{ - // Add one for a guard card, used to detect errors. - const size_t words = align_size_up(covered_words, card_size_in_words); - return words / card_size_in_words + 1; -} - size_t CardTableModRefBS::compute_byte_map_size() { assert(_guard_index == cards_required(_whole_heap.word_size()) - 1, @@ -64,27 +57,50 @@ CardTableModRefBS::CardTableModRefBS(MemRegion whole_heap, int max_covered_regions): ModRefBarrierSet(max_covered_regions), _whole_heap(whole_heap), - _guard_index(cards_required(whole_heap.word_size()) - 1), - _last_valid_index(_guard_index - 1), + _guard_index(0), + _guard_region(), + _last_valid_index(0), _page_size(os::vm_page_size()), - _byte_map_size(compute_byte_map_size()) + _byte_map_size(0), + _covered(NULL), + _committed(NULL), + _cur_covered_regions(0), + _byte_map(NULL), + byte_map_base(NULL), + // LNC functionality + _lowest_non_clean(NULL), + _lowest_non_clean_chunk_size(NULL), + _lowest_non_clean_base_chunk_index(NULL), + _last_LNC_resizing_collection(NULL) { _kind = BarrierSet::CardTableModRef; - HeapWord* low_bound = _whole_heap.start(); - HeapWord* high_bound = _whole_heap.end(); - assert((uintptr_t(low_bound) & (card_size - 1)) == 0, "heap must start at card boundary"); - assert((uintptr_t(high_bound) & (card_size - 1)) == 0, "heap must end at card boundary"); + assert((uintptr_t(_whole_heap.start()) & (card_size - 1)) == 0, "heap must start at card boundary"); + assert((uintptr_t(_whole_heap.end()) & (card_size - 1)) == 0, "heap must end at card boundary"); assert(card_size <= 512, "card_size must be less than 512"); // why? - _covered = new MemRegion[max_covered_regions]; - _committed = new MemRegion[max_covered_regions]; - if (_covered == NULL || _committed == NULL) { - vm_exit_during_initialization("couldn't alloc card table covered region set."); + _covered = new MemRegion[_max_covered_regions]; + if (_covered == NULL) { + vm_exit_during_initialization("Could not allocate card table covered region set."); } +} + +void CardTableModRefBS::initialize() { + _guard_index = cards_required(_whole_heap.word_size()) - 1; + _last_valid_index = _guard_index - 1; + + _byte_map_size = compute_byte_map_size(); + + HeapWord* low_bound = _whole_heap.start(); + HeapWord* high_bound = _whole_heap.end(); _cur_covered_regions = 0; + _committed = new MemRegion[_max_covered_regions]; + if (_committed == NULL) { + vm_exit_during_initialization("Could not allocate card table committed region set."); + } + const size_t rs_align = _page_size == (size_t) os::vm_page_size() ? 0 : MAX2(_page_size, (size_t) os::vm_allocation_granularity()); ReservedSpace heap_rs(_byte_map_size, rs_align, false); @@ -114,20 +130,20 @@ CardTableModRefBS::CardTableModRefBS(MemRegion whole_heap, !ExecMem, "card table last card"); *guard_card = last_card; - _lowest_non_clean = - NEW_C_HEAP_ARRAY(CardArr, max_covered_regions, mtGC); + _lowest_non_clean = + NEW_C_HEAP_ARRAY(CardArr, _max_covered_regions, mtGC); _lowest_non_clean_chunk_size = - NEW_C_HEAP_ARRAY(size_t, max_covered_regions, mtGC); + NEW_C_HEAP_ARRAY(size_t, _max_covered_regions, mtGC); _lowest_non_clean_base_chunk_index = - NEW_C_HEAP_ARRAY(uintptr_t, max_covered_regions, mtGC); + NEW_C_HEAP_ARRAY(uintptr_t, _max_covered_regions, mtGC); _last_LNC_resizing_collection = - NEW_C_HEAP_ARRAY(int, max_covered_regions, mtGC); + NEW_C_HEAP_ARRAY(int, _max_covered_regions, mtGC); if (_lowest_non_clean == NULL || _lowest_non_clean_chunk_size == NULL || _lowest_non_clean_base_chunk_index == NULL || _last_LNC_resizing_collection == NULL) vm_exit_during_initialization("couldn't allocate an LNC array."); - for (int i = 0; i < max_covered_regions; i++) { + for (int i = 0; i < _max_covered_regions; i++) { _lowest_non_clean[i] = NULL; _lowest_non_clean_chunk_size[i] = 0; _last_LNC_resizing_collection[i] = -1; @@ -650,7 +666,7 @@ void CardTableModRefBS::verify_region(MemRegion mr, jbyte val, bool val_equals) { jbyte* start = byte_for(mr.start()); jbyte* end = byte_for(mr.last()); - bool failures = false; + bool failures = false; for (jbyte* curr = start; curr <= end; ++curr) { jbyte curr_val = *curr; bool failed = (val_equals) ? (curr_val != val) : (curr_val == val); diff --git a/hotspot/src/share/vm/memory/cardTableModRefBS.hpp b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp index 5e72b4640f9..c824e6185a0 100644 --- a/hotspot/src/share/vm/memory/cardTableModRefBS.hpp +++ b/hotspot/src/share/vm/memory/cardTableModRefBS.hpp @@ -96,12 +96,12 @@ class CardTableModRefBS: public ModRefBarrierSet { // The declaration order of these const fields is important; see the // constructor before changing. const MemRegion _whole_heap; // the region covered by the card table - const size_t _guard_index; // index of very last element in the card + size_t _guard_index; // index of very last element in the card // table; it is set to a guard value // (last_card) and should never be modified - const size_t _last_valid_index; // index of the last valid element + size_t _last_valid_index; // index of the last valid element const size_t _page_size; // page size used when mapping _byte_map - const size_t _byte_map_size; // in bytes + size_t _byte_map_size; // in bytes jbyte* _byte_map; // the card marking array int _cur_covered_regions; @@ -123,7 +123,12 @@ class CardTableModRefBS: public ModRefBarrierSet { protected: // Initialization utilities; covered_words is the size of the covered region // in, um, words. - inline size_t cards_required(size_t covered_words); + inline size_t cards_required(size_t covered_words) { + // Add one for a guard card, used to detect errors. + const size_t words = align_size_up(covered_words, card_size_in_words); + return words / card_size_in_words + 1; + } + inline size_t compute_byte_map_size(); // Finds and return the index of the region, if any, to which the given @@ -137,7 +142,7 @@ class CardTableModRefBS: public ModRefBarrierSet { int find_covering_region_containing(HeapWord* addr); // Resize one of the regions covered by the remembered set. - void resize_covered_region(MemRegion new_region); + virtual void resize_covered_region(MemRegion new_region); // Returns the leftmost end of a committed region corresponding to a // covered region before covered region "ind", or else "NULL" if "ind" is @@ -282,6 +287,8 @@ public: CardTableModRefBS(MemRegion whole_heap, int max_covered_regions); ~CardTableModRefBS(); + virtual void initialize(); + // *** Barrier set functions. bool has_write_ref_pre_barrier() { return false; } diff --git a/hotspot/src/share/vm/memory/cardTableRS.cpp b/hotspot/src/share/vm/memory/cardTableRS.cpp index 7c9d3618673..90f72d19efd 100644 --- a/hotspot/src/share/vm/memory/cardTableRS.cpp +++ b/hotspot/src/share/vm/memory/cardTableRS.cpp @@ -54,9 +54,10 @@ CardTableRS::CardTableRS(MemRegion whole_heap, #else _ct_bs = new CardTableModRefBSForCTRS(whole_heap, max_covered_regions); #endif + _ct_bs->initialize(); set_bs(_ct_bs); _last_cur_val_in_gen = NEW_C_HEAP_ARRAY3(jbyte, GenCollectedHeap::max_gens + 1, - mtGC, 0, AllocFailStrategy::RETURN_NULL); + mtGC, CURRENT_PC, AllocFailStrategy::RETURN_NULL); if (_last_cur_val_in_gen == NULL) { vm_exit_during_initialization("Could not create last_cur_val_in_gen array."); } diff --git a/hotspot/src/share/vm/memory/collectorPolicy.cpp b/hotspot/src/share/vm/memory/collectorPolicy.cpp index af6e39d05f0..6589766bd65 100644 --- a/hotspot/src/share/vm/memory/collectorPolicy.cpp +++ b/hotspot/src/share/vm/memory/collectorPolicy.cpp @@ -176,13 +176,9 @@ size_t CollectorPolicy::compute_heap_alignment() { size_t alignment = GenRemSet::max_alignment_constraint(); - // Parallel GC does its own alignment of the generations to avoid requiring a - // large page (256M on some platforms) for the permanent generation. The - // other collectors should also be updated to do their own alignment and then - // this use of lcm() should be removed. - if (UseLargePages && !UseParallelGC) { + if (UseLargePages) { // In presence of large pages we have to make sure that our - // alignment is large page aware + // alignment is large page aware. alignment = lcm(os::large_page_size(), alignment); } @@ -909,7 +905,8 @@ void MarkSweepPolicy::initialize_alignments() { } void MarkSweepPolicy::initialize_generations() { - _generations = NEW_C_HEAP_ARRAY3(GenerationSpecPtr, number_of_generations(), mtGC, 0, AllocFailStrategy::RETURN_NULL); + _generations = NEW_C_HEAP_ARRAY3(GenerationSpecPtr, number_of_generations(), mtGC, CURRENT_PC, + AllocFailStrategy::RETURN_NULL); if (_generations == NULL) { vm_exit_during_initialization("Unable to allocate gen spec"); } diff --git a/hotspot/src/share/vm/memory/defNewGeneration.cpp b/hotspot/src/share/vm/memory/defNewGeneration.cpp index 281228d28eb..ae3726281ec 100644 --- a/hotspot/src/share/vm/memory/defNewGeneration.cpp +++ b/hotspot/src/share/vm/memory/defNewGeneration.cpp @@ -214,9 +214,11 @@ DefNewGeneration::DefNewGeneration(ReservedSpace rs, _max_eden_size = size - (2*_max_survivor_size); // allocate the performance counters + GenCollectorPolicy* gcp = (GenCollectorPolicy*) GenCollectedHeap::heap()->collector_policy(); // Generation counters -- generation 0, 3 subspaces - _gen_counters = new GenerationCounters("new", 0, 3, &_virtual_space); + _gen_counters = new GenerationCounters("new", 0, 3, + gcp->min_young_size(), gcp->max_young_size(), &_virtual_space); _gc_counters = new CollectorCounters(policy, 0); _eden_counters = new CSpaceCounters("eden", 0, _max_eden_size, _eden_space, @@ -790,7 +792,7 @@ oop DefNewGeneration::copy_to_survivor_space(oop old) { // Try allocating obj in to-space (unless too old) if (old->age() < tenuring_threshold()) { - obj = (oop) to()->allocate(s); + obj = (oop) to()->allocate_aligned(s); } // Otherwise try allocating obj tenured diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp index 2f5c0165893..2eb6870fd19 100644 --- a/hotspot/src/share/vm/memory/filemap.cpp +++ b/hotspot/src/share/vm/memory/filemap.cpp @@ -24,9 +24,14 @@ #include "precompiled.hpp" #include "classfile/classLoader.hpp" +#include "classfile/sharedClassUtil.hpp" #include "classfile/symbolTable.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/altHashing.hpp" #include "memory/filemap.hpp" +#include "memory/metadataFactory.hpp" +#include "memory/oopFactory.hpp" +#include "oops/objArrayOop.hpp" #include "runtime/arguments.hpp" #include "runtime/java.hpp" #include "runtime/os.hpp" @@ -42,7 +47,6 @@ #endif PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC - extern address JVM_FunctionAtStart(); extern address JVM_FunctionAtEnd(); @@ -78,16 +82,27 @@ void FileMapInfo::fail_stop(const char *msg, ...) { void FileMapInfo::fail_continue(const char *msg, ...) { va_list ap; va_start(ap, msg); - if (RequireSharedSpaces) { - fail(msg, ap); + MetaspaceShared::set_archive_loading_failed(); + if (PrintSharedArchiveAndExit && _validating_classpath_entry_table) { + // If we are doing PrintSharedArchiveAndExit and some of the classpath entries + // do not validate, we can still continue "limping" to validate the remaining + // entries. No need to quit. + tty->print("["); + tty->vprint(msg, ap); + tty->print_cr("]"); } else { - if (PrintSharedSpaces) { - tty->print_cr("UseSharedSpaces: %s", msg); + if (RequireSharedSpaces) { + fail(msg, ap); + } else { + if (PrintSharedSpaces) { + tty->print_cr("UseSharedSpaces: %s", msg); + } } } va_end(ap); UseSharedSpaces = false; - close(); + assert(current_info() != NULL, "singleton must be registered"); + current_info()->close(); } // Fill in the fileMapInfo structure with data about this VM instance. @@ -122,67 +137,201 @@ template static void get_header_version(char (&header_version) [N]) { } } +FileMapInfo::FileMapInfo() { + assert(_current_info == NULL, "must be singleton"); // not thread safe + _current_info = this; + memset(this, 0, sizeof(FileMapInfo)); + _file_offset = 0; + _file_open = false; + _header = SharedClassUtil::allocate_file_map_header(); + _header->_version = _invalid_version; +} + +FileMapInfo::~FileMapInfo() { + assert(_current_info == this, "must be singleton"); // not thread safe + _current_info = NULL; +} + void FileMapInfo::populate_header(size_t alignment) { - _header._magic = 0xf00baba2; - _header._version = _current_version; - _header._alignment = alignment; - _header._obj_alignment = ObjectAlignmentInBytes; + _header->populate(this, alignment); +} + +size_t FileMapInfo::FileMapHeader::data_size() { + return SharedClassUtil::file_map_header_size() - sizeof(FileMapInfo::FileMapHeaderBase); +} + +void FileMapInfo::FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) { + _magic = 0xf00baba2; + _version = _current_version; + _alignment = alignment; + _obj_alignment = ObjectAlignmentInBytes; + _classpath_entry_table_size = mapinfo->_classpath_entry_table_size; + _classpath_entry_table = mapinfo->_classpath_entry_table; + _classpath_entry_size = mapinfo->_classpath_entry_size; // The following fields are for sanity checks for whether this archive // will function correctly with this JVM and the bootclasspath it's // invoked with. // JVM version string ... changes on each build. - get_header_version(_header._jvm_ident); + get_header_version(_jvm_ident); +} - // Build checks on classpath and jar files - _header._num_jars = 0; - ClassPathEntry *cpe = ClassLoader::classpath_entry(0); - for ( ; cpe != NULL; cpe = cpe->next()) { +void FileMapInfo::allocate_classpath_entry_table() { + int bytes = 0; + int count = 0; + char* strptr = NULL; + char* strptr_max = NULL; + Thread* THREAD = Thread::current(); - if (cpe->is_jar_file()) { - if (_header._num_jars >= JVM_SHARED_JARS_MAX) { - fail_stop("Too many jar files to share.", NULL); - } + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); + size_t entry_size = SharedClassUtil::shared_class_path_entry_size(); - // Jar file - record timestamp and file size. - struct stat st; - const char *path = cpe->name(); - if (os::stat(path, &st) != 0) { - // If we can't access a jar file in the boot path, then we can't - // make assumptions about where classes get loaded from. - fail_stop("Unable to open jar file %s.", path); - } - _header._jar[_header._num_jars]._timestamp = st.st_mtime; - _header._jar[_header._num_jars]._filesize = st.st_size; - _header._num_jars++; - } else { + for (int pass=0; pass<2; pass++) { + ClassPathEntry *cpe = ClassLoader::classpath_entry(0); - // If directories appear in boot classpath, they must be empty to - // avoid having to verify each individual class file. - const char* name = ((ClassPathDirEntry*)cpe)->name(); - if (!os::dir_is_empty(name)) { - fail_stop("Boot classpath directory %s is not empty.", name); + for (int cur_entry = 0 ; cpe != NULL; cpe = cpe->next(), cur_entry++) { + const char *name = cpe->name(); + int name_bytes = (int)(strlen(name) + 1); + + if (pass == 0) { + count ++; + bytes += (int)entry_size; + bytes += name_bytes; + if (TraceClassPaths || (TraceClassLoading && Verbose)) { + tty->print_cr("[Add main shared path (%s) %s]", (cpe->is_jar_file() ? "jar" : "dir"), name); + } + } else { + SharedClassPathEntry* ent = shared_classpath(cur_entry); + if (cpe->is_jar_file()) { + struct stat st; + if (os::stat(name, &st) != 0) { + // The file/dir must exist, or it would not have been added + // into ClassLoader::classpath_entry(). + // + // If we can't access a jar file in the boot path, then we can't + // make assumptions about where classes get loaded from. + FileMapInfo::fail_stop("Unable to open jar file %s.", name); + } + + EXCEPTION_MARK; // The following call should never throw, but would exit VM on error. + SharedClassUtil::update_shared_classpath(cpe, ent, st.st_mtime, st.st_size, THREAD); + } else { + ent->_filesize = -1; + if (!os::dir_is_empty(name)) { + ClassLoader::exit_with_path_failure("Cannot have non-empty directory in archived classpaths", name); + } + } + ent->_name = strptr; + if (strptr + name_bytes <= strptr_max) { + strncpy(strptr, name, (size_t)name_bytes); // name_bytes includes trailing 0. + strptr += name_bytes; + } else { + assert(0, "miscalculated buffer size"); + } } } + + if (pass == 0) { + EXCEPTION_MARK; // The following call should never throw, but would exit VM on error. + Array* arr = MetadataFactory::new_array(loader_data, (bytes + 7)/8, THREAD); + strptr = (char*)(arr->data()); + strptr_max = strptr + bytes; + SharedClassPathEntry* table = (SharedClassPathEntry*)strptr; + strptr += entry_size * count; + + _classpath_entry_table_size = count; + _classpath_entry_table = table; + _classpath_entry_size = entry_size; + } } } +bool FileMapInfo::validate_classpath_entry_table() { + _validating_classpath_entry_table = true; + + int count = _header->_classpath_entry_table_size; + + _classpath_entry_table = _header->_classpath_entry_table; + _classpath_entry_size = _header->_classpath_entry_size; + + for (int i=0; i_name; + bool ok = true; + if (TraceClassPaths || (TraceClassLoading && Verbose)) { + tty->print_cr("[Checking shared classpath entry: %s]", name); + } + if (os::stat(name, &st) != 0) { + fail_continue("Required classpath entry does not exist: %s", name); + ok = false; + } else if (ent->is_dir()) { + if (!os::dir_is_empty(name)) { + fail_continue("directory is not empty: %s", name); + ok = false; + } + } else { + if (ent->_timestamp != st.st_mtime || + ent->_filesize != st.st_size) { + ok = false; + if (PrintSharedArchiveAndExit) { + fail_continue(ent->_timestamp != st.st_mtime ? + "Timestamp mismatch" : + "File size mismatch"); + } else { + fail_continue("A jar file is not the one used while building" + " the shared archive file: %s", name); + } + } + } + if (ok) { + if (TraceClassPaths || (TraceClassLoading && Verbose)) { + tty->print_cr("[ok]"); + } + } else if (!PrintSharedArchiveAndExit) { + _validating_classpath_entry_table = false; + return false; + } + } + + _classpath_entry_table_size = _header->_classpath_entry_table_size; + _validating_classpath_entry_table = false; + return true; +} + // Read the FileMapInfo information from the file. bool FileMapInfo::init_from_file(int fd) { - - size_t n = read(fd, &_header, sizeof(struct FileMapHeader)); - if (n != sizeof(struct FileMapHeader)) { + size_t sz = _header->data_size(); + char* addr = _header->data(); + size_t n = os::read(fd, addr, (unsigned int)sz); + if (n != sz) { fail_continue("Unable to read the file header."); return false; } - if (_header._version != current_version()) { + if (_header->_version != current_version()) { fail_continue("The shared archive file has the wrong version."); return false; } _file_offset = (long)n; + + size_t info_size = _header->_paths_misc_info_size; + _paths_misc_info = NEW_C_HEAP_ARRAY_RETURN_NULL(char, info_size, mtClass); + if (_paths_misc_info == NULL) { + fail_continue("Unable to read the file header."); + return false; + } + n = os::read(fd, _paths_misc_info, (unsigned int)info_size); + if (n != info_size) { + fail_continue("Unable to read the shared path info header."); + FREE_C_HEAP_ARRAY(char, _paths_misc_info, mtClass); + _paths_misc_info = NULL; + return false; + } + + _file_offset += (long)n; return true; } @@ -237,7 +386,16 @@ void FileMapInfo::open_for_write() { // Write the header to the file, seek to the next allocation boundary. void FileMapInfo::write_header() { - write_bytes_aligned(&_header, sizeof(FileMapHeader)); + int info_size = ClassLoader::get_shared_paths_misc_info_size(); + + _header->_paths_misc_info_size = info_size; + + align_file_position(); + size_t sz = _header->data_size(); + char* addr = _header->data(); + write_bytes(addr, (int)sz); // skip the C++ vtable + write_bytes(ClassLoader::get_shared_paths_misc_info(), info_size); + align_file_position(); } @@ -247,7 +405,7 @@ void FileMapInfo::write_space(int i, Metaspace* space, bool read_only) { align_file_position(); size_t used = space->used_bytes_slow(Metaspace::NonClassType); size_t capacity = space->capacity_bytes_slow(Metaspace::NonClassType); - struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i]; + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; write_region(i, (char*)space->bottom(), used, capacity, read_only, false); } @@ -257,7 +415,7 @@ void FileMapInfo::write_space(int i, Metaspace* space, bool read_only) { void FileMapInfo::write_region(int region, char* base, size_t size, size_t capacity, bool read_only, bool allow_exec) { - struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[region]; + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[region]; if (_file_open) { guarantee(si->_file_offset == _file_offset, "file offset mismatch."); @@ -287,7 +445,7 @@ void FileMapInfo::write_bytes(const void* buffer, int nbytes) { // close and remove the file. See bug 6372906. close(); remove(_full_path); - fail_stop("Unable to write to shared archive file.", NULL); + fail_stop("Unable to write to shared archive file."); } } _file_offset += nbytes; @@ -305,7 +463,7 @@ void FileMapInfo::align_file_position() { // that the written file is the correct length. _file_offset -= 1; if (lseek(_fd, _file_offset, SEEK_SET) < 0) { - fail_stop("Unable to seek.", NULL); + fail_stop("Unable to seek."); } char zero = 0; write_bytes(&zero, 1); @@ -339,7 +497,7 @@ void FileMapInfo::close() { // JVM/TI RedefineClasses() support: // Remap the shared readonly space to shared readwrite, private. bool FileMapInfo::remap_shared_readonly_as_readwrite() { - struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[0]; + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0]; if (!si->_read_only) { // the space is already readwrite so we are done return true; @@ -367,7 +525,7 @@ bool FileMapInfo::remap_shared_readonly_as_readwrite() { // Map the whole region at once, assumed to be allocated contiguously. ReservedSpace FileMapInfo::reserve_shared_memory() { - struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[0]; + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0]; char* requested_addr = si->_base; size_t size = FileMapInfo::shared_spaces_size(); @@ -376,7 +534,7 @@ ReservedSpace FileMapInfo::reserve_shared_memory() { // other reserved memory (like the code cache). ReservedSpace rs(size, os::vm_allocation_granularity(), false, requested_addr); if (!rs.is_reserved()) { - fail_continue(err_msg("Unable to reserve shared space at required address " INTPTR_FORMAT, requested_addr)); + fail_continue("Unable to reserve shared space at required address " INTPTR_FORMAT, requested_addr); return rs; } // the reserved virtual memory is for mapping class data sharing archive @@ -389,7 +547,7 @@ ReservedSpace FileMapInfo::reserve_shared_memory() { static const char* shared_region_name[] = { "ReadOnly", "ReadWrite", "MiscData", "MiscCode"}; char* FileMapInfo::map_region(int i) { - struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i]; + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; size_t used = si->_used; size_t alignment = os::vm_allocation_granularity(); size_t size = align_size_up(used, alignment); @@ -400,7 +558,7 @@ char* FileMapInfo::map_region(int i) { requested_addr, size, si->_read_only, si->_allow_exec); if (base == NULL || base != si->_base) { - fail_continue(err_msg("Unable to map %s shared space at required address.", shared_region_name[i])); + fail_continue("Unable to map %s shared space at required address.", shared_region_name[i]); return NULL; } #ifdef _WINDOWS @@ -415,7 +573,7 @@ char* FileMapInfo::map_region(int i) { // Unmap a memory region in the address space. void FileMapInfo::unmap_region(int i) { - struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i]; + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; size_t used = si->_used; size_t size = align_size_up(used, os::vm_allocation_granularity()); if (!os::unmap_memory(si->_base, size)) { @@ -426,18 +584,27 @@ void FileMapInfo::unmap_region(int i) { void FileMapInfo::assert_mark(bool check) { if (!check) { - fail_stop("Mark mismatch while restoring from shared file.", NULL); + fail_stop("Mark mismatch while restoring from shared file."); } } FileMapInfo* FileMapInfo::_current_info = NULL; - +SharedClassPathEntry* FileMapInfo::_classpath_entry_table = NULL; +int FileMapInfo::_classpath_entry_table_size = 0; +size_t FileMapInfo::_classpath_entry_size = 0x1234baad; +bool FileMapInfo::_validating_classpath_entry_table = false; // Open the shared archive file, read and validate the header // information (version, boot classpath, etc.). If initialization // fails, shared spaces are disabled and the file is closed. [See // fail_continue.] +// +// Validation of the archive is done in two steps: +// +// [1] validate_header() - done here. This checks the header, including _paths_misc_info. +// [2] validate_classpath_entry_table - this is done later, because the table is in the RW +// region of the archive, which is not mapped yet. bool FileMapInfo::initialize() { assert(UseSharedSpaces, "UseSharedSpaces expected."); @@ -451,92 +618,66 @@ bool FileMapInfo::initialize() { } init_from_file(_fd); - if (!validate()) { + if (!validate_header()) { return false; } - SharedReadOnlySize = _header._space[0]._capacity; - SharedReadWriteSize = _header._space[1]._capacity; - SharedMiscDataSize = _header._space[2]._capacity; - SharedMiscCodeSize = _header._space[3]._capacity; + SharedReadOnlySize = _header->_space[0]._capacity; + SharedReadWriteSize = _header->_space[1]._capacity; + SharedMiscDataSize = _header->_space[2]._capacity; + SharedMiscCodeSize = _header->_space[3]._capacity; return true; } - -bool FileMapInfo::validate() { - if (_header._version != current_version()) { - fail_continue("The shared archive file is the wrong version."); +bool FileMapInfo::FileMapHeader::validate() { + if (_version != current_version()) { + FileMapInfo::fail_continue("The shared archive file is the wrong version."); return false; } - if (_header._magic != (int)0xf00baba2) { - fail_continue("The shared archive file has a bad magic number."); + if (_magic != (int)0xf00baba2) { + FileMapInfo::fail_continue("The shared archive file has a bad magic number."); return false; } char header_version[JVM_IDENT_MAX]; get_header_version(header_version); - if (strncmp(_header._jvm_ident, header_version, JVM_IDENT_MAX-1) != 0) { - fail_continue("The shared archive file was created by a different" - " version or build of HotSpot."); - return false; - } - if (_header._obj_alignment != ObjectAlignmentInBytes) { - fail_continue("The shared archive file's ObjectAlignmentInBytes of %d" - " does not equal the current ObjectAlignmentInBytes of %d.", - _header._obj_alignment, ObjectAlignmentInBytes); - return false; - } - - // Cannot verify interpreter yet, as it can only be created after the GC - // heap has been initialized. - - if (_header._num_jars >= JVM_SHARED_JARS_MAX) { - fail_continue("Too many jar files to share."); - return false; - } - - // Build checks on classpath and jar files - int num_jars_now = 0; - ClassPathEntry *cpe = ClassLoader::classpath_entry(0); - for ( ; cpe != NULL; cpe = cpe->next()) { - - if (cpe->is_jar_file()) { - if (num_jars_now < _header._num_jars) { - - // Jar file - verify timestamp and file size. - struct stat st; - const char *path = cpe->name(); - if (os::stat(path, &st) != 0) { - fail_continue("Unable to open jar file %s.", path); - return false; - } - if (_header._jar[num_jars_now]._timestamp != st.st_mtime || - _header._jar[num_jars_now]._filesize != st.st_size) { - fail_continue("A jar file is not the one used while building" - " the shared archive file."); - return false; - } - } - ++num_jars_now; - } else { - - // If directories appear in boot classpath, they must be empty to - // avoid having to verify each individual class file. - const char* name = ((ClassPathDirEntry*)cpe)->name(); - if (!os::dir_is_empty(name)) { - fail_continue("Boot classpath directory %s is not empty.", name); - return false; - } + if (strncmp(_jvm_ident, header_version, JVM_IDENT_MAX-1) != 0) { + if (TraceClassPaths) { + tty->print_cr("Expected: %s", header_version); + tty->print_cr("Actual: %s", _jvm_ident); } + FileMapInfo::fail_continue("The shared archive file was created by a different" + " version or build of HotSpot"); + return false; } - if (num_jars_now < _header._num_jars) { - fail_continue("The number of jar files in the boot classpath is" - " less than the number the shared archive was created with."); + if (_obj_alignment != ObjectAlignmentInBytes) { + FileMapInfo::fail_continue("The shared archive file's ObjectAlignmentInBytes of %d" + " does not equal the current ObjectAlignmentInBytes of %d.", + _obj_alignment, ObjectAlignmentInBytes); return false; } return true; } +bool FileMapInfo::validate_header() { + bool status = _header->validate(); + + if (status) { + if (!ClassLoader::check_shared_paths_misc_info(_paths_misc_info, _header->_paths_misc_info_size)) { + if (!PrintSharedArchiveAndExit) { + fail_continue("shared class paths mismatch (hint: enable -XX:+TraceClassPaths to diagnose the failure)"); + status = false; + } + } + } + + if (_paths_misc_info != NULL) { + FREE_C_HEAP_ARRAY(char, _paths_misc_info, mtClass); + _paths_misc_info = NULL; + } + return status; +} + // The following method is provided to see whether a given pointer // falls in the mapped shared space. // Param: @@ -545,8 +686,8 @@ bool FileMapInfo::validate() { // True if the p is within the mapped shared space, otherwise, false. bool FileMapInfo::is_in_shared_space(const void* p) { for (int i = 0; i < MetaspaceShared::n_regions; i++) { - if (p >= _header._space[i]._base && - p < _header._space[i]._base + _header._space[i]._used) { + if (p >= _header->_space[i]._base && + p < _header->_space[i]._base + _header->_space[i]._used) { return true; } } @@ -557,7 +698,7 @@ bool FileMapInfo::is_in_shared_space(const void* p) { void FileMapInfo::print_shared_spaces() { gclog_or_tty->print_cr("Shared Spaces:"); for (int i = 0; i < MetaspaceShared::n_regions; i++) { - struct FileMapInfo::FileMapHeader::space_info* si = &_header._space[i]; + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; gclog_or_tty->print(" %s " INTPTR_FORMAT "-" INTPTR_FORMAT, shared_region_name[i], si->_base, si->_base + si->_used); @@ -568,14 +709,14 @@ void FileMapInfo::print_shared_spaces() { void FileMapInfo::stop_sharing_and_unmap(const char* msg) { FileMapInfo *map_info = FileMapInfo::current_info(); if (map_info) { - map_info->fail_continue(msg); + map_info->fail_continue("%s", msg); for (int i = 0; i < MetaspaceShared::n_regions; i++) { - if (map_info->_header._space[i]._base != NULL) { + if (map_info->_header->_space[i]._base != NULL) { map_info->unmap_region(i); - map_info->_header._space[i]._base = NULL; + map_info->_header->_space[i]._base = NULL; } } } else if (DumpSharedSpaces) { - fail_stop(msg, NULL); + fail_stop("%s", msg); } } diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp index 166486fa3f0..821bf66bf6b 100644 --- a/hotspot/src/share/vm/memory/filemap.hpp +++ b/hotspot/src/share/vm/memory/filemap.hpp @@ -37,30 +37,55 @@ // misc data (block offset table, string table, symbols, dictionary, etc.) // tag(666) -static const int JVM_SHARED_JARS_MAX = 128; -static const int JVM_SPACENAME_MAX = 128; static const int JVM_IDENT_MAX = 256; -static const int JVM_ARCH_MAX = 12; - class Metaspace; +class SharedClassPathEntry VALUE_OBJ_CLASS_SPEC { +public: + const char *_name; + time_t _timestamp; // jar timestamp, 0 if is directory + long _filesize; // jar file size, -1 if is directory + bool is_dir() { + return _filesize == -1; + } +}; + class FileMapInfo : public CHeapObj { private: + friend class ManifestStream; enum { _invalid_version = -1, - _current_version = 1 + _current_version = 2 }; bool _file_open; int _fd; long _file_offset; +private: + static SharedClassPathEntry* _classpath_entry_table; + static int _classpath_entry_table_size; + static size_t _classpath_entry_size; + static bool _validating_classpath_entry_table; + // FileMapHeader describes the shared space data in the file to be // mapped. This structure gets written to a file. It is not a class, so // that the compilers don't add any compiler-private data to it. - struct FileMapHeader { +public: + struct FileMapHeaderBase : public CHeapObj { + virtual bool validate() = 0; + virtual void populate(FileMapInfo* info, size_t alignment) = 0; + }; + struct FileMapHeader : FileMapHeaderBase { + // Use data() and data_size() to memcopy to/from the FileMapHeader. We need to + // avoid read/writing the C++ vtable pointer. + static size_t data_size(); + char* data() { + return ((char*)this) + sizeof(FileMapHeaderBase); + } + int _magic; // identify file type. int _version; // (from enum, above.) size_t _alignment; // how shared archive should be aligned @@ -78,44 +103,64 @@ private: // The following fields are all sanity checks for whether this archive // will function correctly with this JVM and the bootclasspath it's // invoked with. - char _arch[JVM_ARCH_MAX]; // architecture char _jvm_ident[JVM_IDENT_MAX]; // identifier for jvm - int _num_jars; // Number of jars in bootclasspath - // Per jar file data: timestamp, size. + // The _paths_misc_info is a variable-size structure that records "miscellaneous" + // information during dumping. It is generated and validated by the + // SharedPathsMiscInfo class. See SharedPathsMiscInfo.hpp and sharedClassUtil.hpp for + // detailed description. + // + // The _paths_misc_info data is stored as a byte array in the archive file header, + // immediately after the _header field. This information is used only when + // checking the validity of the archive and is deallocated after the archive is loaded. + // + // Note that the _paths_misc_info does NOT include information for JAR files + // that existed during dump time. Their information is stored in _classpath_entry_table. + int _paths_misc_info_size; + + // The following is a table of all the class path entries that were used + // during dumping. At run time, we require these files to exist and have the same + // size/modification time, or else the archive will refuse to load. + // + // All of these entries must be JAR files. The dumping process would fail if a non-empty + // directory was specified in the classpaths. If an empty directory was specified + // it is checked by the _paths_misc_info as described above. + // + // FIXME -- if JAR files in the tail of the list were specified but not used during dumping, + // they should be removed from this table, to save space and to avoid spurious + // loading failures during runtime. + int _classpath_entry_table_size; + size_t _classpath_entry_size; + SharedClassPathEntry* _classpath_entry_table; + + virtual bool validate(); + virtual void populate(FileMapInfo* info, size_t alignment); + }; + + FileMapHeader * _header; - struct { - time_t _timestamp; // jar timestamp. - long _filesize; // jar file size. - } _jar[JVM_SHARED_JARS_MAX]; - } _header; const char* _full_path; + char* _paths_misc_info; static FileMapInfo* _current_info; bool init_from_file(int fd); void align_file_position(); + bool validate_header_impl(); public: - FileMapInfo() { - _file_offset = 0; - _file_open = false; - _header._version = _invalid_version; - } + FileMapInfo(); + ~FileMapInfo(); static int current_version() { return _current_version; } void populate_header(size_t alignment); - bool validate(); + bool validate_header(); void invalidate(); - int version() { return _header._version; } - size_t alignment() { return _header._alignment; } - size_t space_capacity(int i) { return _header._space[i]._capacity; } - char* region_base(int i) { return _header._space[i]._base; } - struct FileMapHeader* header() { return &_header; } - - static void set_current_info(FileMapInfo* info) { - CDS_ONLY(_current_info = info;) - } + int version() { return _header->_version; } + size_t alignment() { return _header->_alignment; } + size_t space_capacity(int i) { return _header->_space[i]._capacity; } + char* region_base(int i) { return _header->_space[i]._base; } + struct FileMapHeader* header() { return _header; } static FileMapInfo* current_info() { CDS_ONLY(return _current_info;) @@ -145,8 +190,8 @@ public: bool remap_shared_readonly_as_readwrite(); // Errors. - static void fail_stop(const char *msg, ...); - void fail_continue(const char *msg, ...); + static void fail_stop(const char *msg, ...) ATTRIBUTE_PRINTF(1, 2); + static void fail_continue(const char *msg, ...) ATTRIBUTE_PRINTF(1, 2); // Return true if given address is in the mapped shared space. bool is_in_shared_space(const void* p) NOT_CDS_RETURN_(false); @@ -160,6 +205,22 @@ public: // Stop CDS sharing and unmap CDS regions. static void stop_sharing_and_unmap(const char* msg); + + static void allocate_classpath_entry_table(); + bool validate_classpath_entry_table(); + + static SharedClassPathEntry* shared_classpath(int index) { + char* p = (char*)_classpath_entry_table; + p += _classpath_entry_size * index; + return (SharedClassPathEntry*)p; + } + static const char* shared_classpath_name(int index) { + return shared_classpath(index)->_name; + } + + static int get_number_of_share_classpaths() { + return _classpath_entry_table_size; + } }; #endif // SHARE_VM_MEMORY_FILEMAP_HPP diff --git a/hotspot/src/share/vm/memory/freeList.cpp b/hotspot/src/share/vm/memory/freeList.cpp index f1d4859a040..3ab6f09a8ae 100644 --- a/hotspot/src/share/vm/memory/freeList.cpp +++ b/hotspot/src/share/vm/memory/freeList.cpp @@ -34,7 +34,6 @@ #if INCLUDE_ALL_GCS #include "gc_implementation/concurrentMarkSweep/freeChunk.hpp" -#include "gc_implementation/g1/g1CodeCacheRemSet.hpp" #endif // INCLUDE_ALL_GCS // Free list. A FreeList is used to access a linked list of chunks @@ -333,5 +332,4 @@ template class FreeList; template class FreeList; #if INCLUDE_ALL_GCS template class FreeList; -template class FreeList; #endif // INCLUDE_ALL_GCS diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.cpp b/hotspot/src/share/vm/memory/genCollectedHeap.cpp index 4b5deb03c05..9cd841f038a 100644 --- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp +++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp @@ -708,15 +708,18 @@ void GenCollectedHeap::collect(GCCause::Cause cause) { #else // INCLUDE_ALL_GCS ShouldNotReachHere(); #endif // INCLUDE_ALL_GCS + } else if (cause == GCCause::_wb_young_gc) { + // minor collection for WhiteBox API + collect(cause, 0); } else { #ifdef ASSERT - if (cause == GCCause::_scavenge_alot) { - // minor collection only - collect(cause, 0); - } else { - // Stop-the-world full collection - collect(cause, n_gens() - 1); - } + if (cause == GCCause::_scavenge_alot) { + // minor collection only + collect(cause, 0); + } else { + // Stop-the-world full collection + collect(cause, n_gens() - 1); + } #else // Stop-the-world full collection collect(cause, n_gens() - 1); diff --git a/hotspot/src/share/vm/memory/heapInspection.cpp b/hotspot/src/share/vm/memory/heapInspection.cpp index 7a4c6fc8752..cc8f4fc061a 100644 --- a/hotspot/src/share/vm/memory/heapInspection.cpp +++ b/hotspot/src/share/vm/memory/heapInspection.cpp @@ -135,7 +135,7 @@ KlassInfoTable::KlassInfoTable(bool need_class_stats) { _ref = (HeapWord*) Universe::boolArrayKlassObj(); _buckets = (KlassInfoBucket*) AllocateHeap(sizeof(KlassInfoBucket) * _num_buckets, - mtInternal, 0, AllocFailStrategy::RETURN_NULL); + mtInternal, CURRENT_PC, AllocFailStrategy::RETURN_NULL); if (_buckets != NULL) { _size = _num_buckets; for (int index = 0; index < _size; index++) { diff --git a/hotspot/src/share/vm/memory/memRegion.cpp b/hotspot/src/share/vm/memory/memRegion.cpp index 9eb2be56930..8c33ddca1de 100644 --- a/hotspot/src/share/vm/memory/memRegion.cpp +++ b/hotspot/src/share/vm/memory/memRegion.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2014, 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,11 +103,13 @@ MemRegion MemRegion::minus(const MemRegion mr2) const { } void* MemRegion::operator new(size_t size) throw() { - return (address)AllocateHeap(size, mtGC, 0, AllocFailStrategy::RETURN_NULL); + return (address)AllocateHeap(size, mtGC, CURRENT_PC, + AllocFailStrategy::RETURN_NULL); } void* MemRegion::operator new [](size_t size) throw() { - return (address)AllocateHeap(size, mtGC, 0, AllocFailStrategy::RETURN_NULL); + return (address)AllocateHeap(size, mtGC, CURRENT_PC, + AllocFailStrategy::RETURN_NULL); } void MemRegion::operator delete(void* p) { FreeHeap(p, mtGC); diff --git a/hotspot/src/share/vm/memory/metadataFactory.hpp b/hotspot/src/share/vm/memory/metadataFactory.hpp index dbb936fdf57..0f9936802cd 100644 --- a/hotspot/src/share/vm/memory/metadataFactory.hpp +++ b/hotspot/src/share/vm/memory/metadataFactory.hpp @@ -79,6 +79,12 @@ class MetadataFactory : AllStatic { // Deallocation method for metadata template static void free_metadata(ClassLoaderData* loader_data, T md) { + if (DumpSharedSpaces) { + // FIXME: the freeing code is buggy, especially when PrintSharedSpaces is enabled. + // Disable for now -- this means if you specify bad classes in your classlist you + // may have wasted space inside the archive. + return; + } if (md != NULL) { assert(loader_data != NULL, "shouldn't pass null"); int size = md->size(); diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp index 2f52e388d52..a45b213deaa 100644 --- a/hotspot/src/share/vm/memory/metaspace.cpp +++ b/hotspot/src/share/vm/memory/metaspace.cpp @@ -413,6 +413,7 @@ static bool should_commit_large_pages_when_reserving(size_t bytes) { VirtualSpaceNode::VirtualSpaceNode(size_t bytes) : _top(NULL), _next(NULL), _rs(), _container_count(0) { assert_is_size_aligned(bytes, Metaspace::reserve_alignment()); +#if INCLUDE_CDS // This allocates memory with mmap. For DumpSharedspaces, try to reserve // configurable address, generally at the top of the Java heap so other // memory addresses don't conflict. @@ -428,7 +429,9 @@ VirtualSpaceNode::VirtualSpaceNode(size_t bytes) : _top(NULL), _next(NULL), _rs( _rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages); } MetaspaceShared::set_shared_rs(&_rs); - } else { + } else +#endif + { bool large_pages = should_commit_large_pages_when_reserving(bytes); _rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages); @@ -2939,11 +2942,14 @@ void Metaspace::set_narrow_klass_base_and_shift(address metaspace_base, address // between the lower base and higher address. address lower_base; address higher_address; +#if INCLUDE_CDS if (UseSharedSpaces) { higher_address = MAX2((address)(cds_base + FileMapInfo::shared_spaces_size()), (address)(metaspace_base + compressed_class_space_size())); lower_base = MIN2(metaspace_base, cds_base); - } else { + } else +#endif + { higher_address = metaspace_base + compressed_class_space_size(); lower_base = metaspace_base; @@ -2964,6 +2970,7 @@ void Metaspace::set_narrow_klass_base_and_shift(address metaspace_base, address } } +#if INCLUDE_CDS // Return TRUE if the specified metaspace_base and cds_base are close enough // to work with compressed klass pointers. bool Metaspace::can_use_cds_with_metaspace_addr(char* metaspace_base, address cds_base) { @@ -2974,6 +2981,7 @@ bool Metaspace::can_use_cds_with_metaspace_addr(char* metaspace_base, address cd (address)(metaspace_base + compressed_class_space_size())); return ((uint64_t)(higher_address - lower_base) <= UnscaledClassSpaceMax); } +#endif // Try to allocate the metaspace at the requested addr. void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, address cds_base) { @@ -2993,6 +3001,7 @@ void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, a large_pages, requested_addr, 0); if (!metaspace_rs.is_reserved()) { +#if INCLUDE_CDS if (UseSharedSpaces) { size_t increment = align_size_up(1*G, _reserve_alignment); @@ -3007,7 +3016,7 @@ void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, a _reserve_alignment, large_pages, addr, 0); } } - +#endif // If no successful allocation then try to allocate the space anywhere. If // that fails then OOM doom. At this point we cannot try allocating the // metaspace as if UseCompressedClassPointers is off because too much @@ -3026,12 +3035,13 @@ void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, a // If we got here then the metaspace got allocated. MemTracker::record_virtual_memory_type((address)metaspace_rs.base(), mtClass); +#if INCLUDE_CDS // Verify that we can use shared spaces. Otherwise, turn off CDS. if (UseSharedSpaces && !can_use_cds_with_metaspace_addr(metaspace_rs.base(), cds_base)) { FileMapInfo::stop_sharing_and_unmap( "Could not allocate metaspace at a compatible address"); } - +#endif set_narrow_klass_base_and_shift((address)metaspace_rs.base(), UseSharedSpaces ? (address)cds_base : 0); @@ -3115,6 +3125,9 @@ void Metaspace::global_initialize() { MetaspaceShared::set_max_alignment(max_alignment); if (DumpSharedSpaces) { +#if INCLUDE_CDS + MetaspaceShared::estimate_regions_size(); + SharedReadOnlySize = align_size_up(SharedReadOnlySize, max_alignment); SharedReadWriteSize = align_size_up(SharedReadWriteSize, max_alignment); SharedMiscDataSize = align_size_up(SharedMiscDataSize, max_alignment); @@ -3152,23 +3165,22 @@ void Metaspace::global_initialize() { } Universe::set_narrow_klass_shift(0); -#endif - +#endif // _LP64 +#endif // INCLUDE_CDS } else { +#if INCLUDE_CDS // If using shared space, open the file that contains the shared space // and map in the memory before initializing the rest of metaspace (so // the addresses don't conflict) address cds_address = NULL; if (UseSharedSpaces) { FileMapInfo* mapinfo = new FileMapInfo(); - memset(mapinfo, 0, sizeof(FileMapInfo)); // Open the shared archive file, read and validate the header. If // initialization fails, shared spaces [UseSharedSpaces] are // disabled and the file is closed. // Map in spaces now also if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) { - FileMapInfo::set_current_info(mapinfo); cds_total = FileMapInfo::shared_spaces_size(); cds_address = (address)mapinfo->region_base(0); } else { @@ -3176,21 +3188,23 @@ void Metaspace::global_initialize() { "archive file not closed or shared spaces not disabled."); } } - +#endif // INCLUDE_CDS #ifdef _LP64 // If UseCompressedClassPointers is set then allocate the metaspace area // above the heap and above the CDS area (if it exists). if (using_class_space()) { if (UseSharedSpaces) { +#if INCLUDE_CDS char* cds_end = (char*)(cds_address + cds_total); cds_end = (char *)align_ptr_up(cds_end, _reserve_alignment); allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address); +#endif } else { char* base = (char*)align_ptr_up(Universe::heap()->reserved_region().end(), _reserve_alignment); allocate_metaspace_compressed_klass_ptrs(base, 0); } } -#endif +#endif // _LP64 // Initialize these before initializing the VirtualSpaceList _first_chunk_word_size = InitialBootClassLoaderMetaspaceSize / BytesPerWord; @@ -3380,6 +3394,10 @@ void Metaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) { assert(!SafepointSynchronize::is_at_safepoint() || Thread::current()->is_VM_thread(), "should be the VM thread"); + if (DumpSharedSpaces && PrintSharedSpaces) { + record_deallocation(ptr, vsm()->get_raw_word_size(word_size)); + } + MutexLockerEx ml(vsm()->lock(), Mutex::_no_safepoint_check_flag); if (word_size < TreeChunk >::min_size()) { @@ -3417,8 +3435,9 @@ MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, if (result == NULL) { report_out_of_shared_space(read_only ? SharedReadOnly : SharedReadWrite); } - - space->record_allocation(result, type, space->vsm()->get_raw_word_size(word_size)); + if (PrintSharedSpaces) { + space->record_allocation(result, type, space->vsm()->get_raw_word_size(word_size)); + } // Zero initialize. Copy::fill_to_aligned_words((HeapWord*)result, word_size, 0); @@ -3517,15 +3536,55 @@ const char* Metaspace::metadata_type_name(Metaspace::MetadataType mdtype) { void Metaspace::record_allocation(void* ptr, MetaspaceObj::Type type, size_t word_size) { assert(DumpSharedSpaces, "sanity"); - AllocRecord *rec = new AllocRecord((address)ptr, type, (int)word_size * HeapWordSize); + int byte_size = (int)word_size * HeapWordSize; + AllocRecord *rec = new AllocRecord((address)ptr, type, byte_size); + if (_alloc_record_head == NULL) { _alloc_record_head = _alloc_record_tail = rec; - } else { + } else if (_alloc_record_tail->_ptr + _alloc_record_tail->_byte_size == (address)ptr) { _alloc_record_tail->_next = rec; _alloc_record_tail = rec; + } else { + // slow linear search, but this doesn't happen that often, and only when dumping + for (AllocRecord *old = _alloc_record_head; old; old = old->_next) { + if (old->_ptr == ptr) { + assert(old->_type == MetaspaceObj::DeallocatedType, "sanity"); + int remain_bytes = old->_byte_size - byte_size; + assert(remain_bytes >= 0, "sanity"); + old->_type = type; + + if (remain_bytes == 0) { + delete(rec); + } else { + address remain_ptr = address(ptr) + byte_size; + rec->_ptr = remain_ptr; + rec->_byte_size = remain_bytes; + rec->_type = MetaspaceObj::DeallocatedType; + rec->_next = old->_next; + old->_byte_size = byte_size; + old->_next = rec; + } + return; + } + } + assert(0, "reallocating a freed pointer that was not recorded"); } } +void Metaspace::record_deallocation(void* ptr, size_t word_size) { + assert(DumpSharedSpaces, "sanity"); + + for (AllocRecord *rec = _alloc_record_head; rec; rec = rec->_next) { + if (rec->_ptr == ptr) { + assert(rec->_byte_size == (int)word_size * HeapWordSize, "sanity"); + rec->_type = MetaspaceObj::DeallocatedType; + return; + } + } + + assert(0, "deallocating a pointer that was not recorded"); +} + void Metaspace::iterate(Metaspace::AllocRecordClosure *closure) { assert(DumpSharedSpaces, "unimplemented for !DumpSharedSpaces"); diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp index 545bf0e7fbd..ef9ad65b088 100644 --- a/hotspot/src/share/vm/memory/metaspace.hpp +++ b/hotspot/src/share/vm/memory/metaspace.hpp @@ -171,9 +171,10 @@ class Metaspace : public CHeapObj { static const MetaspaceTracer* tracer() { return _tracer; } private: - // This is used by DumpSharedSpaces only, where only _vsm is used. So we will + // These 2 methods are used by DumpSharedSpaces only, where only _vsm is used. So we will // maintain a single list for now. void record_allocation(void* ptr, MetaspaceObj::Type type, size_t word_size); + void record_deallocation(void* ptr, size_t word_size); #ifdef _LP64 static void set_narrow_klass_base_and_shift(address metaspace_base, address cds_base); diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp index 8aff392b5a1..67c1618e3b5 100644 --- a/hotspot/src/share/vm/memory/metaspaceShared.cpp +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp @@ -26,6 +26,7 @@ #include "classfile/dictionary.hpp" #include "classfile/loaderConstraints.hpp" #include "classfile/placeholders.hpp" +#include "classfile/sharedClassUtil.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "code/codeCache.hpp" @@ -47,6 +48,10 @@ int MetaspaceShared::_max_alignment = 0; ReservedSpace* MetaspaceShared::_shared_rs = NULL; +bool MetaspaceShared::_link_classes_made_progress; +bool MetaspaceShared::_check_classes_made_progress; +bool MetaspaceShared::_has_error_classes; +bool MetaspaceShared::_archive_loading_failed = false; // Read/write a data stream for restoring/preserving metadata pointers and // miscellaneous data from/to the shared archive file. @@ -446,6 +451,23 @@ void VM_PopulateDumpSharedSpace::doit() { SystemDictionary::classes_do(collect_classes); tty->print_cr("Number of classes %d", _global_klass_objects->length()); + { + int num_type_array = 0, num_obj_array = 0, num_inst = 0; + for (int i = 0; i < _global_klass_objects->length(); i++) { + Klass* k = _global_klass_objects->at(i); + if (k->oop_is_instance()) { + num_inst ++; + } else if (k->oop_is_objArray()) { + num_obj_array ++; + } else { + assert(k->oop_is_typeArray(), "sanity"); + num_type_array ++; + } + } + tty->print_cr(" instance classes = %5d", num_inst); + tty->print_cr(" obj array classes = %5d", num_obj_array); + tty->print_cr(" type array classes = %5d", num_type_array); + } // Update all the fingerprints in the shared methods. tty->print("Calculating fingerprints ... "); @@ -611,38 +633,58 @@ void VM_PopulateDumpSharedSpace::doit() { #undef fmt_space } -static void link_shared_classes(Klass* obj, TRAPS) { + +void MetaspaceShared::link_one_shared_class(Klass* obj, TRAPS) { Klass* k = obj; if (k->oop_is_instance()) { InstanceKlass* ik = (InstanceKlass*) k; // Link the class to cause the bytecodes to be rewritten and the - // cpcache to be created. - if (ik->init_state() < InstanceKlass::linked) { - ik->link_class(THREAD); - guarantee(!HAS_PENDING_EXCEPTION, "exception in class rewriting"); + // cpcache to be created. Class verification is done according + // to -Xverify setting. + _link_classes_made_progress |= try_link_class(ik, THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class"); + } +} + +void MetaspaceShared::check_one_shared_class(Klass* k) { + if (k->oop_is_instance() && InstanceKlass::cast(k)->check_sharing_error_state()) { + _check_classes_made_progress = true; + } +} + +void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) { + // We need to iterate because verification may cause additional classes + // to be loaded. + do { + _link_classes_made_progress = false; + SystemDictionary::classes_do(link_one_shared_class, THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class"); + } while (_link_classes_made_progress); + + if (_has_error_classes) { + // Mark all classes whose super class or interfaces failed verification. + do { + // Not completely sure if we need to do this iteratively. Anyway, + // we should come here only if there are unverifiable classes, which + // shouldn't happen in normal cases. So better safe than sorry. + _check_classes_made_progress = false; + SystemDictionary::classes_do(check_one_shared_class); + } while (_check_classes_made_progress); + + if (IgnoreUnverifiableClassesDuringDump) { + // This is useful when running JCK or SQE tests. You should not + // enable this when running real apps. + SystemDictionary::remove_classes_in_error_state(); + } else { + tty->print_cr("Please remove the unverifiable classes from your class list and try again"); + exit(1); } } } - -// Support for a simple checksum of the contents of the class list -// file to prevent trivial tampering. The algorithm matches that in -// the MakeClassList program used by the J2SE build process. -#define JSUM_SEED ((jlong)CONST64(0xcafebabebabecafe)) -static jlong -jsum(jlong start, const char *buf, const int len) -{ - jlong h = start; - char *p = (char *)buf, *e = p + len; - while (p < e) { - char c = *p++; - if (c <= ' ') { - /* Skip spaces and control characters */ - continue; - } - h = 31 * h + c; - } - return h; +void MetaspaceShared::prepare_for_dumping() { + ClassLoader::initialize_shared_path(); + FileMapInfo::allocate_classpath_entry_table(); } // Preload classes from a list, populate the shared spaces and dump to a @@ -651,72 +693,112 @@ void MetaspaceShared::preload_and_dump(TRAPS) { TraceTime timer("Dump Shared Spaces", TraceStartupTime); ResourceMark rm; + tty->print_cr("Allocated shared space: %d bytes at " PTR_FORMAT, + MetaspaceShared::shared_rs()->size(), + MetaspaceShared::shared_rs()->base()); + // Preload classes to be shared. // Should use some os:: method rather than fopen() here. aB. - // Construct the path to the class list (in jre/lib) - // Walk up two directories from the location of the VM and - // optionally tack on "lib" (depending on platform) - char class_list_path[JVM_MAXPATHLEN]; - os::jvm_path(class_list_path, sizeof(class_list_path)); - for (int i = 0; i < 3; i++) { - char *end = strrchr(class_list_path, *os::file_separator()); - if (end != NULL) *end = '\0'; - } - int class_list_path_len = (int)strlen(class_list_path); - if (class_list_path_len >= 3) { - if (strcmp(class_list_path + class_list_path_len - 3, "lib") != 0) { - strcat(class_list_path, os::file_separator()); - strcat(class_list_path, "lib"); + const char* class_list_path; + if (SharedClassListFile == NULL) { + // Construct the path to the class list (in jre/lib) + // Walk up two directories from the location of the VM and + // optionally tack on "lib" (depending on platform) + char class_list_path_str[JVM_MAXPATHLEN]; + os::jvm_path(class_list_path_str, sizeof(class_list_path_str)); + for (int i = 0; i < 3; i++) { + char *end = strrchr(class_list_path_str, *os::file_separator()); + if (end != NULL) *end = '\0'; } + int class_list_path_len = (int)strlen(class_list_path_str); + if (class_list_path_len >= 3) { + if (strcmp(class_list_path_str + class_list_path_len - 3, "lib") != 0) { + strcat(class_list_path_str, os::file_separator()); + strcat(class_list_path_str, "lib"); + } + } + strcat(class_list_path_str, os::file_separator()); + strcat(class_list_path_str, "classlist"); + class_list_path = class_list_path_str; + } else { + class_list_path = SharedClassListFile; } - strcat(class_list_path, os::file_separator()); - strcat(class_list_path, "classlist"); + int class_count = 0; + GrowableArray* class_promote_order = new GrowableArray(); + + // sun.io.Converters + static const char obj_array_sig[] = "[[Ljava/lang/Object;"; + SymbolTable::new_permanent_symbol(obj_array_sig, THREAD); + + // java.util.HashMap + static const char map_entry_array_sig[] = "[Ljava/util/Map$Entry;"; + SymbolTable::new_permanent_symbol(map_entry_array_sig, THREAD); + + tty->print_cr("Loading classes to share ..."); + _has_error_classes = false; + class_count += preload_and_dump(class_list_path, class_promote_order, + THREAD); + if (ExtraSharedClassListFile) { + class_count += preload_and_dump(ExtraSharedClassListFile, class_promote_order, + THREAD); + } + tty->print_cr("Loading classes to share: done."); + + if (PrintSharedSpaces) { + tty->print_cr("Shared spaces: preloaded %d classes", class_count); + } + + // Rewrite and link classes + tty->print_cr("Rewriting and linking classes ..."); + + // Link any classes which got missed. This would happen if we have loaded classes that + // were not explicitly specified in the classlist. E.g., if an interface implemented by class K + // fails verification, all other interfaces that were not specified in the classlist but + // are implemented by K are not verified. + link_and_cleanup_shared_classes(CATCH); + tty->print_cr("Rewriting and linking classes: done"); + + // Create and dump the shared spaces. Everything so far is loaded + // with the null class loader. + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); + VM_PopulateDumpSharedSpace op(loader_data, class_promote_order); + VMThread::execute(&op); + + // Since various initialization steps have been undone by this process, + // it is not reasonable to continue running a java process. + exit(0); +} + +int MetaspaceShared::preload_and_dump(const char * class_list_path, + GrowableArray* class_promote_order, + TRAPS) { FILE* file = fopen(class_list_path, "r"); + char class_name[256]; + int class_count = 0; + if (file != NULL) { - jlong computed_jsum = JSUM_SEED; - jlong file_jsum = 0; - - char class_name[256]; - int class_count = 0; - GrowableArray* class_promote_order = new GrowableArray(); - - // sun.io.Converters - static const char obj_array_sig[] = "[[Ljava/lang/Object;"; - SymbolTable::new_permanent_symbol(obj_array_sig, THREAD); - - // java.util.HashMap - static const char map_entry_array_sig[] = "[Ljava/util/Map$Entry;"; - SymbolTable::new_permanent_symbol(map_entry_array_sig, THREAD); - - tty->print("Loading classes to share ... "); while ((fgets(class_name, sizeof class_name, file)) != NULL) { - if (*class_name == '#') { - jint fsh, fsl; - if (sscanf(class_name, "# %8x%8x\n", &fsh, &fsl) == 2) { - file_jsum = ((jlong)(fsh) << 32) | (fsl & 0xffffffff); - } - + if (*class_name == '#') { // comment continue; } // Remove trailing newline size_t name_len = strlen(class_name); - class_name[name_len-1] = '\0'; - - computed_jsum = jsum(computed_jsum, class_name, (const int)name_len - 1); + if (class_name[name_len-1] == '\n') { + class_name[name_len-1] = '\0'; + } // Got a class name - load it. TempNewSymbol class_name_symbol = SymbolTable::new_permanent_symbol(class_name, THREAD); guarantee(!HAS_PENDING_EXCEPTION, "Exception creating a symbol."); Klass* klass = SystemDictionary::resolve_or_null(class_name_symbol, THREAD); - guarantee(!HAS_PENDING_EXCEPTION, "Exception resolving a class."); + CLEAR_PENDING_EXCEPTION; if (klass != NULL) { if (PrintSharedSpaces && Verbose && WizardMode) { tty->print_cr("Shared spaces preloaded: %s", class_name); } - InstanceKlass* ik = InstanceKlass::cast(klass); // Should be class load order as per -XX:+TraceClassLoadingPreorder @@ -726,52 +808,15 @@ void MetaspaceShared::preload_and_dump(TRAPS) { // cpcache to be created. The linking is done as soon as classes // are loaded in order that the related data structures (klass and // cpCache) are located together. - - if (ik->init_state() < InstanceKlass::linked) { - ik->link_class(THREAD); - guarantee(!(HAS_PENDING_EXCEPTION), "exception in class rewriting"); - } - - // TODO: Resolve klasses in constant pool - ik->constants()->resolve_class_constants(THREAD); + try_link_class(ik, THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class"); class_count++; } else { - if (PrintSharedSpaces && Verbose && WizardMode) { - tty->cr(); - tty->print_cr(" Preload failed: %s", class_name); - } + //tty->print_cr("Preload failed: %s", class_name); } - file_jsum = 0; // Checksum must be on last line of file } - if (computed_jsum != file_jsum) { - tty->cr(); - tty->print_cr("Preload failed: checksum of class list was incorrect."); - exit(1); - } - - tty->print_cr("done. "); - - if (PrintSharedSpaces) { - tty->print_cr("Shared spaces: preloaded %d classes", class_count); - } - - // Rewrite and unlink classes. - tty->print("Rewriting and linking classes ... "); - - // Link any classes which got missed. (It's not quite clear why - // they got missed.) This iteration would be unsafe if we weren't - // single-threaded at this point; however we can't do it on the VM - // thread because it requires object allocation. - SystemDictionary::classes_do(link_shared_classes, CATCH); - tty->print_cr("done. "); - - // Create and dump the shared spaces. Everything so far is loaded - // with the null class loader. - ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); - VM_PopulateDumpSharedSpace op(loader_data, class_promote_order); - VMThread::execute(&op); - + fclose(file); } else { char errmsg[JVM_MAXPATHLEN]; os::lasterror(errmsg, JVM_MAXPATHLEN); @@ -779,11 +824,39 @@ void MetaspaceShared::preload_and_dump(TRAPS) { exit(1); } - // Since various initialization steps have been undone by this process, - // it is not reasonable to continue running a java process. - exit(0); + return class_count; } +// Returns true if the class's status has changed +bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) { + assert(DumpSharedSpaces, "should only be called during dumping"); + if (ik->init_state() < InstanceKlass::linked) { + bool saved = BytecodeVerificationLocal; + if (!SharedClassUtil::is_shared_boot_class(ik)) { + // The verification decision is based on BytecodeVerificationRemote + // for non-system classes. Since we are using the NULL classloader + // to load non-system classes during dumping, we need to temporarily + // change BytecodeVerificationLocal to be the same as + // BytecodeVerificationRemote. Note this can cause the parent system + // classes also being verified. The extra overhead is acceptable during + // dumping. + BytecodeVerificationLocal = BytecodeVerificationRemote; + } + ik->link_class(THREAD); + if (HAS_PENDING_EXCEPTION) { + ResourceMark rm; + tty->print_cr("Preload Error: Verification failed for %s", + ik->external_name()); + CLEAR_PENDING_EXCEPTION; + ik->set_in_error_state(); + _has_error_classes = true; + } + BytecodeVerificationLocal = saved; + return true; + } else { + return false; + } +} // Closure for serializing initialization data in from a data area // (ptr_array) read from the shared file. @@ -867,7 +940,8 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { (_rw_base = mapinfo->map_region(rw)) != NULL && (_md_base = mapinfo->map_region(md)) != NULL && (_mc_base = mapinfo->map_region(mc)) != NULL && - (image_alignment == (size_t)max_alignment())) { + (image_alignment == (size_t)max_alignment()) && + mapinfo->validate_classpath_entry_table()) { // Success (no need to do anything) return true; } else { @@ -884,7 +958,7 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { // If -Xshare:on is specified, print out the error message and exit VM, // otherwise, set UseSharedSpaces to false and continue. if (RequireSharedSpaces) { - vm_exit_during_initialization("Unable to use shared archive.", NULL); + vm_exit_during_initialization("Unable to use shared archive.", "Failed map_region for using -Xshare:on."); } else { FLAG_SET_DEFAULT(UseSharedSpaces, false); } @@ -984,6 +1058,20 @@ void MetaspaceShared::initialize_shared_spaces() { // Close the mapinfo file mapinfo->close(); + + if (PrintSharedArchiveAndExit) { + if (PrintSharedDictionary) { + tty->print_cr("\nShared classes:\n"); + SystemDictionary::print_shared(false); + } + if (_archive_loading_failed) { + tty->print_cr("archive is invalid"); + vm_exit(1); + } else { + tty->print_cr("archive is valid"); + vm_exit(0); + } + } } // JVM/TI RedefineClasses() support: @@ -999,3 +1087,49 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() { } return true; } + +int MetaspaceShared::count_class(const char* classlist_file) { + if (classlist_file == NULL) { + return 0; + } + char class_name[256]; + int class_count = 0; + FILE* file = fopen(classlist_file, "r"); + if (file != NULL) { + while ((fgets(class_name, sizeof class_name, file)) != NULL) { + if (*class_name == '#') { // comment + continue; + } + class_count++; + } + fclose(file); + } else { + char errmsg[JVM_MAXPATHLEN]; + os::lasterror(errmsg, JVM_MAXPATHLEN); + tty->print_cr("Loading classlist failed: %s", errmsg); + exit(1); + } + + return class_count; +} + +// the sizes are good for typical large applications that have a lot of shared +// classes +void MetaspaceShared::estimate_regions_size() { + int class_count = count_class(SharedClassListFile); + class_count += count_class(ExtraSharedClassListFile); + + if (class_count > LargeThresholdClassCount) { + if (class_count < HugeThresholdClassCount) { + SET_ESTIMATED_SIZE(Large, ReadOnly); + SET_ESTIMATED_SIZE(Large, ReadWrite); + SET_ESTIMATED_SIZE(Large, MiscData); + SET_ESTIMATED_SIZE(Large, MiscCode); + } else { + SET_ESTIMATED_SIZE(Huge, ReadOnly); + SET_ESTIMATED_SIZE(Huge, ReadWrite); + SET_ESTIMATED_SIZE(Huge, MiscData); + SET_ESTIMATED_SIZE(Huge, MiscCode); + } + } +} diff --git a/hotspot/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp index 12c8af510f0..cc514c05050 100644 --- a/hotspot/src/share/vm/memory/metaspaceShared.hpp +++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp @@ -30,6 +30,19 @@ #include "utilities/exceptions.hpp" #include "utilities/macros.hpp" +#define LargeSharedArchiveSize (300*M) +#define HugeSharedArchiveSize (800*M) +#define ReadOnlyRegionPercentage 0.4 +#define ReadWriteRegionPercentage 0.55 +#define MiscDataRegionPercentage 0.03 +#define MiscCodeRegionPercentage 0.02 +#define LargeThresholdClassCount 5000 +#define HugeThresholdClassCount 40000 + +#define SET_ESTIMATED_SIZE(type, region) \ + Shared ##region## Size = FLAG_IS_DEFAULT(Shared ##region## Size) ? \ + (uintx)(type ## SharedArchiveSize * region ## RegionPercentage) : Shared ## region ## Size + class FileMapInfo; // Class Data Sharing Support @@ -38,7 +51,10 @@ class MetaspaceShared : AllStatic { // CDS support static ReservedSpace* _shared_rs; static int _max_alignment; - + static bool _link_classes_made_progress; + static bool _check_classes_made_progress; + static bool _has_error_classes; + static bool _archive_loading_failed; public: enum { vtbl_list_size = 17, // number of entries in the shared space vtable list. @@ -67,7 +83,11 @@ class MetaspaceShared : AllStatic { NOT_CDS(return 0); } + static void prepare_for_dumping() NOT_CDS_RETURN; static void preload_and_dump(TRAPS) NOT_CDS_RETURN; + static int preload_and_dump(const char * class_list_path, + GrowableArray* class_promote_order, + TRAPS) NOT_CDS_RETURN; static ReservedSpace* shared_rs() { CDS_ONLY(return _shared_rs); @@ -78,6 +98,9 @@ class MetaspaceShared : AllStatic { CDS_ONLY(_shared_rs = rs;) } + static void set_archive_loading_failed() { + _archive_loading_failed = true; + } static bool map_shared_spaces(FileMapInfo* mapinfo) NOT_CDS_RETURN_(false); static void initialize_shared_spaces() NOT_CDS_RETURN; @@ -97,5 +120,13 @@ class MetaspaceShared : AllStatic { static bool remap_shared_readonly_as_readwrite() NOT_CDS_RETURN_(true); static void print_shared_spaces(); + + static bool try_link_class(InstanceKlass* ik, TRAPS); + static void link_one_shared_class(Klass* obj, TRAPS); + static void check_one_shared_class(Klass* obj); + static void link_and_cleanup_shared_classes(TRAPS); + + static int count_class(const char* classlist_file); + static void estimate_regions_size() NOT_CDS_RETURN; }; #endif // SHARE_VM_MEMORY_METASPACE_SHARED_HPP diff --git a/hotspot/src/share/vm/memory/resourceArea.hpp b/hotspot/src/share/vm/memory/resourceArea.hpp index e1cafe7f048..02dffc7a4b4 100644 --- a/hotspot/src/share/vm/memory/resourceArea.hpp +++ b/hotspot/src/share/vm/memory/resourceArea.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,11 +49,11 @@ class ResourceArea: public Arena { debug_only(static int _warned;) // to suppress multiple warnings public: - ResourceArea() { + ResourceArea() : Arena(mtThread) { debug_only(_nesting = 0;) } - ResourceArea(size_t init_size) : Arena(init_size) { + ResourceArea(size_t init_size) : Arena(mtThread, init_size) { debug_only(_nesting = 0;); } @@ -64,7 +64,7 @@ public: if (UseMallocOnly) { // use malloc, but save pointer in res. area for later freeing char** save = (char**)internal_malloc_4(sizeof(char*)); - return (*save = (char*)os::malloc(size, mtThread)); + return (*save = (char*)os::malloc(size, mtThread, CURRENT_PC)); } #endif return (char*)Amalloc(size, alloc_failmode); diff --git a/hotspot/src/share/vm/memory/sharedHeap.cpp b/hotspot/src/share/vm/memory/sharedHeap.cpp index e0a5d9961cb..5d268c38515 100644 --- a/hotspot/src/share/vm/memory/sharedHeap.cpp +++ b/hotspot/src/share/vm/memory/sharedHeap.cpp @@ -159,9 +159,9 @@ SharedHeap::StrongRootsScope::~StrongRootsScope() { Monitor* SharedHeap::StrongRootsScope::_lock = new Monitor(Mutex::leaf, "StrongRootsScope lock", false); void SharedHeap::StrongRootsScope::mark_worker_done_with_threads(uint n_workers) { - // The Thread work barrier is only needed by G1. + // The Thread work barrier is only needed by G1 Class Unloading. // No need to use the barrier if this is single-threaded code. - if (UseG1GC && n_workers > 0) { + if (UseG1GC && ClassUnloadingWithConcurrentMark && n_workers > 0) { uint new_value = (uint)Atomic::add(1, &_n_workers_done_with_threads); if (new_value == n_workers) { // This thread is last. Notify the others. @@ -172,6 +172,9 @@ void SharedHeap::StrongRootsScope::mark_worker_done_with_threads(uint n_workers) } void SharedHeap::StrongRootsScope::wait_until_all_workers_done_with_threads(uint n_workers) { + assert(UseG1GC, "Currently only used by G1"); + assert(ClassUnloadingWithConcurrentMark, "Currently only needed when doing G1 Class Unloading"); + // No need to use the barrier if this is single-threaded code. if (n_workers > 0 && (uint)_n_workers_done_with_threads != n_workers) { MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag); diff --git a/hotspot/src/share/vm/memory/space.cpp b/hotspot/src/share/vm/memory/space.cpp index 330dd3fdef9..41f1a72150a 100644 --- a/hotspot/src/share/vm/memory/space.cpp +++ b/hotspot/src/share/vm/memory/space.cpp @@ -28,6 +28,7 @@ #include "gc_implementation/shared/liveRange.hpp" #include "gc_implementation/shared/markSweep.hpp" #include "gc_implementation/shared/spaceDecorator.hpp" +#include "gc_interface/collectedHeap.inline.hpp" #include "memory/blockOffsetTable.inline.hpp" #include "memory/defNewGeneration.hpp" #include "memory/genCollectedHeap.hpp" @@ -720,6 +721,27 @@ inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size, } while (true); } +HeapWord* ContiguousSpace::allocate_aligned(size_t size) { + assert(Heap_lock->owned_by_self() || (SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread()), "not locked"); + HeapWord* end_value = end(); + + HeapWord* obj = CollectedHeap::align_allocation_or_fail(top(), end_value, SurvivorAlignmentInBytes); + if (obj == NULL) { + return NULL; + } + + if (pointer_delta(end_value, obj) >= size) { + HeapWord* new_top = obj + size; + set_top(new_top); + assert(is_ptr_aligned(obj, SurvivorAlignmentInBytes) && is_aligned(new_top), + "checking alignment"); + return obj; + } else { + set_top(obj); + return NULL; + } +} + // Requires locking. HeapWord* ContiguousSpace::allocate(size_t size) { return allocate_impl(size, end()); diff --git a/hotspot/src/share/vm/memory/space.hpp b/hotspot/src/share/vm/memory/space.hpp index 0653884e043..f78a8c7ab4b 100644 --- a/hotspot/src/share/vm/memory/space.hpp +++ b/hotspot/src/share/vm/memory/space.hpp @@ -526,6 +526,7 @@ class ContiguousSpace: public CompactibleSpace { // Allocation (return NULL if full) virtual HeapWord* allocate(size_t word_size); virtual HeapWord* par_allocate(size_t word_size); + HeapWord* allocate_aligned(size_t word_size); // Iteration void oop_iterate(ExtendedOopClosure* cl); diff --git a/hotspot/src/share/vm/memory/tenuredGeneration.cpp b/hotspot/src/share/vm/memory/tenuredGeneration.cpp index bc3f19cf849..c159d8092f2 100644 --- a/hotspot/src/share/vm/memory/tenuredGeneration.cpp +++ b/hotspot/src/share/vm/memory/tenuredGeneration.cpp @@ -53,9 +53,11 @@ TenuredGeneration::TenuredGeneration(ReservedSpace rs, // initialize performance counters const char* gen_name = "old"; + GenCollectorPolicy* gcp = (GenCollectorPolicy*) GenCollectedHeap::heap()->collector_policy(); // Generation Counters -- generation 1, 1 subspace - _gen_counters = new GenerationCounters(gen_name, 1, 1, &_virtual_space); + _gen_counters = new GenerationCounters(gen_name, 1, 1, + gcp->min_old_size(), gcp->max_old_size(), &_virtual_space); _gc_counters = new CollectorCounters("MSC", 1); diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp index 8ea866f4dec..a85ec509dff 100644 --- a/hotspot/src/share/vm/memory/universe.cpp +++ b/hotspot/src/share/vm/memory/universe.cpp @@ -26,6 +26,9 @@ #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.hpp" #include "classfile/javaClasses.hpp" +#if INCLUDE_CDS +#include "classfile/sharedClassUtil.hpp" +#endif #include "classfile/stringTable.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" @@ -34,6 +37,7 @@ #include "gc_interface/collectedHeap.inline.hpp" #include "interpreter/interpreter.hpp" #include "memory/cardTableModRefBS.hpp" +#include "memory/filemap.hpp" #include "memory/gcLocker.inline.hpp" #include "memory/genCollectedHeap.hpp" #include "memory/genRemSet.hpp" @@ -239,8 +243,9 @@ void Universe::check_alignment(uintx size, uintx alignment, const char* name) { void initialize_basic_type_klass(Klass* k, TRAPS) { Klass* ok = SystemDictionary::Object_klass(); if (UseSharedSpaces) { + ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); assert(k->super() == ok, "u3"); - k->restore_unshareable_info(CHECK); + k->restore_unshareable_info(loader_data, Handle(), CHECK); } else { k->initialize_supers(ok, CHECK); } @@ -666,6 +671,10 @@ jint universe_init() { SymbolTable::create_table(); StringTable::create_table(); ClassLoader::create_package_info_table(); + + if (DumpSharedSpaces) { + MetaspaceShared::prepare_for_dumping(); + } } return JNI_OK; @@ -1155,6 +1164,11 @@ bool universe_post_init() { MemoryService::add_metaspace_memory_pools(); MemoryService::set_universe_heap(Universe::_collectedHeap); +#if INCLUDE_CDS + if (UseSharedSpaces) { + SharedClassUtil::initialize(CHECK_false); + } +#endif return true; } diff --git a/hotspot/src/share/vm/oops/arrayKlass.cpp b/hotspot/src/share/vm/oops/arrayKlass.cpp index e8a367a3766..06f9bf136ad 100644 --- a/hotspot/src/share/vm/oops/arrayKlass.cpp +++ b/hotspot/src/share/vm/oops/arrayKlass.cpp @@ -78,7 +78,6 @@ ArrayKlass::ArrayKlass(Symbol* name) { set_dimension(1); set_higher_dimension(NULL); set_lower_dimension(NULL); - set_component_mirror(NULL); // Arrays don't add any new methods, so their vtable is the same size as // the vtable of klass Object. int vtable_size = Universe::base_vtable_size(); @@ -160,14 +159,6 @@ void ArrayKlass::array_klasses_do(void f(Klass* k)) { } } -// GC support - -void ArrayKlass::oops_do(OopClosure* cl) { - Klass::oops_do(cl); - - cl->do_oop(adr_component_mirror()); -} - // JVM support jint ArrayKlass::compute_modifier_flags(TRAPS) const { @@ -182,12 +173,11 @@ jint ArrayKlass::jvmti_class_status() const { void ArrayKlass::remove_unshareable_info() { Klass::remove_unshareable_info(); - // Clear the java mirror - set_component_mirror(NULL); } -void ArrayKlass::restore_unshareable_info(TRAPS) { - Klass::restore_unshareable_info(CHECK); +void ArrayKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { + assert(loader_data == ClassLoaderData::the_null_class_loader_data(), "array classes belong to null loader"); + Klass::restore_unshareable_info(loader_data, protection_domain, CHECK); // Klass recreates the component mirror also } @@ -216,10 +206,6 @@ void ArrayKlass::oop_print_on(oop obj, outputStream* st) { void ArrayKlass::verify_on(outputStream* st) { Klass::verify_on(st); - - if (component_mirror() != NULL) { - guarantee(component_mirror()->klass() != NULL, "should have a class"); - } } void ArrayKlass::oop_verify_on(oop obj, outputStream* st) { diff --git a/hotspot/src/share/vm/oops/arrayKlass.hpp b/hotspot/src/share/vm/oops/arrayKlass.hpp index f42d96e1679..39ae5768aaf 100644 --- a/hotspot/src/share/vm/oops/arrayKlass.hpp +++ b/hotspot/src/share/vm/oops/arrayKlass.hpp @@ -39,7 +39,6 @@ class ArrayKlass: public Klass { Klass* volatile _higher_dimension; // Refers the (n+1)'th-dimensional array (if present). Klass* volatile _lower_dimension; // Refers the (n-1)'th-dimensional array (if present). int _vtable_len; // size of vtable for this klass - oop _component_mirror; // component type, as a java/lang/Class protected: // Constructors @@ -70,13 +69,6 @@ class ArrayKlass: public Klass { // type of elements (T_OBJECT for both oop arrays and array-arrays) BasicType element_type() const { return layout_helper_element_type(layout_helper()); } - oop component_mirror() const { return _component_mirror; } - void set_component_mirror(oop m) { klass_oop_store(&_component_mirror, m); } - oop* adr_component_mirror() { return (oop*)&this->_component_mirror;} - - // Compiler/Interpreter offset - static ByteSize component_mirror_offset() { return in_ByteSize(offset_of(ArrayKlass, _component_mirror)); } - virtual Klass* java_super() const;//{ return SystemDictionary::Object_klass(); } // Allocation @@ -122,9 +114,6 @@ class ArrayKlass: public Klass { void array_klasses_do(void f(Klass* k)); void array_klasses_do(void f(Klass* k, TRAPS), TRAPS); - // GC support - virtual void oops_do(OopClosure* cl); - // Return a handle. static void complete_create_array_klass(ArrayKlass* k, KlassHandle super_klass, TRAPS); @@ -137,7 +126,7 @@ class ArrayKlass: public Klass { // CDS support - remove and restore oops from metadata. Oops are not shared. virtual void remove_unshareable_info(); - virtual void restore_unshareable_info(TRAPS); + virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); // Printing void print_on(outputStream* st) const; diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp index 2dba59265b1..b80cde69cc7 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp @@ -68,7 +68,7 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/parNew/parOopClosures.inline.hpp" #include "gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp" #include "gc_implementation/parallelScavenge/psPromotionManager.inline.hpp" @@ -2303,12 +2303,14 @@ void InstanceKlass::remove_unshareable_info() { array_klasses_do(remove_unshareable_in_class); } -void restore_unshareable_in_class(Klass* k, TRAPS) { - k->restore_unshareable_info(CHECK); +static void restore_unshareable_in_class(Klass* k, TRAPS) { + // Array classes have null protection domain. + // --> see ArrayKlass::complete_create_array_klass() + k->restore_unshareable_info(ClassLoaderData::the_null_class_loader_data(), Handle(), CHECK); } -void InstanceKlass::restore_unshareable_info(TRAPS) { - Klass::restore_unshareable_info(CHECK); +void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { + Klass::restore_unshareable_info(loader_data, protection_domain, CHECK); instanceKlassHandle ik(THREAD, this); Array* methods = ik->methods(); @@ -2334,6 +2336,38 @@ void InstanceKlass::restore_unshareable_info(TRAPS) { ik->array_klasses_do(restore_unshareable_in_class, CHECK); } +// returns true IFF is_in_error_state() has been changed as a result of this call. +bool InstanceKlass::check_sharing_error_state() { + assert(DumpSharedSpaces, "should only be called during dumping"); + bool old_state = is_in_error_state(); + + if (!is_in_error_state()) { + bool bad = false; + for (InstanceKlass* sup = java_super(); sup; sup = sup->java_super()) { + if (sup->is_in_error_state()) { + bad = true; + break; + } + } + if (!bad) { + Array* interfaces = transitive_interfaces(); + for (int i = 0; i < interfaces->length(); i++) { + Klass* iface = interfaces->at(i); + if (InstanceKlass::cast(iface)->is_in_error_state()) { + bad = true; + break; + } + } + } + + if (bad) { + set_in_error_state(); + } + } + + return (old_state != is_in_error_state()); +} + static void clear_all_breakpoints(Method* m) { m->clear_all_breakpoints(); } diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp index f2904057146..737bf70364b 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.hpp +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp @@ -980,6 +980,13 @@ class InstanceKlass: public Klass { u2 idnum_allocated_count() const { return _idnum_allocated_count; } +public: + void set_in_error_state() { + assert(DumpSharedSpaces, "only call this when dumping archive"); + _init_state = initialization_error; + } + bool check_sharing_error_state(); + private: // initialization state #ifdef ASSERT @@ -1038,7 +1045,7 @@ private: public: // CDS support - remove and restore oops from metadata. Oops are not shared. virtual void remove_unshareable_info(); - virtual void restore_unshareable_info(TRAPS); + virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); // jvm support jint compute_modifier_flags(TRAPS) const; diff --git a/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp b/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp index 2fd8e48397c..09a73f79de5 100644 --- a/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp @@ -42,7 +42,7 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/parNew/parOopClosures.inline.hpp" #include "gc_implementation/parallelScavenge/psPromotionManager.inline.hpp" #include "gc_implementation/parallelScavenge/psScavenge.inline.hpp" diff --git a/hotspot/src/share/vm/oops/instanceRefKlass.cpp b/hotspot/src/share/vm/oops/instanceRefKlass.cpp index 7340e5adc91..a149893148c 100644 --- a/hotspot/src/share/vm/oops/instanceRefKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceRefKlass.cpp @@ -38,7 +38,7 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/parNew/parOopClosures.inline.hpp" #include "gc_implementation/parallelScavenge/psPromotionManager.inline.hpp" #include "gc_implementation/parallelScavenge/psScavenge.inline.hpp" diff --git a/hotspot/src/share/vm/oops/klass.cpp b/hotspot/src/share/vm/oops/klass.cpp index 1df975312c7..810c8d729cd 100644 --- a/hotspot/src/share/vm/oops/klass.cpp +++ b/hotspot/src/share/vm/oops/klass.cpp @@ -184,6 +184,7 @@ Klass::Klass() { // The klass doesn't have any references at this point. clear_modified_oops(); clear_accumulated_modified_oops(); + _shared_class_path_index = -1; } jint Klass::array_layout_helper(BasicType etype) { @@ -500,13 +501,12 @@ void Klass::remove_unshareable_info() { set_class_loader_data(NULL); } -void Klass::restore_unshareable_info(TRAPS) { +void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) { TRACE_INIT_ID(this); // If an exception happened during CDS restore, some of these fields may already be // set. We leave the class on the CLD list, even if incomplete so that we don't // modify the CLD list outside a safepoint. if (class_loader_data() == NULL) { - ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data(); // Restore class_loader_data to the null class loader data set_class_loader_data(loader_data); @@ -515,12 +515,12 @@ void Klass::restore_unshareable_info(TRAPS) { loader_data->add_class(this); } - // Recreate the class mirror. The protection_domain is always null for - // boot loader, for now. + // Recreate the class mirror. // Only recreate it if not present. A previous attempt to restore may have // gotten an OOM later but keep the mirror if it was created. if (java_mirror() == NULL) { - java_lang_Class::create_mirror(this, Handle(NULL), Handle(NULL), CHECK); + Handle loader = loader_data->class_loader(); + java_lang_Class::create_mirror(this, loader, protection_domain, CHECK); } } diff --git a/hotspot/src/share/vm/oops/klass.hpp b/hotspot/src/share/vm/oops/klass.hpp index e443dddeed8..5af2104e594 100644 --- a/hotspot/src/share/vm/oops/klass.hpp +++ b/hotspot/src/share/vm/oops/klass.hpp @@ -147,6 +147,16 @@ class Klass : public Metadata { jbyte _modified_oops; // Card Table Equivalent (YC/CMS support) jbyte _accumulated_modified_oops; // Mod Union Equivalent (CMS support) +private: + // This is an index into FileMapHeader::_classpath_entry_table[], to + // associate this class with the JAR file where it's loaded from during + // dump time. If a class is not loaded from the shared archive, this field is + // -1. + jshort _shared_class_path_index; + + friend class SharedClassUtil; +protected: + // Constructor Klass(); @@ -253,6 +263,15 @@ class Klass : public Metadata { void clear_accumulated_modified_oops() { _accumulated_modified_oops = 0; } bool has_accumulated_modified_oops() { return _accumulated_modified_oops == 1; } + int shared_classpath_index() const { + return _shared_class_path_index; + }; + + void set_shared_classpath_index(int index) { + _shared_class_path_index = index; + }; + + protected: // internal accessors void set_subklass(Klass* s); void set_next_sibling(Klass* s); @@ -422,7 +441,7 @@ class Klass : public Metadata { public: // CDS support - remove and restore oops from metadata. Oops are not shared. virtual void remove_unshareable_info(); - virtual void restore_unshareable_info(TRAPS); + virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS); protected: // computes the subtype relationship @@ -546,7 +565,7 @@ class Klass : public Metadata { TRACE_DEFINE_KLASS_METHODS; // garbage collection support - virtual void oops_do(OopClosure* cl); + void oops_do(OopClosure* cl); // Iff the class loader (or mirror for anonymous classes) is alive the // Klass is considered alive. diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp index 54fa43a7212..589d758e38c 100644 --- a/hotspot/src/share/vm/oops/method.cpp +++ b/hotspot/src/share/vm/oops/method.cpp @@ -283,6 +283,13 @@ address Method::bcp_from(int bci) const { return bcp; } +address Method::bcp_from(address bcp) const { + if (is_native() && bcp == NULL) { + return code_base(); + } else { + return bcp; + } +} int Method::size(bool is_native) { // If native, then include pointers for native_function and signature_handler diff --git a/hotspot/src/share/vm/oops/method.hpp b/hotspot/src/share/vm/oops/method.hpp index 23752b4f8f5..16e7f7a813f 100644 --- a/hotspot/src/share/vm/oops/method.hpp +++ b/hotspot/src/share/vm/oops/method.hpp @@ -648,7 +648,8 @@ class Method : public Metadata { // Returns the byte code index from the byte code pointer int bci_from(address bcp) const; - address bcp_from(int bci) const; + address bcp_from(int bci) const; + address bcp_from(address bcp) const; int validate_bci_from_bcp(address bcp) const; int validate_bci(int bci) const; diff --git a/hotspot/src/share/vm/oops/methodData.cpp b/hotspot/src/share/vm/oops/methodData.cpp index 40dfee7fbea..fdf8c7fd112 100644 --- a/hotspot/src/share/vm/oops/methodData.cpp +++ b/hotspot/src/share/vm/oops/methodData.cpp @@ -86,7 +86,7 @@ ProfileData::ProfileData() { char* ProfileData::print_data_on_helper(const MethodData* md) const { DataLayout* dp = md->extra_data_base(); - DataLayout* end = md->extra_data_limit(); + DataLayout* end = md->args_data_limit(); stringStream ss; for (;; dp = MethodData::next_extra(dp)) { assert(dp < end, "moved past end of extra data"); @@ -1048,14 +1048,15 @@ void MethodData::post_initialize(BytecodeStream* stream) { stream->next(); data->post_initialize(stream, this); } - if (_parameters_type_data_di != -1) { + if (_parameters_type_data_di != no_parameters) { parameters_type_data()->post_initialize(NULL, this); } } // Initialize the MethodData* corresponding to a given method. MethodData::MethodData(methodHandle method, int size, TRAPS) - : _extra_data_lock(Monitor::leaf, "MDO extra data lock") { + : _extra_data_lock(Monitor::leaf, "MDO extra data lock"), + _parameters_type_data_di(parameters_uninitialized) { No_Safepoint_Verifier no_safepoint; // init function atomic wrt GC ResourceMark rm; // Set the method back-pointer. @@ -1111,7 +1112,7 @@ MethodData::MethodData(methodHandle method, int size, TRAPS) DataLayout *dp = data_layout_at(data_size + extra_size + arg_data_size); dp->initialize(DataLayout::parameters_type_data_tag, 0, parms_cell); } else { - _parameters_type_data_di = -1; + _parameters_type_data_di = no_parameters; } // Set an initial hint. Don't use set_hint_di() because @@ -1236,7 +1237,7 @@ DataLayout* MethodData::next_extra(DataLayout* dp) { } ProfileData* MethodData::bci_to_extra_data_helper(int bci, Method* m, DataLayout*& dp, bool concurrent) { - DataLayout* end = extra_data_limit(); + DataLayout* end = args_data_limit(); for (;; dp = next_extra(dp)) { assert(dp < end, "moved past end of extra data"); @@ -1285,7 +1286,7 @@ ProfileData* MethodData::bci_to_extra_data(int bci, Method* m, bool create_if_mi "code needs to be adjusted"); DataLayout* dp = extra_data_base(); - DataLayout* end = extra_data_limit(); + DataLayout* end = args_data_limit(); // Allocation in the extra data space has to be atomic because not // all entries have the same size and non atomic concurrent @@ -1330,7 +1331,7 @@ ProfileData* MethodData::bci_to_extra_data(int bci, Method* m, bool create_if_mi ArgInfoData *MethodData::arg_info() { DataLayout* dp = extra_data_base(); - DataLayout* end = extra_data_limit(); + DataLayout* end = args_data_limit(); for (; dp < end; dp = next_extra(dp)) { if (dp->tag() == DataLayout::arg_info_data_tag) return new ArgInfoData(dp); @@ -1357,7 +1358,7 @@ void MethodData::print_value_on(outputStream* st) const { void MethodData::print_data_on(outputStream* st) const { ResourceMark rm; ProfileData* data = first_data(); - if (_parameters_type_data_di != -1) { + if (_parameters_type_data_di != no_parameters) { parameters_type_data()->print_data_on(st); } for ( ; is_valid(data); data = next_data(data)) { @@ -1367,7 +1368,7 @@ void MethodData::print_data_on(outputStream* st) const { } st->print_cr("--- Extra data:"); DataLayout* dp = extra_data_base(); - DataLayout* end = extra_data_limit(); + DataLayout* end = args_data_limit(); for (;; dp = next_extra(dp)) { assert(dp < end, "moved past end of extra data"); // No need for "OrderAccess::load_acquire" ops, @@ -1565,7 +1566,7 @@ public: // redefined method void MethodData::clean_extra_data(CleanExtraDataClosure* cl) { DataLayout* dp = extra_data_base(); - DataLayout* end = extra_data_limit(); + DataLayout* end = args_data_limit(); int shift = 0; for (; dp < end; dp = next_extra(dp)) { @@ -1610,7 +1611,7 @@ void MethodData::clean_extra_data(CleanExtraDataClosure* cl) { void MethodData::verify_extra_data_clean(CleanExtraDataClosure* cl) { #ifdef ASSERT DataLayout* dp = extra_data_base(); - DataLayout* end = extra_data_limit(); + DataLayout* end = args_data_limit(); for (; dp < end; dp = next_extra(dp)) { switch(dp->tag()) { diff --git a/hotspot/src/share/vm/oops/methodData.hpp b/hotspot/src/share/vm/oops/methodData.hpp index ad290402d90..edb82ae9ddb 100644 --- a/hotspot/src/share/vm/oops/methodData.hpp +++ b/hotspot/src/share/vm/oops/methodData.hpp @@ -2107,7 +2107,12 @@ private: // data index for the area dedicated to parameters. -1 if no // parameter profiling. + enum { no_parameters = -2, parameters_uninitialized = -1 }; int _parameters_type_data_di; + int parameters_size_in_bytes() const { + ParametersTypeData* param = parameters_type_data(); + return param == NULL ? 0 : param->size_in_bytes(); + } // Beginning of the data entries intptr_t _data[1]; @@ -2130,7 +2135,7 @@ private: // Helper for data_at DataLayout* limit_data_position() const { - return (DataLayout*)((address)data_base() + _data_size); + return data_layout_at(_data_size); } bool out_of_bounds(int data_index) const { return data_index >= data_size(); @@ -2371,10 +2376,11 @@ public: } // Add a handful of extra data records, for trap tracking. - DataLayout* extra_data_base() const { return limit_data_position(); } + DataLayout* extra_data_base() const { return limit_data_position(); } DataLayout* extra_data_limit() const { return (DataLayout*)((address)this + size_in_bytes()); } - int extra_data_size() const { return (address)extra_data_limit() - - (address)extra_data_base(); } + DataLayout* args_data_limit() const { return (DataLayout*)((address)this + size_in_bytes() - + parameters_size_in_bytes()); } + int extra_data_size() const { return (address)extra_data_limit() - (address)extra_data_base(); } static DataLayout* next_extra(DataLayout* dp); // Return (uint)-1 for overflow. @@ -2429,11 +2435,12 @@ public: // Return pointer to area dedicated to parameters in MDO ParametersTypeData* parameters_type_data() const { - return _parameters_type_data_di != -1 ? data_layout_at(_parameters_type_data_di)->data_in()->as_ParametersTypeData() : NULL; + assert(_parameters_type_data_di != parameters_uninitialized, "called too early"); + return _parameters_type_data_di != no_parameters ? data_layout_at(_parameters_type_data_di)->data_in()->as_ParametersTypeData() : NULL; } int parameters_type_data_di() const { - assert(_parameters_type_data_di != -1, "no args type data"); + assert(_parameters_type_data_di != parameters_uninitialized && _parameters_type_data_di != no_parameters, "no args type data"); return _parameters_type_data_di; } @@ -2480,8 +2487,8 @@ public: static bool profile_return_jsr292_only(); void clean_method_data(BoolObjectClosure* is_alive); - void clean_weak_method_links(); + Mutex* extra_data_lock() { return &_extra_data_lock; } }; #endif // SHARE_VM_OOPS_METHODDATAOOP_HPP diff --git a/hotspot/src/share/vm/oops/objArrayKlass.cpp b/hotspot/src/share/vm/oops/objArrayKlass.cpp index 0d17ce64dfd..a4c51d09b1f 100644 --- a/hotspot/src/share/vm/oops/objArrayKlass.cpp +++ b/hotspot/src/share/vm/oops/objArrayKlass.cpp @@ -51,7 +51,7 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/parNew/parOopClosures.inline.hpp" #include "gc_implementation/parallelScavenge/psCompactionManager.hpp" #include "gc_implementation/parallelScavenge/psPromotionManager.inline.hpp" diff --git a/hotspot/src/share/vm/oops/oop.pcgc.inline.hpp b/hotspot/src/share/vm/oops/oop.pcgc.inline.hpp index 5a8d4d0afaf..b613e4ee73f 100644 --- a/hotspot/src/share/vm/oops/oop.pcgc.inline.hpp +++ b/hotspot/src/share/vm/oops/oop.pcgc.inline.hpp @@ -55,8 +55,6 @@ inline void oopDesc::follow_contents(ParCompactionManager* cm) { klass()->oop_follow_contents(cm, this); } -// Used by parallel old GC. - inline oop oopDesc::forward_to_atomic(oop p) { assert(ParNewGeneration::is_legal_forward_ptr(p), "illegal forwarding pointer value."); diff --git a/hotspot/src/share/vm/opto/c2_globals.hpp b/hotspot/src/share/vm/opto/c2_globals.hpp index d745cbb022b..e4a5e20b43f 100644 --- a/hotspot/src/share/vm/opto/c2_globals.hpp +++ b/hotspot/src/share/vm/opto/c2_globals.hpp @@ -650,6 +650,9 @@ product(bool, UseMathExactIntrinsics, true, \ "Enables intrinsification of various java.lang.Math functions") \ \ + product(bool, UseMultiplyToLenIntrinsic, false, \ + "Enables intrinsification of BigInteger.multiplyToLen()") \ + \ product(bool, UseTypeSpeculation, true, \ "Speculatively propagate types from profiles") \ \ diff --git a/hotspot/src/share/vm/opto/callGenerator.cpp b/hotspot/src/share/vm/opto/callGenerator.cpp index 246446167ca..0b3ee7a7279 100644 --- a/hotspot/src/share/vm/opto/callGenerator.cpp +++ b/hotspot/src/share/vm/opto/callGenerator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2014, 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 @@ -357,7 +357,7 @@ void LateInlineCallGenerator::do_late_inline() { // Make sure the state is a MergeMem for parsing. if (!map->in(TypeFunc::Memory)->is_MergeMem()) { - Node* mem = MergeMemNode::make(C, map->in(TypeFunc::Memory)); + Node* mem = MergeMemNode::make(map->in(TypeFunc::Memory)); C->initial_gvn()->set_type_bottom(mem); map->set_req(TypeFunc::Memory, mem); } diff --git a/hotspot/src/share/vm/opto/callnode.cpp b/hotspot/src/share/vm/opto/callnode.cpp index 275052bf8a9..ccf23118bb3 100644 --- a/hotspot/src/share/vm/opto/callnode.cpp +++ b/hotspot/src/share/vm/opto/callnode.cpp @@ -688,7 +688,7 @@ Node *CallNode::match( const ProjNode *proj, const Matcher *match ) { return new MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj); case TypeFunc::Parms+1: // For LONG & DOUBLE returns - assert(tf()->_range->field_at(TypeFunc::Parms+1) == Type::HALF, ""); + assert(tf()->range()->field_at(TypeFunc::Parms+1) == Type::HALF, ""); // 2nd half of doubles and longs return new MachProjNode(this,proj->_con, RegMask::Empty, (uint)OptoReg::Bad); @@ -778,7 +778,7 @@ bool CallNode::has_non_debug_use(Node *n) { } // Returns the unique CheckCastPP of a call -// or 'this' if there are several CheckCastPP +// or 'this' if there are several CheckCastPP or unexpected uses // or returns NULL if there is no one. Node *CallNode::result_cast() { Node *cast = NULL; @@ -794,6 +794,13 @@ Node *CallNode::result_cast() { return this; // more than 1 CheckCastPP } cast = use; + } else if (!use->is_Initialize() && + !use->is_AddP()) { + // Expected uses are restricted to a CheckCastPP, an Initialize + // node, and AddP nodes. If we encounter any other use (a Phi + // node can be seen in rare cases) return this to prevent + // incorrect optimizations. + return this; } } return cast; @@ -1808,3 +1815,57 @@ Node *UnlockNode::Ideal(PhaseGVN *phase, bool can_reshape) { } return result; } + +ArrayCopyNode::ArrayCopyNode(Compile* C, bool alloc_tightly_coupled) + : CallNode(arraycopy_type(), NULL, TypeRawPtr::BOTTOM), _alloc_tightly_coupled(alloc_tightly_coupled), _kind(ArrayCopy) { + init_class_id(Class_ArrayCopy); + init_flags(Flag_is_macro); + C->add_macro_node(this); +} + +uint ArrayCopyNode::size_of() const { return sizeof(*this); } + +ArrayCopyNode* ArrayCopyNode::make(GraphKit* kit, bool may_throw, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* length, + bool alloc_tightly_coupled, + Node* src_klass, Node* dest_klass, + Node* src_length, Node* dest_length) { + + ArrayCopyNode* ac = new ArrayCopyNode(kit->C, alloc_tightly_coupled); + Node* prev_mem = kit->set_predefined_input_for_runtime_call(ac); + + ac->init_req(ArrayCopyNode::Src, src); + ac->init_req(ArrayCopyNode::SrcPos, src_offset); + ac->init_req(ArrayCopyNode::Dest, dest); + ac->init_req(ArrayCopyNode::DestPos, dest_offset); + ac->init_req(ArrayCopyNode::Length, length); + ac->init_req(ArrayCopyNode::SrcLen, src_length); + ac->init_req(ArrayCopyNode::DestLen, dest_length); + ac->init_req(ArrayCopyNode::SrcKlass, src_klass); + ac->init_req(ArrayCopyNode::DestKlass, dest_klass); + + if (may_throw) { + ac->set_req(TypeFunc::I_O , kit->i_o()); + kit->add_safepoint_edges(ac, false); + } + + return ac; +} + +void ArrayCopyNode::connect_outputs(GraphKit* kit) { + kit->set_all_memory_call(this, true); + kit->set_control(kit->gvn().transform(new ProjNode(this,TypeFunc::Control))); + kit->set_i_o(kit->gvn().transform(new ProjNode(this, TypeFunc::I_O))); + kit->make_slow_call_ex(this, kit->env()->Throwable_klass(), true); + kit->set_all_memory_call(this); +} + +#ifndef PRODUCT +const char* ArrayCopyNode::_kind_names[] = {"arraycopy", "arraycopy, validated arguments", "clone", "oop array clone", "CopyOf", "CopyOfRange"}; +void ArrayCopyNode::dump_spec(outputStream *st) const { + CallNode::dump_spec(st); + st->print(" (%s%s)", _kind_names[_kind], _alloc_tightly_coupled ? ", tightly coupled allocation" : ""); +} +#endif diff --git a/hotspot/src/share/vm/opto/callnode.hpp b/hotspot/src/share/vm/opto/callnode.hpp index 6eb78b0cb7b..8c57610919d 100644 --- a/hotspot/src/share/vm/opto/callnode.hpp +++ b/hotspot/src/share/vm/opto/callnode.hpp @@ -1063,4 +1063,108 @@ public: virtual bool guaranteed_safepoint() { return false; } }; +class GraphKit; + +class ArrayCopyNode : public CallNode { +private: + + // What kind of arraycopy variant is this? + enum { + ArrayCopy, // System.arraycopy() + ArrayCopyNoTest, // System.arraycopy(), all arguments validated + CloneBasic, // A clone that can be copied by 64 bit chunks + CloneOop, // An oop array clone + CopyOf, // Arrays.copyOf() + CopyOfRange // Arrays.copyOfRange() + } _kind; + +#ifndef PRODUCT + static const char* _kind_names[CopyOfRange+1]; +#endif + // Is the alloc obtained with + // AllocateArrayNode::Ideal_array_allocation() tighly coupled + // (arraycopy follows immediately the allocation)? + // We cache the result of LibraryCallKit::tightly_coupled_allocation + // here because it's much easier to find whether there's a tightly + // couple allocation at parse time than at macro expansion time. At + // macro expansion time, for every use of the allocation node we + // would need to figure out whether it happens after the arraycopy (and + // can be ignored) or between the allocation and the arraycopy. At + // parse time, it's straightforward because whatever happens after + // the arraycopy is not parsed yet so doesn't exist when + // LibraryCallKit::tightly_coupled_allocation() is called. + bool _alloc_tightly_coupled; + + static const TypeFunc* arraycopy_type() { + const Type** fields = TypeTuple::fields(ParmLimit - TypeFunc::Parms); + fields[Src] = TypeInstPtr::BOTTOM; + fields[SrcPos] = TypeInt::INT; + fields[Dest] = TypeInstPtr::BOTTOM; + fields[DestPos] = TypeInt::INT; + fields[Length] = TypeInt::INT; + fields[SrcLen] = TypeInt::INT; + fields[DestLen] = TypeInt::INT; + fields[SrcKlass] = TypeKlassPtr::BOTTOM; + fields[DestKlass] = TypeKlassPtr::BOTTOM; + const TypeTuple *domain = TypeTuple::make(ParmLimit, fields); + + // create result type (range) + fields = TypeTuple::fields(0); + + const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0, fields); + + return TypeFunc::make(domain, range); + } + + ArrayCopyNode(Compile* C, bool alloc_tightly_coupled); + +public: + + enum { + Src = TypeFunc::Parms, + SrcPos, + Dest, + DestPos, + Length, + SrcLen, + DestLen, + SrcKlass, + DestKlass, + ParmLimit + }; + + static ArrayCopyNode* make(GraphKit* kit, bool may_throw, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* length, + bool alloc_tightly_coupled, + Node* src_klass = NULL, Node* dest_klass = NULL, + Node* src_length = NULL, Node* dest_length = NULL); + + void connect_outputs(GraphKit* kit); + + bool is_arraycopy() const { return _kind == ArrayCopy; } + bool is_arraycopy_notest() const { return _kind == ArrayCopyNoTest; } + bool is_clonebasic() const { return _kind == CloneBasic; } + bool is_cloneoop() const { return _kind == CloneOop; } + bool is_copyof() const { return _kind == CopyOf; } + bool is_copyofrange() const { return _kind == CopyOfRange; } + + void set_arraycopy() { _kind = ArrayCopy; } + void set_arraycopy_notest() { _kind = ArrayCopyNoTest; } + void set_clonebasic() { _kind = CloneBasic; } + void set_cloneoop() { _kind = CloneOop; } + void set_copyof() { _kind = CopyOf; } + void set_copyofrange() { _kind = CopyOfRange; } + + virtual int Opcode() const; + virtual uint size_of() const; // Size is bigger + virtual bool guaranteed_safepoint() { return false; } + + bool is_alloc_tightly_coupled() const { return _alloc_tightly_coupled; } + +#ifndef PRODUCT + virtual void dump_spec(outputStream *st) const; +#endif +}; #endif // SHARE_VM_OPTO_CALLNODE_HPP diff --git a/hotspot/src/share/vm/opto/cfgnode.cpp b/hotspot/src/share/vm/opto/cfgnode.cpp index d99118edc83..ab633c106a6 100644 --- a/hotspot/src/share/vm/opto/cfgnode.cpp +++ b/hotspot/src/share/vm/opto/cfgnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -108,6 +108,7 @@ static Node *merge_region(RegionNode *region, PhaseGVN *phase) { rreq++; // One more input to Region } // Found a region to merge into Region + igvn->_worklist.push(r); // Clobber pointer to the now dead 'r' region->set_req(i, phase->C->top()); } @@ -449,6 +450,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Remove TOP or NULL input paths. If only 1 input path remains, this Region // degrades to a copy. bool add_to_worklist = false; + bool modified = false; int cnt = 0; // Count of values merging DEBUG_ONLY( int cnt_orig = req(); ) // Save original inputs count int del_it = 0; // The last input path we delete @@ -459,6 +461,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Remove useless control copy inputs if( n->is_Region() && n->as_Region()->is_copy() ) { set_req(i, n->nonnull_req()); + modified = true; i--; continue; } @@ -466,12 +469,14 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node *call = n->in(0); if (call->is_Call() && call->as_Call()->entry_point() == OptoRuntime::rethrow_stub()) { set_req(i, call->in(0)); + modified = true; i--; continue; } } if( phase->type(n) == Type::TOP ) { set_req(i, NULL); // Ignore TOP inputs + modified = true; i--; continue; } @@ -691,7 +696,7 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) { } } - return NULL; + return modified ? this : NULL; } @@ -1871,7 +1876,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { igvn->register_new_node_with_optimizer(new_base); hook->add_req(new_base); } - MergeMemNode* result = MergeMemNode::make(phase->C, new_base); + MergeMemNode* result = MergeMemNode::make(new_base); for (uint i = 1; i < req(); ++i) { Node *ii = in(i); if (ii->is_MergeMem()) { diff --git a/hotspot/src/share/vm/opto/chaitin.cpp b/hotspot/src/share/vm/opto/chaitin.cpp index 822d83a9c23..50c7d596bf2 100644 --- a/hotspot/src/share/vm/opto/chaitin.cpp +++ b/hotspot/src/share/vm/opto/chaitin.cpp @@ -1620,7 +1620,7 @@ void PhaseChaitin::fixup_spills() { C->check_node_count(0, "out of nodes fixing spills"); if (C->failing()) return; // Transform node - MachNode *cisc = mach->cisc_version(stk_offset, C)->as_Mach(); + MachNode *cisc = mach->cisc_version(stk_offset)->as_Mach(); cisc->set_req(inp,fp); // Base register is frame pointer if( cisc->oper_input_base() > 1 && mach->oper_input_base() <= 1 ) { assert( cisc->oper_input_base() == 2, "Only adding one edge"); diff --git a/hotspot/src/share/vm/opto/classes.hpp b/hotspot/src/share/vm/opto/classes.hpp index 54a63db1a98..238e3491862 100644 --- a/hotspot/src/share/vm/opto/classes.hpp +++ b/hotspot/src/share/vm/opto/classes.hpp @@ -37,6 +37,7 @@ macro(Allocate) macro(AllocateArray) macro(AndI) macro(AndL) +macro(ArrayCopy) macro(AryEq) macro(AtanD) macro(Binary) diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp index 585f9315241..a70e538d744 100644 --- a/hotspot/src/share/vm/opto/compile.cpp +++ b/hotspot/src/share/vm/opto/compile.cpp @@ -647,6 +647,10 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr _printer(IdealGraphPrinter::printer()), #endif _congraph(NULL), + _comp_arena(mtCompiler), + _node_arena(mtCompiler), + _old_arena(mtCompiler), + _Compile_types(mtCompiler), _replay_inline_data(NULL), _late_inlines(comp_arena(), 2, 0, NULL), _string_late_inlines(comp_arena(), 2, 0, NULL), @@ -954,6 +958,10 @@ Compile::Compile( ciEnv* ci_env, _in_dump_cnt(0), _printer(NULL), #endif + _comp_arena(mtCompiler), + _node_arena(mtCompiler), + _old_arena(mtCompiler), + _Compile_types(mtCompiler), _dead_node_list(comp_arena()), _dead_node_count(0), _congraph(NULL), @@ -1039,6 +1047,7 @@ void Compile::Init(int aliaslevel) { _node_note_array = NULL; _default_node_notes = NULL; + DEBUG_ONLY( _modified_nodes = NULL; ) // Used in Optimize() _immutable_memory = NULL; // filled in at first inquiry @@ -1247,6 +1256,18 @@ void Compile::print_missing_nodes() { } } } +void Compile::record_modified_node(Node* n) { + if (_modified_nodes != NULL && !_inlining_incrementally && + n->outcnt() != 0 && !n->is_Con()) { + _modified_nodes->push(n); + } +} + +void Compile::remove_modified_node(Node* n) { + if (_modified_nodes != NULL) { + _modified_nodes->remove(n); + } +} #endif #ifndef PRODUCT @@ -2035,6 +2056,9 @@ void Compile::Optimize() { // Iterative Global Value Numbering, including ideal transforms // Initialize IterGVN with types and values from parse-time GVN PhaseIterGVN igvn(initial_gvn()); +#ifdef ASSERT + _modified_nodes = new (comp_arena()) Unique_Node_List(comp_arena()); +#endif { NOT_PRODUCT( TracePhase t2("iterGVN", &_t_iterGVN, TimeCompiler); ) igvn.optimize(); @@ -2197,6 +2221,7 @@ void Compile::Optimize() { } } + DEBUG_ONLY( _modified_nodes = NULL; ) } // (End scope of igvn; run destructor if necessary for asserts.) process_print_inlining(); @@ -2825,7 +2850,7 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { // oops implicit null check is not generated. // This will allow to generate normal oop implicit null check. if (Matcher::gen_narrow_oop_implicit_null_checks()) - new_in2 = ConNode::make(this, TypeNarrowOop::NULL_PTR); + new_in2 = ConNode::make(TypeNarrowOop::NULL_PTR); // // This transformation together with CastPP transformation above // will generated code for implicit NULL checks for compressed oops. @@ -2864,9 +2889,9 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { // NullCheck base_reg // } else if (t->isa_oopptr()) { - new_in2 = ConNode::make(this, t->make_narrowoop()); + new_in2 = ConNode::make(t->make_narrowoop()); } else if (t->isa_klassptr()) { - new_in2 = ConNode::make(this, t->make_narrowklass()); + new_in2 = ConNode::make(t->make_narrowklass()); } } if (new_in2 != NULL) { @@ -2899,11 +2924,11 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { const Type* t = in1->bottom_type(); if (t == TypePtr::NULL_PTR) { assert(t->isa_oopptr(), "null klass?"); - n->subsume_by(ConNode::make(this, TypeNarrowOop::NULL_PTR), this); + n->subsume_by(ConNode::make(TypeNarrowOop::NULL_PTR), this); } else if (t->isa_oopptr()) { - n->subsume_by(ConNode::make(this, t->make_narrowoop()), this); + n->subsume_by(ConNode::make(t->make_narrowoop()), this); } else if (t->isa_klassptr()) { - n->subsume_by(ConNode::make(this, t->make_narrowklass()), this); + n->subsume_by(ConNode::make(t->make_narrowklass()), this); } } if (in1->outcnt() == 0) { @@ -2964,7 +2989,7 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { if (d) { // Replace them with a fused divmod if supported if (Matcher::has_match_rule(Op_DivModI)) { - DivModINode* divmod = DivModINode::make(this, n); + DivModINode* divmod = DivModINode::make(n); d->subsume_by(divmod->div_proj(), this); n->subsume_by(divmod->mod_proj(), this); } else { @@ -2984,7 +3009,7 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { if (d) { // Replace them with a fused divmod if supported if (Matcher::has_match_rule(Op_DivModL)) { - DivModLNode* divmod = DivModLNode::make(this, n); + DivModLNode* divmod = DivModLNode::make(n); d->subsume_by(divmod->div_proj(), this); n->subsume_by(divmod->mod_proj(), this); } else { @@ -3010,7 +3035,7 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { if (n->req()-1 > 2) { // Replace many operand PackNodes with a binary tree for matching PackNode* p = (PackNode*) n; - Node* btp = p->binary_tree_pack(this, 1, n->req()); + Node* btp = p->binary_tree_pack(1, n->req()); n->subsume_by(btp, this); } break; @@ -3035,11 +3060,11 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) { if (t != NULL && t->is_con()) { juint shift = t->get_con(); if (shift > mask) { // Unsigned cmp - n->set_req(2, ConNode::make(this, TypeInt::make(shift & mask))); + n->set_req(2, ConNode::make(TypeInt::make(shift & mask))); } } else { if (t == NULL || t->_lo < 0 || t->_hi > (int)mask) { - Node* shift = new AndINode(in2, ConNode::make(this, TypeInt::make(mask))); + Node* shift = new AndINode(in2, ConNode::make(TypeInt::make(mask))); n->set_req(2, shift); } } @@ -3770,6 +3795,56 @@ void Compile::ConstantTable::fill_jump_table(CodeBuffer& cb, MachConstantNode* n } } +//----------------------------static_subtype_check----------------------------- +// Shortcut important common cases when superklass is exact: +// (0) superklass is java.lang.Object (can occur in reflective code) +// (1) subklass is already limited to a subtype of superklass => always ok +// (2) subklass does not overlap with superklass => always fail +// (3) superklass has NO subtypes and we can check with a simple compare. +int Compile::static_subtype_check(ciKlass* superk, ciKlass* subk) { + if (StressReflectiveCode) { + return SSC_full_test; // Let caller generate the general case. + } + + if (superk == env()->Object_klass()) { + return SSC_always_true; // (0) this test cannot fail + } + + ciType* superelem = superk; + if (superelem->is_array_klass()) + superelem = superelem->as_array_klass()->base_element_type(); + + if (!subk->is_interface()) { // cannot trust static interface types yet + if (subk->is_subtype_of(superk)) { + return SSC_always_true; // (1) false path dead; no dynamic test needed + } + if (!(superelem->is_klass() && superelem->as_klass()->is_interface()) && + !superk->is_subtype_of(subk)) { + return SSC_always_false; + } + } + + // If casting to an instance klass, it must have no subtypes + if (superk->is_interface()) { + // Cannot trust interfaces yet. + // %%% S.B. superk->nof_implementors() == 1 + } else if (superelem->is_instance_klass()) { + ciInstanceKlass* ik = superelem->as_instance_klass(); + if (!ik->has_subklass() && !ik->is_interface()) { + if (!ik->is_final()) { + // Add a dependency if there is a chance of a later subclass. + dependencies()->assert_leaf_type(ik); + } + return SSC_easy_test; // (3) caller can do a simple ptr comparison + } + } else { + // A primitive array type has no subtypes. + return SSC_easy_test; // (3) caller can do a simple ptr comparison + } + + return SSC_full_test; +} + // The message about the current inlining is accumulated in // _print_inlining_stream and transfered into the _print_inlining_list // once we know whether inlining succeeds or not. For regular @@ -4031,6 +4106,7 @@ void Compile::cleanup_expensive_nodes(PhaseIterGVN &igvn) { int j = 0; int identical = 0; int i = 0; + bool modified = false; for (; i < _expensive_nodes->length()-1; i++) { assert(j <= i, "can't write beyond current index"); if (_expensive_nodes->at(i)->Opcode() == _expensive_nodes->at(i+1)->Opcode()) { @@ -4043,20 +4119,23 @@ void Compile::cleanup_expensive_nodes(PhaseIterGVN &igvn) { identical = 0; } else { Node* n = _expensive_nodes->at(i); - igvn.hash_delete(n); - n->set_req(0, NULL); + igvn.replace_input_of(n, 0, NULL); igvn.hash_insert(n); + modified = true; } } if (identical > 0) { _expensive_nodes->at_put(j++, _expensive_nodes->at(i)); } else if (_expensive_nodes->length() >= 1) { Node* n = _expensive_nodes->at(i); - igvn.hash_delete(n); - n->set_req(0, NULL); + igvn.replace_input_of(n, 0, NULL); igvn.hash_insert(n); + modified = true; } _expensive_nodes->trunc_to(j); + if (modified) { + igvn.optimize(); + } } void Compile::add_expensive_node(Node * n) { diff --git a/hotspot/src/share/vm/opto/compile.hpp b/hotspot/src/share/vm/opto/compile.hpp index 29380e1f898..a84cd34474a 100644 --- a/hotspot/src/share/vm/opto/compile.hpp +++ b/hotspot/src/share/vm/opto/compile.hpp @@ -344,6 +344,8 @@ class Compile : public Phase { VectorSet _dead_node_list; // Set of dead nodes uint _dead_node_count; // Number of dead nodes; VectorSet::Size() is O(N). // So use this to keep count and make the call O(1). + DEBUG_ONLY( Unique_Node_List* _modified_nodes; ) // List of nodes which inputs were modified + debug_only(static int _debug_idx;) // Monotonic counter (not reset), use -XX:BreakAtNode= Arena _node_arena; // Arena for new-space Nodes Arena _old_arena; // Arena for old-space Nodes, lifetime during xform @@ -599,6 +601,10 @@ class Compile : public Phase { bool method_has_option(const char * option) { return method() != NULL && method()->has_option(option); } + template + bool method_has_option_value(const char * option, T& value) { + return method() != NULL && method()->has_option_value(option, value); + } #ifndef PRODUCT bool trace_opto_output() const { return _trace_opto_output; } bool parsed_irreducible_loop() const { return _parsed_irreducible_loop; } @@ -766,6 +772,11 @@ class Compile : public Phase { void print_missing_nodes(); #endif + // Record modified nodes to check that they are put on IGVN worklist + void record_modified_node(Node* n) NOT_DEBUG_RETURN; + void remove_modified_node(Node* n) NOT_DEBUG_RETURN; + DEBUG_ONLY( Unique_Node_List* modified_nodes() const { return _modified_nodes; } ) + // Constant table ConstantTable& constant_table() { return _constant_table; } @@ -1193,6 +1204,10 @@ class Compile : public Phase { // Definitions of pd methods static void pd_compiler2_init(); + // Static parse-time type checking logic for gen_subtype_check: + enum { SSC_always_false, SSC_always_true, SSC_easy_test, SSC_full_test }; + int static_subtype_check(ciKlass* superk, ciKlass* subk); + // Auxiliary method for randomized fuzzing/stressing static bool randomized_select(int count); }; diff --git a/hotspot/src/share/vm/opto/connode.cpp b/hotspot/src/share/vm/opto/connode.cpp index 485e8df7951..fa074f97b3d 100644 --- a/hotspot/src/share/vm/opto/connode.cpp +++ b/hotspot/src/share/vm/opto/connode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ uint ConNode::hash() const { } //------------------------------make------------------------------------------- -ConNode *ConNode::make( Compile* C, const Type *t ) { +ConNode *ConNode::make(const Type *t) { switch( t->basic_type() ) { case T_INT: return new ConINode( t->is_int() ); case T_LONG: return new ConLNode( t->is_long() ); diff --git a/hotspot/src/share/vm/opto/connode.hpp b/hotspot/src/share/vm/opto/connode.hpp index 446b807a846..d1c68862ebf 100644 --- a/hotspot/src/share/vm/opto/connode.hpp +++ b/hotspot/src/share/vm/opto/connode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,7 +46,7 @@ public: virtual const RegMask &in_RegMask(uint) const { return RegMask::Empty; } // Polymorphic factory method: - static ConNode* make( Compile* C, const Type *t ); + static ConNode* make(const Type *t); }; //------------------------------ConINode--------------------------------------- @@ -57,7 +57,7 @@ public: virtual int Opcode() const; // Factory method: - static ConINode* make( Compile* C, int con ) { + static ConINode* make(int con) { return new ConINode( TypeInt::make(con) ); } @@ -71,7 +71,7 @@ public: virtual int Opcode() const; // Factory methods: - static ConPNode* make( Compile *C ,address con ) { + static ConPNode* make(address con) { if (con == NULL) return new ConPNode( TypePtr::NULL_PTR ) ; else @@ -105,7 +105,7 @@ public: virtual int Opcode() const; // Factory method: - static ConLNode* make( Compile *C ,jlong con ) { + static ConLNode* make(jlong con) { return new ConLNode( TypeLong::make(con) ); } @@ -119,7 +119,7 @@ public: virtual int Opcode() const; // Factory method: - static ConFNode* make( Compile *C, float con ) { + static ConFNode* make(float con) { return new ConFNode( TypeF::make(con) ); } @@ -133,7 +133,7 @@ public: virtual int Opcode() const; // Factory method: - static ConDNode* make( Compile *C, double con ) { + static ConDNode* make(double con) { return new ConDNode( TypeD::make(con) ); } diff --git a/hotspot/src/share/vm/opto/divnode.cpp b/hotspot/src/share/vm/opto/divnode.cpp index 8a27c24e37e..4677d00a9f7 100644 --- a/hotspot/src/share/vm/opto/divnode.cpp +++ b/hotspot/src/share/vm/opto/divnode.cpp @@ -479,7 +479,10 @@ Node *DivINode::Ideal(PhaseGVN *phase, bool can_reshape) { if (i == 0) return NULL; // Dividing by zero constant does not idealize - set_req(0,NULL); // Dividing by a not-zero constant; no faulting + if (in(0) != NULL) { + phase->igvn_rehash_node_delayed(this); + set_req(0, NULL); // Dividing by a not-zero constant; no faulting + } // Dividing by MININT does not optimize as a power-of-2 shift. if( i == min_jint ) return NULL; @@ -578,7 +581,10 @@ Node *DivLNode::Ideal( PhaseGVN *phase, bool can_reshape) { if (l == 0) return NULL; // Dividing by zero constant does not idealize - set_req(0,NULL); // Dividing by a not-zero constant; no faulting + if (in(0) != NULL) { + phase->igvn_rehash_node_delayed(this); + set_req(0, NULL); // Dividing by a not-zero constant; no faulting + } // Dividing by MINLONG does not optimize as a power-of-2 shift. if( l == min_jlong ) return NULL; @@ -1274,7 +1280,7 @@ DivModNode::DivModNode( Node *c, Node *dividend, Node *divisor ) : MultiNode(3) } //------------------------------make------------------------------------------ -DivModINode* DivModINode::make(Compile* C, Node* div_or_mod) { +DivModINode* DivModINode::make(Node* div_or_mod) { Node* n = div_or_mod; assert(n->Opcode() == Op_DivI || n->Opcode() == Op_ModI, "only div or mod input pattern accepted"); @@ -1286,7 +1292,7 @@ DivModINode* DivModINode::make(Compile* C, Node* div_or_mod) { } //------------------------------make------------------------------------------ -DivModLNode* DivModLNode::make(Compile* C, Node* div_or_mod) { +DivModLNode* DivModLNode::make(Node* div_or_mod) { Node* n = div_or_mod; assert(n->Opcode() == Op_DivL || n->Opcode() == Op_ModL, "only div or mod input pattern accepted"); diff --git a/hotspot/src/share/vm/opto/divnode.hpp b/hotspot/src/share/vm/opto/divnode.hpp index 03f5be546bc..195803e2000 100644 --- a/hotspot/src/share/vm/opto/divnode.hpp +++ b/hotspot/src/share/vm/opto/divnode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -168,7 +168,7 @@ public: virtual Node *match( const ProjNode *proj, const Matcher *m ); // Make a divmod and associated projections from a div or mod. - static DivModINode* make(Compile* C, Node* div_or_mod); + static DivModINode* make(Node* div_or_mod); }; //------------------------------DivModLNode--------------------------------------- @@ -181,7 +181,7 @@ public: virtual Node *match( const ProjNode *proj, const Matcher *m ); // Make a divmod and associated projections from a div or mod. - static DivModLNode* make(Compile* C, Node* div_or_mod); + static DivModLNode* make(Node* div_or_mod); }; #endif // SHARE_VM_OPTO_DIVNODE_HPP diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp index 10791a5991d..d32e83199c7 100644 --- a/hotspot/src/share/vm/opto/escape.cpp +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -945,7 +945,8 @@ void ConnectionGraph::process_call_arguments(CallNode *call) { strcmp(call->as_CallLeaf()->_name, "sha256_implCompress") == 0 || strcmp(call->as_CallLeaf()->_name, "sha256_implCompressMB") == 0 || strcmp(call->as_CallLeaf()->_name, "sha512_implCompress") == 0 || - strcmp(call->as_CallLeaf()->_name, "sha512_implCompressMB") == 0) + strcmp(call->as_CallLeaf()->_name, "sha512_implCompressMB") == 0 || + strcmp(call->as_CallLeaf()->_name, "multiplyToLen") == 0) ))) { call->dump(); fatal(err_msg_res("EA unexpected CallLeaf %s", call->as_CallLeaf()->_name)); @@ -1452,7 +1453,6 @@ int ConnectionGraph::find_init_values(JavaObjectNode* pta, PointsToNode* init_va return 0; InitializeNode* ini = alloc->as_Allocate()->initialization(); - Compile* C = _compile; bool visited_bottom_offset = false; GrowableArray offsets_worklist; diff --git a/hotspot/src/share/vm/opto/graphKit.cpp b/hotspot/src/share/vm/opto/graphKit.cpp index e7269aa514e..8b8969fc370 100644 --- a/hotspot/src/share/vm/opto/graphKit.cpp +++ b/hotspot/src/share/vm/opto/graphKit.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2014, 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 @@ -591,7 +591,7 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, Node* arg) { C->log()->elem("hot_throw preallocated='1' reason='%s'", Deoptimization::trap_reason_name(reason)); const TypeInstPtr* ex_con = TypeInstPtr::make(ex_obj); - Node* ex_node = _gvn.transform( ConNode::make(C, ex_con) ); + Node* ex_node = _gvn.transform(ConNode::make(ex_con)); // Clear the detail message of the preallocated exception object. // Weblogic sometimes mutates the detail message of exceptions @@ -706,7 +706,7 @@ SafePointNode* GraphKit::clone_map() { if (map() == NULL) return NULL; // Clone the memory edge first - Node* mem = MergeMemNode::make(C, map()->memory()); + Node* mem = MergeMemNode::make(map()->memory()); gvn().set_type_bottom(mem); SafePointNode *clonemap = (SafePointNode*)map()->clone(); @@ -1135,7 +1135,7 @@ Node* GraphKit::ConvI2UL(Node* offset) { return longcon((julong) offset_con); } Node* conv = _gvn.transform( new ConvI2LNode(offset)); - Node* mask = _gvn.transform( ConLNode::make(C, (julong) max_juint) ); + Node* mask = _gvn.transform(ConLNode::make((julong) max_juint)); return _gvn.transform( new AndLNode(conv, mask) ); } @@ -1435,7 +1435,7 @@ Node* GraphKit::reset_memory() { //------------------------------set_all_memory--------------------------------- void GraphKit::set_all_memory(Node* newmem) { - Node* mergemem = MergeMemNode::make(C, newmem); + Node* mergemem = MergeMemNode::make(newmem); gvn().set_type_bottom(mergemem); map()->set_memory(mergemem); } @@ -1464,9 +1464,9 @@ Node* GraphKit::make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, Node* mem = memory(adr_idx); Node* ld; if (require_atomic_access && bt == T_LONG) { - ld = LoadLNode::make_atomic(C, ctl, mem, adr, adr_type, t, mo); + ld = LoadLNode::make_atomic(ctl, mem, adr, adr_type, t, mo); } else if (require_atomic_access && bt == T_DOUBLE) { - ld = LoadDNode::make_atomic(C, ctl, mem, adr, adr_type, t, mo); + ld = LoadDNode::make_atomic(ctl, mem, adr, adr_type, t, mo); } else { ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, mo); } @@ -1488,9 +1488,9 @@ Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt, Node *mem = memory(adr_idx); Node* st; if (require_atomic_access && bt == T_LONG) { - st = StoreLNode::make_atomic(C, ctl, mem, adr, adr_type, val, mo); + st = StoreLNode::make_atomic(ctl, mem, adr, adr_type, val, mo); } else if (require_atomic_access && bt == T_DOUBLE) { - st = StoreDNode::make_atomic(C, ctl, mem, adr, adr_type, val, mo); + st = StoreDNode::make_atomic(ctl, mem, adr, adr_type, val, mo); } else { st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, mo); } @@ -2084,9 +2084,9 @@ Node* GraphKit::just_allocated_object(Node* current_control) { void GraphKit::round_double_arguments(ciMethod* dest_method) { // (Note: TypeFunc::make has a cache that makes this fast.) const TypeFunc* tf = TypeFunc::make(dest_method); - int nargs = tf->_domain->_cnt - TypeFunc::Parms; + int nargs = tf->domain()->cnt() - TypeFunc::Parms; for (int j = 0; j < nargs; j++) { - const Type *targ = tf->_domain->field_at(j + TypeFunc::Parms); + const Type *targ = tf->domain()->field_at(j + TypeFunc::Parms); if( targ->basic_type() == T_DOUBLE ) { // If any parameters are doubles, they must be rounded before // the call, dstore_rounding does gvn.transform @@ -2188,10 +2188,10 @@ void GraphKit::record_profiled_arguments_for_speculation(ciMethod* dest_method, return; } const TypeFunc* tf = TypeFunc::make(dest_method); - int nargs = tf->_domain->_cnt - TypeFunc::Parms; + int nargs = tf->domain()->cnt() - TypeFunc::Parms; int skip = Bytecodes::has_receiver(bc) ? 1 : 0; for (int j = skip, i = 0; j < nargs && i < TypeProfileArgsLimit; j++) { - const Type *targ = tf->_domain->field_at(j + TypeFunc::Parms); + const Type *targ = tf->domain()->field_at(j + TypeFunc::Parms); if (targ->basic_type() == T_OBJECT || targ->basic_type() == T_ARRAY) { bool maybe_null = true; ciKlass* better_type = NULL; @@ -2520,6 +2520,21 @@ void GraphKit::make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool sep set_control(norm); } +static IfNode* gen_subtype_check_compare(Node* ctrl, Node* in1, Node* in2, BoolTest::mask test, float p, PhaseGVN* gvn, BasicType bt) { + Node* cmp = NULL; + switch(bt) { + case T_INT: cmp = new CmpINode(in1, in2); break; + case T_ADDRESS: cmp = new CmpPNode(in1, in2); break; + default: fatal(err_msg("unexpected comparison type %s", type2name(bt))); + } + gvn->transform(cmp); + Node* bol = gvn->transform(new BoolNode(cmp, test)); + IfNode* iff = new IfNode(ctrl, bol, p, COUNT_UNKNOWN); + gvn->transform(iff); + if (!bol->is_Con()) gvn->record_for_igvn(iff); + return iff; +} + //-------------------------------gen_subtype_check----------------------------- // Generate a subtyping check. Takes as input the subtype and supertype. @@ -2529,16 +2544,17 @@ void GraphKit::make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool sep // but that's not exposed to the optimizer. This call also doesn't take in an // Object; if you wish to check an Object you need to load the Object's class // prior to coming here. -Node* GraphKit::gen_subtype_check(Node* subklass, Node* superklass) { +Node* Phase::gen_subtype_check(Node* subklass, Node* superklass, Node** ctrl, MergeMemNode* mem, PhaseGVN* gvn) { + Compile* C = gvn->C; // Fast check for identical types, perhaps identical constants. // The types can even be identical non-constants, in cases // involving Array.newInstance, Object.clone, etc. if (subklass == superklass) - return top(); // false path is dead; no test needed. + return C->top(); // false path is dead; no test needed. - if (_gvn.type(superklass)->singleton()) { - ciKlass* superk = _gvn.type(superklass)->is_klassptr()->klass(); - ciKlass* subk = _gvn.type(subklass)->is_klassptr()->klass(); + if (gvn->type(superklass)->singleton()) { + ciKlass* superk = gvn->type(superklass)->is_klassptr()->klass(); + ciKlass* subk = gvn->type(subklass)->is_klassptr()->klass(); // In the common case of an exact superklass, try to fold up the // test before generating code. You may ask, why not just generate @@ -2549,25 +2565,23 @@ Node* GraphKit::gen_subtype_check(Node* subklass, Node* superklass) { // Foo[] fa = blah(); Foo x = fa[0]; fa[1] = x; // Here, the type of 'fa' is often exact, so the store check // of fa[1]=x will fold up, without testing the nullness of x. - switch (static_subtype_check(superk, subk)) { - case SSC_always_false: + switch (C->static_subtype_check(superk, subk)) { + case Compile::SSC_always_false: { - Node* always_fail = control(); - set_control(top()); + Node* always_fail = *ctrl; + *ctrl = gvn->C->top(); return always_fail; } - case SSC_always_true: - return top(); - case SSC_easy_test: + case Compile::SSC_always_true: + return C->top(); + case Compile::SSC_easy_test: { // Just do a direct pointer compare and be done. - Node* cmp = _gvn.transform( new CmpPNode(subklass, superklass) ); - Node* bol = _gvn.transform( new BoolNode(cmp, BoolTest::eq) ); - IfNode* iff = create_and_xform_if(control(), bol, PROB_STATIC_FREQUENT, COUNT_UNKNOWN); - set_control( _gvn.transform( new IfTrueNode (iff) ) ); - return _gvn.transform( new IfFalseNode(iff) ); + IfNode* iff = gen_subtype_check_compare(*ctrl, subklass, superklass, BoolTest::eq, PROB_STATIC_FREQUENT, gvn, T_ADDRESS); + *ctrl = gvn->transform(new IfTrueNode(iff)); + return gvn->transform(new IfFalseNode(iff)); } - case SSC_full_test: + case Compile::SSC_full_test: break; default: ShouldNotReachHere(); @@ -2579,11 +2593,11 @@ Node* GraphKit::gen_subtype_check(Node* subklass, Node* superklass) { // will always succeed. We could leave a dependency behind to ensure this. // First load the super-klass's check-offset - Node *p1 = basic_plus_adr( superklass, superklass, in_bytes(Klass::super_check_offset_offset()) ); - Node *chk_off = _gvn.transform(new LoadINode(NULL, memory(p1), p1, _gvn.type(p1)->is_ptr(), - TypeInt::INT, MemNode::unordered)); + Node *p1 = gvn->transform(new AddPNode(superklass, superklass, gvn->MakeConX(in_bytes(Klass::super_check_offset_offset())))); + Node* m = mem->memory_at(C->get_alias_index(gvn->type(p1)->is_ptr())); + Node *chk_off = gvn->transform(new LoadINode(NULL, m, p1, gvn->type(p1)->is_ptr(), TypeInt::INT, MemNode::unordered)); int cacheoff_con = in_bytes(Klass::secondary_super_cache_offset()); - bool might_be_cache = (find_int_con(chk_off, cacheoff_con) == cacheoff_con); + bool might_be_cache = (gvn->find_int_con(chk_off, cacheoff_con) == cacheoff_con); // Load from the sub-klass's super-class display list, or a 1-word cache of // the secondary superclass list, or a failing value with a sentinel offset @@ -2591,42 +2605,44 @@ Node* GraphKit::gen_subtype_check(Node* subklass, Node* superklass) { // hierarchy and we have to scan the secondary superclass list the hard way. // Worst-case type is a little odd: NULL is allowed as a result (usually // klass loads can never produce a NULL). - Node *chk_off_X = ConvI2X(chk_off); - Node *p2 = _gvn.transform( new AddPNode(subklass,subklass,chk_off_X) ); + Node *chk_off_X = chk_off; +#ifdef _LP64 + chk_off_X = gvn->transform(new ConvI2LNode(chk_off_X)); +#endif + Node *p2 = gvn->transform(new AddPNode(subklass,subklass,chk_off_X)); // For some types like interfaces the following loadKlass is from a 1-word // cache which is mutable so can't use immutable memory. Other // types load from the super-class display table which is immutable. - Node *kmem = might_be_cache ? memory(p2) : immutable_memory(); - Node *nkls = _gvn.transform( LoadKlassNode::make( _gvn, kmem, p2, _gvn.type(p2)->is_ptr(), TypeKlassPtr::OBJECT_OR_NULL ) ); + m = mem->memory_at(C->get_alias_index(gvn->type(p2)->is_ptr())); + Node *kmem = might_be_cache ? m : C->immutable_memory(); + Node *nkls = gvn->transform(LoadKlassNode::make(*gvn, kmem, p2, gvn->type(p2)->is_ptr(), TypeKlassPtr::OBJECT_OR_NULL)); // Compile speed common case: ARE a subtype and we canNOT fail if( superklass == nkls ) - return top(); // false path is dead; no test needed. + return C->top(); // false path is dead; no test needed. // See if we get an immediate positive hit. Happens roughly 83% of the // time. Test to see if the value loaded just previously from the subklass // is exactly the superklass. - Node *cmp1 = _gvn.transform( new CmpPNode( superklass, nkls ) ); - Node *bol1 = _gvn.transform( new BoolNode( cmp1, BoolTest::eq ) ); - IfNode *iff1 = create_and_xform_if( control(), bol1, PROB_LIKELY(0.83f), COUNT_UNKNOWN ); - Node *iftrue1 = _gvn.transform( new IfTrueNode ( iff1 ) ); - set_control( _gvn.transform( new IfFalseNode( iff1 ) ) ); + IfNode *iff1 = gen_subtype_check_compare(*ctrl, superklass, nkls, BoolTest::eq, PROB_LIKELY(0.83f), gvn, T_ADDRESS); + Node *iftrue1 = gvn->transform( new IfTrueNode (iff1)); + *ctrl = gvn->transform(new IfFalseNode(iff1)); // Compile speed common case: Check for being deterministic right now. If // chk_off is a constant and not equal to cacheoff then we are NOT a // subklass. In this case we need exactly the 1 test above and we can // return those results immediately. if (!might_be_cache) { - Node* not_subtype_ctrl = control(); - set_control(iftrue1); // We need exactly the 1 test above + Node* not_subtype_ctrl = *ctrl; + *ctrl = iftrue1; // We need exactly the 1 test above return not_subtype_ctrl; } // Gather the various success & failures here RegionNode *r_ok_subtype = new RegionNode(4); - record_for_igvn(r_ok_subtype); + gvn->record_for_igvn(r_ok_subtype); RegionNode *r_not_subtype = new RegionNode(3); - record_for_igvn(r_not_subtype); + gvn->record_for_igvn(r_not_subtype); r_ok_subtype->init_req(1, iftrue1); @@ -2635,21 +2651,17 @@ Node* GraphKit::gen_subtype_check(Node* subklass, Node* superklass) { // check-offset points into the subklass display list or the 1-element // cache. If it points to the display (and NOT the cache) and the display // missed then it's not a subtype. - Node *cacheoff = _gvn.intcon(cacheoff_con); - Node *cmp2 = _gvn.transform( new CmpINode( chk_off, cacheoff ) ); - Node *bol2 = _gvn.transform( new BoolNode( cmp2, BoolTest::ne ) ); - IfNode *iff2 = create_and_xform_if( control(), bol2, PROB_LIKELY(0.63f), COUNT_UNKNOWN ); - r_not_subtype->init_req(1, _gvn.transform( new IfTrueNode (iff2) ) ); - set_control( _gvn.transform( new IfFalseNode(iff2) ) ); + Node *cacheoff = gvn->intcon(cacheoff_con); + IfNode *iff2 = gen_subtype_check_compare(*ctrl, chk_off, cacheoff, BoolTest::ne, PROB_LIKELY(0.63f), gvn, T_INT); + r_not_subtype->init_req(1, gvn->transform(new IfTrueNode (iff2))); + *ctrl = gvn->transform(new IfFalseNode(iff2)); // Check for self. Very rare to get here, but it is taken 1/3 the time. // No performance impact (too rare) but allows sharing of secondary arrays // which has some footprint reduction. - Node *cmp3 = _gvn.transform( new CmpPNode( subklass, superklass ) ); - Node *bol3 = _gvn.transform( new BoolNode( cmp3, BoolTest::eq ) ); - IfNode *iff3 = create_and_xform_if( control(), bol3, PROB_LIKELY(0.36f), COUNT_UNKNOWN ); - r_ok_subtype->init_req(2, _gvn.transform( new IfTrueNode ( iff3 ) ) ); - set_control( _gvn.transform( new IfFalseNode( iff3 ) ) ); + IfNode *iff3 = gen_subtype_check_compare(*ctrl, subklass, superklass, BoolTest::eq, PROB_LIKELY(0.36f), gvn, T_ADDRESS); + r_ok_subtype->init_req(2, gvn->transform(new IfTrueNode(iff3))); + *ctrl = gvn->transform(new IfFalseNode(iff3)); // -- Roads not taken here: -- // We could also have chosen to perform the self-check at the beginning @@ -2672,68 +2684,16 @@ Node* GraphKit::gen_subtype_check(Node* subklass, Node* superklass) { // out of line, and it can only improve I-cache density. // The decision to inline or out-of-line this final check is platform // dependent, and is found in the AD file definition of PartialSubtypeCheck. - Node* psc = _gvn.transform( - new PartialSubtypeCheckNode(control(), subklass, superklass) ); + Node* psc = gvn->transform( + new PartialSubtypeCheckNode(*ctrl, subklass, superklass)); - Node *cmp4 = _gvn.transform( new CmpPNode( psc, null() ) ); - Node *bol4 = _gvn.transform( new BoolNode( cmp4, BoolTest::ne ) ); - IfNode *iff4 = create_and_xform_if( control(), bol4, PROB_FAIR, COUNT_UNKNOWN ); - r_not_subtype->init_req(2, _gvn.transform( new IfTrueNode (iff4) ) ); - r_ok_subtype ->init_req(3, _gvn.transform( new IfFalseNode(iff4) ) ); + IfNode *iff4 = gen_subtype_check_compare(*ctrl, psc, gvn->zerocon(T_OBJECT), BoolTest::ne, PROB_FAIR, gvn, T_ADDRESS); + r_not_subtype->init_req(2, gvn->transform(new IfTrueNode (iff4))); + r_ok_subtype ->init_req(3, gvn->transform(new IfFalseNode(iff4))); // Return false path; set default control to true path. - set_control( _gvn.transform(r_ok_subtype) ); - return _gvn.transform(r_not_subtype); -} - -//----------------------------static_subtype_check----------------------------- -// Shortcut important common cases when superklass is exact: -// (0) superklass is java.lang.Object (can occur in reflective code) -// (1) subklass is already limited to a subtype of superklass => always ok -// (2) subklass does not overlap with superklass => always fail -// (3) superklass has NO subtypes and we can check with a simple compare. -int GraphKit::static_subtype_check(ciKlass* superk, ciKlass* subk) { - if (StressReflectiveCode) { - return SSC_full_test; // Let caller generate the general case. - } - - if (superk == env()->Object_klass()) { - return SSC_always_true; // (0) this test cannot fail - } - - ciType* superelem = superk; - if (superelem->is_array_klass()) - superelem = superelem->as_array_klass()->base_element_type(); - - if (!subk->is_interface()) { // cannot trust static interface types yet - if (subk->is_subtype_of(superk)) { - return SSC_always_true; // (1) false path dead; no dynamic test needed - } - if (!(superelem->is_klass() && superelem->as_klass()->is_interface()) && - !superk->is_subtype_of(subk)) { - return SSC_always_false; - } - } - - // If casting to an instance klass, it must have no subtypes - if (superk->is_interface()) { - // Cannot trust interfaces yet. - // %%% S.B. superk->nof_implementors() == 1 - } else if (superelem->is_instance_klass()) { - ciInstanceKlass* ik = superelem->as_instance_klass(); - if (!ik->has_subklass() && !ik->is_interface()) { - if (!ik->is_final()) { - // Add a dependency if there is a chance of a later subclass. - C->dependencies()->assert_leaf_type(ik); - } - return SSC_easy_test; // (3) caller can do a simple ptr comparison - } - } else { - // A primitive array type has no subtypes. - return SSC_easy_test; // (3) caller can do a simple ptr comparison - } - - return SSC_full_test; + *ctrl = gvn->transform(r_ok_subtype); + return gvn->transform(r_not_subtype); } // Profile-driven exact type check: @@ -2813,7 +2773,7 @@ Node* GraphKit::maybe_cast_profiled_receiver(Node* not_null_obj, ciKlass* exact_kls = spec_klass == NULL ? profile_has_unique_klass() : spec_klass; if (exact_kls != NULL) {// no cast failures here if (require_klass == NULL || - static_subtype_check(require_klass, exact_kls) == SSC_always_true) { + C->static_subtype_check(require_klass, exact_kls) == Compile::SSC_always_true) { // If we narrow the type to match what the type profile sees or // the speculative type, we can then remove the rest of the // cast. @@ -2833,7 +2793,7 @@ Node* GraphKit::maybe_cast_profiled_receiver(Node* not_null_obj, } return exact_obj; } - // assert(ssc == SSC_always_true)... except maybe the profile lied to us. + // assert(ssc == Compile::SSC_always_true)... except maybe the profile lied to us. } return NULL; @@ -2938,8 +2898,8 @@ Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replac ciKlass* superk = _gvn.type(superklass)->is_klassptr()->klass(); ciKlass* subk = _gvn.type(obj)->is_oopptr()->klass(); if (subk != NULL && subk->is_loaded()) { - int static_res = static_subtype_check(superk, subk); - known_statically = (static_res == SSC_always_true || static_res == SSC_always_false); + int static_res = C->static_subtype_check(superk, subk); + known_statically = (static_res == Compile::SSC_always_true || static_res == Compile::SSC_always_false); } } @@ -3007,13 +2967,13 @@ Node* GraphKit::gen_checkcast(Node *obj, Node* superklass, if (tk->singleton()) { const TypeOopPtr* objtp = _gvn.type(obj)->isa_oopptr(); if (objtp != NULL && objtp->klass() != NULL) { - switch (static_subtype_check(tk->klass(), objtp->klass())) { - case SSC_always_true: + switch (C->static_subtype_check(tk->klass(), objtp->klass())) { + case Compile::SSC_always_true: // If we know the type check always succeed then we don't use // the profiling data at this bytecode. Don't lose it, feed it // to the type system as a speculative type. return record_profiled_receiver_for_speculation(obj); - case SSC_always_false: + case Compile::SSC_always_false: // It needs a null check because a null will *pass* the cast check. // A non-null value will always produce an exception. return null_assert(obj); @@ -3364,7 +3324,7 @@ Node* GraphKit::set_output_for_allocation(AllocateNode* alloc, // This will allow us to observe initializations when they occur, // and link them properly (as a group) to the InitializeNode. assert(init->in(InitializeNode::Memory) == malloc, ""); - MergeMemNode* minit_in = MergeMemNode::make(C, malloc); + MergeMemNode* minit_in = MergeMemNode::make(malloc); init->set_req(InitializeNode::Memory, minit_in); record_for_igvn(minit_in); // fold it up later, if possible Node* minit_out = memory(rawidx); diff --git a/hotspot/src/share/vm/opto/graphKit.hpp b/hotspot/src/share/vm/opto/graphKit.hpp index 683be5c9060..26f5dfd21c1 100644 --- a/hotspot/src/share/vm/opto/graphKit.hpp +++ b/hotspot/src/share/vm/opto/graphKit.hpp @@ -829,17 +829,13 @@ class GraphKit : public Phase { Node* gen_checkcast( Node *subobj, Node* superkls, Node* *failure_control = NULL ); - // Generate a subtyping check. Takes as input the subtype and supertype. - // Returns 2 values: sets the default control() to the true path and - // returns the false path. Only reads from constant memory taken from the - // default memory; does not write anything. It also doesn't take in an - // Object; if you wish to check an Object you need to load the Object's - // class prior to coming here. - Node* gen_subtype_check(Node* subklass, Node* superklass); - - // Static parse-time type checking logic for gen_subtype_check: - enum { SSC_always_false, SSC_always_true, SSC_easy_test, SSC_full_test }; - int static_subtype_check(ciKlass* superk, ciKlass* subk); + Node* gen_subtype_check(Node* subklass, Node* superklass) { + MergeMemNode* mem = merged_memory(); + Node* ctrl = control(); + Node* n = Phase::gen_subtype_check(subklass, superklass, &ctrl, mem, &_gvn); + set_control(ctrl); + return n; + } // Exact type check used for predicted calls and casts. // Rewrites (*casted_receiver) to be casted to the stronger type. diff --git a/hotspot/src/share/vm/opto/idealKit.cpp b/hotspot/src/share/vm/opto/idealKit.cpp index a0a9cbe62cc..3038982e648 100644 --- a/hotspot/src/share/vm/opto/idealKit.cpp +++ b/hotspot/src/share/vm/opto/idealKit.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2014, 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 @@ -320,7 +320,7 @@ Node* IdealKit::copy_cvstate() { Node* ns = new_cvstate(); for (uint i = 0; i < ns->req(); i++) ns->init_req(i, _cvstate->in(i)); // We must clone memory since it will be updated as we do stores. - ns->set_req(TypeFunc::Memory, MergeMemNode::make(C, ns->in(TypeFunc::Memory))); + ns->set_req(TypeFunc::Memory, MergeMemNode::make(ns->in(TypeFunc::Memory))); return ns; } @@ -359,7 +359,7 @@ Node* IdealKit::load(Node* ctl, Node* mem = memory(adr_idx); Node* ld; if (require_atomic_access && bt == T_LONG) { - ld = LoadLNode::make_atomic(C, ctl, mem, adr, adr_type, t, MemNode::unordered); + ld = LoadLNode::make_atomic(ctl, mem, adr, adr_type, t, MemNode::unordered); } else { ld = LoadNode::make(_gvn, ctl, mem, adr, adr_type, t, bt, MemNode::unordered); } @@ -375,7 +375,7 @@ Node* IdealKit::store(Node* ctl, Node* adr, Node *val, BasicType bt, Node *mem = memory(adr_idx); Node* st; if (require_atomic_access && bt == T_LONG) { - st = StoreLNode::make_atomic(C, ctl, mem, adr, adr_type, val, mo); + st = StoreLNode::make_atomic(ctl, mem, adr, adr_type, val, mo); } else { st = StoreNode::make(_gvn, ctl, mem, adr, adr_type, val, bt, mo); } diff --git a/hotspot/src/share/vm/opto/ifnode.cpp b/hotspot/src/share/vm/opto/ifnode.cpp index 96551a804d2..c0080c81d57 100644 --- a/hotspot/src/share/vm/opto/ifnode.cpp +++ b/hotspot/src/share/vm/opto/ifnode.cpp @@ -503,7 +503,7 @@ int IfNode::is_range_check(Node* &range, Node* &index, jint &offset) { jint off = 0; if (l->is_top()) { return 0; - } else if (l->is_Add()) { + } else if (l->Opcode() == Op_AddI) { if ((off = l->in(1)->find_int_con(0)) != 0) { ind = l->in(2); } else if ((off = l->in(2)->find_int_con(0)) != 0) { diff --git a/hotspot/src/share/vm/opto/lcm.cpp b/hotspot/src/share/vm/opto/lcm.cpp index a601752d4cd..187d2699768 100644 --- a/hotspot/src/share/vm/opto/lcm.cpp +++ b/hotspot/src/share/vm/opto/lcm.cpp @@ -464,7 +464,9 @@ Node* PhaseCFG::select(Block* block, Node_List &worklist, GrowableArray &re iop == Op_CreateEx || // Create-exception must start block iop == Op_CheckCastPP ) { - worklist.map(i,worklist.pop()); + // select the node n + // remove n from worklist and retain the order of remaining nodes + worklist.remove((uint)i); return n; } @@ -550,7 +552,9 @@ Node* PhaseCFG::select(Block* block, Node_List &worklist, GrowableArray &re assert(idx >= 0, "index should be set"); Node *n = worklist[(uint)idx]; // Get the winner - worklist.map((uint)idx, worklist.pop()); // Compress worklist + // select the node n + // remove n from worklist and retain the order of remaining nodes + worklist.remove((uint)idx); return n; } diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp index 8e01709fcda..9f60b701469 100644 --- a/hotspot/src/share/vm/opto/library_call.cpp +++ b/hotspot/src/share/vm/opto/library_call.cpp @@ -146,15 +146,10 @@ class LibraryCallKit : public GraphKit { Node* generate_negative_guard(Node* index, RegionNode* region, // resulting CastII of index: Node* *pos_index = NULL); - Node* generate_nonpositive_guard(Node* index, bool never_negative, - // resulting CastII of index: - Node* *pos_index = NULL); Node* generate_limit_guard(Node* offset, Node* subseq_length, Node* array_length, RegionNode* region); Node* generate_current_thread(Node* &tls_output); - address basictype2arraycopy(BasicType t, Node *src_offset, Node *dest_offset, - bool disjoint_bases, const char* &name, bool dest_uninitialized); Node* load_mirror_from_klass(Node* klass); Node* load_klass_from_mirror_common(Node* mirror, bool never_see_null, RegionNode* region, int null_path, @@ -264,47 +259,8 @@ class LibraryCallKit : public GraphKit { // Helper functions for inlining arraycopy bool inline_arraycopy(); - void generate_arraycopy(const TypePtr* adr_type, - BasicType basic_elem_type, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* copy_length, - bool disjoint_bases = false, - bool length_never_negative = false, - RegionNode* slow_region = NULL); AllocateArrayNode* tightly_coupled_allocation(Node* ptr, RegionNode* slow_region); - void generate_clear_array(const TypePtr* adr_type, - Node* dest, - BasicType basic_elem_type, - Node* slice_off, - Node* slice_len, - Node* slice_end); - bool generate_block_arraycopy(const TypePtr* adr_type, - BasicType basic_elem_type, - AllocateNode* alloc, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* dest_size, bool dest_uninitialized); - void generate_slow_arraycopy(const TypePtr* adr_type, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* copy_length, bool dest_uninitialized); - Node* generate_checkcast_arraycopy(const TypePtr* adr_type, - Node* dest_elem_klass, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* copy_length, bool dest_uninitialized); - Node* generate_generic_arraycopy(const TypePtr* adr_type, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* copy_length, bool dest_uninitialized); - void generate_unchecked_arraycopy(const TypePtr* adr_type, - BasicType basic_elem_type, - bool disjoint_bases, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* copy_length, bool dest_uninitialized); typedef enum { LS_xadd, LS_xchg, LS_cmpxchg } LoadStoreKind; bool inline_unsafe_load_store(BasicType type, LoadStoreKind kind); bool inline_unsafe_ordered_store(BasicType type); @@ -329,6 +285,7 @@ class LibraryCallKit : public GraphKit { bool inline_updateCRC32(); bool inline_updateBytesCRC32(); bool inline_updateByteBufferCRC32(); + bool inline_multiplyToLen(); }; @@ -337,8 +294,12 @@ CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) { vmIntrinsics::ID id = m->intrinsic_id(); assert(id != vmIntrinsics::_none, "must be a VM intrinsic"); - if (DisableIntrinsic[0] != '\0' - && strstr(DisableIntrinsic, vmIntrinsics::name_at(id)) != NULL) { + ccstr disable_intr = NULL; + + if ((DisableIntrinsic[0] != '\0' + && strstr(DisableIntrinsic, vmIntrinsics::name_at(id)) != NULL) || + (method_has_option_value("DisableIntrinsic", disable_intr) + && strstr(disable_intr, vmIntrinsics::name_at(id)) != NULL)) { // disabled by a user request on the command line: // example: -XX:DisableIntrinsic=_hashCode,_getClass return NULL; @@ -521,6 +482,10 @@ CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) { if (!UseAESIntrinsics) return NULL; break; + case vmIntrinsics::_multiplyToLen: + if (!UseMultiplyToLenIntrinsic) return NULL; + break; + case vmIntrinsics::_cipherBlockChaining_encryptAESCrypt: case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: if (!UseAESIntrinsics) return NULL; @@ -880,7 +845,6 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_isArray: case vmIntrinsics::_isPrimitive: case vmIntrinsics::_getSuperclass: - case vmIntrinsics::_getComponentType: case vmIntrinsics::_getClassAccessFlags: return inline_native_Class_query(intrinsic_id()); case vmIntrinsics::_floatToRawIntBits: @@ -920,6 +884,9 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_digestBase_implCompressMB: return inline_digestBase_implCompressMB(predicate); + case vmIntrinsics::_multiplyToLen: + return inline_multiplyToLen(); + case vmIntrinsics::_encodeISOArray: return inline_encodeISOArray(); @@ -1049,25 +1016,6 @@ inline Node* LibraryCallKit::generate_negative_guard(Node* index, RegionNode* re return is_neg; } -inline Node* LibraryCallKit::generate_nonpositive_guard(Node* index, bool never_negative, - Node* *pos_index) { - if (stopped()) - return NULL; // already stopped - if (_gvn.type(index)->higher_equal(TypeInt::POS1)) // [1,maxint] - return NULL; // index is already adequately typed - Node* cmp_le = _gvn.transform(new CmpINode(index, intcon(0))); - BoolTest::mask le_or_eq = (never_negative ? BoolTest::eq : BoolTest::le); - Node* bol_le = _gvn.transform(new BoolNode(cmp_le, le_or_eq)); - Node* is_notp = generate_guard(bol_le, NULL, PROB_MIN); - if (is_notp != NULL && pos_index != NULL) { - // Emulate effect of Parse::adjust_map_after_if. - Node* ccast = new CastIINode(index, TypeInt::POS1); - ccast->set_req(0, control()); - (*pos_index) = _gvn.transform(ccast); - } - return is_notp; -} - // Make sure that 'position' is a valid limit index, in [0..length]. // There are two equivalent plans for checking this: // A. (offset + copyLength) unsigned<= arrayLength @@ -1905,7 +1853,7 @@ bool LibraryCallKit::inline_pow() { Node *bolyplus1 = _gvn.transform(new BoolNode( cmpyplus1, BoolTest::eq )); Node* correctedsign = NULL; if (ConditionalMoveLimit != 0) { - correctedsign = _gvn.transform( CMoveNode::make(C, NULL, bolyplus1, signnode, longcon(0), TypeLong::LONG)); + correctedsign = _gvn.transform(CMoveNode::make(NULL, bolyplus1, signnode, longcon(0), TypeLong::LONG)); } else { IfNode *ifyplus1 = create_and_xform_if(ylong_path,bolyplus1, PROB_FAIR, COUNT_UNKNOWN); RegionNode *r = new RegionNode(3); @@ -1934,7 +1882,7 @@ bool LibraryCallKit::inline_pow() { // (1&(long)y)==1?-DPow(abs(x), y):DPow(abs(x), y) Node *signresult = NULL; if (ConditionalMoveLimit != 0) { - signresult = _gvn.transform( CMoveNode::make(C, NULL, bol3, absxpowy, negabsxpowy, Type::DOUBLE)); + signresult = _gvn.transform(CMoveNode::make(NULL, bol3, absxpowy, negabsxpowy, Type::DOUBLE)); } else { IfNode *ifyeven = create_and_xform_if(ylong_path,bol3, PROB_FAIR, COUNT_UNKNOWN); RegionNode *r = new RegionNode(3); @@ -2268,7 +2216,7 @@ LibraryCallKit::generate_min_max(vmIntrinsics::ID id, Node* x0, Node* y0) { // which could hinder other optimizations. // Since Math.min/max is often used with arraycopy, we want // tightly_coupled_allocation to be able to see beyond min/max expressions. - Node* cmov = CMoveNode::make(C, NULL, best_bol, + Node* cmov = CMoveNode::make(NULL, best_bol, answer_if_false, answer_if_true, TypeInt::make(lo, hi, widen)); @@ -2658,7 +2606,8 @@ bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, Bas if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder); if (!is_store) { - Node* p = make_load(control(), adr, value_type, type, adr_type, MemNode::unordered, is_volatile); + MemNode::MemOrd mo = is_volatile ? MemNode::acquire : MemNode::unordered; + Node* p = make_load(control(), adr, value_type, type, adr_type, mo, is_volatile); // load value switch (type) { case T_BOOLEAN: @@ -3462,10 +3411,6 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) { prim_return_value = null(); return_type = TypeInstPtr::MIRROR->cast_to_ptr_type(TypePtr::BotPTR); break; - case vmIntrinsics::_getComponentType: - prim_return_value = null(); - return_type = TypeInstPtr::MIRROR->cast_to_ptr_type(TypePtr::BotPTR); - break; case vmIntrinsics::_getClassAccessFlags: prim_return_value = intcon(JVM_ACC_ABSTRACT | JVM_ACC_FINAL | JVM_ACC_PUBLIC); return_type = TypeInt::INT; // not bool! 6297094 @@ -3582,17 +3527,6 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) { } break; - case vmIntrinsics::_getComponentType: - if (generate_array_guard(kls, region) != NULL) { - // Be sure to pin the oop load to the guard edge just created: - Node* is_array_ctrl = region->in(region->req()-1); - Node* cma = basic_plus_adr(kls, in_bytes(ArrayKlass::component_mirror_offset())); - Node* cmo = make_load(is_array_ctrl, cma, TypeInstPtr::MIRROR, T_OBJECT, MemNode::unordered); - phi->add_req(cmo); - } - query_value = null(); // non-array case is null - break; - case vmIntrinsics::_getClassAccessFlags: p = basic_plus_adr(kls, in_bytes(Klass::access_flags_offset())); query_value = make_load(NULL, p, TypeInt::INT, T_INT, MemNode::unordered); @@ -3921,20 +3855,26 @@ bool LibraryCallKit::inline_array_copyOf(bool is_copyOfRange) { Node* orig_tail = _gvn.transform(new SubINode(orig_length, start)); Node* moved = generate_min_max(vmIntrinsics::_min, orig_tail, length); - newcopy = new_array(klass_node, length, 0); // no argments to push + newcopy = new_array(klass_node, length, 0); // no arguments to push // Generate a direct call to the right arraycopy function(s). // We know the copy is disjoint but we might not know if the // oop stores need checking. // Extreme case: Arrays.copyOf((Integer[])x, 10, String[].class). // This will fail a store-check if x contains any non-nulls. - bool disjoint_bases = true; - // if start > orig_length then the length of the copy may be - // negative. - bool length_never_negative = !is_copyOfRange; - generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT, - original, start, newcopy, intcon(0), moved, - disjoint_bases, length_never_negative); + + Node* alloc = tightly_coupled_allocation(newcopy, NULL); + + ArrayCopyNode* ac = ArrayCopyNode::make(this, true, original, start, newcopy, intcon(0), moved, alloc != NULL, + load_object_klass(original), klass_node); + if (!is_copyOfRange) { + ac->set_copyof(); + } else { + ac->set_copyofrange(); + } + Node* n = _gvn.transform(ac); + assert(n == ac, "cannot disappear"); + ac->connect_outputs(this); } } // original reexecute is set back here @@ -4445,10 +4385,12 @@ void LibraryCallKit::copy_to_clone(Node* obj, Node* alloc_obj, Node* obj_size, b countx = _gvn.transform(new URShiftXNode(countx, intcon(LogBytesPerLong) )); const TypePtr* raw_adr_type = TypeRawPtr::BOTTOM; - bool disjoint_bases = true; - generate_unchecked_arraycopy(raw_adr_type, T_LONG, disjoint_bases, - src, NULL, dest, NULL, countx, - /*dest_uninitialized*/true); + + ArrayCopyNode* ac = ArrayCopyNode::make(this, false, src, NULL, dest, NULL, countx, false); + ac->set_clonebasic(); + Node* n = _gvn.transform(ac); + assert(n == ac, "cannot disappear"); + set_predefined_output_for_runtime_call(ac, ac->in(TypeFunc::Memory), raw_adr_type); // If necessary, emit some card marks afterwards. (Non-arrays only.) if (card_mark) { @@ -4557,12 +4499,13 @@ bool LibraryCallKit::inline_native_clone(bool is_virtual) { PreserveJVMState pjvms2(this); set_control(is_obja); // Generate a direct call to the right arraycopy function(s). - bool disjoint_bases = true; - bool length_never_negative = true; - generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT, - obj, intcon(0), alloc_obj, intcon(0), - obj_length, - disjoint_bases, length_never_negative); + Node* alloc = tightly_coupled_allocation(alloc_obj, NULL); + ArrayCopyNode* ac = ArrayCopyNode::make(this, true, obj, intcon(0), alloc_obj, intcon(0), obj_length, alloc != NULL); + ac->set_cloneoop(); + Node* n = _gvn.transform(ac); + assert(n == ac, "cannot disappear"); + ac->connect_outputs(this); + result_reg->init_req(_objArray_path, control()); result_val->init_req(_objArray_path, alloc_obj); result_i_o ->set_req(_objArray_path, i_o()); @@ -4656,42 +4599,6 @@ bool LibraryCallKit::inline_native_clone(bool is_virtual) { return true; } -//------------------------------basictype2arraycopy---------------------------- -address LibraryCallKit::basictype2arraycopy(BasicType t, - Node* src_offset, - Node* dest_offset, - bool disjoint_bases, - const char* &name, - bool dest_uninitialized) { - const TypeInt* src_offset_inttype = gvn().find_int_type(src_offset);; - const TypeInt* dest_offset_inttype = gvn().find_int_type(dest_offset);; - - bool aligned = false; - bool disjoint = disjoint_bases; - - // if the offsets are the same, we can treat the memory regions as - // disjoint, because either the memory regions are in different arrays, - // or they are identical (which we can treat as disjoint.) We can also - // treat a copy with a destination index less that the source index - // as disjoint since a low->high copy will work correctly in this case. - if (src_offset_inttype != NULL && src_offset_inttype->is_con() && - dest_offset_inttype != NULL && dest_offset_inttype->is_con()) { - // both indices are constants - int s_offs = src_offset_inttype->get_con(); - int d_offs = dest_offset_inttype->get_con(); - int element_size = type2aelembytes(t); - aligned = ((arrayOopDesc::base_offset_in_bytes(t) + s_offs * element_size) % HeapWordSize == 0) && - ((arrayOopDesc::base_offset_in_bytes(t) + d_offs * element_size) % HeapWordSize == 0); - if (s_offs >= d_offs) disjoint = true; - } else if (src_offset == dest_offset && src_offset != NULL) { - // This can occur if the offsets are identical non-constants. - disjoint = true; - } - - return StubRoutines::select_arraycopy_function(t, aligned, disjoint, name, dest_uninitialized); -} - - //------------------------------inline_arraycopy----------------------- // public static native void java.lang.System.arraycopy(Object src, int srcPos, // Object dest, int destPos, @@ -4704,13 +4611,26 @@ bool LibraryCallKit::inline_arraycopy() { Node* dest_offset = argument(3); // type: int Node* length = argument(4); // type: int - // Compile time checks. If any of these checks cannot be verified at compile time, - // we do not make a fast path for this call. Instead, we let the call remain as it - // is. The checks we choose to mandate at compile time are: - // + // The following tests must be performed // (1) src and dest are arrays. - const Type* src_type = src->Value(&_gvn); - const Type* dest_type = dest->Value(&_gvn); + // (2) src and dest arrays must have elements of the same BasicType + // (3) src and dest must not be null. + // (4) src_offset must not be negative. + // (5) dest_offset must not be negative. + // (6) length must not be negative. + // (7) src_offset + length must not exceed length of src. + // (8) dest_offset + length must not exceed length of dest. + // (9) each element of an oop array must be assignable + + // (3) src and dest must not be null. + // always do this here because we need the JVM state for uncommon traps + src = null_check(src, T_ARRAY); + dest = null_check(dest, T_ARRAY); + + bool notest = false; + + const Type* src_type = _gvn.type(src); + const Type* dest_type = _gvn.type(dest); const TypeAryPtr* top_src = src_type->isa_aryptr(); const TypeAryPtr* top_dest = dest_type->isa_aryptr(); @@ -4768,556 +4688,119 @@ bool LibraryCallKit::inline_arraycopy() { } } - if (!has_src || !has_dest) { - // Conservatively insert a memory barrier on all memory slices. - // Do not let writes into the source float below the arraycopy. - insert_mem_bar(Op_MemBarCPUOrder); + if (has_src && has_dest) { + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); + BasicType dest_elem = top_dest->klass()->as_array_klass()->element_type()->basic_type(); + if (src_elem == T_ARRAY) src_elem = T_OBJECT; + if (dest_elem == T_ARRAY) dest_elem = T_OBJECT; - // Call StubRoutines::generic_arraycopy stub. - generate_arraycopy(TypeRawPtr::BOTTOM, T_CONFLICT, - src, src_offset, dest, dest_offset, length); - - // Do not let reads from the destination float above the arraycopy. - // Since we cannot type the arrays, we don't know which slices - // might be affected. We could restrict this barrier only to those - // memory slices which pertain to array elements--but don't bother. - if (!InsertMemBarAfterArraycopy) - // (If InsertMemBarAfterArraycopy, there is already one in place.) - insert_mem_bar(Op_MemBarCPUOrder); - return true; - } - - // (2) src and dest arrays must have elements of the same BasicType - // Figure out the size and type of the elements we will be copying. - BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); - BasicType dest_elem = top_dest->klass()->as_array_klass()->element_type()->basic_type(); - if (src_elem == T_ARRAY) src_elem = T_OBJECT; - if (dest_elem == T_ARRAY) dest_elem = T_OBJECT; - - if (src_elem != dest_elem || dest_elem == T_VOID) { - // The component types are not the same or are not recognized. Punt. - // (But, avoid the native method wrapper to JVM_ArrayCopy.) - generate_slow_arraycopy(TypePtr::BOTTOM, - src, src_offset, dest, dest_offset, length, - /*dest_uninitialized*/false); - return true; - } - - if (src_elem == T_OBJECT) { - // If both arrays are object arrays then having the exact types - // for both will remove the need for a subtype check at runtime - // before the call and may make it possible to pick a faster copy - // routine (without a subtype check on every element) - // Do we have the exact type of src? - bool could_have_src = src_spec; - // Do we have the exact type of dest? - bool could_have_dest = dest_spec; - ciKlass* src_k = top_src->klass(); - ciKlass* dest_k = top_dest->klass(); - if (!src_spec) { - src_k = src_type->speculative_type_not_null(); - if (src_k != NULL && src_k->is_array_klass()) { + if (src_elem == dest_elem && src_elem == T_OBJECT) { + // If both arrays are object arrays then having the exact types + // for both will remove the need for a subtype check at runtime + // before the call and may make it possible to pick a faster copy + // routine (without a subtype check on every element) + // Do we have the exact type of src? + bool could_have_src = src_spec; + // Do we have the exact type of dest? + bool could_have_dest = dest_spec; + ciKlass* src_k = top_src->klass(); + ciKlass* dest_k = top_dest->klass(); + if (!src_spec) { + src_k = src_type->speculative_type_not_null(); + if (src_k != NULL && src_k->is_array_klass()) { could_have_src = true; - } - } - if (!dest_spec) { - dest_k = dest_type->speculative_type_not_null(); - if (dest_k != NULL && dest_k->is_array_klass()) { - could_have_dest = true; - } - } - if (could_have_src && could_have_dest) { - // If we can have both exact types, emit the missing guards - if (could_have_src && !src_spec) { - src = maybe_cast_profiled_obj(src, src_k); - } - if (could_have_dest && !dest_spec) { - dest = maybe_cast_profiled_obj(dest, dest_k); - } - } - } - - //--------------------------------------------------------------------------- - // We will make a fast path for this call to arraycopy. - - // We have the following tests left to perform: - // - // (3) src and dest must not be null. - // (4) src_offset must not be negative. - // (5) dest_offset must not be negative. - // (6) length must not be negative. - // (7) src_offset + length must not exceed length of src. - // (8) dest_offset + length must not exceed length of dest. - // (9) each element of an oop array must be assignable - - RegionNode* slow_region = new RegionNode(1); - record_for_igvn(slow_region); - - // (3) operands must not be null - // We currently perform our null checks with the null_check routine. - // This means that the null exceptions will be reported in the caller - // rather than (correctly) reported inside of the native arraycopy call. - // This should be corrected, given time. We do our null check with the - // stack pointer restored. - src = null_check(src, T_ARRAY); - dest = null_check(dest, T_ARRAY); - - // (4) src_offset must not be negative. - generate_negative_guard(src_offset, slow_region); - - // (5) dest_offset must not be negative. - generate_negative_guard(dest_offset, slow_region); - - // (6) length must not be negative (moved to generate_arraycopy()). - // generate_negative_guard(length, slow_region); - - // (7) src_offset + length must not exceed length of src. - generate_limit_guard(src_offset, length, - load_array_length(src), - slow_region); - - // (8) dest_offset + length must not exceed length of dest. - generate_limit_guard(dest_offset, length, - load_array_length(dest), - slow_region); - - // (9) each element of an oop array must be assignable - // The generate_arraycopy subroutine checks this. - - // This is where the memory effects are placed: - const TypePtr* adr_type = TypeAryPtr::get_array_body_type(dest_elem); - generate_arraycopy(adr_type, dest_elem, - src, src_offset, dest, dest_offset, length, - false, false, slow_region); - - return true; -} - -//-----------------------------generate_arraycopy---------------------- -// Generate an optimized call to arraycopy. -// Caller must guard against non-arrays. -// Caller must determine a common array basic-type for both arrays. -// Caller must validate offsets against array bounds. -// The slow_region has already collected guard failure paths -// (such as out of bounds length or non-conformable array types). -// The generated code has this shape, in general: -// -// if (length == 0) return // via zero_path -// slowval = -1 -// if (types unknown) { -// slowval = call generic copy loop -// if (slowval == 0) return // via checked_path -// } else if (indexes in bounds) { -// if ((is object array) && !(array type check)) { -// slowval = call checked copy loop -// if (slowval == 0) return // via checked_path -// } else { -// call bulk copy loop -// return // via fast_path -// } -// } -// // adjust params for remaining work: -// if (slowval != -1) { -// n = -1^slowval; src_offset += n; dest_offset += n; length -= n -// } -// slow_region: -// call slow arraycopy(src, src_offset, dest, dest_offset, length) -// return // via slow_call_path -// -// This routine is used from several intrinsics: System.arraycopy, -// Object.clone (the array subcase), and Arrays.copyOf[Range]. -// -void -LibraryCallKit::generate_arraycopy(const TypePtr* adr_type, - BasicType basic_elem_type, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* copy_length, - bool disjoint_bases, - bool length_never_negative, - RegionNode* slow_region) { - - if (slow_region == NULL) { - slow_region = new RegionNode(1); - record_for_igvn(slow_region); - } - - Node* original_dest = dest; - AllocateArrayNode* alloc = NULL; // used for zeroing, if needed - bool dest_uninitialized = false; - - // See if this is the initialization of a newly-allocated array. - // If so, we will take responsibility here for initializing it to zero. - // (Note: Because tightly_coupled_allocation performs checks on the - // out-edges of the dest, we need to avoid making derived pointers - // from it until we have checked its uses.) - if (ReduceBulkZeroing - && !ZeroTLAB // pointless if already zeroed - && basic_elem_type != T_CONFLICT // avoid corner case - && !src->eqv_uncast(dest) - && ((alloc = tightly_coupled_allocation(dest, slow_region)) - != NULL) - && _gvn.find_int_con(alloc->in(AllocateNode::ALength), 1) > 0 - && alloc->maybe_set_complete(&_gvn)) { - // "You break it, you buy it." - InitializeNode* init = alloc->initialization(); - assert(init->is_complete(), "we just did this"); - init->set_complete_with_arraycopy(); - assert(dest->is_CheckCastPP(), "sanity"); - assert(dest->in(0)->in(0) == init, "dest pinned"); - adr_type = TypeRawPtr::BOTTOM; // all initializations are into raw memory - // From this point on, every exit path is responsible for - // initializing any non-copied parts of the object to zero. - // Also, if this flag is set we make sure that arraycopy interacts properly - // with G1, eliding pre-barriers. See CR 6627983. - dest_uninitialized = true; - } else { - // No zeroing elimination here. - alloc = NULL; - //original_dest = dest; - //dest_uninitialized = false; - } - - // Results are placed here: - enum { fast_path = 1, // normal void-returning assembly stub - checked_path = 2, // special assembly stub with cleanup - slow_call_path = 3, // something went wrong; call the VM - zero_path = 4, // bypass when length of copy is zero - bcopy_path = 5, // copy primitive array by 64-bit blocks - PATH_LIMIT = 6 - }; - RegionNode* result_region = new RegionNode(PATH_LIMIT); - PhiNode* result_i_o = new PhiNode(result_region, Type::ABIO); - PhiNode* result_memory = new PhiNode(result_region, Type::MEMORY, adr_type); - record_for_igvn(result_region); - _gvn.set_type_bottom(result_i_o); - _gvn.set_type_bottom(result_memory); - assert(adr_type != TypePtr::BOTTOM, "must be RawMem or a T[] slice"); - - // The slow_control path: - Node* slow_control; - Node* slow_i_o = i_o(); - Node* slow_mem = memory(adr_type); - debug_only(slow_control = (Node*) badAddress); - - // Checked control path: - Node* checked_control = top(); - Node* checked_mem = NULL; - Node* checked_i_o = NULL; - Node* checked_value = NULL; - - if (basic_elem_type == T_CONFLICT) { - assert(!dest_uninitialized, ""); - Node* cv = generate_generic_arraycopy(adr_type, - src, src_offset, dest, dest_offset, - copy_length, dest_uninitialized); - if (cv == NULL) cv = intcon(-1); // failure (no stub available) - checked_control = control(); - checked_i_o = i_o(); - checked_mem = memory(adr_type); - checked_value = cv; - set_control(top()); // no fast path - } - - Node* not_pos = generate_nonpositive_guard(copy_length, length_never_negative); - if (not_pos != NULL) { - PreserveJVMState pjvms(this); - set_control(not_pos); - - // (6) length must not be negative. - if (!length_never_negative) { - generate_negative_guard(copy_length, slow_region); - } - - // copy_length is 0. - if (!stopped() && dest_uninitialized) { - Node* dest_length = alloc->in(AllocateNode::ALength); - if (copy_length->eqv_uncast(dest_length) - || _gvn.find_int_con(dest_length, 1) <= 0) { - // There is no zeroing to do. No need for a secondary raw memory barrier. - } else { - // Clear the whole thing since there are no source elements to copy. - generate_clear_array(adr_type, dest, basic_elem_type, - intcon(0), NULL, - alloc->in(AllocateNode::AllocSize)); - // Use a secondary InitializeNode as raw memory barrier. - // Currently it is needed only on this path since other - // paths have stub or runtime calls as raw memory barriers. - InitializeNode* init = insert_mem_bar_volatile(Op_Initialize, - Compile::AliasIdxRaw, - top())->as_Initialize(); - init->set_complete(&_gvn); // (there is no corresponding AllocateNode) - } - } - - // Present the results of the fast call. - result_region->init_req(zero_path, control()); - result_i_o ->init_req(zero_path, i_o()); - result_memory->init_req(zero_path, memory(adr_type)); - } - - if (!stopped() && dest_uninitialized) { - // We have to initialize the *uncopied* part of the array to zero. - // The copy destination is the slice dest[off..off+len]. The other slices - // are dest_head = dest[0..off] and dest_tail = dest[off+len..dest.length]. - Node* dest_size = alloc->in(AllocateNode::AllocSize); - Node* dest_length = alloc->in(AllocateNode::ALength); - Node* dest_tail = _gvn.transform(new AddINode(dest_offset, copy_length)); - - // If there is a head section that needs zeroing, do it now. - if (find_int_con(dest_offset, -1) != 0) { - generate_clear_array(adr_type, dest, basic_elem_type, - intcon(0), dest_offset, - NULL); - } - - // Next, perform a dynamic check on the tail length. - // It is often zero, and we can win big if we prove this. - // There are two wins: Avoid generating the ClearArray - // with its attendant messy index arithmetic, and upgrade - // the copy to a more hardware-friendly word size of 64 bits. - Node* tail_ctl = NULL; - if (!stopped() && !dest_tail->eqv_uncast(dest_length)) { - Node* cmp_lt = _gvn.transform(new CmpINode(dest_tail, dest_length)); - Node* bol_lt = _gvn.transform(new BoolNode(cmp_lt, BoolTest::lt)); - tail_ctl = generate_slow_guard(bol_lt, NULL); - assert(tail_ctl != NULL || !stopped(), "must be an outcome"); - } - - // At this point, let's assume there is no tail. - if (!stopped() && alloc != NULL && basic_elem_type != T_OBJECT) { - // There is no tail. Try an upgrade to a 64-bit copy. - bool didit = false; - { PreserveJVMState pjvms(this); - didit = generate_block_arraycopy(adr_type, basic_elem_type, alloc, - src, src_offset, dest, dest_offset, - dest_size, dest_uninitialized); - if (didit) { - // Present the results of the block-copying fast call. - result_region->init_req(bcopy_path, control()); - result_i_o ->init_req(bcopy_path, i_o()); - result_memory->init_req(bcopy_path, memory(adr_type)); } } - if (didit) - set_control(top()); // no regular fast path - } - - // Clear the tail, if any. - if (tail_ctl != NULL) { - Node* notail_ctl = stopped() ? NULL : control(); - set_control(tail_ctl); - if (notail_ctl == NULL) { - generate_clear_array(adr_type, dest, basic_elem_type, - dest_tail, NULL, - dest_size); - } else { - // Make a local merge. - Node* done_ctl = new RegionNode(3); - Node* done_mem = new PhiNode(done_ctl, Type::MEMORY, adr_type); - done_ctl->init_req(1, notail_ctl); - done_mem->init_req(1, memory(adr_type)); - generate_clear_array(adr_type, dest, basic_elem_type, - dest_tail, NULL, - dest_size); - done_ctl->init_req(2, control()); - done_mem->init_req(2, memory(adr_type)); - set_control( _gvn.transform(done_ctl)); - set_memory( _gvn.transform(done_mem), adr_type ); + if (!dest_spec) { + dest_k = dest_type->speculative_type_not_null(); + if (dest_k != NULL && dest_k->is_array_klass()) { + could_have_dest = true; + } + } + if (could_have_src && could_have_dest) { + // If we can have both exact types, emit the missing guards + if (could_have_src && !src_spec) { + src = maybe_cast_profiled_obj(src, src_k); + } + if (could_have_dest && !dest_spec) { + dest = maybe_cast_profiled_obj(dest, dest_k); + } } } } - BasicType copy_type = basic_elem_type; - assert(basic_elem_type != T_ARRAY, "caller must fix this"); - if (!stopped() && copy_type == T_OBJECT) { - // If src and dest have compatible element types, we can copy bits. - // Types S[] and D[] are compatible if D is a supertype of S. - // - // If they are not, we will use checked_oop_disjoint_arraycopy, - // which performs a fast optimistic per-oop check, and backs off - // further to JVM_ArrayCopy on the first per-oop check that fails. - // (Actually, we don't move raw bits only; the GC requires card marks.) + if (!too_many_traps(Deoptimization::Reason_intrinsic) && !src->is_top() && !dest->is_top()) { + // validate arguments: enables transformation the ArrayCopyNode + notest = true; - // Get the Klass* for both src and dest + RegionNode* slow_region = new RegionNode(1); + record_for_igvn(slow_region); + + // (1) src and dest are arrays. + generate_non_array_guard(load_object_klass(src), slow_region); + generate_non_array_guard(load_object_klass(dest), slow_region); + + // (2) src and dest arrays must have elements of the same BasicType + // done at macro expansion or at Ideal transformation time + + // (4) src_offset must not be negative. + generate_negative_guard(src_offset, slow_region); + + // (5) dest_offset must not be negative. + generate_negative_guard(dest_offset, slow_region); + + // (7) src_offset + length must not exceed length of src. + generate_limit_guard(src_offset, length, + load_array_length(src), + slow_region); + + // (8) dest_offset + length must not exceed length of dest. + generate_limit_guard(dest_offset, length, + load_array_length(dest), + slow_region); + + // (9) each element of an oop array must be assignable Node* src_klass = load_object_klass(src); Node* dest_klass = load_object_klass(dest); - - // Generate the subtype check. - // This might fold up statically, or then again it might not. - // - // Non-static example: Copying List.elements to a new String[]. - // The backing store for a List is always an Object[], - // but its elements are always type String, if the generic types - // are correct at the source level. - // - // Test S[] against D[], not S against D, because (probably) - // the secondary supertype cache is less busy for S[] than S. - // This usually only matters when D is an interface. Node* not_subtype_ctrl = gen_subtype_check(src_klass, dest_klass); - // Plug failing path into checked_oop_disjoint_arraycopy + if (not_subtype_ctrl != top()) { PreserveJVMState pjvms(this); set_control(not_subtype_ctrl); - // (At this point we can assume disjoint_bases, since types differ.) - int ek_offset = in_bytes(ObjArrayKlass::element_klass_offset()); - Node* p1 = basic_plus_adr(dest_klass, ek_offset); - Node* n1 = LoadKlassNode::make(_gvn, immutable_memory(), p1, TypeRawPtr::BOTTOM); - Node* dest_elem_klass = _gvn.transform(n1); - Node* cv = generate_checkcast_arraycopy(adr_type, - dest_elem_klass, - src, src_offset, dest, dest_offset, - ConvI2X(copy_length), dest_uninitialized); - if (cv == NULL) cv = intcon(-1); // failure (no stub available) - checked_control = control(); - checked_i_o = i_o(); - checked_mem = memory(adr_type); - checked_value = cv; + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_make_not_entrant); + assert(stopped(), "Should be stopped"); } - // At this point we know we do not need type checks on oop stores. - - // Let's see if we need card marks: - if (alloc != NULL && use_ReduceInitialCardMarks()) { - // If we do not need card marks, copy using the jint or jlong stub. - copy_type = LP64_ONLY(UseCompressedOops ? T_INT : T_LONG) NOT_LP64(T_INT); - assert(type2aelembytes(basic_elem_type) == type2aelembytes(copy_type), - "sizes agree"); + { + PreserveJVMState pjvms(this); + set_control(_gvn.transform(slow_region)); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_make_not_entrant); + assert(stopped(), "Should be stopped"); } } - if (!stopped()) { - // Generate the fast path, if possible. - PreserveJVMState pjvms(this); - generate_unchecked_arraycopy(adr_type, copy_type, disjoint_bases, - src, src_offset, dest, dest_offset, - ConvI2X(copy_length), dest_uninitialized); - - // Present the results of the fast call. - result_region->init_req(fast_path, control()); - result_i_o ->init_req(fast_path, i_o()); - result_memory->init_req(fast_path, memory(adr_type)); + if (stopped()) { + return true; } - // Here are all the slow paths up to this point, in one bundle: - slow_control = top(); - if (slow_region != NULL) - slow_control = _gvn.transform(slow_region); - DEBUG_ONLY(slow_region = (RegionNode*)badAddress); + AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL); + ArrayCopyNode* ac = ArrayCopyNode::make(this, true, src, src_offset, dest, dest_offset, length, alloc != NULL, + // Create LoadRange and LoadKlass nodes for use during macro expansion here + // so the compiler has a chance to eliminate them: during macro expansion, + // we have to set their control (CastPP nodes are eliminated). + load_object_klass(src), load_object_klass(dest), + load_array_length(src), load_array_length(dest)); - set_control(checked_control); - if (!stopped()) { - // Clean up after the checked call. - // The returned value is either 0 or -1^K, - // where K = number of partially transferred array elements. - Node* cmp = _gvn.transform(new CmpINode(checked_value, intcon(0))); - Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq)); - IfNode* iff = create_and_map_if(control(), bol, PROB_MAX, COUNT_UNKNOWN); - - // If it is 0, we are done, so transfer to the end. - Node* checks_done = _gvn.transform(new IfTrueNode(iff)); - result_region->init_req(checked_path, checks_done); - result_i_o ->init_req(checked_path, checked_i_o); - result_memory->init_req(checked_path, checked_mem); - - // If it is not zero, merge into the slow call. - set_control( _gvn.transform(new IfFalseNode(iff) )); - RegionNode* slow_reg2 = new RegionNode(3); - PhiNode* slow_i_o2 = new PhiNode(slow_reg2, Type::ABIO); - PhiNode* slow_mem2 = new PhiNode(slow_reg2, Type::MEMORY, adr_type); - record_for_igvn(slow_reg2); - slow_reg2 ->init_req(1, slow_control); - slow_i_o2 ->init_req(1, slow_i_o); - slow_mem2 ->init_req(1, slow_mem); - slow_reg2 ->init_req(2, control()); - slow_i_o2 ->init_req(2, checked_i_o); - slow_mem2 ->init_req(2, checked_mem); - - slow_control = _gvn.transform(slow_reg2); - slow_i_o = _gvn.transform(slow_i_o2); - slow_mem = _gvn.transform(slow_mem2); - - if (alloc != NULL) { - // We'll restart from the very beginning, after zeroing the whole thing. - // This can cause double writes, but that's OK since dest is brand new. - // So we ignore the low 31 bits of the value returned from the stub. - } else { - // We must continue the copy exactly where it failed, or else - // another thread might see the wrong number of writes to dest. - Node* checked_offset = _gvn.transform(new XorINode(checked_value, intcon(-1))); - Node* slow_offset = new PhiNode(slow_reg2, TypeInt::INT); - slow_offset->init_req(1, intcon(0)); - slow_offset->init_req(2, checked_offset); - slow_offset = _gvn.transform(slow_offset); - - // Adjust the arguments by the conditionally incoming offset. - Node* src_off_plus = _gvn.transform(new AddINode(src_offset, slow_offset)); - Node* dest_off_plus = _gvn.transform(new AddINode(dest_offset, slow_offset)); - Node* length_minus = _gvn.transform(new SubINode(copy_length, slow_offset)); - - // Tweak the node variables to adjust the code produced below: - src_offset = src_off_plus; - dest_offset = dest_off_plus; - copy_length = length_minus; - } + if (notest) { + ac->set_arraycopy_notest(); } - set_control(slow_control); - if (!stopped()) { - // Generate the slow path, if needed. - PreserveJVMState pjvms(this); // replace_in_map may trash the map + Node* n = _gvn.transform(ac); + assert(n == ac, "cannot disappear"); + ac->connect_outputs(this); - set_memory(slow_mem, adr_type); - set_i_o(slow_i_o); - - if (dest_uninitialized) { - generate_clear_array(adr_type, dest, basic_elem_type, - intcon(0), NULL, - alloc->in(AllocateNode::AllocSize)); - } - - generate_slow_arraycopy(adr_type, - src, src_offset, dest, dest_offset, - copy_length, /*dest_uninitialized*/false); - - result_region->init_req(slow_call_path, control()); - result_i_o ->init_req(slow_call_path, i_o()); - result_memory->init_req(slow_call_path, memory(adr_type)); - } - - // Remove unused edges. - for (uint i = 1; i < result_region->req(); i++) { - if (result_region->in(i) == NULL) - result_region->init_req(i, top()); - } - - // Finished; return the combined state. - set_control( _gvn.transform(result_region)); - set_i_o( _gvn.transform(result_i_o) ); - set_memory( _gvn.transform(result_memory), adr_type ); - - // The memory edges above are precise in order to model effects around - // array copies accurately to allow value numbering of field loads around - // arraycopy. Such field loads, both before and after, are common in Java - // collections and similar classes involving header/array data structures. - // - // But with low number of register or when some registers are used or killed - // by arraycopy calls it causes registers spilling on stack. See 6544710. - // The next memory barrier is added to avoid it. If the arraycopy can be - // optimized away (which it can, sometimes) then we can manually remove - // the membar also. - // - // Do not let reads from the cloned object float above the arraycopy. - if (alloc != NULL) { - // Do not let stores that initialize this object be reordered with - // a subsequent store that would make this object accessible by - // other threads. - // Record what AllocateNode this StoreStore protects so that - // escape analysis can go from the MemBarStoreStoreNode to the - // AllocateNode and eliminate the MemBarStoreStoreNode if possible - // based on the escape status of the AllocateNode. - insert_mem_bar(Op_MemBarStoreStore, alloc->proj_out(AllocateNode::RawAddress)); - } else if (InsertMemBarAfterArraycopy) - insert_mem_bar(Op_MemBarCPUOrder); + return true; } @@ -5397,305 +4880,6 @@ LibraryCallKit::tightly_coupled_allocation(Node* ptr, return alloc; } -// Helper for initialization of arrays, creating a ClearArray. -// It writes zero bits in [start..end), within the body of an array object. -// The memory effects are all chained onto the 'adr_type' alias category. -// -// Since the object is otherwise uninitialized, we are free -// to put a little "slop" around the edges of the cleared area, -// as long as it does not go back into the array's header, -// or beyond the array end within the heap. -// -// The lower edge can be rounded down to the nearest jint and the -// upper edge can be rounded up to the nearest MinObjAlignmentInBytes. -// -// Arguments: -// adr_type memory slice where writes are generated -// dest oop of the destination array -// basic_elem_type element type of the destination -// slice_idx array index of first element to store -// slice_len number of elements to store (or NULL) -// dest_size total size in bytes of the array object -// -// Exactly one of slice_len or dest_size must be non-NULL. -// If dest_size is non-NULL, zeroing extends to the end of the object. -// If slice_len is non-NULL, the slice_idx value must be a constant. -void -LibraryCallKit::generate_clear_array(const TypePtr* adr_type, - Node* dest, - BasicType basic_elem_type, - Node* slice_idx, - Node* slice_len, - Node* dest_size) { - // one or the other but not both of slice_len and dest_size: - assert((slice_len != NULL? 1: 0) + (dest_size != NULL? 1: 0) == 1, ""); - if (slice_len == NULL) slice_len = top(); - if (dest_size == NULL) dest_size = top(); - - // operate on this memory slice: - Node* mem = memory(adr_type); // memory slice to operate on - - // scaling and rounding of indexes: - int scale = exact_log2(type2aelembytes(basic_elem_type)); - int abase = arrayOopDesc::base_offset_in_bytes(basic_elem_type); - int clear_low = (-1 << scale) & (BytesPerInt - 1); - int bump_bit = (-1 << scale) & BytesPerInt; - - // determine constant starts and ends - const intptr_t BIG_NEG = -128; - assert(BIG_NEG + 2*abase < 0, "neg enough"); - intptr_t slice_idx_con = (intptr_t) find_int_con(slice_idx, BIG_NEG); - intptr_t slice_len_con = (intptr_t) find_int_con(slice_len, BIG_NEG); - if (slice_len_con == 0) { - return; // nothing to do here - } - intptr_t start_con = (abase + (slice_idx_con << scale)) & ~clear_low; - intptr_t end_con = find_intptr_t_con(dest_size, -1); - if (slice_idx_con >= 0 && slice_len_con >= 0) { - assert(end_con < 0, "not two cons"); - end_con = round_to(abase + ((slice_idx_con + slice_len_con) << scale), - BytesPerLong); - } - - if (start_con >= 0 && end_con >= 0) { - // Constant start and end. Simple. - mem = ClearArrayNode::clear_memory(control(), mem, dest, - start_con, end_con, &_gvn); - } else if (start_con >= 0 && dest_size != top()) { - // Constant start, pre-rounded end after the tail of the array. - Node* end = dest_size; - mem = ClearArrayNode::clear_memory(control(), mem, dest, - start_con, end, &_gvn); - } else if (start_con >= 0 && slice_len != top()) { - // Constant start, non-constant end. End needs rounding up. - // End offset = round_up(abase + ((slice_idx_con + slice_len) << scale), 8) - intptr_t end_base = abase + (slice_idx_con << scale); - int end_round = (-1 << scale) & (BytesPerLong - 1); - Node* end = ConvI2X(slice_len); - if (scale != 0) - end = _gvn.transform(new LShiftXNode(end, intcon(scale) )); - end_base += end_round; - end = _gvn.transform(new AddXNode(end, MakeConX(end_base))); - end = _gvn.transform(new AndXNode(end, MakeConX(~end_round))); - mem = ClearArrayNode::clear_memory(control(), mem, dest, - start_con, end, &_gvn); - } else if (start_con < 0 && dest_size != top()) { - // Non-constant start, pre-rounded end after the tail of the array. - // This is almost certainly a "round-to-end" operation. - Node* start = slice_idx; - start = ConvI2X(start); - if (scale != 0) - start = _gvn.transform(new LShiftXNode( start, intcon(scale) )); - start = _gvn.transform(new AddXNode(start, MakeConX(abase))); - if ((bump_bit | clear_low) != 0) { - int to_clear = (bump_bit | clear_low); - // Align up mod 8, then store a jint zero unconditionally - // just before the mod-8 boundary. - if (((abase + bump_bit) & ~to_clear) - bump_bit - < arrayOopDesc::length_offset_in_bytes() + BytesPerInt) { - bump_bit = 0; - assert((abase & to_clear) == 0, "array base must be long-aligned"); - } else { - // Bump 'start' up to (or past) the next jint boundary: - start = _gvn.transform(new AddXNode(start, MakeConX(bump_bit))); - assert((abase & clear_low) == 0, "array base must be int-aligned"); - } - // Round bumped 'start' down to jlong boundary in body of array. - start = _gvn.transform(new AndXNode(start, MakeConX(~to_clear))); - if (bump_bit != 0) { - // Store a zero to the immediately preceding jint: - Node* x1 = _gvn.transform(new AddXNode(start, MakeConX(-bump_bit))); - Node* p1 = basic_plus_adr(dest, x1); - mem = StoreNode::make(_gvn, control(), mem, p1, adr_type, intcon(0), T_INT, MemNode::unordered); - mem = _gvn.transform(mem); - } - } - Node* end = dest_size; // pre-rounded - mem = ClearArrayNode::clear_memory(control(), mem, dest, - start, end, &_gvn); - } else { - // Non-constant start, unrounded non-constant end. - // (Nobody zeroes a random midsection of an array using this routine.) - ShouldNotReachHere(); // fix caller - } - - // Done. - set_memory(mem, adr_type); -} - - -bool -LibraryCallKit::generate_block_arraycopy(const TypePtr* adr_type, - BasicType basic_elem_type, - AllocateNode* alloc, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* dest_size, bool dest_uninitialized) { - // See if there is an advantage from block transfer. - int scale = exact_log2(type2aelembytes(basic_elem_type)); - if (scale >= LogBytesPerLong) - return false; // it is already a block transfer - - // Look at the alignment of the starting offsets. - int abase = arrayOopDesc::base_offset_in_bytes(basic_elem_type); - - intptr_t src_off_con = (intptr_t) find_int_con(src_offset, -1); - intptr_t dest_off_con = (intptr_t) find_int_con(dest_offset, -1); - if (src_off_con < 0 || dest_off_con < 0) - // At present, we can only understand constants. - return false; - - intptr_t src_off = abase + (src_off_con << scale); - intptr_t dest_off = abase + (dest_off_con << scale); - - if (((src_off | dest_off) & (BytesPerLong-1)) != 0) { - // Non-aligned; too bad. - // One more chance: Pick off an initial 32-bit word. - // This is a common case, since abase can be odd mod 8. - if (((src_off | dest_off) & (BytesPerLong-1)) == BytesPerInt && - ((src_off ^ dest_off) & (BytesPerLong-1)) == 0) { - Node* sptr = basic_plus_adr(src, src_off); - Node* dptr = basic_plus_adr(dest, dest_off); - Node* sval = make_load(control(), sptr, TypeInt::INT, T_INT, adr_type, MemNode::unordered); - store_to_memory(control(), dptr, sval, T_INT, adr_type, MemNode::unordered); - src_off += BytesPerInt; - dest_off += BytesPerInt; - } else { - return false; - } - } - assert(src_off % BytesPerLong == 0, ""); - assert(dest_off % BytesPerLong == 0, ""); - - // Do this copy by giant steps. - Node* sptr = basic_plus_adr(src, src_off); - Node* dptr = basic_plus_adr(dest, dest_off); - Node* countx = dest_size; - countx = _gvn.transform(new SubXNode(countx, MakeConX(dest_off))); - countx = _gvn.transform(new URShiftXNode(countx, intcon(LogBytesPerLong))); - - bool disjoint_bases = true; // since alloc != NULL - generate_unchecked_arraycopy(adr_type, T_LONG, disjoint_bases, - sptr, NULL, dptr, NULL, countx, dest_uninitialized); - - return true; -} - - -// Helper function; generates code for the slow case. -// We make a call to a runtime method which emulates the native method, -// but without the native wrapper overhead. -void -LibraryCallKit::generate_slow_arraycopy(const TypePtr* adr_type, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* copy_length, bool dest_uninitialized) { - assert(!dest_uninitialized, "Invariant"); - Node* call = make_runtime_call(RC_NO_LEAF | RC_UNCOMMON, - OptoRuntime::slow_arraycopy_Type(), - OptoRuntime::slow_arraycopy_Java(), - "slow_arraycopy", adr_type, - src, src_offset, dest, dest_offset, - copy_length); - - // Handle exceptions thrown by this fellow: - make_slow_call_ex(call, env()->Throwable_klass(), false); -} - -// Helper function; generates code for cases requiring runtime checks. -Node* -LibraryCallKit::generate_checkcast_arraycopy(const TypePtr* adr_type, - Node* dest_elem_klass, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* copy_length, bool dest_uninitialized) { - if (stopped()) return NULL; - - address copyfunc_addr = StubRoutines::checkcast_arraycopy(dest_uninitialized); - if (copyfunc_addr == NULL) { // Stub was not generated, go slow path. - return NULL; - } - - // Pick out the parameters required to perform a store-check - // for the target array. This is an optimistic check. It will - // look in each non-null element's class, at the desired klass's - // super_check_offset, for the desired klass. - int sco_offset = in_bytes(Klass::super_check_offset_offset()); - Node* p3 = basic_plus_adr(dest_elem_klass, sco_offset); - Node* n3 = new LoadINode(NULL, memory(p3), p3, _gvn.type(p3)->is_ptr(), TypeInt::INT, MemNode::unordered); - Node* check_offset = ConvI2X(_gvn.transform(n3)); - Node* check_value = dest_elem_klass; - - Node* src_start = array_element_address(src, src_offset, T_OBJECT); - Node* dest_start = array_element_address(dest, dest_offset, T_OBJECT); - - // (We know the arrays are never conjoint, because their types differ.) - Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::checkcast_arraycopy_Type(), - copyfunc_addr, "checkcast_arraycopy", adr_type, - // five arguments, of which two are - // intptr_t (jlong in LP64) - src_start, dest_start, - copy_length XTOP, - check_offset XTOP, - check_value); - - return _gvn.transform(new ProjNode(call, TypeFunc::Parms)); -} - - -// Helper function; generates code for cases requiring runtime checks. -Node* -LibraryCallKit::generate_generic_arraycopy(const TypePtr* adr_type, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* copy_length, bool dest_uninitialized) { - assert(!dest_uninitialized, "Invariant"); - if (stopped()) return NULL; - address copyfunc_addr = StubRoutines::generic_arraycopy(); - if (copyfunc_addr == NULL) { // Stub was not generated, go slow path. - return NULL; - } - - Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::generic_arraycopy_Type(), - copyfunc_addr, "generic_arraycopy", adr_type, - src, src_offset, dest, dest_offset, copy_length); - - return _gvn.transform(new ProjNode(call, TypeFunc::Parms)); -} - -// Helper function; generates the fast out-of-line call to an arraycopy stub. -void -LibraryCallKit::generate_unchecked_arraycopy(const TypePtr* adr_type, - BasicType basic_elem_type, - bool disjoint_bases, - Node* src, Node* src_offset, - Node* dest, Node* dest_offset, - Node* copy_length, bool dest_uninitialized) { - if (stopped()) return; // nothing to do - - Node* src_start = src; - Node* dest_start = dest; - if (src_offset != NULL || dest_offset != NULL) { - assert(src_offset != NULL && dest_offset != NULL, ""); - src_start = array_element_address(src, src_offset, basic_elem_type); - dest_start = array_element_address(dest, dest_offset, basic_elem_type); - } - - // Figure out which arraycopy runtime method to call. - const char* copyfunc_name = "arraycopy"; - address copyfunc_addr = - basictype2arraycopy(basic_elem_type, src_offset, dest_offset, - disjoint_bases, copyfunc_name, dest_uninitialized); - - // Call it. Note that the count_ix value is not scaled to a byte-size. - make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::fast_arraycopy_Type(), - copyfunc_addr, copyfunc_name, adr_type, - src_start, dest_start, copy_length XTOP); -} - //-------------inline_encodeISOArray----------------------------------- // encode char[] to byte[] in ISO_8859_1 bool LibraryCallKit::inline_encodeISOArray() { @@ -5737,6 +4921,106 @@ bool LibraryCallKit::inline_encodeISOArray() { return true; } +//-------------inline_multiplyToLen----------------------------------- +bool LibraryCallKit::inline_multiplyToLen() { + assert(UseMultiplyToLenIntrinsic, "not implementated on this platform"); + + address stubAddr = StubRoutines::multiplyToLen(); + if (stubAddr == NULL) { + return false; // Intrinsic's stub is not implemented on this platform + } + const char* stubName = "multiplyToLen"; + + assert(callee()->signature()->size() == 5, "multiplyToLen has 5 parameters"); + + Node* x = argument(1); + Node* xlen = argument(2); + Node* y = argument(3); + Node* ylen = argument(4); + Node* z = argument(5); + + const Type* x_type = x->Value(&_gvn); + const Type* y_type = y->Value(&_gvn); + const TypeAryPtr* top_x = x_type->isa_aryptr(); + const TypeAryPtr* top_y = y_type->isa_aryptr(); + if (top_x == NULL || top_x->klass() == NULL || + top_y == NULL || top_y->klass() == NULL) { + // failed array check + return false; + } + + BasicType x_elem = x_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType y_elem = y_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + if (x_elem != T_INT || y_elem != T_INT) { + return false; + } + + // Set the original stack and the reexecute bit for the interpreter to reexecute + // the bytecode that invokes BigInteger.multiplyToLen() if deoptimization happens + // on the return from z array allocation in runtime. + { PreserveReexecuteState preexecs(this); + jvms()->set_should_reexecute(true); + + Node* x_start = array_element_address(x, intcon(0), x_elem); + Node* y_start = array_element_address(y, intcon(0), y_elem); + // 'x_start' points to x array + scaled xlen + // 'y_start' points to y array + scaled ylen + + // Allocate the result array + Node* zlen = _gvn.transform(new AddINode(xlen, ylen)); + Node* klass_node = makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_INT))); + + IdealKit ideal(this); + +#define __ ideal. + Node* one = __ ConI(1); + Node* zero = __ ConI(0); + IdealVariable need_alloc(ideal), z_alloc(ideal); __ declarations_done(); + __ set(need_alloc, zero); + __ set(z_alloc, z); + __ if_then(z, BoolTest::eq, null()); { + __ increment (need_alloc, one); + } __ else_(); { + // Update graphKit memory and control from IdealKit. + sync_kit(ideal); + Node* zlen_arg = load_array_length(z); + // Update IdealKit memory and control from graphKit. + __ sync_kit(this); + __ if_then(zlen_arg, BoolTest::lt, zlen); { + __ increment (need_alloc, one); + } __ end_if(); + } __ end_if(); + + __ if_then(__ value(need_alloc), BoolTest::ne, zero); { + // Update graphKit memory and control from IdealKit. + sync_kit(ideal); + Node * narr = new_array(klass_node, zlen, 1); + // Update IdealKit memory and control from graphKit. + __ sync_kit(this); + __ set(z_alloc, narr); + } __ end_if(); + + sync_kit(ideal); + z = __ value(z_alloc); + _gvn.set_type(z, TypeAryPtr::INTS); + // Final sync IdealKit and GraphKit. + final_sync(ideal); +#undef __ + + Node* z_start = array_element_address(z, intcon(0), T_INT); + + Node* call = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::multiplyToLen_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + x_start, xlen, y_start, ylen, z_start, zlen); + } // original reexecute is set back here + + C->set_has_split_ifs(true); // Has chance for split-if optimization + set_result(z); + return true; +} + + /** * Calculate CRC32 for byte. * int java.util.zip.CRC32.update(int crc, int b) @@ -5915,8 +5199,19 @@ Node * LibraryCallKit::load_field_from_object(Node * fromObj, const char * field type = Type::get_const_basic_type(bt); } + if (support_IRIW_for_not_multiple_copy_atomic_cpu && is_vol) { + insert_mem_bar(Op_MemBarVolatile); // StoreLoad barrier + } // Build the load. - Node* loadedField = make_load(NULL, adr, type, bt, adr_type, MemNode::unordered, is_vol); + MemNode::MemOrd mo = is_vol ? MemNode::acquire : MemNode::unordered; + Node* loadedField = make_load(NULL, adr, type, bt, adr_type, mo, is_vol); + // If reference is volatile, prevent following memory ops from + // floating up past the volatile read. Also prevents commoning + // another volatile read. + if (is_vol) { + // Memory barrier includes bogus read of value to force load BEFORE membar + insert_mem_bar(Op_MemBarAcquire, loadedField); + } return loadedField; } diff --git a/hotspot/src/share/vm/opto/loopPredicate.cpp b/hotspot/src/share/vm/opto/loopPredicate.cpp index 4f2ddd3d068..2539ae4d402 100644 --- a/hotspot/src/share/vm/opto/loopPredicate.cpp +++ b/hotspot/src/share/vm/opto/loopPredicate.cpp @@ -107,8 +107,7 @@ ProjNode* PhaseIdealLoop::create_new_if_for_predicate(ProjNode* cont_proj, Node* rgn = new RegionNode(1); rgn->add_req(uncommon_proj); register_control(rgn, loop, uncommon_proj); - _igvn.hash_delete(call); - call->set_req(0, rgn); + _igvn.replace_input_of(call, 0, rgn); // When called from beautify_loops() idom is not constructed yet. if (_idom != NULL) { set_idom(call, rgn, dom_depth(rgn)); @@ -166,8 +165,7 @@ ProjNode* PhaseIdealLoop::create_new_if_for_predicate(ProjNode* cont_proj, Node* if (new_entry == NULL) { // Attach if_cont to iff - _igvn.hash_delete(iff); - iff->set_req(0, if_cont); + _igvn.replace_input_of(iff, 0, if_cont); if (_idom != NULL) { set_idom(iff, if_cont, dom_depth(iff)); } @@ -194,8 +192,7 @@ ProjNode* PhaseIterGVN::create_new_if_for_predicate(ProjNode* cont_proj, Node* n rgn = new RegionNode(1); register_new_node_with_optimizer(rgn); rgn->add_req(uncommon_proj); - hash_delete(call); - call->set_req(0, rgn); + replace_input_of(call, 0, rgn); } else { // Find region's edge corresponding to uncommon_proj for (; proj_index < rgn->req(); proj_index++) @@ -766,9 +763,7 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) { loop->dump_head(); } #endif - } else if ((cl != NULL) && (proj->_con == predicate_proj->_con) && - loop->is_range_check_if(iff, this, invar)) { - + } else if (cl != NULL && loop->is_range_check_if(iff, this, invar)) { // Range check for counted loops const Node* cmp = bol->in(1)->as_Cmp(); Node* idx = cmp->in(1); @@ -803,18 +798,31 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) { } // Test the lower bound - Node* lower_bound_bol = rc_predicate(loop, ctrl, scale, offset, init, limit, stride, rng, false); + BoolNode* lower_bound_bol = rc_predicate(loop, ctrl, scale, offset, init, limit, stride, rng, false); + // Negate test if necessary + bool negated = false; + if (proj->_con != predicate_proj->_con) { + lower_bound_bol = new BoolNode(lower_bound_bol->in(1), lower_bound_bol->_test.negate()); + register_new_node(lower_bound_bol, ctrl); + negated = true; + } IfNode* lower_bound_iff = lower_bound_proj->in(0)->as_If(); _igvn.hash_delete(lower_bound_iff); lower_bound_iff->set_req(1, lower_bound_bol); - if (TraceLoopPredicate) tty->print_cr("lower bound check if: %d", lower_bound_iff->_idx); + if (TraceLoopPredicate) tty->print_cr("lower bound check if: %s %d ", negated ? " negated" : "", lower_bound_iff->_idx); // Test the upper bound - Node* upper_bound_bol = rc_predicate(loop, lower_bound_proj, scale, offset, init, limit, stride, rng, true); + BoolNode* upper_bound_bol = rc_predicate(loop, lower_bound_proj, scale, offset, init, limit, stride, rng, true); + negated = false; + if (proj->_con != predicate_proj->_con) { + upper_bound_bol = new BoolNode(upper_bound_bol->in(1), upper_bound_bol->_test.negate()); + register_new_node(upper_bound_bol, ctrl); + negated = true; + } IfNode* upper_bound_iff = upper_bound_proj->in(0)->as_If(); _igvn.hash_delete(upper_bound_iff); upper_bound_iff->set_req(1, upper_bound_bol); - if (TraceLoopPredicate) tty->print_cr("upper bound check if: %d", lower_bound_iff->_idx); + if (TraceLoopPredicate) tty->print_cr("upper bound check if: %s %d ", negated ? " negated" : "", lower_bound_iff->_idx); // Fall through into rest of the clean up code which will move // any dependent nodes onto the upper bound test. diff --git a/hotspot/src/share/vm/opto/loopTransform.cpp b/hotspot/src/share/vm/opto/loopTransform.cpp index 73ec0617370..9701cfd2cf2 100644 --- a/hotspot/src/share/vm/opto/loopTransform.cpp +++ b/hotspot/src/share/vm/opto/loopTransform.cpp @@ -924,15 +924,13 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_ if( bol->outcnt() != 1 ) { bol = bol->clone(); register_new_node(bol,main_end->in(CountedLoopEndNode::TestControl)); - _igvn.hash_delete(main_end); - main_end->set_req(CountedLoopEndNode::TestValue, bol); + _igvn.replace_input_of(main_end, CountedLoopEndNode::TestValue, bol); } // Need only 1 user of 'cmp' because I will be hacking the loop bounds. if( cmp->outcnt() != 1 ) { cmp = cmp->clone(); register_new_node(cmp,main_end->in(CountedLoopEndNode::TestControl)); - _igvn.hash_delete(bol); - bol->set_req(1, cmp); + _igvn.replace_input_of(bol, 1, cmp); } //------------------------------ @@ -1118,8 +1116,7 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_ Node* pre_bol = pre_end->in(CountedLoopEndNode::TestValue)->as_Bool(); BoolNode* new_bol0 = new BoolNode(pre_bol->in(1), new_test); register_new_node( new_bol0, pre_head->in(0) ); - _igvn.hash_delete(pre_end); - pre_end->set_req(CountedLoopEndNode::TestValue, new_bol0); + _igvn.replace_input_of(pre_end, CountedLoopEndNode::TestValue, new_bol0); // Modify main loop guard condition assert(min_iff->in(CountedLoopEndNode::TestValue) == min_bol, "guard okay"); BoolNode* new_bol1 = new BoolNode(min_bol->in(1), new_test); @@ -1130,8 +1127,7 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_ BoolNode* main_bol = main_end->in(CountedLoopEndNode::TestValue)->as_Bool(); BoolNode* new_bol2 = new BoolNode(main_bol->in(1), new_test); register_new_node( new_bol2, main_end->in(CountedLoopEndNode::TestControl) ); - _igvn.hash_delete(main_end); - main_end->set_req(CountedLoopEndNode::TestValue, new_bol2); + _igvn.replace_input_of(main_end, CountedLoopEndNode::TestValue, new_bol2); } // Flag main loop @@ -1346,8 +1342,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad Node* bol2 = loop_end->in(1)->clone(); bol2->set_req(1, cmp2); register_new_node(bol2, ctrl2); - _igvn.hash_delete(loop_end); - loop_end->set_req(1, bol2); + _igvn.replace_input_of(loop_end, 1, bol2); } // Step 3: Find the min-trip test guaranteed before a 'main' loop. // Make it a 1-trip test (means at least 2 trips). @@ -1356,8 +1351,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad // can edit it's inputs directly. Hammer in the new limit for the // minimum-trip guard. assert(opaq->outcnt() == 1, ""); - _igvn.hash_delete(opaq); - opaq->set_req(1, new_limit); + _igvn.replace_input_of(opaq, 1, new_limit); } // Adjust max trip count. The trip count is intentionally rounded @@ -1407,8 +1401,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad register_new_node( cmp2, ctrl2 ); Node *bol2 = new BoolNode( cmp2, loop_end->test_trip() ); register_new_node( bol2, ctrl2 ); - _igvn.hash_delete(loop_end); - loop_end->set_req(CountedLoopEndNode::TestValue, bol2); + _igvn.replace_input_of(loop_end, CountedLoopEndNode::TestValue, bol2); // Step 3: Find the min-trip test guaranteed before a 'main' loop. // Make it a 1-trip test (means at least 2 trips). @@ -1997,8 +1990,7 @@ void PhaseIdealLoop::do_range_check( IdealLoopTree *loop, Node_List &old_new ) { : (Node*)new MaxINode(pre_limit, orig_limit); register_new_node(pre_limit, pre_ctrl); } - _igvn.hash_delete(pre_opaq); - pre_opaq->set_req(1, pre_limit); + _igvn.replace_input_of(pre_opaq, 1, pre_limit); // Note:: we are making the main loop limit no longer precise; // need to round up based on stride. @@ -2027,10 +2019,9 @@ void PhaseIdealLoop::do_range_check( IdealLoopTree *loop, Node_List &old_new ) { Node *main_bol = main_cle->in(1); // Hacking loop bounds; need private copies of exit test if( main_bol->outcnt() > 1 ) {// BoolNode shared? - _igvn.hash_delete(main_cle); main_bol = main_bol->clone();// Clone a private BoolNode register_new_node( main_bol, main_cle->in(0) ); - main_cle->set_req(1,main_bol); + _igvn.replace_input_of(main_cle, 1, main_bol); } Node *main_cmp = main_bol->in(1); if( main_cmp->outcnt() > 1 ) { // CmpNode shared? diff --git a/hotspot/src/share/vm/opto/loopnode.cpp b/hotspot/src/share/vm/opto/loopnode.cpp index 5ee44d084bd..5ca0ab059db 100644 --- a/hotspot/src/share/vm/opto/loopnode.cpp +++ b/hotspot/src/share/vm/opto/loopnode.cpp @@ -133,7 +133,7 @@ Node *PhaseIdealLoop::get_early_ctrl( Node *n ) { // Return earliest legal location assert(early == find_non_split_ctrl(early), "unexpected early control"); - if (n->is_expensive()) { + if (n->is_expensive() && !_verify_only && !_verify_me) { assert(n->in(0), "should have control input"); early = get_early_ctrl_for_expensive(n, early); } @@ -226,8 +226,7 @@ Node *PhaseIdealLoop::get_early_ctrl_for_expensive(Node *n, Node* earliest) { } if (ctl != n->in(0)) { - _igvn.hash_delete(n); - n->set_req(0, ctl); + _igvn.replace_input_of(n, 0, ctl); _igvn.hash_insert(n); } @@ -521,8 +520,7 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { assert(check_iff->in(1)->Opcode() == Op_Conv2B && check_iff->in(1)->in(1)->Opcode() == Op_Opaque1, ""); Node* opq = check_iff->in(1)->in(1); - _igvn.hash_delete(opq); - opq->set_req(1, bol); + _igvn.replace_input_of(opq, 1, bol); // Update ctrl. set_ctrl(opq, check_iff->in(0)); set_ctrl(check_iff->in(1), check_iff->in(0)); @@ -690,7 +688,7 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { incr->set_req(2,stride); incr = _igvn.register_new_node_with_optimizer(incr); set_early_ctrl( incr ); - _igvn.hash_delete(phi); + _igvn.rehash_node_delayed(phi); phi->set_req_X( LoopNode::LoopBackControl, incr, &_igvn ); // If phi type is more restrictive than Int, raise to @@ -743,8 +741,8 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { iffalse = iff2; iftrue = ift2; } else { - _igvn.hash_delete(iffalse); - _igvn.hash_delete(iftrue); + _igvn.rehash_node_delayed(iffalse); + _igvn.rehash_node_delayed(iftrue); iffalse->set_req_X( 0, le, &_igvn ); iftrue ->set_req_X( 0, le, &_igvn ); } @@ -1257,6 +1255,7 @@ void IdealLoopTree::split_fall_in( PhaseIdealLoop *phase, int fall_in_cnt ) { _head->del_req(i); } } + igvn.rehash_node_delayed(_head); // Transform landing pad igvn.register_new_node_with_optimizer(landing_pad, _head); // Insert landing pad into the header @@ -1397,7 +1396,7 @@ void IdealLoopTree::merge_many_backedges( PhaseIdealLoop *phase ) { igvn.register_new_node_with_optimizer(r, _head); // Plug region into end of loop _head, followed by hot_tail while( _head->req() > 3 ) _head->del_req( _head->req()-1 ); - _head->set_req(2, r); + igvn.replace_input_of(_head, 2, r); if( hot_idx ) _head->add_req(hot_tail); // Split all the Phis up between '_head' loop and the Region 'r' @@ -1419,7 +1418,7 @@ void IdealLoopTree::merge_many_backedges( PhaseIdealLoop *phase ) { igvn.register_new_node_with_optimizer(phi, n); // Add the merge phi to the old Phi while( n->req() > 3 ) n->del_req( n->req()-1 ); - n->set_req(2, phi); + igvn.replace_input_of(n, 2, phi); if( hot_idx ) n->add_req(hot_phi); } } @@ -1495,13 +1494,14 @@ bool IdealLoopTree::beautify_loops( PhaseIdealLoop *phase ) { if( fall_in_cnt > 1 ) { // Since I am just swapping inputs I do not need to update def-use info Node *tmp = _head->in(1); + igvn.rehash_node_delayed(_head); _head->set_req( 1, _head->in(fall_in_cnt) ); _head->set_req( fall_in_cnt, tmp ); // Swap also all Phis for (DUIterator_Fast imax, i = _head->fast_outs(imax); i < imax; i++) { Node* phi = _head->fast_out(i); if( phi->is_Phi() ) { - igvn.hash_delete(phi); // Yank from hash before hacking edges + igvn.rehash_node_delayed(phi); // Yank from hash before hacking edges tmp = phi->in(1); phi->set_req( 1, phi->in(fall_in_cnt) ); phi->set_req( fall_in_cnt, tmp ); @@ -2905,6 +2905,7 @@ int PhaseIdealLoop::build_loop_tree_impl( Node *n, int pre_order ) { uint k = 0; // Probably cfg->in(0) while( cfg->in(k) != m ) k++; // But check incase cfg is a Region cfg->set_req( k, if_t ); // Now point to NeverBranch + _igvn._worklist.push(cfg); // Now create the never-taken loop exit Node *if_f = new CProjNode( iff, 1 ); diff --git a/hotspot/src/share/vm/opto/loopopts.cpp b/hotspot/src/share/vm/opto/loopopts.cpp index e25a6b0762e..20ad4ff1f6d 100644 --- a/hotspot/src/share/vm/opto/loopopts.cpp +++ b/hotspot/src/share/vm/opto/loopopts.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2014, 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 @@ -625,7 +625,7 @@ Node *PhaseIdealLoop::conditional_move( Node *region ) { set_ctrl(inp, cmov_ctrl); } } - Node *cmov = CMoveNode::make( C, cmov_ctrl, iff->in(1), phi->in(1+flip), phi->in(2-flip), _igvn.type(phi) ); + Node *cmov = CMoveNode::make(cmov_ctrl, iff->in(1), phi->in(1+flip), phi->in(2-flip), _igvn.type(phi)); register_new_node( cmov, cmov_ctrl ); _igvn.replace_node( phi, cmov ); #ifndef PRODUCT @@ -2574,7 +2574,7 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { new_head->set_unswitch_count(head->unswitch_count()); // Preserve _igvn.register_new_node_with_optimizer(new_head); assert(first_not_peeled->in(0) == last_peel, "last_peel <- first_not_peeled"); - first_not_peeled->set_req(0, new_head); + _igvn.replace_input_of(first_not_peeled, 0, new_head); set_loop(new_head, loop); loop->_body.push(new_head); not_peel.set(new_head->_idx); diff --git a/hotspot/src/share/vm/opto/machnode.cpp b/hotspot/src/share/vm/opto/machnode.cpp index 69139e104f7..a2112199f26 100644 --- a/hotspot/src/share/vm/opto/machnode.cpp +++ b/hotspot/src/share/vm/opto/machnode.cpp @@ -178,7 +178,7 @@ uint MachNode::cmp( const Node &node ) const { } // Return an equivalent instruction using memory for cisc_operand position -MachNode *MachNode::cisc_version(int offset, Compile* C) { +MachNode *MachNode::cisc_version(int offset) { ShouldNotCallThis(); return NULL; } @@ -411,7 +411,7 @@ int MachNode::operand_index(const MachOper *oper) const { //------------------------------peephole--------------------------------------- // Apply peephole rule(s) to this instruction -MachNode *MachNode::peephole( Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted, Compile* C ) { +MachNode *MachNode::peephole(Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted) { return NULL; } diff --git a/hotspot/src/share/vm/opto/machnode.hpp b/hotspot/src/share/vm/opto/machnode.hpp index 78ab196b3a5..a557c02e6c6 100644 --- a/hotspot/src/share/vm/opto/machnode.hpp +++ b/hotspot/src/share/vm/opto/machnode.hpp @@ -152,7 +152,7 @@ public: virtual uint cmp( const MachOper &oper ) const; // Virtual clone, since I do not know how big the MachOper is. - virtual MachOper *clone(Compile* C) const = 0; + virtual MachOper *clone() const = 0; // Return ideal Type from simple operands. Fail for complex operands. virtual const Type *type() const; @@ -202,10 +202,10 @@ public: // Copy inputs and operands to new node of instruction. // Called from cisc_version() and short_branch_version(). // !!!! The method's body is defined in ad_.cpp file. - void fill_new_machnode(MachNode *n, Compile* C) const; + void fill_new_machnode(MachNode *n) const; // Return an equivalent instruction using memory for cisc_operand position - virtual MachNode *cisc_version(int offset, Compile* C); + virtual MachNode *cisc_version(int offset); // Modify this instruction's register mask to use stack version for cisc_operand virtual void use_cisc_RegMask(); @@ -317,7 +317,7 @@ public: virtual const class TypePtr *adr_type() const; // Apply peephole rule(s) to this instruction - virtual MachNode *peephole( Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted, Compile* C ); + virtual MachNode *peephole(Block *block, int block_index, PhaseRegAlloc *ra_, int &deleted); // Top-level ideal Opcode matched virtual int ideal_Opcode() const { return Op_Node; } @@ -627,7 +627,7 @@ public: virtual void save_label(Label** label, uint* block_num) = 0; // Support for short branches - virtual MachNode *short_branch_version(Compile* C) { return NULL; } + virtual MachNode *short_branch_version() { return NULL; } virtual bool pinned() const { return true; }; }; @@ -985,7 +985,7 @@ public: labelOper(labelOper* l) : _label(l->_label) , _block_num(l->_block_num) {} - virtual MachOper *clone(Compile* C) const; + virtual MachOper *clone() const; virtual Label *label() const { assert(_label != NULL, "need Label"); return _label; } @@ -1012,7 +1012,7 @@ public: methodOper() : _method(0) {} methodOper(intptr_t method) : _method(method) {} - virtual MachOper *clone(Compile* C) const; + virtual MachOper *clone() const; virtual intptr_t method() const { return _method; } diff --git a/hotspot/src/share/vm/opto/macro.cpp b/hotspot/src/share/vm/opto/macro.cpp index 5b1f4397642..40cc26aa0d1 100644 --- a/hotspot/src/share/vm/opto/macro.cpp +++ b/hotspot/src/share/vm/opto/macro.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2014, 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 @@ void PhaseMacroExpand::copy_call_debug_info(CallNode *oldcall, CallNode * newcal newcall->add_req(old_in); } - newcall->set_jvms(oldcall->jvms()); + // JVMS may be shared so clone it before we modify it + newcall->set_jvms(oldcall->jvms() != NULL ? oldcall->jvms()->clone_deep(C) : NULL); for (JVMState *jvms = newcall->jvms(); jvms != NULL; jvms = jvms->caller()) { jvms->set_map(newcall); jvms->set_locoff(jvms->locoff()+jvms_adj); @@ -702,6 +703,7 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray result_cast(); + assert(res == NULL || res->is_CheckCastPP(), "unexpected AllocateNode result"); const TypeOopPtr* res_type = NULL; if (res != NULL) { // Could be NULL when there are no users res_type = _igvn.type(res)->isa_oopptr(); @@ -791,6 +793,7 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray del_req(last--); } + _igvn._worklist.push(sfpt); // rollback processed safepoints while (safepoints_done.length() > 0) { SafePointNode* sfpt_done = safepoints_done.pop(); @@ -815,6 +818,7 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray debug_start(); int end = jvms->debug_end(); sfpt->replace_edges_in_range(res, sobj, start, end); + _igvn._worklist.push(sfpt); safepoints_done.append_if_missing(sfpt); // keep it for rollback } return true; @@ -1034,6 +1039,8 @@ bool PhaseMacroExpand::eliminate_boxing_node(CallStaticJavaNode *boxing) { return false; } + assert(boxing->result_cast() == NULL, "unexpected boxing node result"); + extract_call_projections(boxing); const TypeTuple* r = boxing->tf()->range(); @@ -1775,6 +1782,7 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, Node *pf_region = new RegionNode(3); Node *pf_phi_rawmem = new PhiNode( pf_region, Type::MEMORY, TypeRawPtr::BOTTOM ); + transform_later(pf_region); // Generate several prefetch instructions. uint lines = (length != NULL) ? AllocatePrefetchLines : AllocateInstancePrefetchLines; @@ -2462,6 +2470,8 @@ void PhaseMacroExpand::eliminate_macro_nodes() { assert(!n->as_AbstractLock()->is_eliminated(), "sanity"); _has_locks = true; break; + case Node::Class_ArrayCopy: + break; default: assert(n->Opcode() == Op_LoopLimit || n->Opcode() == Op_Opaque1 || @@ -2537,6 +2547,25 @@ bool PhaseMacroExpand::expand_macro_nodes() { } } + // expand arraycopy "macro" nodes first + // For ReduceBulkZeroing, we must first process all arraycopy nodes + // before the allocate nodes are expanded. + int macro_idx = C->macro_count() - 1; + while (macro_idx >= 0) { + Node * n = C->macro_node(macro_idx); + assert(n->is_macro(), "only macro nodes expected here"); + if (_igvn.type(n) == Type::TOP || n->in(0)->is_top() ) { + // node is unreachable, so don't try to expand it + C->remove_macro_node(n); + } else if (n->is_ArrayCopy()){ + int macro_count = C->macro_count(); + expand_arraycopy_node(n->as_ArrayCopy()); + assert(C->macro_count() < macro_count, "must have deleted a node from macro list"); + } + if (C->failing()) return true; + macro_idx --; + } + // expand "macro" nodes // nodes are removed from the macro list as they are processed while (C->macro_count() > 0) { diff --git a/hotspot/src/share/vm/opto/macro.hpp b/hotspot/src/share/vm/opto/macro.hpp index 5cefbb1c063..3cb2e8a2bcf 100644 --- a/hotspot/src/share/vm/opto/macro.hpp +++ b/hotspot/src/share/vm/opto/macro.hpp @@ -37,7 +37,7 @@ class PhaseMacroExpand : public Phase { private: PhaseIterGVN &_igvn; - // Helper methods roughly modelled after GraphKit: + // Helper methods roughly modeled after GraphKit: Node* top() const { return C->top(); } Node* intcon(jint con) const { return _igvn.intcon(con); } Node* longcon(jlong con) const { return _igvn.longcon(con); } @@ -101,6 +101,86 @@ private: void expand_lock_node(LockNode *lock); void expand_unlock_node(UnlockNode *unlock); + // More helper methods modeled after GraphKit for array copy + void insert_mem_bar(Node** ctrl, Node** mem, int opcode, Node* precedent = NULL); + Node* array_element_address(Node* ary, Node* idx, BasicType elembt); + Node* ConvI2L(Node* offset); + Node* make_leaf_call(Node* ctrl, Node* mem, + const TypeFunc* call_type, address call_addr, + const char* call_name, + const TypePtr* adr_type, + Node* parm0 = NULL, Node* parm1 = NULL, + Node* parm2 = NULL, Node* parm3 = NULL, + Node* parm4 = NULL, Node* parm5 = NULL, + Node* parm6 = NULL, Node* parm7 = NULL); + + // helper methods modeled after LibraryCallKit for array copy + Node* generate_guard(Node** ctrl, Node* test, RegionNode* region, float true_prob); + Node* generate_slow_guard(Node** ctrl, Node* test, RegionNode* region); + void generate_negative_guard(Node** ctrl, Node* index, RegionNode* region); + void generate_limit_guard(Node** ctrl, Node* offset, Node* subseq_length, Node* array_length, RegionNode* region); + + // More helper methods for array copy + Node* generate_nonpositive_guard(Node** ctrl, Node* index, bool never_negative); + void finish_arraycopy_call(Node* call, Node** ctrl, MergeMemNode** mem, const TypePtr* adr_type); + address basictype2arraycopy(BasicType t, + Node* src_offset, + Node* dest_offset, + bool disjoint_bases, + const char* &name, + bool dest_uninitialized); + Node* generate_arraycopy(ArrayCopyNode *ac, + AllocateArrayNode* alloc, + Node** ctrl, MergeMemNode* mem, Node** io, + const TypePtr* adr_type, + BasicType basic_elem_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, + bool disjoint_bases = false, + bool length_never_negative = false, + RegionNode* slow_region = NULL); + void generate_clear_array(Node* ctrl, MergeMemNode* merge_mem, + const TypePtr* adr_type, + Node* dest, + BasicType basic_elem_type, + Node* slice_idx, + Node* slice_len, + Node* dest_size); + bool generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, Node* io, + const TypePtr* adr_type, + BasicType basic_elem_type, + AllocateNode* alloc, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* dest_size, bool dest_uninitialized); + MergeMemNode* generate_slow_arraycopy(ArrayCopyNode *ac, + Node** ctrl, Node* mem, Node** io, + const TypePtr* adr_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, bool dest_uninitialized); + Node* generate_checkcast_arraycopy(Node** ctrl, MergeMemNode** mem, + const TypePtr* adr_type, + Node* dest_elem_klass, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, bool dest_uninitialized); + Node* generate_generic_arraycopy(Node** ctrl, MergeMemNode** mem, + const TypePtr* adr_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, bool dest_uninitialized); + void generate_unchecked_arraycopy(Node** ctrl, MergeMemNode** mem, + const TypePtr* adr_type, + BasicType basic_elem_type, + bool disjoint_bases, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, bool dest_uninitialized); + + void expand_arraycopy_node(ArrayCopyNode *ac); + int replace_input(Node *use, Node *oldref, Node *newref); void copy_call_debug_info(CallNode *oldcall, CallNode * newcall); Node* opt_bits_test(Node* ctrl, Node* region, int edge, Node* word, int mask, int bits, bool return_fast_path = false); diff --git a/hotspot/src/share/vm/opto/macroArrayCopy.cpp b/hotspot/src/share/vm/opto/macroArrayCopy.cpp new file mode 100644 index 00000000000..8b8a177d20c --- /dev/null +++ b/hotspot/src/share/vm/opto/macroArrayCopy.cpp @@ -0,0 +1,1237 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "oops/objArrayKlass.hpp" +#include "opto/convertnode.hpp" +#include "opto/graphKit.hpp" +#include "opto/macro.hpp" +#include "opto/runtime.hpp" + + +void PhaseMacroExpand::insert_mem_bar(Node** ctrl, Node** mem, int opcode, Node* precedent) { + MemBarNode* mb = MemBarNode::make(C, opcode, Compile::AliasIdxBot, precedent); + mb->init_req(TypeFunc::Control, *ctrl); + mb->init_req(TypeFunc::Memory, *mem); + transform_later(mb); + *ctrl = new ProjNode(mb,TypeFunc::Control); + transform_later(*ctrl); + Node* mem_proj = new ProjNode(mb,TypeFunc::Memory); + transform_later(mem_proj); + *mem = mem_proj; +} + +Node* PhaseMacroExpand::array_element_address(Node* ary, Node* idx, BasicType elembt) { + uint shift = exact_log2(type2aelembytes(elembt)); + uint header = arrayOopDesc::base_offset_in_bytes(elembt); + Node* base = basic_plus_adr(ary, header); +#ifdef _LP64 + // see comment in GraphKit::array_element_address + int index_max = max_jint - 1; // array size is max_jint, index is one less + const TypeLong* lidxtype = TypeLong::make(CONST64(0), index_max, Type::WidenMax); + idx = transform_later( new ConvI2LNode(idx, lidxtype) ); +#endif + Node* scale = new LShiftXNode(idx, intcon(shift)); + transform_later(scale); + return basic_plus_adr(ary, base, scale); +} + +Node* PhaseMacroExpand::ConvI2L(Node* offset) { + return transform_later(new ConvI2LNode(offset)); +} + +Node* PhaseMacroExpand::make_leaf_call(Node* ctrl, Node* mem, + const TypeFunc* call_type, address call_addr, + const char* call_name, + const TypePtr* adr_type, + Node* parm0, Node* parm1, + Node* parm2, Node* parm3, + Node* parm4, Node* parm5, + Node* parm6, Node* parm7) { + int size = call_type->domain()->cnt(); + Node* call = new CallLeafNoFPNode(call_type, call_addr, call_name, adr_type); + call->init_req(TypeFunc::Control, ctrl); + call->init_req(TypeFunc::I_O , top()); + call->init_req(TypeFunc::Memory , mem); + call->init_req(TypeFunc::ReturnAdr, top()); + call->init_req(TypeFunc::FramePtr, top()); + + // Hook each parm in order. Stop looking at the first NULL. + if (parm0 != NULL) { call->init_req(TypeFunc::Parms+0, parm0); + if (parm1 != NULL) { call->init_req(TypeFunc::Parms+1, parm1); + if (parm2 != NULL) { call->init_req(TypeFunc::Parms+2, parm2); + if (parm3 != NULL) { call->init_req(TypeFunc::Parms+3, parm3); + if (parm4 != NULL) { call->init_req(TypeFunc::Parms+4, parm4); + if (parm5 != NULL) { call->init_req(TypeFunc::Parms+5, parm5); + if (parm6 != NULL) { call->init_req(TypeFunc::Parms+6, parm6); + if (parm7 != NULL) { call->init_req(TypeFunc::Parms+7, parm7); + /* close each nested if ===> */ } } } } } } } } + assert(call->in(call->req()-1) != NULL, "must initialize all parms"); + + return call; +} + + +//------------------------------generate_guard--------------------------- +// Helper function for generating guarded fast-slow graph structures. +// The given 'test', if true, guards a slow path. If the test fails +// then a fast path can be taken. (We generally hope it fails.) +// In all cases, GraphKit::control() is updated to the fast path. +// The returned value represents the control for the slow path. +// The return value is never 'top'; it is either a valid control +// or NULL if it is obvious that the slow path can never be taken. +// Also, if region and the slow control are not NULL, the slow edge +// is appended to the region. +Node* PhaseMacroExpand::generate_guard(Node** ctrl, Node* test, RegionNode* region, float true_prob) { + if ((*ctrl)->is_top()) { + // Already short circuited. + return NULL; + } + // Build an if node and its projections. + // If test is true we take the slow path, which we assume is uncommon. + if (_igvn.type(test) == TypeInt::ZERO) { + // The slow branch is never taken. No need to build this guard. + return NULL; + } + + IfNode* iff = new IfNode(*ctrl, test, true_prob, COUNT_UNKNOWN); + transform_later(iff); + + Node* if_slow = new IfTrueNode(iff); + transform_later(if_slow); + + if (region != NULL) { + region->add_req(if_slow); + } + + Node* if_fast = new IfFalseNode(iff); + transform_later(if_fast); + + *ctrl = if_fast; + + return if_slow; +} + +inline Node* PhaseMacroExpand::generate_slow_guard(Node** ctrl, Node* test, RegionNode* region) { + return generate_guard(ctrl, test, region, PROB_UNLIKELY_MAG(3)); +} + +void PhaseMacroExpand::generate_negative_guard(Node** ctrl, Node* index, RegionNode* region) { + if ((*ctrl)->is_top()) + return; // already stopped + if (_igvn.type(index)->higher_equal(TypeInt::POS)) // [0,maxint] + return; // index is already adequately typed + Node* cmp_lt = new CmpINode(index, intcon(0)); + transform_later(cmp_lt); + Node* bol_lt = new BoolNode(cmp_lt, BoolTest::lt); + transform_later(bol_lt); + generate_guard(ctrl, bol_lt, region, PROB_MIN); +} + +void PhaseMacroExpand::generate_limit_guard(Node** ctrl, Node* offset, Node* subseq_length, Node* array_length, RegionNode* region) { + if ((*ctrl)->is_top()) + return; // already stopped + bool zero_offset = _igvn.type(offset) == TypeInt::ZERO; + if (zero_offset && subseq_length->eqv_uncast(array_length)) + return; // common case of whole-array copy + Node* last = subseq_length; + if (!zero_offset) { // last += offset + last = new AddINode(last, offset); + transform_later(last); + } + Node* cmp_lt = new CmpUNode(array_length, last); + transform_later(cmp_lt); + Node* bol_lt = new BoolNode(cmp_lt, BoolTest::lt); + transform_later(bol_lt); + generate_guard(ctrl, bol_lt, region, PROB_MIN); +} + +Node* PhaseMacroExpand::generate_nonpositive_guard(Node** ctrl, Node* index, bool never_negative) { + if ((*ctrl)->is_top()) return NULL; + + if (_igvn.type(index)->higher_equal(TypeInt::POS1)) // [1,maxint] + return NULL; // index is already adequately typed + Node* cmp_le = new CmpINode(index, intcon(0)); + transform_later(cmp_le); + BoolTest::mask le_or_eq = (never_negative ? BoolTest::eq : BoolTest::le); + Node* bol_le = new BoolNode(cmp_le, le_or_eq); + transform_later(bol_le); + Node* is_notp = generate_guard(ctrl, bol_le, NULL, PROB_MIN); + + return is_notp; +} + +void PhaseMacroExpand::finish_arraycopy_call(Node* call, Node** ctrl, MergeMemNode** mem, const TypePtr* adr_type) { + transform_later(call); + + *ctrl = new ProjNode(call,TypeFunc::Control); + transform_later(*ctrl); + Node* newmem = new ProjNode(call, TypeFunc::Memory); + transform_later(newmem); + + uint alias_idx = C->get_alias_index(adr_type); + if (alias_idx != Compile::AliasIdxBot) { + *mem = MergeMemNode::make(*mem); + (*mem)->set_memory_at(alias_idx, newmem); + } else { + *mem = MergeMemNode::make(newmem); + } + transform_later(*mem); +} + +address PhaseMacroExpand::basictype2arraycopy(BasicType t, + Node* src_offset, + Node* dest_offset, + bool disjoint_bases, + const char* &name, + bool dest_uninitialized) { + const TypeInt* src_offset_inttype = _igvn.find_int_type(src_offset);; + const TypeInt* dest_offset_inttype = _igvn.find_int_type(dest_offset);; + + bool aligned = false; + bool disjoint = disjoint_bases; + + // if the offsets are the same, we can treat the memory regions as + // disjoint, because either the memory regions are in different arrays, + // or they are identical (which we can treat as disjoint.) We can also + // treat a copy with a destination index less that the source index + // as disjoint since a low->high copy will work correctly in this case. + if (src_offset_inttype != NULL && src_offset_inttype->is_con() && + dest_offset_inttype != NULL && dest_offset_inttype->is_con()) { + // both indices are constants + int s_offs = src_offset_inttype->get_con(); + int d_offs = dest_offset_inttype->get_con(); + int element_size = type2aelembytes(t); + aligned = ((arrayOopDesc::base_offset_in_bytes(t) + s_offs * element_size) % HeapWordSize == 0) && + ((arrayOopDesc::base_offset_in_bytes(t) + d_offs * element_size) % HeapWordSize == 0); + if (s_offs >= d_offs) disjoint = true; + } else if (src_offset == dest_offset && src_offset != NULL) { + // This can occur if the offsets are identical non-constants. + disjoint = true; + } + + return StubRoutines::select_arraycopy_function(t, aligned, disjoint, name, dest_uninitialized); +} + +#define COMMA , +#define XTOP LP64_ONLY(COMMA top()) + +// Generate an optimized call to arraycopy. +// Caller must guard against non-arrays. +// Caller must determine a common array basic-type for both arrays. +// Caller must validate offsets against array bounds. +// The slow_region has already collected guard failure paths +// (such as out of bounds length or non-conformable array types). +// The generated code has this shape, in general: +// +// if (length == 0) return // via zero_path +// slowval = -1 +// if (types unknown) { +// slowval = call generic copy loop +// if (slowval == 0) return // via checked_path +// } else if (indexes in bounds) { +// if ((is object array) && !(array type check)) { +// slowval = call checked copy loop +// if (slowval == 0) return // via checked_path +// } else { +// call bulk copy loop +// return // via fast_path +// } +// } +// // adjust params for remaining work: +// if (slowval != -1) { +// n = -1^slowval; src_offset += n; dest_offset += n; length -= n +// } +// slow_region: +// call slow arraycopy(src, src_offset, dest, dest_offset, length) +// return // via slow_call_path +// +// This routine is used from several intrinsics: System.arraycopy, +// Object.clone (the array subcase), and Arrays.copyOf[Range]. +// +Node* PhaseMacroExpand::generate_arraycopy(ArrayCopyNode *ac, AllocateArrayNode* alloc, + Node** ctrl, MergeMemNode* mem, Node** io, + const TypePtr* adr_type, + BasicType basic_elem_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, + bool disjoint_bases, + bool length_never_negative, + RegionNode* slow_region) { + if (slow_region == NULL) { + slow_region = new RegionNode(1); + transform_later(slow_region); + } + + Node* original_dest = dest; + bool dest_uninitialized = false; + + // See if this is the initialization of a newly-allocated array. + // If so, we will take responsibility here for initializing it to zero. + // (Note: Because tightly_coupled_allocation performs checks on the + // out-edges of the dest, we need to avoid making derived pointers + // from it until we have checked its uses.) + if (ReduceBulkZeroing + && !ZeroTLAB // pointless if already zeroed + && basic_elem_type != T_CONFLICT // avoid corner case + && !src->eqv_uncast(dest) + && alloc != NULL + && _igvn.find_int_con(alloc->in(AllocateNode::ALength), 1) > 0 + && alloc->maybe_set_complete(&_igvn)) { + // "You break it, you buy it." + InitializeNode* init = alloc->initialization(); + assert(init->is_complete(), "we just did this"); + init->set_complete_with_arraycopy(); + assert(dest->is_CheckCastPP(), "sanity"); + assert(dest->in(0)->in(0) == init, "dest pinned"); + adr_type = TypeRawPtr::BOTTOM; // all initializations are into raw memory + // From this point on, every exit path is responsible for + // initializing any non-copied parts of the object to zero. + // Also, if this flag is set we make sure that arraycopy interacts properly + // with G1, eliding pre-barriers. See CR 6627983. + dest_uninitialized = true; + } else { + // No zeroing elimination here. + alloc = NULL; + //original_dest = dest; + //dest_uninitialized = false; + } + + uint alias_idx = C->get_alias_index(adr_type); + + // Results are placed here: + enum { fast_path = 1, // normal void-returning assembly stub + checked_path = 2, // special assembly stub with cleanup + slow_call_path = 3, // something went wrong; call the VM + zero_path = 4, // bypass when length of copy is zero + bcopy_path = 5, // copy primitive array by 64-bit blocks + PATH_LIMIT = 6 + }; + RegionNode* result_region = new RegionNode(PATH_LIMIT); + PhiNode* result_i_o = new PhiNode(result_region, Type::ABIO); + PhiNode* result_memory = new PhiNode(result_region, Type::MEMORY, adr_type); + assert(adr_type != TypePtr::BOTTOM, "must be RawMem or a T[] slice"); + transform_later(result_region); + transform_later(result_i_o); + transform_later(result_memory); + + // The slow_control path: + Node* slow_control; + Node* slow_i_o = *io; + Node* slow_mem = mem->memory_at(alias_idx); + DEBUG_ONLY(slow_control = (Node*) badAddress); + + // Checked control path: + Node* checked_control = top(); + Node* checked_mem = NULL; + Node* checked_i_o = NULL; + Node* checked_value = NULL; + + if (basic_elem_type == T_CONFLICT) { + assert(!dest_uninitialized, ""); + Node* cv = generate_generic_arraycopy(ctrl, &mem, + adr_type, + src, src_offset, dest, dest_offset, + copy_length, dest_uninitialized); + if (cv == NULL) cv = intcon(-1); // failure (no stub available) + checked_control = *ctrl; + checked_i_o = *io; + checked_mem = mem->memory_at(alias_idx); + checked_value = cv; + *ctrl = top(); + } + + Node* not_pos = generate_nonpositive_guard(ctrl, copy_length, length_never_negative); + if (not_pos != NULL) { + Node* local_ctrl = not_pos, *local_io = *io; + MergeMemNode* local_mem = MergeMemNode::make(mem); + transform_later(local_mem); + + // (6) length must not be negative. + if (!length_never_negative) { + generate_negative_guard(&local_ctrl, copy_length, slow_region); + } + + // copy_length is 0. + if (dest_uninitialized) { + assert(!local_ctrl->is_top(), "no ctrl?"); + Node* dest_length = alloc->in(AllocateNode::ALength); + if (copy_length->eqv_uncast(dest_length) + || _igvn.find_int_con(dest_length, 1) <= 0) { + // There is no zeroing to do. No need for a secondary raw memory barrier. + } else { + // Clear the whole thing since there are no source elements to copy. + generate_clear_array(local_ctrl, local_mem, + adr_type, dest, basic_elem_type, + intcon(0), NULL, + alloc->in(AllocateNode::AllocSize)); + // Use a secondary InitializeNode as raw memory barrier. + // Currently it is needed only on this path since other + // paths have stub or runtime calls as raw memory barriers. + MemBarNode* mb = MemBarNode::make(C, Op_Initialize, + Compile::AliasIdxRaw, + top()); + transform_later(mb); + mb->set_req(TypeFunc::Control,local_ctrl); + mb->set_req(TypeFunc::Memory, local_mem->memory_at(Compile::AliasIdxRaw)); + local_ctrl = transform_later(new ProjNode(mb, TypeFunc::Control)); + local_mem->set_memory_at(Compile::AliasIdxRaw, transform_later(new ProjNode(mb, TypeFunc::Memory))); + + InitializeNode* init = mb->as_Initialize(); + init->set_complete(&_igvn); // (there is no corresponding AllocateNode) + } + } + + // Present the results of the fast call. + result_region->init_req(zero_path, local_ctrl); + result_i_o ->init_req(zero_path, local_io); + result_memory->init_req(zero_path, local_mem->memory_at(alias_idx)); + } + + if (!(*ctrl)->is_top() && dest_uninitialized) { + // We have to initialize the *uncopied* part of the array to zero. + // The copy destination is the slice dest[off..off+len]. The other slices + // are dest_head = dest[0..off] and dest_tail = dest[off+len..dest.length]. + Node* dest_size = alloc->in(AllocateNode::AllocSize); + Node* dest_length = alloc->in(AllocateNode::ALength); + Node* dest_tail = transform_later( new AddINode(dest_offset, copy_length)); + + // If there is a head section that needs zeroing, do it now. + if (_igvn.find_int_con(dest_offset, -1) != 0) { + generate_clear_array(*ctrl, mem, + adr_type, dest, basic_elem_type, + intcon(0), dest_offset, + NULL); + } + + // Next, perform a dynamic check on the tail length. + // It is often zero, and we can win big if we prove this. + // There are two wins: Avoid generating the ClearArray + // with its attendant messy index arithmetic, and upgrade + // the copy to a more hardware-friendly word size of 64 bits. + Node* tail_ctl = NULL; + if (!(*ctrl)->is_top() && !dest_tail->eqv_uncast(dest_length)) { + Node* cmp_lt = transform_later( new CmpINode(dest_tail, dest_length) ); + Node* bol_lt = transform_later( new BoolNode(cmp_lt, BoolTest::lt) ); + tail_ctl = generate_slow_guard(ctrl, bol_lt, NULL); + assert(tail_ctl != NULL || !(*ctrl)->is_top(), "must be an outcome"); + } + + // At this point, let's assume there is no tail. + if (!(*ctrl)->is_top() && alloc != NULL && basic_elem_type != T_OBJECT) { + // There is no tail. Try an upgrade to a 64-bit copy. + bool didit = false; + { + Node* local_ctrl = *ctrl, *local_io = *io; + MergeMemNode* local_mem = MergeMemNode::make(mem); + transform_later(local_mem); + + didit = generate_block_arraycopy(&local_ctrl, &local_mem, local_io, + adr_type, basic_elem_type, alloc, + src, src_offset, dest, dest_offset, + dest_size, dest_uninitialized); + if (didit) { + // Present the results of the block-copying fast call. + result_region->init_req(bcopy_path, local_ctrl); + result_i_o ->init_req(bcopy_path, local_io); + result_memory->init_req(bcopy_path, local_mem->memory_at(alias_idx)); + } + } + if (didit) { + *ctrl = top(); // no regular fast path + } + } + + // Clear the tail, if any. + if (tail_ctl != NULL) { + Node* notail_ctl = (*ctrl)->is_top() ? NULL : *ctrl; + *ctrl = tail_ctl; + if (notail_ctl == NULL) { + generate_clear_array(*ctrl, mem, + adr_type, dest, basic_elem_type, + dest_tail, NULL, + dest_size); + } else { + // Make a local merge. + Node* done_ctl = transform_later(new RegionNode(3)); + Node* done_mem = transform_later(new PhiNode(done_ctl, Type::MEMORY, adr_type)); + done_ctl->init_req(1, notail_ctl); + done_mem->init_req(1, mem->memory_at(alias_idx)); + generate_clear_array(*ctrl, mem, + adr_type, dest, basic_elem_type, + dest_tail, NULL, + dest_size); + done_ctl->init_req(2, *ctrl); + done_mem->init_req(2, mem->memory_at(alias_idx)); + *ctrl = done_ctl; + mem->set_memory_at(alias_idx, done_mem); + } + } + } + + BasicType copy_type = basic_elem_type; + assert(basic_elem_type != T_ARRAY, "caller must fix this"); + if (!(*ctrl)->is_top() && copy_type == T_OBJECT) { + // If src and dest have compatible element types, we can copy bits. + // Types S[] and D[] are compatible if D is a supertype of S. + // + // If they are not, we will use checked_oop_disjoint_arraycopy, + // which performs a fast optimistic per-oop check, and backs off + // further to JVM_ArrayCopy on the first per-oop check that fails. + // (Actually, we don't move raw bits only; the GC requires card marks.) + + // Get the klass* for both src and dest + Node* src_klass = ac->in(ArrayCopyNode::SrcKlass); + Node* dest_klass = ac->in(ArrayCopyNode::DestKlass); + + assert(src_klass != NULL && dest_klass != NULL, "should have klasses"); + + // Generate the subtype check. + // This might fold up statically, or then again it might not. + // + // Non-static example: Copying List.elements to a new String[]. + // The backing store for a List is always an Object[], + // but its elements are always type String, if the generic types + // are correct at the source level. + // + // Test S[] against D[], not S against D, because (probably) + // the secondary supertype cache is less busy for S[] than S. + // This usually only matters when D is an interface. + Node* not_subtype_ctrl = ac->is_arraycopy_notest() ? top() : Phase::gen_subtype_check(src_klass, dest_klass, ctrl, mem, &_igvn); + // Plug failing path into checked_oop_disjoint_arraycopy + if (not_subtype_ctrl != top()) { + Node* local_ctrl = not_subtype_ctrl; + MergeMemNode* local_mem = MergeMemNode::make(mem); + transform_later(local_mem); + + // (At this point we can assume disjoint_bases, since types differ.) + int ek_offset = in_bytes(ObjArrayKlass::element_klass_offset()); + Node* p1 = basic_plus_adr(dest_klass, ek_offset); + Node* n1 = LoadKlassNode::make(_igvn, C->immutable_memory(), p1, TypeRawPtr::BOTTOM); + Node* dest_elem_klass = transform_later(n1); + Node* cv = generate_checkcast_arraycopy(&local_ctrl, &local_mem, + adr_type, + dest_elem_klass, + src, src_offset, dest, dest_offset, + ConvI2X(copy_length), dest_uninitialized); + if (cv == NULL) cv = intcon(-1); // failure (no stub available) + checked_control = local_ctrl; + checked_i_o = *io; + checked_mem = local_mem->memory_at(alias_idx); + checked_value = cv; + } + // At this point we know we do not need type checks on oop stores. + + // Let's see if we need card marks: + if (alloc != NULL && GraphKit::use_ReduceInitialCardMarks()) { + // If we do not need card marks, copy using the jint or jlong stub. + copy_type = LP64_ONLY(UseCompressedOops ? T_INT : T_LONG) NOT_LP64(T_INT); + assert(type2aelembytes(basic_elem_type) == type2aelembytes(copy_type), + "sizes agree"); + } + } + + if (!(*ctrl)->is_top()) { + // Generate the fast path, if possible. + Node* local_ctrl = *ctrl; + MergeMemNode* local_mem = MergeMemNode::make(mem); + transform_later(local_mem); + + generate_unchecked_arraycopy(&local_ctrl, &local_mem, + adr_type, copy_type, disjoint_bases, + src, src_offset, dest, dest_offset, + ConvI2X(copy_length), dest_uninitialized); + + // Present the results of the fast call. + result_region->init_req(fast_path, local_ctrl); + result_i_o ->init_req(fast_path, *io); + result_memory->init_req(fast_path, local_mem->memory_at(alias_idx)); + } + + // Here are all the slow paths up to this point, in one bundle: + assert(slow_region != NULL, "allocated on entry"); + slow_control = slow_region; + DEBUG_ONLY(slow_region = (RegionNode*)badAddress); + + *ctrl = checked_control; + if (!(*ctrl)->is_top()) { + // Clean up after the checked call. + // The returned value is either 0 or -1^K, + // where K = number of partially transferred array elements. + Node* cmp = new CmpINode(checked_value, intcon(0)); + transform_later(cmp); + Node* bol = new BoolNode(cmp, BoolTest::eq); + transform_later(bol); + IfNode* iff = new IfNode(*ctrl, bol, PROB_MAX, COUNT_UNKNOWN); + transform_later(iff); + + // If it is 0, we are done, so transfer to the end. + Node* checks_done = new IfTrueNode(iff); + transform_later(checks_done); + result_region->init_req(checked_path, checks_done); + result_i_o ->init_req(checked_path, checked_i_o); + result_memory->init_req(checked_path, checked_mem); + + // If it is not zero, merge into the slow call. + *ctrl = new IfFalseNode(iff); + transform_later(*ctrl); + RegionNode* slow_reg2 = new RegionNode(3); + PhiNode* slow_i_o2 = new PhiNode(slow_reg2, Type::ABIO); + PhiNode* slow_mem2 = new PhiNode(slow_reg2, Type::MEMORY, adr_type); + transform_later(slow_reg2); + transform_later(slow_i_o2); + transform_later(slow_mem2); + slow_reg2 ->init_req(1, slow_control); + slow_i_o2 ->init_req(1, slow_i_o); + slow_mem2 ->init_req(1, slow_mem); + slow_reg2 ->init_req(2, *ctrl); + slow_i_o2 ->init_req(2, checked_i_o); + slow_mem2 ->init_req(2, checked_mem); + + slow_control = slow_reg2; + slow_i_o = slow_i_o2; + slow_mem = slow_mem2; + + if (alloc != NULL) { + // We'll restart from the very beginning, after zeroing the whole thing. + // This can cause double writes, but that's OK since dest is brand new. + // So we ignore the low 31 bits of the value returned from the stub. + } else { + // We must continue the copy exactly where it failed, or else + // another thread might see the wrong number of writes to dest. + Node* checked_offset = new XorINode(checked_value, intcon(-1)); + Node* slow_offset = new PhiNode(slow_reg2, TypeInt::INT); + transform_later(checked_offset); + transform_later(slow_offset); + slow_offset->init_req(1, intcon(0)); + slow_offset->init_req(2, checked_offset); + + // Adjust the arguments by the conditionally incoming offset. + Node* src_off_plus = new AddINode(src_offset, slow_offset); + transform_later(src_off_plus); + Node* dest_off_plus = new AddINode(dest_offset, slow_offset); + transform_later(dest_off_plus); + Node* length_minus = new SubINode(copy_length, slow_offset); + transform_later(length_minus); + + // Tweak the node variables to adjust the code produced below: + src_offset = src_off_plus; + dest_offset = dest_off_plus; + copy_length = length_minus; + } + } + *ctrl = slow_control; + if (!(*ctrl)->is_top()) { + Node* local_ctrl = *ctrl, *local_io = slow_i_o; + MergeMemNode* local_mem = MergeMemNode::make(mem); + transform_later(local_mem); + + // Generate the slow path, if needed. + local_mem->set_memory_at(alias_idx, slow_mem); + + if (dest_uninitialized) { + generate_clear_array(local_ctrl, local_mem, + adr_type, dest, basic_elem_type, + intcon(0), NULL, + alloc->in(AllocateNode::AllocSize)); + } + + local_mem = generate_slow_arraycopy(ac, + &local_ctrl, local_mem, &local_io, + adr_type, + src, src_offset, dest, dest_offset, + copy_length, /*dest_uninitialized*/false); + + result_region->init_req(slow_call_path, local_ctrl); + result_i_o ->init_req(slow_call_path, local_io); + result_memory->init_req(slow_call_path, local_mem->memory_at(alias_idx)); + } else { + ShouldNotReachHere(); // no call to generate_slow_arraycopy: + // projections were not extracted + } + + // Remove unused edges. + for (uint i = 1; i < result_region->req(); i++) { + if (result_region->in(i) == NULL) { + result_region->init_req(i, top()); + } + } + + // Finished; return the combined state. + *ctrl = result_region; + *io = result_i_o; + mem->set_memory_at(alias_idx, result_memory); + + // mem no longer guaranteed to stay a MergeMemNode + Node* out_mem = mem; + DEBUG_ONLY(mem = NULL); + + // The memory edges above are precise in order to model effects around + // array copies accurately to allow value numbering of field loads around + // arraycopy. Such field loads, both before and after, are common in Java + // collections and similar classes involving header/array data structures. + // + // But with low number of register or when some registers are used or killed + // by arraycopy calls it causes registers spilling on stack. See 6544710. + // The next memory barrier is added to avoid it. If the arraycopy can be + // optimized away (which it can, sometimes) then we can manually remove + // the membar also. + // + // Do not let reads from the cloned object float above the arraycopy. + if (alloc != NULL && !alloc->initialization()->does_not_escape()) { + // Do not let stores that initialize this object be reordered with + // a subsequent store that would make this object accessible by + // other threads. + insert_mem_bar(ctrl, &out_mem, Op_MemBarStoreStore); + } else if (InsertMemBarAfterArraycopy) { + insert_mem_bar(ctrl, &out_mem, Op_MemBarCPUOrder); + } + + _igvn.replace_node(_memproj_fallthrough, out_mem); + _igvn.replace_node(_ioproj_fallthrough, *io); + _igvn.replace_node(_fallthroughcatchproj, *ctrl); + + return out_mem; +} + +// Helper for initialization of arrays, creating a ClearArray. +// It writes zero bits in [start..end), within the body of an array object. +// The memory effects are all chained onto the 'adr_type' alias category. +// +// Since the object is otherwise uninitialized, we are free +// to put a little "slop" around the edges of the cleared area, +// as long as it does not go back into the array's header, +// or beyond the array end within the heap. +// +// The lower edge can be rounded down to the nearest jint and the +// upper edge can be rounded up to the nearest MinObjAlignmentInBytes. +// +// Arguments: +// adr_type memory slice where writes are generated +// dest oop of the destination array +// basic_elem_type element type of the destination +// slice_idx array index of first element to store +// slice_len number of elements to store (or NULL) +// dest_size total size in bytes of the array object +// +// Exactly one of slice_len or dest_size must be non-NULL. +// If dest_size is non-NULL, zeroing extends to the end of the object. +// If slice_len is non-NULL, the slice_idx value must be a constant. +void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem, + const TypePtr* adr_type, + Node* dest, + BasicType basic_elem_type, + Node* slice_idx, + Node* slice_len, + Node* dest_size) { + // one or the other but not both of slice_len and dest_size: + assert((slice_len != NULL? 1: 0) + (dest_size != NULL? 1: 0) == 1, ""); + if (slice_len == NULL) slice_len = top(); + if (dest_size == NULL) dest_size = top(); + + uint alias_idx = C->get_alias_index(adr_type); + + // operate on this memory slice: + Node* mem = merge_mem->memory_at(alias_idx); // memory slice to operate on + + // scaling and rounding of indexes: + int scale = exact_log2(type2aelembytes(basic_elem_type)); + int abase = arrayOopDesc::base_offset_in_bytes(basic_elem_type); + int clear_low = (-1 << scale) & (BytesPerInt - 1); + int bump_bit = (-1 << scale) & BytesPerInt; + + // determine constant starts and ends + const intptr_t BIG_NEG = -128; + assert(BIG_NEG + 2*abase < 0, "neg enough"); + intptr_t slice_idx_con = (intptr_t) _igvn.find_int_con(slice_idx, BIG_NEG); + intptr_t slice_len_con = (intptr_t) _igvn.find_int_con(slice_len, BIG_NEG); + if (slice_len_con == 0) { + return; // nothing to do here + } + intptr_t start_con = (abase + (slice_idx_con << scale)) & ~clear_low; + intptr_t end_con = _igvn.find_intptr_t_con(dest_size, -1); + if (slice_idx_con >= 0 && slice_len_con >= 0) { + assert(end_con < 0, "not two cons"); + end_con = round_to(abase + ((slice_idx_con + slice_len_con) << scale), + BytesPerLong); + } + + if (start_con >= 0 && end_con >= 0) { + // Constant start and end. Simple. + mem = ClearArrayNode::clear_memory(ctrl, mem, dest, + start_con, end_con, &_igvn); + } else if (start_con >= 0 && dest_size != top()) { + // Constant start, pre-rounded end after the tail of the array. + Node* end = dest_size; + mem = ClearArrayNode::clear_memory(ctrl, mem, dest, + start_con, end, &_igvn); + } else if (start_con >= 0 && slice_len != top()) { + // Constant start, non-constant end. End needs rounding up. + // End offset = round_up(abase + ((slice_idx_con + slice_len) << scale), 8) + intptr_t end_base = abase + (slice_idx_con << scale); + int end_round = (-1 << scale) & (BytesPerLong - 1); + Node* end = ConvI2X(slice_len); + if (scale != 0) + end = transform_later(new LShiftXNode(end, intcon(scale) )); + end_base += end_round; + end = transform_later(new AddXNode(end, MakeConX(end_base)) ); + end = transform_later(new AndXNode(end, MakeConX(~end_round)) ); + mem = ClearArrayNode::clear_memory(ctrl, mem, dest, + start_con, end, &_igvn); + } else if (start_con < 0 && dest_size != top()) { + // Non-constant start, pre-rounded end after the tail of the array. + // This is almost certainly a "round-to-end" operation. + Node* start = slice_idx; + start = ConvI2X(start); + if (scale != 0) + start = transform_later(new LShiftXNode( start, intcon(scale) )); + start = transform_later(new AddXNode(start, MakeConX(abase)) ); + if ((bump_bit | clear_low) != 0) { + int to_clear = (bump_bit | clear_low); + // Align up mod 8, then store a jint zero unconditionally + // just before the mod-8 boundary. + if (((abase + bump_bit) & ~to_clear) - bump_bit + < arrayOopDesc::length_offset_in_bytes() + BytesPerInt) { + bump_bit = 0; + assert((abase & to_clear) == 0, "array base must be long-aligned"); + } else { + // Bump 'start' up to (or past) the next jint boundary: + start = transform_later( new AddXNode(start, MakeConX(bump_bit)) ); + assert((abase & clear_low) == 0, "array base must be int-aligned"); + } + // Round bumped 'start' down to jlong boundary in body of array. + start = transform_later(new AndXNode(start, MakeConX(~to_clear)) ); + if (bump_bit != 0) { + // Store a zero to the immediately preceding jint: + Node* x1 = transform_later(new AddXNode(start, MakeConX(-bump_bit)) ); + Node* p1 = basic_plus_adr(dest, x1); + mem = StoreNode::make(_igvn, ctrl, mem, p1, adr_type, intcon(0), T_INT, MemNode::unordered); + mem = transform_later(mem); + } + } + Node* end = dest_size; // pre-rounded + mem = ClearArrayNode::clear_memory(ctrl, mem, dest, + start, end, &_igvn); + } else { + // Non-constant start, unrounded non-constant end. + // (Nobody zeroes a random midsection of an array using this routine.) + ShouldNotReachHere(); // fix caller + } + + // Done. + merge_mem->set_memory_at(alias_idx, mem); +} + +bool PhaseMacroExpand::generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, Node* io, + const TypePtr* adr_type, + BasicType basic_elem_type, + AllocateNode* alloc, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* dest_size, bool dest_uninitialized) { + // See if there is an advantage from block transfer. + int scale = exact_log2(type2aelembytes(basic_elem_type)); + if (scale >= LogBytesPerLong) + return false; // it is already a block transfer + + // Look at the alignment of the starting offsets. + int abase = arrayOopDesc::base_offset_in_bytes(basic_elem_type); + + intptr_t src_off_con = (intptr_t) _igvn.find_int_con(src_offset, -1); + intptr_t dest_off_con = (intptr_t) _igvn.find_int_con(dest_offset, -1); + if (src_off_con < 0 || dest_off_con < 0) { + // At present, we can only understand constants. + return false; + } + + intptr_t src_off = abase + (src_off_con << scale); + intptr_t dest_off = abase + (dest_off_con << scale); + + if (((src_off | dest_off) & (BytesPerLong-1)) != 0) { + // Non-aligned; too bad. + // One more chance: Pick off an initial 32-bit word. + // This is a common case, since abase can be odd mod 8. + if (((src_off | dest_off) & (BytesPerLong-1)) == BytesPerInt && + ((src_off ^ dest_off) & (BytesPerLong-1)) == 0) { + Node* sptr = basic_plus_adr(src, src_off); + Node* dptr = basic_plus_adr(dest, dest_off); + uint alias_idx = C->get_alias_index(adr_type); + Node* sval = transform_later(LoadNode::make(_igvn, *ctrl, (*mem)->memory_at(alias_idx), sptr, adr_type, TypeInt::INT, T_INT, MemNode::unordered)); + Node* st = transform_later(StoreNode::make(_igvn, *ctrl, (*mem)->memory_at(alias_idx), dptr, adr_type, sval, T_INT, MemNode::unordered)); + (*mem)->set_memory_at(alias_idx, st); + src_off += BytesPerInt; + dest_off += BytesPerInt; + } else { + return false; + } + } + assert(src_off % BytesPerLong == 0, ""); + assert(dest_off % BytesPerLong == 0, ""); + + // Do this copy by giant steps. + Node* sptr = basic_plus_adr(src, src_off); + Node* dptr = basic_plus_adr(dest, dest_off); + Node* countx = dest_size; + countx = transform_later(new SubXNode(countx, MakeConX(dest_off))); + countx = transform_later(new URShiftXNode(countx, intcon(LogBytesPerLong))); + + bool disjoint_bases = true; // since alloc != NULL + generate_unchecked_arraycopy(ctrl, mem, + adr_type, T_LONG, disjoint_bases, + sptr, NULL, dptr, NULL, countx, dest_uninitialized); + + return true; +} + +// Helper function; generates code for the slow case. +// We make a call to a runtime method which emulates the native method, +// but without the native wrapper overhead. +MergeMemNode* PhaseMacroExpand::generate_slow_arraycopy(ArrayCopyNode *ac, + Node** ctrl, Node* mem, Node** io, + const TypePtr* adr_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, bool dest_uninitialized) { + assert(!dest_uninitialized, "Invariant"); + + const TypeFunc* call_type = OptoRuntime::slow_arraycopy_Type(); + CallNode* call = new CallStaticJavaNode(call_type, OptoRuntime::slow_arraycopy_Java(), + "slow_arraycopy", + ac->jvms()->bci(), TypePtr::BOTTOM); + + call->init_req(TypeFunc::Control, *ctrl); + call->init_req(TypeFunc::I_O , *io); + call->init_req(TypeFunc::Memory , mem); + call->init_req(TypeFunc::ReturnAdr, top()); + call->init_req(TypeFunc::FramePtr, top()); + call->init_req(TypeFunc::Parms+0, src); + call->init_req(TypeFunc::Parms+1, src_offset); + call->init_req(TypeFunc::Parms+2, dest); + call->init_req(TypeFunc::Parms+3, dest_offset); + call->init_req(TypeFunc::Parms+4, copy_length); + copy_call_debug_info(ac, call); + + call->set_cnt(PROB_UNLIKELY_MAG(4)); // Same effect as RC_UNCOMMON. + _igvn.replace_node(ac, call); + transform_later(call); + + extract_call_projections(call); + *ctrl = _fallthroughcatchproj->clone(); + transform_later(*ctrl); + + Node* m = _memproj_fallthrough->clone(); + transform_later(m); + + uint alias_idx = C->get_alias_index(adr_type); + MergeMemNode* out_mem; + if (alias_idx != Compile::AliasIdxBot) { + out_mem = MergeMemNode::make(mem); + out_mem->set_memory_at(alias_idx, m); + } else { + out_mem = MergeMemNode::make(m); + } + transform_later(out_mem); + + *io = _ioproj_fallthrough->clone(); + transform_later(*io); + + return out_mem; +} + +// Helper function; generates code for cases requiring runtime checks. +Node* PhaseMacroExpand::generate_checkcast_arraycopy(Node** ctrl, MergeMemNode** mem, + const TypePtr* adr_type, + Node* dest_elem_klass, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, bool dest_uninitialized) { + if ((*ctrl)->is_top()) return NULL; + + address copyfunc_addr = StubRoutines::checkcast_arraycopy(dest_uninitialized); + if (copyfunc_addr == NULL) { // Stub was not generated, go slow path. + return NULL; + } + + // Pick out the parameters required to perform a store-check + // for the target array. This is an optimistic check. It will + // look in each non-null element's class, at the desired klass's + // super_check_offset, for the desired klass. + int sco_offset = in_bytes(Klass::super_check_offset_offset()); + Node* p3 = basic_plus_adr(dest_elem_klass, sco_offset); + Node* n3 = new LoadINode(NULL, *mem /*memory(p3)*/, p3, _igvn.type(p3)->is_ptr(), TypeInt::INT, MemNode::unordered); + Node* check_offset = ConvI2X(transform_later(n3)); + Node* check_value = dest_elem_klass; + + Node* src_start = array_element_address(src, src_offset, T_OBJECT); + Node* dest_start = array_element_address(dest, dest_offset, T_OBJECT); + + const TypeFunc* call_type = OptoRuntime::checkcast_arraycopy_Type(); + Node* call = make_leaf_call(*ctrl, *mem, call_type, copyfunc_addr, "checkcast_arraycopy", adr_type, + src_start, dest_start, copy_length XTOP, check_offset XTOP, check_value); + + finish_arraycopy_call(call, ctrl, mem, adr_type); + + Node* proj = new ProjNode(call, TypeFunc::Parms); + transform_later(proj); + + return proj; +} + +// Helper function; generates code for cases requiring runtime checks. +Node* PhaseMacroExpand::generate_generic_arraycopy(Node** ctrl, MergeMemNode** mem, + const TypePtr* adr_type, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, bool dest_uninitialized) { + if ((*ctrl)->is_top()) return NULL; + assert(!dest_uninitialized, "Invariant"); + + address copyfunc_addr = StubRoutines::generic_arraycopy(); + if (copyfunc_addr == NULL) { // Stub was not generated, go slow path. + return NULL; + } + + const TypeFunc* call_type = OptoRuntime::generic_arraycopy_Type(); + Node* call = make_leaf_call(*ctrl, *mem, call_type, copyfunc_addr, "generic_arraycopy", adr_type, + src, src_offset, dest, dest_offset, copy_length); + + finish_arraycopy_call(call, ctrl, mem, adr_type); + + Node* proj = new ProjNode(call, TypeFunc::Parms); + transform_later(proj); + + return proj; +} + +// Helper function; generates the fast out-of-line call to an arraycopy stub. +void PhaseMacroExpand::generate_unchecked_arraycopy(Node** ctrl, MergeMemNode** mem, + const TypePtr* adr_type, + BasicType basic_elem_type, + bool disjoint_bases, + Node* src, Node* src_offset, + Node* dest, Node* dest_offset, + Node* copy_length, bool dest_uninitialized) { + if ((*ctrl)->is_top()) return; + + Node* src_start = src; + Node* dest_start = dest; + if (src_offset != NULL || dest_offset != NULL) { + src_start = array_element_address(src, src_offset, basic_elem_type); + dest_start = array_element_address(dest, dest_offset, basic_elem_type); + } + + // Figure out which arraycopy runtime method to call. + const char* copyfunc_name = "arraycopy"; + address copyfunc_addr = + basictype2arraycopy(basic_elem_type, src_offset, dest_offset, + disjoint_bases, copyfunc_name, dest_uninitialized); + + const TypeFunc* call_type = OptoRuntime::fast_arraycopy_Type(); + Node* call = make_leaf_call(*ctrl, *mem, call_type, copyfunc_addr, copyfunc_name, adr_type, + src_start, dest_start, copy_length XTOP); + + finish_arraycopy_call(call, ctrl, mem, adr_type); +} + +void PhaseMacroExpand::expand_arraycopy_node(ArrayCopyNode *ac) { + Node* ctrl = ac->in(TypeFunc::Control); + Node* io = ac->in(TypeFunc::I_O); + Node* src = ac->in(ArrayCopyNode::Src); + Node* src_offset = ac->in(ArrayCopyNode::SrcPos); + Node* dest = ac->in(ArrayCopyNode::Dest); + Node* dest_offset = ac->in(ArrayCopyNode::DestPos); + Node* length = ac->in(ArrayCopyNode::Length); + MergeMemNode* merge_mem = NULL; + + if (ac->is_clonebasic()) { + assert (src_offset == NULL && dest_offset == NULL, "for clone offsets should be null"); + Node* mem = ac->in(TypeFunc::Memory); + const char* copyfunc_name = "arraycopy"; + address copyfunc_addr = + basictype2arraycopy(T_LONG, NULL, NULL, + true, copyfunc_name, true); + + const TypePtr* raw_adr_type = TypeRawPtr::BOTTOM; + const TypeFunc* call_type = OptoRuntime::fast_arraycopy_Type(); + + Node* call = make_leaf_call(ctrl, mem, call_type, copyfunc_addr, copyfunc_name, raw_adr_type, src, dest, length XTOP); + transform_later(call); + + _igvn.replace_node(ac, call); + return; + } else if (ac->is_copyof() || ac->is_copyofrange() || ac->is_cloneoop()) { + Node* mem = ac->in(TypeFunc::Memory); + merge_mem = MergeMemNode::make(mem); + transform_later(merge_mem); + + RegionNode* slow_region = new RegionNode(1); + transform_later(slow_region); + + AllocateArrayNode* alloc = NULL; + if (ac->is_alloc_tightly_coupled()) { + alloc = AllocateArrayNode::Ideal_array_allocation(dest, &_igvn); + assert(alloc != NULL, "expect alloc"); + } + + generate_arraycopy(ac, alloc, &ctrl, merge_mem, &io, + TypeAryPtr::OOPS, T_OBJECT, + src, src_offset, dest, dest_offset, length, + true, !ac->is_copyofrange()); + + return; + } + + AllocateArrayNode* alloc = NULL; + if (ac->is_alloc_tightly_coupled()) { + alloc = AllocateArrayNode::Ideal_array_allocation(dest, &_igvn); + assert(alloc != NULL, "expect alloc"); + } + + assert(ac->is_arraycopy() || ac->is_arraycopy_notest(), "should be an arraycopy"); + + // Compile time checks. If any of these checks cannot be verified at compile time, + // we do not make a fast path for this call. Instead, we let the call remain as it + // is. The checks we choose to mandate at compile time are: + // + // (1) src and dest are arrays. + const Type* src_type = src->Value(&_igvn); + const Type* dest_type = dest->Value(&_igvn); + const TypeAryPtr* top_src = src_type->isa_aryptr(); + const TypeAryPtr* top_dest = dest_type->isa_aryptr(); + + if (top_src == NULL || top_src->klass() == NULL || + top_dest == NULL || top_dest->klass() == NULL) { + // Conservatively insert a memory barrier on all memory slices. + // Do not let writes into the source float below the arraycopy. + { + Node* mem = ac->in(TypeFunc::Memory); + insert_mem_bar(&ctrl, &mem, Op_MemBarCPUOrder); + + merge_mem = MergeMemNode::make(mem); + transform_later(merge_mem); + } + + // Call StubRoutines::generic_arraycopy stub. + Node* mem = generate_arraycopy(ac, NULL, &ctrl, merge_mem, &io, + TypeRawPtr::BOTTOM, T_CONFLICT, + src, src_offset, dest, dest_offset, length); + + // Do not let reads from the destination float above the arraycopy. + // Since we cannot type the arrays, we don't know which slices + // might be affected. We could restrict this barrier only to those + // memory slices which pertain to array elements--but don't bother. + if (!InsertMemBarAfterArraycopy) { + // (If InsertMemBarAfterArraycopy, there is already one in place.) + insert_mem_bar(&ctrl, &mem, Op_MemBarCPUOrder); + } + return; + } + // (2) src and dest arrays must have elements of the same BasicType + // Figure out the size and type of the elements we will be copying. + BasicType src_elem = top_src->klass()->as_array_klass()->element_type()->basic_type(); + BasicType dest_elem = top_dest->klass()->as_array_klass()->element_type()->basic_type(); + if (src_elem == T_ARRAY) src_elem = T_OBJECT; + if (dest_elem == T_ARRAY) dest_elem = T_OBJECT; + + if (src_elem != dest_elem || dest_elem == T_VOID) { + // The component types are not the same or are not recognized. Punt. + // (But, avoid the native method wrapper to JVM_ArrayCopy.) + { + Node* mem = ac->in(TypeFunc::Memory); + merge_mem = generate_slow_arraycopy(ac, &ctrl, mem, &io, TypePtr::BOTTOM, src, src_offset, dest, dest_offset, length, false); + } + + _igvn.replace_node(_memproj_fallthrough, merge_mem); + _igvn.replace_node(_ioproj_fallthrough, io); + _igvn.replace_node(_fallthroughcatchproj, ctrl); + return; + } + + //--------------------------------------------------------------------------- + // We will make a fast path for this call to arraycopy. + + // We have the following tests left to perform: + // + // (3) src and dest must not be null. + // (4) src_offset must not be negative. + // (5) dest_offset must not be negative. + // (6) length must not be negative. + // (7) src_offset + length must not exceed length of src. + // (8) dest_offset + length must not exceed length of dest. + // (9) each element of an oop array must be assignable + + { + Node* mem = ac->in(TypeFunc::Memory); + merge_mem = MergeMemNode::make(mem); + transform_later(merge_mem); + } + + RegionNode* slow_region = new RegionNode(1); + transform_later(slow_region); + + if (!ac->is_arraycopy_notest()) { + // (3) operands must not be null + // We currently perform our null checks with the null_check routine. + // This means that the null exceptions will be reported in the caller + // rather than (correctly) reported inside of the native arraycopy call. + // This should be corrected, given time. We do our null check with the + // stack pointer restored. + // null checks done library_call.cpp + + // (4) src_offset must not be negative. + generate_negative_guard(&ctrl, src_offset, slow_region); + + // (5) dest_offset must not be negative. + generate_negative_guard(&ctrl, dest_offset, slow_region); + + // (6) length must not be negative (moved to generate_arraycopy()). + // generate_negative_guard(length, slow_region); + + // (7) src_offset + length must not exceed length of src. + Node* alen = ac->in(ArrayCopyNode::SrcLen); + assert(alen != NULL, "need src len"); + generate_limit_guard(&ctrl, + src_offset, length, + alen, + slow_region); + + // (8) dest_offset + length must not exceed length of dest. + alen = ac->in(ArrayCopyNode::DestLen); + assert(alen != NULL, "need dest len"); + generate_limit_guard(&ctrl, + dest_offset, length, + alen, + slow_region); + + // (9) each element of an oop array must be assignable + // The generate_arraycopy subroutine checks this. + } + // This is where the memory effects are placed: + const TypePtr* adr_type = TypeAryPtr::get_array_body_type(dest_elem); + generate_arraycopy(ac, alloc, &ctrl, merge_mem, &io, + adr_type, dest_elem, + src, src_offset, dest, dest_offset, length, + false, false, slow_region); +} diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp index cce4a739473..8ecb54b2dc9 100644 --- a/hotspot/src/share/vm/opto/matcher.cpp +++ b/hotspot/src/share/vm/opto/matcher.cpp @@ -305,7 +305,7 @@ void Matcher::match( ) { // to avoid false sharing if the corresponding mach node is not used. // The corresponding mach node is only used in rare cases for derived // pointers. - Node* new_ideal_null = ConNode::make(C, TypePtr::NULL_PTR); + Node* new_ideal_null = ConNode::make(TypePtr::NULL_PTR); // Swap out to old-space; emptying new-space Arena *old = C->node_arena()->move_contents(C->old_arena()); @@ -1643,8 +1643,8 @@ MachNode *Matcher::ReduceInst( State *s, int rule, Node *&mem ) { } // Build the object to represent this state & prepare for recursive calls - MachNode *mach = s->MachNodeGenerator( rule, C ); - mach->_opnds[0] = s->MachOperGenerator( _reduceOp[rule], C ); + MachNode *mach = s->MachNodeGenerator(rule); + mach->_opnds[0] = s->MachOperGenerator(_reduceOp[rule]); assert( mach->_opnds[0] != NULL, "Missing result operand" ); Node *leaf = s->_leaf; // Check for instruction or instruction chain rule @@ -1756,13 +1756,13 @@ void Matcher::ReduceInst_Chain_Rule( State *s, int rule, Node *&mem, MachNode *m assert( 0 <= opnd_class_instance && opnd_class_instance < NUM_OPERANDS, "Bad AD file: Instruction chain rule must chain from operand"); // Insert operand into array of operands for this instruction - mach->_opnds[1] = s->MachOperGenerator( opnd_class_instance, C ); + mach->_opnds[1] = s->MachOperGenerator(opnd_class_instance); ReduceOper( s, newrule, mem, mach ); } else { // Chain from the result of an instruction assert( newrule >= _LAST_MACH_OPER, "Do NOT chain from internal operand"); - mach->_opnds[1] = s->MachOperGenerator( _reduceOp[catch_op], C ); + mach->_opnds[1] = s->MachOperGenerator(_reduceOp[catch_op]); Node *mem1 = (Node*)1; debug_only(Node *save_mem_node = _mem_node;) mach->add_req( ReduceInst(s, newrule, mem1) ); @@ -1807,7 +1807,7 @@ uint Matcher::ReduceInst_Interior( State *s, int rule, Node *&mem, MachNode *mac if( newrule < NUM_OPERANDS ) { // Operand/operandClass or internalOp/instruction? // Operand/operandClass // Insert operand into array of operands for this instruction - mach->_opnds[num_opnds++] = newstate->MachOperGenerator( opnd_class_instance, C ); + mach->_opnds[num_opnds++] = newstate->MachOperGenerator(opnd_class_instance); ReduceOper( newstate, newrule, mem, mach ); } else { // Child is internal operand or new instruction @@ -1818,7 +1818,7 @@ uint Matcher::ReduceInst_Interior( State *s, int rule, Node *&mem, MachNode *mac } else { // instruction --> call build operand( ) to catch result // --> ReduceInst( newrule ) - mach->_opnds[num_opnds++] = s->MachOperGenerator( _reduceOp[catch_op], C ); + mach->_opnds[num_opnds++] = s->MachOperGenerator(_reduceOp[catch_op]); Node *mem1 = (Node*)1; debug_only(Node *save_mem_node = _mem_node;) mach->add_req( ReduceInst( newstate, newrule, mem1 ) ); diff --git a/hotspot/src/share/vm/opto/mathexactnode.cpp b/hotspot/src/share/vm/opto/mathexactnode.cpp index 00466ad3d50..a96656854f5 100644 --- a/hotspot/src/share/vm/opto/mathexactnode.cpp +++ b/hotspot/src/share/vm/opto/mathexactnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -191,7 +191,7 @@ struct IdealHelper { NativeType val1 = TypeClass::as_self(type1)->get_con(); NativeType val2 = TypeClass::as_self(type2)->get_con(); if (node->will_overflow(val1, val2) == false) { - Node* con_result = ConINode::make(phase->C, 0); + Node* con_result = ConINode::make(0); return con_result; } return NULL; diff --git a/hotspot/src/share/vm/opto/memnode.cpp b/hotspot/src/share/vm/opto/memnode.cpp index 1c4d2bd773e..3d8271dc92a 100644 --- a/hotspot/src/share/vm/opto/memnode.cpp +++ b/hotspot/src/share/vm/opto/memnode.cpp @@ -933,12 +933,12 @@ Node *LoadNode::make(PhaseGVN& gvn, Node *ctl, Node *mem, Node *adr, const TypeP return (LoadNode*)NULL; } -LoadLNode* LoadLNode::make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, MemOrd mo) { +LoadLNode* LoadLNode::make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, MemOrd mo) { bool require_atomic = true; return new LoadLNode(ctl, mem, adr, adr_type, rt->is_long(), mo, require_atomic); } -LoadDNode* LoadDNode::make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, MemOrd mo) { +LoadDNode* LoadDNode::make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, MemOrd mo) { bool require_atomic = true; return new LoadDNode(ctl, mem, adr, adr_type, rt, mo, require_atomic); } @@ -1471,6 +1471,7 @@ Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* ctrl = in(MemNode::Control); Node* address = in(MemNode::Address); + bool progress = false; // Skip up past a SafePoint control. Cannot do this for Stores because // pointer stores & cardmarks must stay on the same side of a SafePoint. @@ -1478,6 +1479,7 @@ Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) { phase->C->get_alias_index(phase->type(address)->is_ptr()) != Compile::AliasIdxRaw ) { ctrl = ctrl->in(0); set_req(MemNode::Control,ctrl); + progress = true; } intptr_t ignore = 0; @@ -1490,6 +1492,7 @@ Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) { && all_controls_dominate(base, phase->C->start())) { // A method-invariant, non-null address (constant or 'this' argument). set_req(MemNode::Control, NULL); + progress = true; } } @@ -1550,7 +1553,7 @@ Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) { } } - return NULL; // No further progress + return progress ? this : NULL; } // Helper to recognize certain Klass fields which are invariant across @@ -1796,13 +1799,6 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const { } const Type* aift = load_array_final_field(tkls, klass); if (aift != NULL) return aift; - if (tkls->offset() == in_bytes(ArrayKlass::component_mirror_offset()) - && klass->is_array_klass()) { - // The field is ArrayKlass::_component_mirror. Return its (constant) value. - // (Folds up aClassConstant.getComponentType, common in Arrays.copyOf.) - assert(Opcode() == Op_LoadP, "must load an oop from _component_mirror"); - return TypeInstPtr::make(klass->as_array_klass()->component_mirror()); - } if (tkls->offset() == in_bytes(Klass::java_mirror_offset())) { // The field is Klass::_java_mirror. Return its (constant) value. // (Folds up the 2nd indirection in anObjConstant.getClass().) @@ -2014,7 +2010,6 @@ const Type* LoadSNode::Value(PhaseTransform *phase) const { //----------------------------LoadKlassNode::make------------------------------ // Polymorphic factory method: Node *LoadKlassNode::make( PhaseGVN& gvn, Node *mem, Node *adr, const TypePtr* at, const TypeKlassPtr *tk ) { - Compile* C = gvn.C; Node *ctl = NULL; // sanity check the alias category against the created node type const TypePtr *adr_type = adr->bottom_type()->isa_ptr(); @@ -2198,18 +2193,15 @@ Node* LoadNode::klass_identity_common(PhaseTransform *phase ) { } // Simplify k.java_mirror.as_klass to plain k, where k is a Klass*. - // Simplify ak.component_mirror.array_klass to plain ak, ak an ArrayKlass. // See inline_native_Class_query for occurrences of these patterns. // Java Example: x.getClass().isAssignableFrom(y) - // Java Example: Array.newInstance(x.getClass().getComponentType(), n) // // This improves reflective code, often making the Class // mirror go completely dead. (Current exception: Class // mirrors may appear in debug info, but we could clean them out by // introducing a new debug info operator for Klass*.java_mirror). if (toop->isa_instptr() && toop->klass() == phase->C->env()->Class_klass() - && (offset == java_lang_Class::klass_offset_in_bytes() || - offset == java_lang_Class::array_klass_offset_in_bytes())) { + && offset == java_lang_Class::klass_offset_in_bytes()) { // We are loading a special hidden field from a Class mirror, // the field which points to its Klass or ArrayKlass metaobject. if (base->is_Load()) { @@ -2221,9 +2213,6 @@ Node* LoadNode::klass_identity_common(PhaseTransform *phase ) { && adr2->is_AddP() ) { int mirror_field = in_bytes(Klass::java_mirror_offset()); - if (offset == java_lang_Class::array_klass_offset_in_bytes()) { - mirror_field = in_bytes(ArrayKlass::component_mirror_offset()); - } if (tkls->offset() == mirror_field) { return adr2->in(AddPNode::Base); } @@ -2379,12 +2368,12 @@ StoreNode* StoreNode::make(PhaseGVN& gvn, Node* ctl, Node* mem, Node* adr, const return (StoreNode*)NULL; } -StoreLNode* StoreLNode::make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, MemOrd mo) { +StoreLNode* StoreLNode::make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, MemOrd mo) { bool require_atomic = true; return new StoreLNode(ctl, mem, adr, adr_type, val, mo, require_atomic); } -StoreDNode* StoreDNode::make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, MemOrd mo) { +StoreDNode* StoreDNode::make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, MemOrd mo) { bool require_atomic = true; return new StoreDNode(ctl, mem, adr, adr_type, val, mo, require_atomic); } @@ -2460,7 +2449,7 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { // and I need to disappear. if (moved != NULL) { // %%% hack to ensure that Ideal returns a new node: - mem = MergeMemNode::make(phase->C, mem); + mem = MergeMemNode::make(mem); return mem; // fold me away } } @@ -2797,9 +2786,10 @@ bool ClearArrayNode::step_through(Node** np, uint instance_id, PhaseTransform* p assert(n->is_ClearArray(), "sanity"); intptr_t offset; AllocateNode* alloc = AllocateNode::Ideal_allocation(n->in(3), phase, offset); - // This method is called only before Allocate nodes are expanded during - // macro nodes expansion. Before that ClearArray nodes are only generated - // in LibraryCallKit::generate_arraycopy() which follows allocations. + // This method is called only before Allocate nodes are expanded + // during macro nodes expansion. Before that ClearArray nodes are + // only generated in PhaseMacroExpand::generate_arraycopy() (before + // Allocate nodes are expanded) which follows allocations. assert(alloc != NULL, "should have allocation"); if (alloc->_idx == instance_id) { // Can not bypass initialization of the instance we are looking for. @@ -2820,7 +2810,6 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest, intptr_t start_offset, Node* end_offset, PhaseGVN* phase) { - Compile* C = phase->C; intptr_t offset = start_offset; int unit = BytesPerLong; @@ -2847,7 +2836,6 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest, return mem; } - Compile* C = phase->C; int unit = BytesPerLong; Node* zbase = start_offset; Node* zend = end_offset; @@ -2875,7 +2863,6 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest, return mem; } - Compile* C = phase->C; assert((end_offset % BytesPerInt) == 0, "odd end offset"); intptr_t done_offset = end_offset; if ((done_offset % BytesPerLong) != 0) { @@ -2944,6 +2931,7 @@ Node *MemBarNode::Ideal(PhaseGVN *phase, bool can_reshape) { return NULL; } + bool progress = false; // Eliminate volatile MemBars for scalar replaced objects. if (can_reshape && req() == (Precedent+1)) { bool eliminate = false; @@ -2966,6 +2954,7 @@ Node *MemBarNode::Ideal(PhaseGVN *phase, bool can_reshape) { phase->is_IterGVN()->_worklist.push(my_mem); // remove dead node later my_mem = NULL; } + progress = true; } if (my_mem != NULL && my_mem->is_Mem()) { const TypeOopPtr* t_oop = my_mem->in(MemNode::Address)->bottom_type()->isa_oopptr(); @@ -2995,7 +2984,7 @@ Node *MemBarNode::Ideal(PhaseGVN *phase, bool can_reshape) { return new ConINode(TypeInt::ZERO); } } - return NULL; + return progress ? this : NULL; } //------------------------------Value------------------------------------------ @@ -3497,6 +3486,7 @@ Node* InitializeNode::capture_store(StoreNode* st, intptr_t start, // if it redundantly stored the same value (or zero to fresh memory). // In any case, wire it in: + phase->igvn_rehash_node_delayed(this); set_req(i, new_st); // The caller may now kill the old guy. @@ -4126,7 +4116,7 @@ MergeMemNode::MergeMemNode(Node *new_base) : Node(1+Compile::AliasIdxRaw) { // Make a new, untransformed MergeMem with the same base as 'mem'. // If mem is itself a MergeMem, populate the result with the same edges. -MergeMemNode* MergeMemNode::make(Compile* C, Node* mem) { +MergeMemNode* MergeMemNode::make(Node* mem) { return new MergeMemNode(mem); } diff --git a/hotspot/src/share/vm/opto/memnode.hpp b/hotspot/src/share/vm/opto/memnode.hpp index 000ccdcbbbc..4a7064ad6d6 100644 --- a/hotspot/src/share/vm/opto/memnode.hpp +++ b/hotspot/src/share/vm/opto/memnode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -333,7 +333,7 @@ public: virtual int store_Opcode() const { return Op_StoreL; } virtual BasicType memory_type() const { return T_LONG; } bool require_atomic_access() const { return _require_atomic_access; } - static LoadLNode* make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, + static LoadLNode* make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, MemOrd mo); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const { @@ -384,7 +384,7 @@ public: virtual int store_Opcode() const { return Op_StoreD; } virtual BasicType memory_type() const { return T_DOUBLE; } bool require_atomic_access() const { return _require_atomic_access; } - static LoadDNode* make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, + static LoadDNode* make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, const Type* rt, MemOrd mo); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const { @@ -593,7 +593,7 @@ public: virtual int Opcode() const; virtual BasicType memory_type() const { return T_LONG; } bool require_atomic_access() const { return _require_atomic_access; } - static StoreLNode* make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, MemOrd mo); + static StoreLNode* make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, MemOrd mo); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const { StoreNode::dump_spec(st); @@ -629,7 +629,7 @@ public: virtual int Opcode() const; virtual BasicType memory_type() const { return T_DOUBLE; } bool require_atomic_access() const { return _require_atomic_access; } - static StoreDNode* make_atomic(Compile *C, Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, MemOrd mo); + static StoreDNode* make_atomic(Node* ctl, Node* mem, Node* adr, const TypePtr* adr_type, Node* val, MemOrd mo); #ifndef PRODUCT virtual void dump_spec(outputStream *st) const { StoreNode::dump_spec(st); @@ -1138,7 +1138,7 @@ public: // If the input is a whole memory state, clone it with all its slices intact. // Otherwise, make a new memory state with just that base memory input. // In either case, the result is a newly created MergeMem. - static MergeMemNode* make(Compile* C, Node* base_memory); + static MergeMemNode* make(Node* base_memory); virtual int Opcode() const; virtual Node *Identity( PhaseTransform *phase ); diff --git a/hotspot/src/share/vm/opto/movenode.cpp b/hotspot/src/share/vm/opto/movenode.cpp index b7e32deb2e4..234cdebf031 100644 --- a/hotspot/src/share/vm/opto/movenode.cpp +++ b/hotspot/src/share/vm/opto/movenode.cpp @@ -88,7 +88,7 @@ Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( in(Condition)->is_Bool() ) { BoolNode* b = in(Condition)->as_Bool(); BoolNode* b2 = b->negate(phase); - return make( phase->C, in(Control), phase->transform(b2), in(IfTrue), in(IfFalse), _type ); + return make(in(Control), phase->transform(b2), in(IfTrue), in(IfFalse), _type); } } return NULL; @@ -158,7 +158,7 @@ const Type *CMoveNode::Value( PhaseTransform *phase ) const { //------------------------------make------------------------------------------- // Make a correctly-flavored CMove. Since _type is directly determined // from the inputs we do not need to specify it here. -CMoveNode *CMoveNode::make( Compile *C, Node *c, Node *bol, Node *left, Node *right, const Type *t ) { +CMoveNode *CMoveNode::make(Node *c, Node *bol, Node *left, Node *right, const Type *t) { switch( t->basic_type() ) { case T_INT: return new CMoveINode( bol, left, right, t->is_int() ); case T_FLOAT: return new CMoveFNode( bol, left, right, t ); @@ -196,7 +196,7 @@ Node *CMoveINode::Ideal(PhaseGVN *phase, bool can_reshape) { if( in(Condition)->is_Bool() ) { BoolNode* b = in(Condition)->as_Bool(); BoolNode* b2 = b->negate(phase); - return make( phase->C, in(Control), phase->transform(b2), in(IfTrue), in(IfFalse), _type ); + return make(in(Control), phase->transform(b2), in(IfTrue), in(IfFalse), _type); } } diff --git a/hotspot/src/share/vm/opto/movenode.hpp b/hotspot/src/share/vm/opto/movenode.hpp index 8aac944ed92..bb99f7ba083 100644 --- a/hotspot/src/share/vm/opto/movenode.hpp +++ b/hotspot/src/share/vm/opto/movenode.hpp @@ -47,7 +47,7 @@ class CMoveNode : public TypeNode { virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual const Type *Value( PhaseTransform *phase ) const; virtual Node *Identity( PhaseTransform *phase ); - static CMoveNode *make( Compile *C, Node *c, Node *bol, Node *left, Node *right, const Type *t ); + static CMoveNode *make(Node *c, Node *bol, Node *left, Node *right, const Type *t); // Helper function to spot cmove graph shapes static Node *is_cmove_id( PhaseTransform *phase, Node *cmp, Node *t, Node *f, BoolNode *b ); }; diff --git a/hotspot/src/share/vm/opto/node.cpp b/hotspot/src/share/vm/opto/node.cpp index f48e87c0c33..c7cd9cd5c37 100644 --- a/hotspot/src/share/vm/opto/node.cpp +++ b/hotspot/src/share/vm/opto/node.cpp @@ -507,7 +507,7 @@ Node *Node::clone() const { (const void*)(&mthis->_opnds), 1)); mach->_opnds = to; for ( uint i = 0; i < nopnds; ++i ) { - to[i] = from[i]->clone(C); + to[i] = from[i]->clone(); } } // cloning CallNode may need to clone JVMState @@ -620,6 +620,7 @@ void Node::destruct() { *(address*)this = badAddress; // smash the C++ vtbl, probably _in = _out = (Node**) badAddress; _max = _cnt = _outmax = _outcnt = 0; + compile->remove_modified_node(this); #endif } @@ -765,6 +766,7 @@ void Node::del_req( uint idx ) { if (n != NULL) n->del_out((Node *)this); _in[idx] = in(--_cnt); // Compact the array _in[_cnt] = NULL; // NULL out emptied slot + Compile::current()->record_modified_node(this); } //------------------------------del_req_ordered-------------------------------- @@ -780,6 +782,7 @@ void Node::del_req_ordered( uint idx ) { Copy::conjoint_words_to_lower((HeapWord*)&_in[idx+1], (HeapWord*)&_in[idx], ((_cnt-idx-1)*sizeof(Node*))); } _in[--_cnt] = NULL; // NULL out emptied slot + Compile::current()->record_modified_node(this); } //------------------------------ins_req---------------------------------------- @@ -1080,6 +1083,9 @@ bool Node::has_special_unique_user() const { if( this->is_Store() ) { // Condition for back-to-back stores folding. return n->Opcode() == op && n->in(MemNode::Memory) == this; + } else if (this->is_Load()) { + // Condition for removing an unused LoadNode from the MemBarAcquire precedence input + return n->Opcode() == Op_MemBarAcquire; } else if( op == Op_AddL ) { // Condition for convL2I(addL(x,y)) ==> addI(convL2I(x),convL2I(y)) return n->Opcode() == Op_ConvL2I && n->in(1) == this; @@ -1297,6 +1303,7 @@ static void kill_dead_code( Node *dead, PhaseIterGVN *igvn ) { // Done with outputs. igvn->hash_delete(dead); igvn->_worklist.remove(dead); + igvn->C->remove_modified_node(dead); igvn->set_type(dead, Type::TOP); if (dead->is_macro()) { igvn->C->remove_macro_node(dead); diff --git a/hotspot/src/share/vm/opto/node.hpp b/hotspot/src/share/vm/opto/node.hpp index 5bb9c6873d7..bcf6911df52 100644 --- a/hotspot/src/share/vm/opto/node.hpp +++ b/hotspot/src/share/vm/opto/node.hpp @@ -40,6 +40,7 @@ class AddPNode; class AliasInfo; class AllocateArrayNode; class AllocateNode; +class ArrayCopyNode; class Block; class BoolNode; class BoxLockNode; @@ -398,6 +399,7 @@ protected: if (*p != NULL) (*p)->del_out((Node *)this); (*p) = n; if (n != NULL) n->add_out((Node *)this); + Compile::current()->record_modified_node(this); } // Light version of set_req() to init inputs after node creation. void init_req( uint i, Node *n ) { @@ -409,6 +411,7 @@ protected: assert( _in[i] == NULL, "sanity"); _in[i] = n; if (n != NULL) n->add_out((Node *)this); + Compile::current()->record_modified_node(this); } // Find first occurrence of n among my edges: int find_edge(Node* n); @@ -559,6 +562,7 @@ public: DEFINE_CLASS_ID(AbstractLock, Call, 3) DEFINE_CLASS_ID(Lock, AbstractLock, 0) DEFINE_CLASS_ID(Unlock, AbstractLock, 1) + DEFINE_CLASS_ID(ArrayCopy, Call, 4) DEFINE_CLASS_ID(MultiBranch, Multi, 1) DEFINE_CLASS_ID(PCTable, MultiBranch, 0) DEFINE_CLASS_ID(Catch, PCTable, 0) @@ -705,6 +709,7 @@ public: DEFINE_CLASS_QUERY(AddP) DEFINE_CLASS_QUERY(Allocate) DEFINE_CLASS_QUERY(AllocateArray) + DEFINE_CLASS_QUERY(ArrayCopy) DEFINE_CLASS_QUERY(Bool) DEFINE_CLASS_QUERY(BoxLock) DEFINE_CLASS_QUERY(Call) diff --git a/hotspot/src/share/vm/opto/output.cpp b/hotspot/src/share/vm/opto/output.cpp index 15bf30b393a..16ec69c219d 100644 --- a/hotspot/src/share/vm/opto/output.cpp +++ b/hotspot/src/share/vm/opto/output.cpp @@ -526,7 +526,7 @@ void Compile::shorten_branches(uint* blk_starts, int& code_size, int& reloc_size if (_matcher->is_short_branch_offset(mach->rule(), br_size, offset)) { // We've got a winner. Replace this branch. - MachNode* replacement = mach->as_MachBranch()->short_branch_version(this); + MachNode* replacement = mach->as_MachBranch()->short_branch_version(); // Update the jmp_size. int new_size = replacement->size(_regalloc); @@ -785,9 +785,10 @@ void Compile::FillLocArray( int idx, MachSafePointNode* sfpt, Node *local, // grow downwards in all implementations. // (If, on some machine, the interpreter's Java locals or stack // were to grow upwards, the embedded doubles would be word-swapped.) - jint *dp = (jint*)&d; - array->append(new ConstantIntValue(dp[1])); - array->append(new ConstantIntValue(dp[0])); + jlong_accessor acc; + acc.long_value = jlong_cast(d); + array->append(new ConstantIntValue(acc.words[1])); + array->append(new ConstantIntValue(acc.words[0])); #endif break; } @@ -804,9 +805,10 @@ void Compile::FillLocArray( int idx, MachSafePointNode* sfpt, Node *local, // grow downwards in all implementations. // (If, on some machine, the interpreter's Java locals or stack // were to grow upwards, the embedded doubles would be word-swapped.) - jint *dp = (jint*)&d; - array->append(new ConstantIntValue(dp[1])); - array->append(new ConstantIntValue(dp[0])); + jlong_accessor acc; + acc.long_value = d; + array->append(new ConstantIntValue(acc.words[1])); + array->append(new ConstantIntValue(acc.words[0])); #endif break; } @@ -1174,7 +1176,7 @@ CodeBuffer* Compile::init_buffer(uint* blk_starts) { // fill in the nop array for bundling computations MachNode *_nop_list[Bundle::_nop_count]; - Bundle::initialize_nops(_nop_list, this); + Bundle::initialize_nops(_nop_list); return cb; } @@ -1408,7 +1410,7 @@ void Compile::fill_buffer(CodeBuffer* cb, uint* blk_starts) { if (_matcher->is_short_branch_offset(mach->rule(), br_size, offset)) { // We've got a winner. Replace this branch. - MachNode* replacement = mach->as_MachBranch()->short_branch_version(this); + MachNode* replacement = mach->as_MachBranch()->short_branch_version(); // Update the jmp_size. int new_size = replacement->size(_regalloc); diff --git a/hotspot/src/share/vm/opto/parse1.cpp b/hotspot/src/share/vm/opto/parse1.cpp index a758f72acdc..118cb62db5c 100644 --- a/hotspot/src/share/vm/opto/parse1.cpp +++ b/hotspot/src/share/vm/opto/parse1.cpp @@ -575,12 +575,13 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) decrement_age(); } } - if (depth() == 1) { + + if (depth() == 1 && !failing()) { // Add check to deoptimize the nmethod if RTM state was changed rtm_deopt(); } - // Check for bailouts during method entry. + // Check for bailouts during method entry or RTM state check setup. if (failing()) { if (log) log->done("parse"); C->set_default_node_notes(caller_nn); @@ -1756,7 +1757,7 @@ void Parse::merge_memory_edges(MergeMemNode* n, int pnum, bool nophi) { if (remerge == NULL) { assert(base != NULL, ""); assert(base->in(0) != NULL, "should not be xformed away"); - remerge = MergeMemNode::make(C, base->in(pnum)); + remerge = MergeMemNode::make(base->in(pnum)); gvn().set_type(remerge, Type::MEMORY); base->set_req(pnum, remerge); } @@ -2199,7 +2200,7 @@ void Parse::add_safepoint() { // down below a SafePoint. // Clone the current memory state - Node* mem = MergeMemNode::make(C, map()->memory()); + Node* mem = MergeMemNode::make(map()->memory()); mem = _gvn.transform(mem); @@ -2213,7 +2214,7 @@ void Parse::add_safepoint() { // Create a node for the polling address if( add_poll_param ) { - Node *polladr = ConPNode::make(C, (address)os::get_polling_page()); + Node *polladr = ConPNode::make((address)os::get_polling_page()); sfpnt->init_req(TypeFunc::Parms+0, _gvn.transform(polladr)); } diff --git a/hotspot/src/share/vm/opto/parseHelper.cpp b/hotspot/src/share/vm/opto/parseHelper.cpp index 45d627d2500..a71fb8f7752 100644 --- a/hotspot/src/share/vm/opto/parseHelper.cpp +++ b/hotspot/src/share/vm/opto/parseHelper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2014, 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 @@ -47,7 +47,7 @@ void GraphKit::make_dtrace_method_entry_exit(ciMethod* method, bool is_entry) { // Get method const TypePtr* method_type = TypeMetadataPtr::make(method); - Node *method_node = _gvn.transform( ConNode::make(C, method_type) ); + Node *method_node = _gvn.transform(ConNode::make(method_type)); kill_dead_locals(); diff --git a/hotspot/src/share/vm/opto/phase.hpp b/hotspot/src/share/vm/opto/phase.hpp index b06e3470df4..df7f896f8ae 100644 --- a/hotspot/src/share/vm/opto/phase.hpp +++ b/hotspot/src/share/vm/opto/phase.hpp @@ -27,7 +27,10 @@ #include "runtime/timer.hpp" -class Compile; +class IfNode; +class MergeMemNode; +class Node; +class PhaseGVN; //------------------------------Phase------------------------------------------ // Most optimizations are done in Phases. Creating a phase does any long @@ -114,9 +117,20 @@ protected: static elapsedTimer _t_instrSched; static elapsedTimer _t_buildOopMaps; #endif + + // Generate a subtyping check. Takes as input the subtype and supertype. + // Returns 2 values: sets the default control() to the true path and + // returns the false path. Only reads from constant memory taken from the + // default memory; does not write anything. It also doesn't take in an + // Object; if you wish to check an Object you need to load the Object's + // class prior to coming here. + // Used in GraphKit and PhaseMacroExpand + static Node* gen_subtype_check(Node* subklass, Node* superklass, Node** ctrl, MergeMemNode* mem, PhaseGVN* gvn); + public: Compile * C; Phase( PhaseNumber pnum ); + #ifndef PRODUCT static void print_timers(); #endif diff --git a/hotspot/src/share/vm/opto/phaseX.cpp b/hotspot/src/share/vm/opto/phaseX.cpp index b4470dcef53..2b0688a90b8 100644 --- a/hotspot/src/share/vm/opto/phaseX.cpp +++ b/hotspot/src/share/vm/opto/phaseX.cpp @@ -42,10 +42,14 @@ NodeHash::NodeHash(uint est_max_size) : _max( round_up(est_max_size < NODE_HASH_MINIMUM_SIZE ? NODE_HASH_MINIMUM_SIZE : est_max_size) ), _a(Thread::current()->resource_area()), _table( NEW_ARENA_ARRAY( _a , Node* , _max ) ), // (Node**)_a->Amalloc(_max * sizeof(Node*)) ), - _inserts(0), _insert_limit( insert_limit() ), - _look_probes(0), _lookup_hits(0), _lookup_misses(0), + _inserts(0), _insert_limit( insert_limit() ) +#ifndef PRODUCT + ,_look_probes(0), _lookup_hits(0), _lookup_misses(0), + _delete_probes(0), _delete_hits(0), _delete_misses(0), _total_insert_probes(0), _total_inserts(0), - _insert_probes(0), _grows(0) { + _insert_probes(0), _grows(0) +#endif +{ // _sentinel must be in the current node space _sentinel = new ProjNode(NULL, TypeFunc::Control); memset(_table,0,sizeof(Node*)*_max); @@ -56,11 +60,14 @@ NodeHash::NodeHash(Arena *arena, uint est_max_size) : _max( round_up(est_max_size < NODE_HASH_MINIMUM_SIZE ? NODE_HASH_MINIMUM_SIZE : est_max_size) ), _a(arena), _table( NEW_ARENA_ARRAY( _a , Node* , _max ) ), - _inserts(0), _insert_limit( insert_limit() ), - _look_probes(0), _lookup_hits(0), _lookup_misses(0), + _inserts(0), _insert_limit( insert_limit() ) +#ifndef PRODUCT + ,_look_probes(0), _lookup_hits(0), _lookup_misses(0), _delete_probes(0), _delete_hits(0), _delete_misses(0), _total_insert_probes(0), _total_inserts(0), - _insert_probes(0), _grows(0) { + _insert_probes(0), _grows(0) +#endif +{ // _sentinel must be in the current node space _sentinel = new ProjNode(NULL, TypeFunc::Control); memset(_table,0,sizeof(Node*)*_max); @@ -87,15 +94,15 @@ Node *NodeHash::hash_find( const Node *n ) { // ((Node*)n)->set_hash( n->hash() ); uint hash = n->hash(); if (hash == Node::NO_HASH) { - debug_only( _lookup_misses++ ); + NOT_PRODUCT( _lookup_misses++ ); return NULL; } uint key = hash & (_max-1); uint stride = key | 0x01; - debug_only( _look_probes++ ); + NOT_PRODUCT( _look_probes++ ); Node *k = _table[key]; // Get hashed value if( !k ) { // ?Miss? - debug_only( _lookup_misses++ ); + NOT_PRODUCT( _lookup_misses++ ); return NULL; // Miss! } @@ -108,16 +115,16 @@ Node *NodeHash::hash_find( const Node *n ) { if( n->in(i)!=k->in(i)) // Different inputs? goto collision; // "goto" is a speed hack... if( n->cmp(*k) ) { // Check for any special bits - debug_only( _lookup_hits++ ); + NOT_PRODUCT( _lookup_hits++ ); return k; // Hit! } } collision: - debug_only( _look_probes++ ); + NOT_PRODUCT( _look_probes++ ); key = (key + stride/*7*/) & (_max-1); // Stride through table with relative prime k = _table[key]; // Get hashed value if( !k ) { // ?Miss? - debug_only( _lookup_misses++ ); + NOT_PRODUCT( _lookup_misses++ ); return NULL; // Miss! } } @@ -132,16 +139,16 @@ Node *NodeHash::hash_find_insert( Node *n ) { // n->set_hash( ); uint hash = n->hash(); if (hash == Node::NO_HASH) { - debug_only( _lookup_misses++ ); + NOT_PRODUCT( _lookup_misses++ ); return NULL; } uint key = hash & (_max-1); uint stride = key | 0x01; // stride must be relatively prime to table siz uint first_sentinel = 0; // replace a sentinel if seen. - debug_only( _look_probes++ ); + NOT_PRODUCT( _look_probes++ ); Node *k = _table[key]; // Get hashed value if( !k ) { // ?Miss? - debug_only( _lookup_misses++ ); + NOT_PRODUCT( _lookup_misses++ ); _table[key] = n; // Insert into table! debug_only(n->enter_hash_lock()); // Lock down the node while in the table. check_grow(); // Grow table if insert hit limit @@ -160,16 +167,16 @@ Node *NodeHash::hash_find_insert( Node *n ) { if( n->in(i)!=k->in(i)) // Different inputs? goto collision; // "goto" is a speed hack... if( n->cmp(*k) ) { // Check for any special bits - debug_only( _lookup_hits++ ); + NOT_PRODUCT( _lookup_hits++ ); return k; // Hit! } } collision: - debug_only( _look_probes++ ); + NOT_PRODUCT( _look_probes++ ); key = (key + stride) & (_max-1); // Stride through table w/ relative prime k = _table[key]; // Get hashed value if( !k ) { // ?Miss? - debug_only( _lookup_misses++ ); + NOT_PRODUCT( _lookup_misses++ ); key = (first_sentinel == 0) ? key : first_sentinel; // ?saw sentinel? _table[key] = n; // Insert into table! debug_only(n->enter_hash_lock()); // Lock down the node while in the table. @@ -200,7 +207,7 @@ void NodeHash::hash_insert( Node *n ) { uint stride = key | 0x01; while( 1 ) { // While probing hash table - debug_only( _insert_probes++ ); + NOT_PRODUCT( _insert_probes++ ); Node *k = _table[key]; // Get hashed value if( !k || (k == _sentinel) ) break; // Found a slot assert( k != n, "already inserted" ); @@ -218,7 +225,7 @@ bool NodeHash::hash_delete( const Node *n ) { Node *k; uint hash = n->hash(); if (hash == Node::NO_HASH) { - debug_only( _delete_misses++ ); + NOT_PRODUCT( _delete_misses++ ); return false; } uint key = hash & (_max-1); @@ -226,10 +233,10 @@ bool NodeHash::hash_delete( const Node *n ) { debug_only( uint counter = 0; ); for( ; /* (k != NULL) && (k != _sentinel) */; ) { debug_only( counter++ ); - debug_only( _delete_probes++ ); + NOT_PRODUCT( _delete_probes++ ); k = _table[key]; // Get hashed value if( !k ) { // Miss? - debug_only( _delete_misses++ ); + NOT_PRODUCT( _delete_misses++ ); #ifdef ASSERT if( VerifyOpto ) { for( uint i=0; i < _max; i++ ) @@ -239,7 +246,7 @@ bool NodeHash::hash_delete( const Node *n ) { return false; // Miss! Not in chain } else if( n == k ) { - debug_only( _delete_hits++ ); + NOT_PRODUCT( _delete_hits++ ); _table[key] = _sentinel; // Hit! Label as deleted entry debug_only(((Node*)n)->exit_hash_lock()); // Unlock the node upon removal from table. return true; @@ -271,11 +278,13 @@ void NodeHash::grow() { uint old_max = _max; Node **old_table = _table; // Construct new table with twice the space +#ifndef PRODUCT _grows++; _total_inserts += _inserts; _total_insert_probes += _insert_probes; - _inserts = 0; _insert_probes = 0; +#endif + _inserts = 0; _max = _max << 1; _table = NEW_ARENA_ARRAY( _a , Node* , _max ); // (Node**)_a->Amalloc( _max * sizeof(Node*) ); memset(_table,0,sizeof(Node*)*_max); @@ -615,7 +624,7 @@ ConNode* PhaseTransform::makecon(const Type *t) { // Make an idealized constant - one of ConINode, ConPNode, etc. ConNode* PhaseValues::uncached_makecon(const Type *t) { assert(t->singleton(), "must be a constant"); - ConNode* x = ConNode::make(C, t); + ConNode* x = ConNode::make(t); ConNode* k = (ConNode*)hash_find_insert(x); // Value numbering if (k == NULL) { set_type(x, t); // Missed, provide type mapping @@ -933,9 +942,32 @@ void PhaseIterGVN::init_verifyPhaseIterGVN() { for (int i = 0; i < _verify_window_size; i++) { _verify_window[i] = NULL; } +#ifdef ASSERT + // Verify that all modified nodes are on _worklist + Unique_Node_List* modified_list = C->modified_nodes(); + while (modified_list != NULL && modified_list->size()) { + Node* n = modified_list->pop(); + if (n->outcnt() != 0 && !n->is_Con() && !_worklist.member(n)) { + n->dump(); + assert(false, "modified node is not on IGVN._worklist"); + } + } +#endif } void PhaseIterGVN::verify_PhaseIterGVN() { +#ifdef ASSERT + // Verify nodes with changed inputs. + Unique_Node_List* modified_list = C->modified_nodes(); + while (modified_list != NULL && modified_list->size()) { + Node* n = modified_list->pop(); + if (n->outcnt() != 0 && !n->is_Con()) { // skip dead and Con nodes + n->dump(); + assert(false, "modified node was not processed by IGVN.transform_old()"); + } + } +#endif + C->verify_graph_edges(); if( VerifyOpto && allow_progress() ) { // Must turn off allow_progress to enable assert and break recursion @@ -964,6 +996,14 @@ void PhaseIterGVN::verify_PhaseIterGVN() { (int) _verify_counter, (int) _verify_full_passes); } } + +#ifdef ASSERT + while (modified_list->size()) { + Node* n = modified_list->pop(); + n->dump(); + assert(false, "VerifyIterativeGVN: new modified node was added"); + } +#endif } #endif /* PRODUCT */ @@ -1066,6 +1106,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { Node* k = n; DEBUG_ONLY(dead_loop_check(k);) DEBUG_ONLY(bool is_new = (k->outcnt() == 0);) + C->remove_modified_node(k); Node* i = k->Ideal(this, /*can_reshape=*/true); assert(i != k || is_new || i->outcnt() > 0, "don't return dead nodes"); #ifndef PRODUCT @@ -1107,6 +1148,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { DEBUG_ONLY(dead_loop_check(k);) // Try idealizing again DEBUG_ONLY(is_new = (k->outcnt() == 0);) + C->remove_modified_node(k); i = k->Ideal(this, /*can_reshape=*/true); assert(i != k || is_new || (i->outcnt() > 0), "don't return dead nodes"); #ifndef PRODUCT @@ -1259,6 +1301,7 @@ void PhaseIterGVN::remove_globally_dead_node( Node *dead ) { _stack.pop(); // Remove dead node from iterative worklist _worklist.remove(dead); + C->remove_modified_node(dead); // Constant node that has no out-edges and has only one in-edge from // root is usually dead. However, sometimes reshaping walk makes // it reachable by adding use edges. So, we will NOT count Con nodes @@ -1288,7 +1331,7 @@ void PhaseIterGVN::subsume_node( Node *old, Node *nn ) { for (DUIterator_Last imin, i = old->last_outs(imin); i >= imin; ) { Node* use = old->last_out(i); // for each use... // use might need re-hashing (but it won't if it's a new node) - bool is_in_table = _table.hash_delete( use ); + rehash_node_delayed(use); // Update use-def info as well // We remove all occurrences of old within use->in, // so as to avoid rehashing any node more than once. @@ -1300,11 +1343,6 @@ void PhaseIterGVN::subsume_node( Node *old, Node *nn ) { ++num_edges; } } - // Insert into GVN hash table if unique - // If a duplicate, 'use' will be cleaned up when pulled off worklist - if( is_in_table ) { - hash_find_insert(use); - } i -= num_edges; // we deleted 1 or more copies of this edge } @@ -1599,7 +1637,7 @@ Node *PhaseCCP::transform_once( Node *n ) { if( t == Type::TOP ) { // cache my top node on the Compile instance if( C->cached_top_node() == NULL || C->cached_top_node()->in(0) == NULL ) { - C->set_cached_top_node( ConNode::make(C, Type::TOP) ); + C->set_cached_top_node(ConNode::make(Type::TOP)); set_type(C->top(), Type::TOP); } nn = C->top(); @@ -1725,7 +1763,7 @@ void PhasePeephole::do_transform() { MachNode *m = n->as_Mach(); int deleted_count = 0; // check for peephole opportunities - MachNode *m2 = m->peephole( block, instruction_index, _regalloc, deleted_count, C ); + MachNode *m2 = m->peephole(block, instruction_index, _regalloc, deleted_count); if( m2 != NULL ) { #ifndef PRODUCT if( PrintOptoPeephole ) { diff --git a/hotspot/src/share/vm/opto/phaseX.hpp b/hotspot/src/share/vm/opto/phaseX.hpp index ccb24819e7c..a3e06c825a7 100644 --- a/hotspot/src/share/vm/opto/phaseX.hpp +++ b/hotspot/src/share/vm/opto/phaseX.hpp @@ -100,7 +100,6 @@ public: #ifndef PRODUCT Node *find_index(uint idx); // For debugging void dump(); // For debugging, dump statistics -#endif uint _grows; // For debugging, count of table grow()s uint _look_probes; // For debugging, count of hash probes uint _lookup_hits; // For debugging, count of hash_finds @@ -111,6 +110,7 @@ public: uint _delete_misses; // For debugging, count of hash probes for deletes uint _total_inserts; // For debugging, total inserts into hash table uint _total_insert_probes; // For debugging, total probes while inserting +#endif }; @@ -311,6 +311,9 @@ public: const Type* limit_type) const { ShouldNotCallThis(); return NULL; } + // Delayed node rehash if this is an IGVN phase + virtual void igvn_rehash_node_delayed(Node* n) {} + #ifndef PRODUCT void dump_old2new_map() const; void dump_new( uint new_lidx ) const; @@ -387,6 +390,9 @@ public: // in a faster or cheaper fashion. Node *transform( Node *n ); Node *transform_no_reclaim( Node *n ); + virtual void record_for_igvn(Node *n) { + C->record_for_igvn(n); + } void replace_with(PhaseGVN* gvn) { _table.replace_with(&gvn->_table); @@ -415,9 +421,6 @@ class PhaseIterGVN : public PhaseGVN { protected: - // Idealize new Node 'n' with respect to its inputs and its value - virtual Node *transform( Node *a_node ); - // Warm up hash table, type table and initial worklist void init_worklist( Node *a_root ); @@ -431,6 +434,10 @@ public: PhaseIterGVN( PhaseGVN *gvn ); // Used after Parser PhaseIterGVN( PhaseIterGVN *igvn, const char *dummy ); // Used after +VerifyOpto + // Idealize new Node 'n' with respect to its inputs and its value + virtual Node *transform( Node *a_node ); + virtual void record_for_igvn(Node *n) { } + virtual PhaseIterGVN *is_IterGVN() { return this; } Unique_Node_List _worklist; // Iterative worklist @@ -488,6 +495,10 @@ public: _worklist.push(n); } + void igvn_rehash_node_delayed(Node* n) { + rehash_node_delayed(n); + } + // Replace ith edge of "n" with "in" void replace_input_of(Node* n, int i, Node* in) { rehash_node_delayed(n); diff --git a/hotspot/src/share/vm/opto/rootnode.cpp b/hotspot/src/share/vm/opto/rootnode.cpp index 56775ed7e01..4cf51528df5 100644 --- a/hotspot/src/share/vm/opto/rootnode.cpp +++ b/hotspot/src/share/vm/opto/rootnode.cpp @@ -35,10 +35,12 @@ //------------------------------Ideal------------------------------------------ // Remove dead inputs Node *RootNode::Ideal(PhaseGVN *phase, bool can_reshape) { + bool modified = false; for( uint i = 1; i < req(); i++ ) { // For all inputs // Check for and remove dead inputs if( phase->type(in(i)) == Type::TOP ) { del_req(i--); // Delete TOP inputs + modified = true; } } @@ -56,7 +58,7 @@ Node *RootNode::Ideal(PhaseGVN *phase, bool can_reshape) { // If we want to get the rest of the win later, we should pattern match // simple recursive call trees to closed-form solutions. - return NULL; // No further opportunities exposed + return modified ? this : NULL; } //============================================================================= diff --git a/hotspot/src/share/vm/opto/runtime.cpp b/hotspot/src/share/vm/opto/runtime.cpp index 2a2fb131dfd..ece252c1af9 100644 --- a/hotspot/src/share/vm/opto/runtime.cpp +++ b/hotspot/src/share/vm/opto/runtime.cpp @@ -922,6 +922,30 @@ const TypeFunc* OptoRuntime::digestBase_implCompressMB_Type() { return TypeFunc::make(domain, range); } +const TypeFunc* OptoRuntime::multiplyToLen_Type() { + // create input type (domain) + int num_args = 6; + int argcnt = num_args; + const Type** fields = TypeTuple::fields(argcnt); + int argp = TypeFunc::Parms; + fields[argp++] = TypePtr::NOTNULL; // x + fields[argp++] = TypeInt::INT; // xlen + fields[argp++] = TypePtr::NOTNULL; // y + fields[argp++] = TypeInt::INT; // ylen + fields[argp++] = TypePtr::NOTNULL; // z + fields[argp++] = TypeInt::INT; // zlen + assert(argp == TypeFunc::Parms+argcnt, "correct decoding"); + const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); + + // no result type needed + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = NULL; + const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields); + return TypeFunc::make(domain, range); +} + + + //------------- Interpreter state access for on stack replacement const TypeFunc* OptoRuntime::osr_end_Type() { // create input type (domain) @@ -1381,11 +1405,11 @@ NamedCounter* OptoRuntime::new_named_counter(JVMState* youngest_jvms, NamedCount } NamedCounter* c; if (tag == NamedCounter::BiasedLockingCounter) { - c = new BiasedLockingNamedCounter(strdup(st.as_string())); + c = new BiasedLockingNamedCounter(st.as_string()); } else if (tag == NamedCounter::RTMLockingCounter) { - c = new RTMLockingNamedCounter(strdup(st.as_string())); + c = new RTMLockingNamedCounter(st.as_string()); } else { - c = new NamedCounter(strdup(st.as_string()), tag); + c = new NamedCounter(st.as_string(), tag); } // atomically add the new counter to the head of the list. We only diff --git a/hotspot/src/share/vm/opto/runtime.hpp b/hotspot/src/share/vm/opto/runtime.hpp index b5e8bc34d98..3257d789ad0 100644 --- a/hotspot/src/share/vm/opto/runtime.hpp +++ b/hotspot/src/share/vm/opto/runtime.hpp @@ -75,11 +75,17 @@ private: public: NamedCounter(const char *n, CounterTag tag = NoTag): - _name(n), + _name(n == NULL ? NULL : os::strdup(n)), _count(0), _next(NULL), _tag(tag) {} + ~NamedCounter() { + if (_name != NULL) { + os::free((void*)_name); + } + } + const char * name() const { return _name; } int count() const { return _count; } address addr() { return (address)&_count; } @@ -304,6 +310,8 @@ private: static const TypeFunc* sha_implCompress_Type(); static const TypeFunc* digestBase_implCompressMB_Type(); + static const TypeFunc* multiplyToLen_Type(); + static const TypeFunc* updateBytesCRC32_Type(); // leaf on stack replacement interpreter accessor types diff --git a/hotspot/src/share/vm/opto/stringopts.cpp b/hotspot/src/share/vm/opto/stringopts.cpp index fcca4684833..1d9bae5a6ac 100644 --- a/hotspot/src/share/vm/opto/stringopts.cpp +++ b/hotspot/src/share/vm/opto/stringopts.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2014, 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 @@ -1438,7 +1438,7 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { } // Make sure the memory state is a MergeMem for parsing. if (!map->in(TypeFunc::Memory)->is_MergeMem()) { - map->set_req(TypeFunc::Memory, MergeMemNode::make(C, map->in(TypeFunc::Memory))); + map->set_req(TypeFunc::Memory, MergeMemNode::make(map->in(TypeFunc::Memory))); } jvms->set_map(map); diff --git a/hotspot/src/share/vm/opto/subnode.cpp b/hotspot/src/share/vm/opto/subnode.cpp index e5f24b6ca6c..1fe558c18d3 100644 --- a/hotspot/src/share/vm/opto/subnode.cpp +++ b/hotspot/src/share/vm/opto/subnode.cpp @@ -1168,7 +1168,6 @@ uint BoolNode::cmp( const Node &n ) const { Node* BoolNode::make_predicate(Node* test_value, PhaseGVN* phase) { if (test_value->is_Con()) return test_value; if (test_value->is_Bool()) return test_value; - Compile* C = phase->C; if (test_value->is_CMove() && test_value->in(CMoveNode::Condition)->is_Bool()) { BoolNode* bol = test_value->in(CMoveNode::Condition)->as_Bool(); @@ -1191,7 +1190,7 @@ Node* BoolNode::make_predicate(Node* test_value, PhaseGVN* phase) { //--------------------------------as_int_value--------------------------------- Node* BoolNode::as_int_value(PhaseGVN* phase) { // Inverse to make_predicate. The CMove probably boils down to a Conv2B. - Node* cmov = CMoveNode::make(phase->C, NULL, this, + Node* cmov = CMoveNode::make(NULL, this, phase->intcon(0), phase->intcon(1), TypeInt::BOOL); return phase->transform(cmov); @@ -1199,10 +1198,57 @@ Node* BoolNode::as_int_value(PhaseGVN* phase) { //----------------------------------negate------------------------------------- BoolNode* BoolNode::negate(PhaseGVN* phase) { - Compile* C = phase->C; return new BoolNode(in(1), _test.negate()); } +// Change "bool eq/ne (cmp (add/sub A B) C)" into false/true if add/sub +// overflows and we can prove that C is not in the two resulting ranges. +// This optimization is similar to the one performed by CmpUNode::Value(). +Node* BoolNode::fold_cmpI(PhaseGVN* phase, SubNode* cmp, Node* cmp1, int cmp_op, + int cmp1_op, const TypeInt* cmp2_type) { + // Only optimize eq/ne integer comparison of add/sub + if((_test._test == BoolTest::eq || _test._test == BoolTest::ne) && + (cmp_op == Op_CmpI) && (cmp1_op == Op_AddI || cmp1_op == Op_SubI)) { + // Skip cases were inputs of add/sub are not integers or of bottom type + const TypeInt* r0 = phase->type(cmp1->in(1))->isa_int(); + const TypeInt* r1 = phase->type(cmp1->in(2))->isa_int(); + if ((r0 != NULL) && (r0 != TypeInt::INT) && + (r1 != NULL) && (r1 != TypeInt::INT) && + (cmp2_type != TypeInt::INT)) { + // Compute exact (long) type range of add/sub result + jlong lo_long = r0->_lo; + jlong hi_long = r0->_hi; + if (cmp1_op == Op_AddI) { + lo_long += r1->_lo; + hi_long += r1->_hi; + } else { + lo_long -= r1->_hi; + hi_long -= r1->_lo; + } + // Check for over-/underflow by casting to integer + int lo_int = (int)lo_long; + int hi_int = (int)hi_long; + bool underflow = lo_long != (jlong)lo_int; + bool overflow = hi_long != (jlong)hi_int; + if ((underflow != overflow) && (hi_int < lo_int)) { + // Overflow on one boundary, compute resulting type ranges: + // tr1 [MIN_INT, hi_int] and tr2 [lo_int, MAX_INT] + int w = MAX2(r0->_widen, r1->_widen); // _widen does not matter here + const TypeInt* tr1 = TypeInt::make(min_jint, hi_int, w); + const TypeInt* tr2 = TypeInt::make(lo_int, max_jint, w); + // Compare second input of cmp to both type ranges + const Type* sub_tr1 = cmp->sub(tr1, cmp2_type); + const Type* sub_tr2 = cmp->sub(tr2, cmp2_type); + if (sub_tr1 == TypeInt::CC_LT && sub_tr2 == TypeInt::CC_GT) { + // The result of the add/sub will never equal cmp2. Replace BoolNode + // by false (0) if it tests for equality and by true (1) otherwise. + return ConINode::make((_test._test == BoolTest::eq) ? 0 : 1); + } + } + } + } + return NULL; +} //------------------------------Ideal------------------------------------------ Node *BoolNode::Ideal(PhaseGVN *phase, bool can_reshape) { @@ -1296,6 +1342,9 @@ Node *BoolNode::Ideal(PhaseGVN *phase, bool can_reshape) { return new BoolNode( ncmp, _test.commute() ); } + // Try to optimize signed integer comparison + return fold_cmpI(phase, cmp->as_Sub(), cmp1, cop, cmp1_op, cmp2_type); + // The transformation below is not valid for either signed or unsigned // comparisons due to wraparound concerns at MAX_VALUE and MIN_VALUE. // This transformation can be resurrected when we are able to @@ -1340,8 +1389,6 @@ Node *BoolNode::Ideal(PhaseGVN *phase, bool can_reshape) { // phase->type( cmp2->in(2) ) == TypeInt::ONE ) // return clone_cmp( cmp, cmp1, cmp2->in(1), phase, BoolTest::le ); // } - - return NULL; } //------------------------------Value------------------------------------------ diff --git a/hotspot/src/share/vm/opto/subnode.hpp b/hotspot/src/share/vm/opto/subnode.hpp index da5d7e378cd..f809a3b90c7 100644 --- a/hotspot/src/share/vm/opto/subnode.hpp +++ b/hotspot/src/share/vm/opto/subnode.hpp @@ -286,6 +286,10 @@ class BoolNode : public Node { virtual uint hash() const; virtual uint cmp( const Node &n ) const; virtual uint size_of() const; + + // Try to optimize signed integer comparison + Node* fold_cmpI(PhaseGVN* phase, SubNode* cmp, Node* cmp1, int cmp_op, + int cmp1_op, const TypeInt* cmp2_type); public: const BoolTest _test; BoolNode( Node *cc, BoolTest::mask t): _test(t), Node(0,cc) { diff --git a/hotspot/src/share/vm/opto/superword.cpp b/hotspot/src/share/vm/opto/superword.cpp index 72fabf9cc22..e20a187c6da 100644 --- a/hotspot/src/share/vm/opto/superword.cpp +++ b/hotspot/src/share/vm/opto/superword.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2014, 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 @@ -1378,9 +1378,23 @@ void SuperWord::output() { if (n->is_Load()) { Node* ctl = n->in(MemNode::Control); Node* mem = first->in(MemNode::Memory); + SWPointer p1(n->as_Mem(), this); + // Identify the memory dependency for the new loadVector node by + // walking up through memory chain. + // This is done to give flexibility to the new loadVector node so that + // it can move above independent storeVector nodes. + while (mem->is_StoreVector()) { + SWPointer p2(mem->as_Mem(), this); + int cmp = p1.cmp(p2); + if (SWPointer::not_equal(cmp) || !SWPointer::comparable(cmp)) { + mem = mem->in(MemNode::Memory); + } else { + break; // dependent memory + } + } Node* adr = low_adr->in(MemNode::Address); const TypePtr* atyp = n->adr_type(); - vn = LoadVectorNode::make(C, opc, ctl, mem, adr, atyp, vlen, velt_basic_type(n)); + vn = LoadVectorNode::make(opc, ctl, mem, adr, atyp, vlen, velt_basic_type(n)); vlen_in_bytes = vn->as_LoadVector()->memory_size(); } else if (n->is_Store()) { // Promote value to be stored to vector @@ -1389,7 +1403,7 @@ void SuperWord::output() { Node* mem = first->in(MemNode::Memory); Node* adr = low_adr->in(MemNode::Address); const TypePtr* atyp = n->adr_type(); - vn = StoreVectorNode::make(C, opc, ctl, mem, adr, atyp, val, vlen); + vn = StoreVectorNode::make(opc, ctl, mem, adr, atyp, val, vlen); vlen_in_bytes = vn->as_StoreVector()->memory_size(); } else if (n->req() == 3) { // Promote operands to vector @@ -1401,7 +1415,7 @@ void SuperWord::output() { in1 = in2; in2 = tmp; } - vn = VectorNode::make(C, opc, in1, in2, vlen, velt_basic_type(n)); + vn = VectorNode::make(opc, in1, in2, vlen, velt_basic_type(n)); vlen_in_bytes = vn->as_Vector()->length_in_bytes(); } else { ShouldNotReachHere(); @@ -1450,11 +1464,11 @@ Node* SuperWord::vector_opd(Node_List* p, int opd_idx) { if (t != NULL && t->is_con()) { juint shift = t->get_con(); if (shift > mask) { // Unsigned cmp - cnt = ConNode::make(C, TypeInt::make(shift & mask)); + cnt = ConNode::make(TypeInt::make(shift & mask)); } } else { if (t == NULL || t->_lo < 0 || t->_hi > (int)mask) { - cnt = ConNode::make(C, TypeInt::make(mask)); + cnt = ConNode::make(TypeInt::make(mask)); _igvn.register_new_node_with_optimizer(cnt); cnt = new AndINode(opd, cnt); _igvn.register_new_node_with_optimizer(cnt); @@ -1462,7 +1476,7 @@ Node* SuperWord::vector_opd(Node_List* p, int opd_idx) { } assert(opd->bottom_type()->isa_int(), "int type only"); // Move non constant shift count into vector register. - cnt = VectorNode::shift_count(C, p0, cnt, vlen, velt_basic_type(p0)); + cnt = VectorNode::shift_count(p0, cnt, vlen, velt_basic_type(p0)); } if (cnt != opd) { _igvn.register_new_node_with_optimizer(cnt); @@ -1475,7 +1489,7 @@ Node* SuperWord::vector_opd(Node_List* p, int opd_idx) { // p0's vector. Use p0's type because size of operand's container in // vector should match p0's size regardless operand's size. const Type* p0_t = velt_type(p0); - VectorNode* vn = VectorNode::scalar2vector(_phase->C, opd, vlen, p0_t); + VectorNode* vn = VectorNode::scalar2vector(opd, vlen, p0_t); _igvn.register_new_node_with_optimizer(vn); _phase->set_ctrl(vn, _phase->get_ctrl(opd)); @@ -1490,7 +1504,7 @@ Node* SuperWord::vector_opd(Node_List* p, int opd_idx) { // Insert pack operation BasicType bt = velt_basic_type(p0); - PackNode* pk = PackNode::make(_phase->C, opd, vlen, bt); + PackNode* pk = PackNode::make(opd, vlen, bt); DEBUG_ONLY( const BasicType opd_bt = opd->bottom_type()->basic_type(); ) for (uint i = 1; i < vlen; i++) { @@ -1546,7 +1560,7 @@ void SuperWord::insert_extracts(Node_List* p) { _igvn.hash_delete(def); int def_pos = alignment(def) / data_size(def); - Node* ex = ExtractNode::make(_phase->C, def, def_pos, velt_basic_type(def)); + Node* ex = ExtractNode::make(def, def_pos, velt_basic_type(def)); _igvn.register_new_node_with_optimizer(ex); _phase->set_ctrl(ex, _phase->get_ctrl(def)); _igvn.replace_input_of(use, idx, ex); diff --git a/hotspot/src/share/vm/opto/type.cpp b/hotspot/src/share/vm/opto/type.cpp index 39662643ac9..0d76394add8 100644 --- a/hotspot/src/share/vm/opto/type.cpp +++ b/hotspot/src/share/vm/opto/type.cpp @@ -265,7 +265,7 @@ void Type::Initialize_shared(Compile* current) { // locking. Arena* save = current->type_arena(); - Arena* shared_type_arena = new (mtCompiler)Arena(); + Arena* shared_type_arena = new (mtCompiler)Arena(mtCompiler); current->set_type_arena(shared_type_arena); _shared_type_dict = @@ -1708,8 +1708,8 @@ const TypeTuple *TypeTuple::LONG_CC_PAIR; // Make a TypeTuple from the range of a method signature const TypeTuple *TypeTuple::make_range(ciSignature* sig) { ciType* return_type = sig->return_type(); - uint total_fields = TypeFunc::Parms + return_type->size(); - const Type **field_array = fields(total_fields); + uint arg_cnt = return_type->size(); + const Type **field_array = fields(arg_cnt); switch (return_type->basic_type()) { case T_LONG: field_array[TypeFunc::Parms] = TypeLong::LONG; @@ -1734,26 +1734,26 @@ const TypeTuple *TypeTuple::make_range(ciSignature* sig) { default: ShouldNotReachHere(); } - return (TypeTuple*)(new TypeTuple(total_fields,field_array))->hashcons(); + return (TypeTuple*)(new TypeTuple(TypeFunc::Parms + arg_cnt, field_array))->hashcons(); } // Make a TypeTuple from the domain of a method signature const TypeTuple *TypeTuple::make_domain(ciInstanceKlass* recv, ciSignature* sig) { - uint total_fields = TypeFunc::Parms + sig->size(); + uint arg_cnt = sig->size(); uint pos = TypeFunc::Parms; const Type **field_array; if (recv != NULL) { - total_fields++; - field_array = fields(total_fields); + arg_cnt++; + field_array = fields(arg_cnt); // Use get_const_type here because it respects UseUniqueSubclasses: field_array[pos++] = get_const_type(recv)->join_speculative(TypePtr::NOTNULL); } else { - field_array = fields(total_fields); + field_array = fields(arg_cnt); } int i = 0; - while (pos < total_fields) { + while (pos < TypeFunc::Parms + arg_cnt) { ciType* type = sig->type_at(i); switch (type->basic_type()) { @@ -1780,7 +1780,8 @@ const TypeTuple *TypeTuple::make_domain(ciInstanceKlass* recv, ciSignature* sig) } i++; } - return (TypeTuple*)(new TypeTuple(total_fields,field_array))->hashcons(); + + return (TypeTuple*)(new TypeTuple(TypeFunc::Parms + arg_cnt, field_array))->hashcons(); } const TypeTuple *TypeTuple::make( uint cnt, const Type **fields ) { @@ -1789,6 +1790,7 @@ const TypeTuple *TypeTuple::make( uint cnt, const Type **fields ) { //------------------------------fields----------------------------------------- // Subroutine call type with space allocated for argument types +// Memory for Control, I_O, Memory, FramePtr, and ReturnAdr is allocated implicitly const Type **TypeTuple::fields( uint arg_cnt ) { const Type **flds = (const Type **)(Compile::current()->type_arena()->Amalloc_4((TypeFunc::Parms+arg_cnt)*sizeof(Type*) )); flds[TypeFunc::Control ] = Type::CONTROL; @@ -5087,11 +5089,11 @@ int TypeFunc::hash(void) const { // Dump Function Type #ifndef PRODUCT void TypeFunc::dump2( Dict &d, uint depth, outputStream *st ) const { - if( _range->_cnt <= Parms ) + if( _range->cnt() <= Parms ) st->print("void"); else { uint i; - for (i = Parms; i < _range->_cnt-1; i++) { + for (i = Parms; i < _range->cnt()-1; i++) { _range->field_at(i)->dump2(d,depth,st); st->print("/"); } @@ -5104,9 +5106,9 @@ void TypeFunc::dump2( Dict &d, uint depth, outputStream *st ) const { return; } d.Insert((void*)this,(void*)this); // Stop recursion - if (Parms < _domain->_cnt) + if (Parms < _domain->cnt()) _domain->field_at(Parms)->dump2(d,depth-1,st); - for (uint i = Parms+1; i < _domain->_cnt; i++) { + for (uint i = Parms+1; i < _domain->cnt(); i++) { st->print(", "); _domain->field_at(i)->dump2(d,depth-1,st); } diff --git a/hotspot/src/share/vm/opto/type.hpp b/hotspot/src/share/vm/opto/type.hpp index e4340df4f9a..1fd7c9f13c3 100644 --- a/hotspot/src/share/vm/opto/type.hpp +++ b/hotspot/src/share/vm/opto/type.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -609,16 +609,16 @@ public: // signature types. class TypeTuple : public Type { TypeTuple( uint cnt, const Type **fields ) : Type(Tuple), _cnt(cnt), _fields(fields) { } + + const uint _cnt; // Count of fields + const Type ** const _fields; // Array of field types + public: virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous -public: - const uint _cnt; // Count of fields - const Type ** const _fields; // Array of field types - // Accessors: uint cnt() const { return _cnt; } const Type* field_at(uint i) const { @@ -635,6 +635,7 @@ public: static const TypeTuple *make_domain(ciInstanceKlass* recv, ciSignature *sig); // Subroutine call type with space allocated for argument types + // Memory for Control, I_O, Memory, FramePtr, and ReturnAdr is allocated implicitly static const Type **fields( uint arg_cnt ); virtual const Type *xmeet( const Type *t ) const; @@ -1447,6 +1448,10 @@ class TypeFunc : public Type { virtual int hash() const; // Type specific hashing virtual bool singleton(void) const; // TRUE if type is a singleton virtual bool empty(void) const; // TRUE if type is vacuous + + const TypeTuple* const _domain; // Domain of inputs + const TypeTuple* const _range; // Range of results + public: // Constants are shared among ADLC and VM enum { Control = AdlcVMDeps::Control, @@ -1457,8 +1462,6 @@ public: Parms = AdlcVMDeps::Parms }; - const TypeTuple* const _domain; // Domain of inputs - const TypeTuple* const _range; // Range of results // Accessors: const TypeTuple* domain() const { return _domain; } diff --git a/hotspot/src/share/vm/opto/vectornode.cpp b/hotspot/src/share/vm/opto/vectornode.cpp index 21b51e5b524..b4a07799efb 100644 --- a/hotspot/src/share/vm/opto/vectornode.cpp +++ b/hotspot/src/share/vm/opto/vectornode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2014, 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 @@ -245,7 +245,7 @@ void VectorNode::vector_operands(Node* n, uint* start, uint* end) { } // Return the vector version of a scalar operation node. -VectorNode* VectorNode::make(Compile* C, int opc, Node* n1, Node* n2, uint vlen, BasicType bt) { +VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt) { const TypeVect* vt = TypeVect::make(bt, vlen); int vopc = VectorNode::opcode(opc, bt); // This method should not be called for unimplemented vectors. @@ -299,7 +299,7 @@ VectorNode* VectorNode::make(Compile* C, int opc, Node* n1, Node* n2, uint vlen, } // Scalar promotion -VectorNode* VectorNode::scalar2vector(Compile* C, Node* s, uint vlen, const Type* opd_t) { +VectorNode* VectorNode::scalar2vector(Node* s, uint vlen, const Type* opd_t) { BasicType bt = opd_t->array_element_basic_type(); const TypeVect* vt = opd_t->singleton() ? TypeVect::make(opd_t, vlen) : TypeVect::make(bt, vlen); @@ -323,7 +323,7 @@ VectorNode* VectorNode::scalar2vector(Compile* C, Node* s, uint vlen, const Type return NULL; } -VectorNode* VectorNode::shift_count(Compile* C, Node* shift, Node* cnt, uint vlen, BasicType bt) { +VectorNode* VectorNode::shift_count(Node* shift, Node* cnt, uint vlen, BasicType bt) { assert(VectorNode::is_shift(shift) && !cnt->is_Con(), "only variable shift count"); // Match shift count type with shift vector type. const TypeVect* vt = TypeVect::make(bt, vlen); @@ -342,7 +342,7 @@ VectorNode* VectorNode::shift_count(Compile* C, Node* shift, Node* cnt, uint vle } // Return initial Pack node. Additional operands added with add_opd() calls. -PackNode* PackNode::make(Compile* C, Node* s, uint vlen, BasicType bt) { +PackNode* PackNode::make(Node* s, uint vlen, BasicType bt) { const TypeVect* vt = TypeVect::make(bt, vlen); switch (bt) { case T_BOOLEAN: @@ -365,18 +365,18 @@ PackNode* PackNode::make(Compile* C, Node* s, uint vlen, BasicType bt) { } // Create a binary tree form for Packs. [lo, hi) (half-open) range -PackNode* PackNode::binary_tree_pack(Compile* C, int lo, int hi) { +PackNode* PackNode::binary_tree_pack(int lo, int hi) { int ct = hi - lo; assert(is_power_of_2(ct), "power of 2"); if (ct == 2) { - PackNode* pk = PackNode::make(C, in(lo), 2, vect_type()->element_basic_type()); + PackNode* pk = PackNode::make(in(lo), 2, vect_type()->element_basic_type()); pk->add_opd(in(lo+1)); return pk; } else { int mid = lo + ct/2; - PackNode* n1 = binary_tree_pack(C, lo, mid); - PackNode* n2 = binary_tree_pack(C, mid, hi ); + PackNode* n1 = binary_tree_pack(lo, mid); + PackNode* n2 = binary_tree_pack(mid, hi ); BasicType bt = n1->vect_type()->element_basic_type(); assert(bt == n2->vect_type()->element_basic_type(), "should be the same"); @@ -402,23 +402,23 @@ PackNode* PackNode::binary_tree_pack(Compile* C, int lo, int hi) { } // Return the vector version of a scalar load node. -LoadVectorNode* LoadVectorNode::make(Compile* C, int opc, Node* ctl, Node* mem, +LoadVectorNode* LoadVectorNode::make(int opc, Node* ctl, Node* mem, Node* adr, const TypePtr* atyp, uint vlen, BasicType bt) { const TypeVect* vt = TypeVect::make(bt, vlen); return new LoadVectorNode(ctl, mem, adr, atyp, vt); } // Return the vector version of a scalar store node. -StoreVectorNode* StoreVectorNode::make(Compile* C, int opc, Node* ctl, Node* mem, +StoreVectorNode* StoreVectorNode::make(int opc, Node* ctl, Node* mem, Node* adr, const TypePtr* atyp, Node* val, uint vlen) { return new StoreVectorNode(ctl, mem, adr, atyp, val); } // Extract a scalar element of vector. -Node* ExtractNode::make(Compile* C, Node* v, uint position, BasicType bt) { +Node* ExtractNode::make(Node* v, uint position, BasicType bt) { assert((int)position < Matcher::max_vector_size(bt), "pos in range"); - ConINode* pos = ConINode::make(C, (int)position); + ConINode* pos = ConINode::make((int)position); switch (bt) { case T_BOOLEAN: return new ExtractUBNode(v, pos); diff --git a/hotspot/src/share/vm/opto/vectornode.hpp b/hotspot/src/share/vm/opto/vectornode.hpp index 0b95a10d8a6..759d724335d 100644 --- a/hotspot/src/share/vm/opto/vectornode.hpp +++ b/hotspot/src/share/vm/opto/vectornode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2014, 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 @@ -52,9 +52,9 @@ class VectorNode : public TypeNode { virtual uint ideal_reg() const { return Matcher::vector_ideal_reg(vect_type()->length_in_bytes()); } - static VectorNode* scalar2vector(Compile* C, Node* s, uint vlen, const Type* opd_t); - static VectorNode* shift_count(Compile* C, Node* shift, Node* cnt, uint vlen, BasicType bt); - static VectorNode* make(Compile* C, int opc, Node* n1, Node* n2, uint vlen, BasicType bt); + static VectorNode* scalar2vector(Node* s, uint vlen, const Type* opd_t); + static VectorNode* shift_count(Node* shift, Node* cnt, uint vlen, BasicType bt); + static VectorNode* make(int opc, Node* n1, Node* n2, uint vlen, BasicType bt); static int opcode(int opc, BasicType bt); static bool implemented(int opc, uint vlen, BasicType bt); @@ -371,7 +371,7 @@ class LoadVectorNode : public LoadNode { virtual int store_Opcode() const { return Op_StoreVector; } - static LoadVectorNode* make(Compile* C, int opc, Node* ctl, Node* mem, + static LoadVectorNode* make(int opc, Node* ctl, Node* mem, Node* adr, const TypePtr* atyp, uint vlen, BasicType bt); }; @@ -394,7 +394,7 @@ class StoreVectorNode : public StoreNode { virtual BasicType memory_type() const { return T_VOID; } virtual int memory_size() const { return vect_type()->length_in_bytes(); } - static StoreVectorNode* make(Compile* C, int opc, Node* ctl, Node* mem, + static StoreVectorNode* make(int opc, Node* ctl, Node* mem, Node* adr, const TypePtr* atyp, Node* val, uint vlen); }; @@ -465,9 +465,9 @@ class PackNode : public VectorNode { } // Create a binary tree form for Packs. [lo, hi) (half-open) range - PackNode* binary_tree_pack(Compile* C, int lo, int hi); + PackNode* binary_tree_pack(int lo, int hi); - static PackNode* make(Compile* C, Node* s, uint vlen, BasicType bt); + static PackNode* make(Node* s, uint vlen, BasicType bt); }; //------------------------------PackBNode-------------------------------------- @@ -552,7 +552,7 @@ class ExtractNode : public Node { virtual int Opcode() const; uint pos() const { return in(2)->get_int(); } - static Node* make(Compile* C, Node* v, uint position, BasicType bt); + static Node* make(Node* v, uint position, BasicType bt); }; //------------------------------ExtractBNode----------------------------------- diff --git a/hotspot/src/share/vm/precompiled/precompiled.hpp b/hotspot/src/share/vm/precompiled/precompiled.hpp index be70cfffd6e..121c8aa14c6 100644 --- a/hotspot/src/share/vm/precompiled/precompiled.hpp +++ b/hotspot/src/share/vm/precompiled/precompiled.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2014, 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 @@ -222,10 +222,17 @@ # include "runtime/vmThread.hpp" # include "runtime/vm_operations.hpp" # include "runtime/vm_version.hpp" +# include "services/allocationSite.hpp" # include "services/lowMemoryDetector.hpp" +# include "services/mallocTracker.hpp" +# include "services/memBaseline.hpp" # include "services/memoryPool.hpp" # include "services/memoryService.hpp" # include "services/memoryUsage.hpp" +# include "services/memReporter.hpp" +# include "services/memTracker.hpp" +# include "services/nmtCommon.hpp" +# include "services/virtualMemoryTracker.hpp" # include "utilities/accessFlags.hpp" # include "utilities/array.hpp" # include "utilities/bitMap.hpp" @@ -240,6 +247,7 @@ # include "utilities/hashtable.hpp" # include "utilities/histogram.hpp" # include "utilities/macros.hpp" +# include "utilities/nativeCallStack.hpp" # include "utilities/numberSeq.hpp" # include "utilities/ostream.hpp" # include "utilities/preserveException.hpp" diff --git a/hotspot/src/share/vm/prims/jni.cpp b/hotspot/src/share/vm/prims/jni.cpp index 51548acdba3..c404b4c7de5 100644 --- a/hotspot/src/share/vm/prims/jni.cpp +++ b/hotspot/src/share/vm/prims/jni.cpp @@ -74,6 +74,7 @@ #include "runtime/signature.hpp" #include "runtime/thread.inline.hpp" #include "runtime/vm_operations.hpp" +#include "services/memTracker.hpp" #include "services/runtimeService.hpp" #include "trace/tracing.hpp" #include "utilities/defaultStream.hpp" @@ -2697,6 +2698,7 @@ static char* get_bad_address() { if (bad_address != NULL) { os::protect_memory(bad_address, size, os::MEM_PROT_READ, /*is_committed*/false); + MemTracker::record_virtual_memory_type((void*)bad_address, mtInternal); } } return bad_address; @@ -3857,11 +3859,13 @@ void TestOldSize_test(); void TestKlass_test(); void TestBitMap_test(); void TestAsUtf8(); +void Test_linked_list(); #if INCLUDE_ALL_GCS void TestOldFreeSpaceCalculation_test(); void TestG1BiasedArray_test(); void TestBufferingOopClosure_test(); void TestCodeCacheRemSet_test(); +void FreeRegionList_test(); #endif void execute_internal_vm_tests() { @@ -3887,6 +3891,7 @@ void execute_internal_vm_tests() { run_unit_test(TestBitMap_test()); run_unit_test(TestAsUtf8()); run_unit_test(ObjectMonitor::sanity_checks()); + run_unit_test(Test_linked_list()); #if INCLUDE_VM_STRUCTS run_unit_test(VMStructs::test()); #endif @@ -3896,6 +3901,9 @@ void execute_internal_vm_tests() { run_unit_test(HeapRegionRemSet::test_prt()); run_unit_test(TestBufferingOopClosure_test()); run_unit_test(TestCodeCacheRemSet_test()); + if (UseG1GC) { + run_unit_test(FreeRegionList_test()); + } #endif tty->print_cr("All internal VM tests passed"); } diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp index 1970d715ebc..1a51e80e00e 100644 --- a/hotspot/src/share/vm/prims/jvm.cpp +++ b/hotspot/src/share/vm/prims/jvm.cpp @@ -28,6 +28,10 @@ #include "classfile/javaClasses.hpp" #include "classfile/stringTable.hpp" #include "classfile/systemDictionary.hpp" +#if INCLUDE_CDS +#include "classfile/sharedClassUtil.hpp" +#include "classfile/systemDictionaryShared.hpp" +#endif #include "classfile/vmSymbols.hpp" #include "gc_interface/collectedHeap.inline.hpp" #include "interpreter/bytecode.hpp" @@ -993,7 +997,15 @@ JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name) h_loader, Handle(), CHECK_NULL); - +#if INCLUDE_CDS + if (k == NULL) { + // If the class is not already loaded, try to see if it's in the shared + // archive for the current classloader (h_loader). + instanceKlassHandle ik = SystemDictionaryShared::find_or_load_shared_class( + klass_name, h_loader, CHECK_NULL); + k = ik(); + } +#endif return (k == NULL) ? NULL : (jclass) JNIHandles::make_local(env, k->java_mirror()); JVM_END @@ -1391,14 +1403,6 @@ JVM_QUICK_ENTRY(jboolean, JVM_IsPrimitiveClass(JNIEnv *env, jclass cls)) JVM_END -JVM_ENTRY(jclass, JVM_GetComponentType(JNIEnv *env, jclass cls)) - JVMWrapper("JVM_GetComponentType"); - oop mirror = JNIHandles::resolve_non_null(cls); - oop result = Reflection::array_component_type(mirror, CHECK_NULL); - return (jclass) JNIHandles::make_local(env, result); -JVM_END - - JVM_ENTRY(jint, JVM_GetClassModifiers(JNIEnv *env, jclass cls)) JVMWrapper("JVM_GetClassModifiers"); if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(cls))) { diff --git a/hotspot/src/share/vm/prims/jvm.h b/hotspot/src/share/vm/prims/jvm.h index 016bed8875e..72278843ab2 100644 --- a/hotspot/src/share/vm/prims/jvm.h +++ b/hotspot/src/share/vm/prims/jvm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -483,9 +483,6 @@ JVM_IsArrayClass(JNIEnv *env, jclass cls); JNIEXPORT jboolean JNICALL JVM_IsPrimitiveClass(JNIEnv *env, jclass cls); -JNIEXPORT jclass JNICALL -JVM_GetComponentType(JNIEnv *env, jclass cls); - JNIEXPORT jint JNICALL JVM_GetClassModifiers(JNIEnv *env, jclass cls); diff --git a/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp b/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp index d2dab57a374..b614b5acea0 100644 --- a/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp +++ b/hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2014, 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 @@ -316,6 +316,7 @@ void JvmtiManageCapabilities::update() { avail.can_generate_frame_pop_events || avail.can_generate_method_entry_events || avail.can_generate_method_exit_events; +#ifdef ZERO bool enter_all_methods = interp_events || avail.can_generate_breakpoint_events; @@ -324,6 +325,7 @@ void JvmtiManageCapabilities::update() { UseFastEmptyMethods = false; UseFastAccessorMethods = false; } +#endif // ZERO if (avail.can_generate_breakpoint_events) { RewriteFrequentPairs = false; diff --git a/hotspot/src/share/vm/prims/whitebox.cpp b/hotspot/src/share/vm/prims/whitebox.cpp index e6805b12e5b..8cd30c31707 100644 --- a/hotspot/src/share/vm/prims/whitebox.cpp +++ b/hotspot/src/share/vm/prims/whitebox.cpp @@ -47,13 +47,16 @@ #include "utilities/exceptions.hpp" #if INCLUDE_ALL_GCS +#include "gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp" #include "gc_implementation/g1/concurrentMark.hpp" #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" #endif // INCLUDE_ALL_GCS -#ifdef INCLUDE_NMT +#if INCLUDE_NMT +#include "services/mallocSiteTable.hpp" #include "services/memTracker.hpp" +#include "utilities/nativeCallStack.hpp" #endif // INCLUDE_NMT #include "compiler/compileBroker.hpp" @@ -225,6 +228,30 @@ WB_ENTRY(jint, WB_StressVirtualSpaceResize(JNIEnv* env, jobject o, (size_t) magnitude, (size_t) iterations); WB_END +WB_ENTRY(jboolean, WB_isObjectInOldGen(JNIEnv* env, jobject o, jobject obj)) + oop p = JNIHandles::resolve(obj); +#if INCLUDE_ALL_GCS + if (UseG1GC) { + G1CollectedHeap* g1 = G1CollectedHeap::heap(); + const HeapRegion* hr = g1->heap_region_containing(p); + if (hr == NULL) { + return false; + } + return !(hr->is_young()); + } else if (UseParallelGC) { + ParallelScavengeHeap* psh = ParallelScavengeHeap::heap(); + return !psh->is_in_young(p); + } +#endif // INCLUDE_ALL_GCS + GenCollectedHeap* gch = GenCollectedHeap::heap(); + return !gch->is_in_young(p); +WB_END + +WB_ENTRY(jlong, WB_GetObjectSize(JNIEnv* env, jobject o, jobject obj)) + oop p = JNIHandles::resolve(obj); + return p->size() * HeapWordSize; +WB_END + #if INCLUDE_ALL_GCS WB_ENTRY(jboolean, WB_G1IsHumongous(JNIEnv* env, jobject o, jobject obj)) G1CollectedHeap* g1 = G1CollectedHeap::heap(); @@ -235,7 +262,7 @@ WB_END WB_ENTRY(jlong, WB_G1NumFreeRegions(JNIEnv* env, jobject o)) G1CollectedHeap* g1 = G1CollectedHeap::heap(); - size_t nr = g1->free_regions(); + size_t nr = g1->num_free_regions(); return (jlong)nr; WB_END @@ -255,14 +282,18 @@ WB_END // NMT picks it up correctly WB_ENTRY(jlong, WB_NMTMalloc(JNIEnv* env, jobject o, jlong size)) jlong addr = 0; - - if (MemTracker::is_on() && !MemTracker::shutdown_in_progress()) { addr = (jlong)(uintptr_t)os::malloc(size, mtTest); - } - return addr; WB_END +// Alloc memory with pseudo call stack. The test can create psudo malloc +// allocation site to stress the malloc tracking. +WB_ENTRY(jlong, WB_NMTMallocWithPseudoStack(JNIEnv* env, jobject o, jlong size, jint pseudo_stack)) + address pc = (address)(size_t)pseudo_stack; + NativeCallStack stack(&pc, 1); + return (jlong)os::malloc(size, mtTest, stack); +WB_END + // Free the memory allocated by NMTAllocTest WB_ENTRY(void, WB_NMTFree(JNIEnv* env, jobject o, jlong mem)) os::free((void*)(uintptr_t)mem, mtTest); @@ -271,10 +302,8 @@ WB_END WB_ENTRY(jlong, WB_NMTReserveMemory(JNIEnv* env, jobject o, jlong size)) jlong addr = 0; - if (MemTracker::is_on() && !MemTracker::shutdown_in_progress()) { addr = (jlong)(uintptr_t)os::reserve_memory(size); MemTracker::record_virtual_memory_type((address)addr, mtTest); - } return addr; WB_END @@ -293,20 +322,20 @@ WB_ENTRY(void, WB_NMTReleaseMemory(JNIEnv* env, jobject o, jlong addr, jlong siz os::release_memory((char *)(uintptr_t)addr, size); WB_END -// Block until the current generation of NMT data to be merged, used to reliably test the NMT feature -WB_ENTRY(jboolean, WB_NMTWaitForDataMerge(JNIEnv* env)) - - if (!MemTracker::is_on() || MemTracker::shutdown_in_progress()) { - return false; - } - - return MemTracker::wbtest_wait_for_data_merge(); -WB_END - WB_ENTRY(jboolean, WB_NMTIsDetailSupported(JNIEnv* env)) - return MemTracker::tracking_level() == MemTracker::NMT_detail; + return MemTracker::tracking_level() == NMT_detail; WB_END +WB_ENTRY(void, WB_NMTOverflowHashBucket(JNIEnv* env, jobject o, jlong num)) + address pc = (address)1; + for (jlong index = 0; index < num; index ++) { + NativeCallStack stack(&pc, 1); + os::malloc(0, mtTest, stack); + pc += MallocSiteTable::hash_buckets(); + } +WB_END + + #endif // INCLUDE_NMT static jmethodID reflected_method_to_jmid(JavaThread* thread, JNIEnv* env, jobject method) { @@ -597,6 +626,15 @@ WB_ENTRY(jobject, WB_GetUint64VMFlag(JNIEnv* env, jobject o, jstring name)) return NULL; WB_END +WB_ENTRY(jobject, WB_GetSizeTVMFlag(JNIEnv* env, jobject o, jstring name)) + uintx result; + if (GetVMFlag (thread, env, name, &result, &CommandLineFlags::size_tAt)) { + ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI + return longBox(thread, env, result); + } + return NULL; +WB_END + WB_ENTRY(jobject, WB_GetDoubleVMFlag(JNIEnv* env, jobject o, jstring name)) double result; if (GetVMFlag (thread, env, name, &result, &CommandLineFlags::doubleAt)) { @@ -637,6 +675,11 @@ WB_ENTRY(void, WB_SetUint64VMFlag(JNIEnv* env, jobject o, jstring name, jlong va SetVMFlag (thread, env, name, &result, &CommandLineFlags::uint64_tAtPut); WB_END +WB_ENTRY(void, WB_SetSizeTVMFlag(JNIEnv* env, jobject o, jstring name, jlong value)) + size_t result = value; + SetVMFlag (thread, env, name, &result, &CommandLineFlags::size_tAtPut); +WB_END + WB_ENTRY(void, WB_SetDoubleVMFlag(JNIEnv* env, jobject o, jstring name, jdouble value)) double result = value; SetVMFlag (thread, env, name, &result, &CommandLineFlags::doubleAtPut); @@ -672,6 +715,9 @@ WB_ENTRY(void, WB_FullGC(JNIEnv* env, jobject o)) Universe::heap()->collect(GCCause::_last_ditch_collection); WB_END +WB_ENTRY(void, WB_YoungGC(JNIEnv* env, jobject o)) + Universe::heap()->collect(GCCause::_wb_young_gc); +WB_END WB_ENTRY(void, WB_ReadReservedMemory(JNIEnv* env, jobject o)) // static+volatile in order to force the read to happen @@ -823,6 +869,8 @@ bool WhiteBox::lookup_bool(const char* field_name, oop object) { static JNINativeMethod methods[] = { {CC"getObjectAddress", CC"(Ljava/lang/Object;)J", (void*)&WB_GetObjectAddress }, + {CC"getObjectSize", CC"(Ljava/lang/Object;)J", (void*)&WB_GetObjectSize }, + {CC"isObjectInOldGen", CC"(Ljava/lang/Object;)Z", (void*)&WB_isObjectInOldGen }, {CC"getHeapOopSize", CC"()I", (void*)&WB_GetHeapOopSize }, {CC"isClassAlive0", CC"(Ljava/lang/String;)Z", (void*)&WB_IsClassAlive }, {CC"parseCommandLine", @@ -843,12 +891,13 @@ static JNINativeMethod methods[] = { #endif // INCLUDE_ALL_GCS #if INCLUDE_NMT {CC"NMTMalloc", CC"(J)J", (void*)&WB_NMTMalloc }, + {CC"NMTMallocWithPseudoStack", CC"(JI)J", (void*)&WB_NMTMallocWithPseudoStack}, {CC"NMTFree", CC"(J)V", (void*)&WB_NMTFree }, {CC"NMTReserveMemory", CC"(J)J", (void*)&WB_NMTReserveMemory }, {CC"NMTCommitMemory", CC"(JJ)V", (void*)&WB_NMTCommitMemory }, {CC"NMTUncommitMemory", CC"(JJ)V", (void*)&WB_NMTUncommitMemory }, {CC"NMTReleaseMemory", CC"(JJ)V", (void*)&WB_NMTReleaseMemory }, - {CC"NMTWaitForDataMerge", CC"()Z", (void*)&WB_NMTWaitForDataMerge}, + {CC"NMTOverflowHashBucket", CC"(J)V", (void*)&WB_NMTOverflowHashBucket}, {CC"NMTIsDetailSupported",CC"()Z", (void*)&WB_NMTIsDetailSupported}, #endif // INCLUDE_NMT {CC"deoptimizeAll", CC"()V", (void*)&WB_DeoptimizeAll }, @@ -880,6 +929,7 @@ static JNINativeMethod methods[] = { {CC"setIntxVMFlag", CC"(Ljava/lang/String;J)V",(void*)&WB_SetIntxVMFlag}, {CC"setUintxVMFlag", CC"(Ljava/lang/String;J)V",(void*)&WB_SetUintxVMFlag}, {CC"setUint64VMFlag", CC"(Ljava/lang/String;J)V",(void*)&WB_SetUint64VMFlag}, + {CC"setSizeTVMFlag", CC"(Ljava/lang/String;J)V",(void*)&WB_SetSizeTVMFlag}, {CC"setDoubleVMFlag", CC"(Ljava/lang/String;D)V",(void*)&WB_SetDoubleVMFlag}, {CC"setStringVMFlag", CC"(Ljava/lang/String;Ljava/lang/String;)V", (void*)&WB_SetStringVMFlag}, @@ -891,12 +941,15 @@ static JNINativeMethod methods[] = { (void*)&WB_GetUintxVMFlag}, {CC"getUint64VMFlag", CC"(Ljava/lang/String;)Ljava/lang/Long;", (void*)&WB_GetUint64VMFlag}, + {CC"getSizeTVMFlag", CC"(Ljava/lang/String;)Ljava/lang/Long;", + (void*)&WB_GetSizeTVMFlag}, {CC"getDoubleVMFlag", CC"(Ljava/lang/String;)Ljava/lang/Double;", (void*)&WB_GetDoubleVMFlag}, {CC"getStringVMFlag", CC"(Ljava/lang/String;)Ljava/lang/String;", (void*)&WB_GetStringVMFlag}, {CC"isInStringTable", CC"(Ljava/lang/String;)Z", (void*)&WB_IsInStringTable }, {CC"fullGC", CC"()V", (void*)&WB_FullGC }, + {CC"youngGC", CC"()V", (void*)&WB_YoungGC }, {CC"readReservedMemory", CC"()V", (void*)&WB_ReadReservedMemory }, {CC"allocateMetaspace", CC"(Ljava/lang/ClassLoader;J)J", (void*)&WB_AllocateMetaspace }, diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index 3f0bff025ec..9ae78d32b06 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "classfile/classLoader.hpp" #include "classfile/javaAssertions.hpp" #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" @@ -43,6 +44,7 @@ #include "services/memTracker.hpp" #include "utilities/defaultStream.hpp" #include "utilities/macros.hpp" +#include "utilities/stringUtils.hpp" #include "utilities/taskqueue.hpp" #if INCLUDE_ALL_GCS #include "gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp" @@ -300,6 +302,11 @@ static ObsoleteFlag obsolete_jvm_flags[] = { { "UseNewReflection", JDK_Version::jdk(9), JDK_Version::jdk(10) }, { "ReflectionWrapResolutionErrors",JDK_Version::jdk(9), JDK_Version::jdk(10) }, { "VerifyReflectionBytecodes", JDK_Version::jdk(9), JDK_Version::jdk(10) }, + { "AutoShutdownNMT", JDK_Version::jdk(9), JDK_Version::jdk(10) }, +#ifndef ZERO + { "UseFastAccessorMethods", JDK_Version::jdk(9), JDK_Version::jdk(10) }, + { "UseFastEmptyMethods", JDK_Version::jdk(9), JDK_Version::jdk(10) }, +#endif // ZERO { NULL, JDK_Version(0), JDK_Version(0) } }; @@ -686,6 +693,10 @@ static bool set_numeric_flag(char* name, char* value, Flag::Flags origin) { if (!is_neg && CommandLineFlags::uint64_tAtPut(name, &uint64_t_v, origin)) { return true; } + size_t size_t_v = (size_t) v; + if (!is_neg && CommandLineFlags::size_tAtPut(name, &size_t_v, origin)) { + return true; + } return false; } @@ -799,7 +810,7 @@ void Arguments::add_string(char*** bldarray, int* count, const char* arg) { } else { *bldarray = REALLOC_C_HEAP_ARRAY(char*, *bldarray, new_count, mtInternal); } - (*bldarray)[*count] = strdup(arg); + (*bldarray)[*count] = os::strdup_check_oom(arg); *count = new_count; } @@ -1070,16 +1081,6 @@ void Arguments::set_mode_flags(Mode mode) { UseCompiler = true; UseLoopCounter = true; -#ifndef ZERO - // Turn these off for mixed and comp. Leave them on for Zero. - if (FLAG_IS_DEFAULT(UseFastAccessorMethods)) { - UseFastAccessorMethods = (mode == _int); - } - if (FLAG_IS_DEFAULT(UseFastEmptyMethods)) { - UseFastEmptyMethods = (mode == _int); - } -#endif - // Default values may be platform/compiler dependent - // use the saved values ClipInlining = Arguments::_ClipInlining; @@ -1120,11 +1121,11 @@ void Arguments::set_mode_flags(Mode mode) { // Conflict: required to use shared spaces (-Xshare:on), but // incompatible command line options were chosen. -static void no_shared_spaces() { +static void no_shared_spaces(const char* message) { if (RequireSharedSpaces) { jio_fprintf(defaultStream::error_stream(), "Class data sharing is inconsistent with other specified options.\n"); - vm_exit_during_initialization("Unable to use shared archive.", NULL); + vm_exit_during_initialization("Unable to use shared archive.", message); } else { FLAG_SET_DEFAULT(UseSharedSpaces, false); } @@ -1431,6 +1432,22 @@ bool verify_object_alignment() { (int)ObjectAlignmentInBytes, os::vm_page_size()); return false; } + if(SurvivorAlignmentInBytes == 0) { + SurvivorAlignmentInBytes = ObjectAlignmentInBytes; + } else { + if (!is_power_of_2(SurvivorAlignmentInBytes)) { + jio_fprintf(defaultStream::error_stream(), + "error: SurvivorAlignmentInBytes=%d must be power of 2\n", + (int)SurvivorAlignmentInBytes); + return false; + } + if (SurvivorAlignmentInBytes < ObjectAlignmentInBytes) { + jio_fprintf(defaultStream::error_stream(), + "error: SurvivorAlignmentInBytes=%d must be greater than ObjectAlignmentInBytes=%d \n", + (int)SurvivorAlignmentInBytes, (int)ObjectAlignmentInBytes); + return false; + } + } return true; } @@ -1570,7 +1587,7 @@ void Arguments::set_ergonomics_flags() { // at link time, or rewrite bytecodes in non-shared methods. if (!DumpSharedSpaces && !RequireSharedSpaces && (FLAG_IS_DEFAULT(UseSharedSpaces) || !UseSharedSpaces)) { - no_shared_spaces(); + no_shared_spaces("COMPILER2 default: -Xshare:auto | off, have to manually setup to on."); } #endif @@ -1869,7 +1886,7 @@ void Arguments::process_java_compiler_argument(char* arg) { } void Arguments::process_java_launcher_argument(const char* launcher, void* extra_info) { - _sun_java_launcher = strdup(launcher); + _sun_java_launcher = os::strdup_check_oom(launcher); } bool Arguments::created_by_java_launcher() { @@ -2372,7 +2389,7 @@ bool Arguments::check_vm_args_consistency() { if (PrintNMTStatistics) { #if INCLUDE_NMT - if (MemTracker::tracking_level() == MemTracker::NMT_off) { + if (MemTracker::tracking_level() == NMT_off) { #endif // INCLUDE_NMT warning("PrintNMTStatistics is disabled, because native memory tracking is not enabled"); PrintNMTStatistics = false; @@ -2979,7 +2996,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, // Redirect GC output to the file. -Xloggc: // ostream_init_log(), when called will use this filename // to initialize a fileStream. - _gc_log_filename = strdup(tail); + _gc_log_filename = os::strdup_check_oom(tail); if (!is_filename_valid(_gc_log_filename)) { jio_fprintf(defaultStream::output_stream(), "Invalid file name for use with -Xloggc: Filename can only contain the " @@ -3291,6 +3308,15 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, } } + // PrintSharedArchiveAndExit will turn on + // -Xshare:on + // -XX:+TraceClassPaths + if (PrintSharedArchiveAndExit) { + FLAG_SET_CMDLINE(bool, UseSharedSpaces, true); + FLAG_SET_CMDLINE(bool, RequireSharedSpaces, true); + FLAG_SET_CMDLINE(bool, TraceClassPaths, true); + } + // Change the default value for flags which have different default values // when working with older JDKs. #ifdef LINUX @@ -3299,9 +3325,55 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, FLAG_SET_DEFAULT(UseLinuxPosixThreadCPUClocks, false); } #endif // LINUX + fix_appclasspath(); return JNI_OK; } +// Remove all empty paths from the app classpath (if IgnoreEmptyClassPaths is enabled) +// +// This is necessary because some apps like to specify classpath like -cp foo.jar:${XYZ}:bar.jar +// in their start-up scripts. If XYZ is empty, the classpath will look like "-cp foo.jar::bar.jar". +// Java treats such empty paths as if the user specified "-cp foo.jar:.:bar.jar". I.e., an empty +// path is treated as the current directory. +// +// This causes problems with CDS, which requires that all directories specified in the classpath +// must be empty. In most cases, applications do NOT want to load classes from the current +// directory anyway. Adding -XX:+IgnoreEmptyClassPaths will make these applications' start-up +// scripts compatible with CDS. +void Arguments::fix_appclasspath() { + if (IgnoreEmptyClassPaths) { + const char separator = *os::path_separator(); + const char* src = _java_class_path->value(); + + // skip over all the leading empty paths + while (*src == separator) { + src ++; + } + + char* copy = AllocateHeap(strlen(src) + 1, mtInternal); + strncpy(copy, src, strlen(src) + 1); + + // trim all trailing empty paths + for (char* tail = copy + strlen(copy) - 1; tail >= copy && *tail == separator; tail--) { + *tail = '\0'; + } + + char from[3] = {separator, separator, '\0'}; + char to [2] = {separator, '\0'}; + while (StringUtils::replace_no_expand(copy, from, to) > 0) { + // Keep replacing "::" -> ":" until we have no more "::" (non-windows) + // Keep replacing ";;" -> ";" until we have no more ";;" (windows) + } + + _java_class_path->set_value(copy); + FreeHeap(copy); // a copy was made by set_value, so don't need this anymore + } + + if (!PrintSharedArchiveAndExit) { + ClassLoader::trace_class_path("[classpath: ", _java_class_path->value()); + } +} + jint Arguments::finalize_vm_init_args(SysClassPath* scp_p, bool scp_assembly_required) { // This must be done after all -D arguments have been processed. scp_p->expand_endorsed(); @@ -3472,9 +3544,8 @@ void Arguments::set_shared_spaces_flags() { "Cannot dump shared archive when UseCompressedOops or UseCompressedClassPointers is off.", NULL); } } else { - // UseCompressedOops and UseCompressedClassPointers must be on for UseSharedSpaces. if (!UseCompressedOops || !UseCompressedClassPointers) { - no_shared_spaces(); + no_shared_spaces("UseCompressedOops and UseCompressedClassPointers must be on for UseSharedSpaces."); } #endif } @@ -3582,15 +3653,24 @@ jint Arguments::parse(const JavaVMInitArgs* args) { CommandLineFlags::printFlags(tty, false); vm_exit(0); } - if (match_option(option, "-XX:NativeMemoryTracking", &tail)) { #if INCLUDE_NMT - MemTracker::init_tracking_options(tail); -#else - jio_fprintf(defaultStream::error_stream(), - "Native Memory Tracking is not supported in this VM\n"); - return JNI_ERR; -#endif + if (match_option(option, "-XX:NativeMemoryTracking", &tail)) { + // The launcher did not setup nmt environment variable properly. + if (!MemTracker::check_launcher_nmt_support(tail)) { + warning("Native Memory Tracking did not setup properly, using wrong launcher?"); + } + + // Verify if nmt option is valid. + if (MemTracker::verify_nmt_option()) { + // Late initialization, still in single-threaded mode. + if (MemTracker::tracking_level() >= NMT_summary) { + MemTracker::init(); + } + } else { + vm_exit_during_initialization("Syntax error, expecting -XX:NativeMemoryTracking=[off|summary|detail]", NULL); + } } +#endif #ifndef PRODUCT @@ -3705,7 +3785,7 @@ jint Arguments::parse(const JavaVMInitArgs* args) { FLAG_SET_DEFAULT(UseSharedSpaces, false); FLAG_SET_DEFAULT(PrintSharedSpaces, false); } - no_shared_spaces(); + no_shared_spaces("CDS Disabled"); #endif // INCLUDE_CDS return JNI_OK; diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp index 64cdc5ca72d..28608e2ba1a 100644 --- a/hotspot/src/share/vm/runtime/arguments.hpp +++ b/hotspot/src/share/vm/runtime/arguments.hpp @@ -579,12 +579,15 @@ class Arguments : AllStatic { _meta_index_dir = meta_index_dir; } - static char *get_java_home() { return _java_home->value(); } - static char *get_dll_dir() { return _sun_boot_library_path->value(); } - static char *get_endorsed_dir() { return _java_endorsed_dirs->value(); } - static char *get_sysclasspath() { return _sun_boot_class_path->value(); } + static char* get_java_home() { return _java_home->value(); } + static char* get_dll_dir() { return _sun_boot_library_path->value(); } + static char* get_endorsed_dir() { return _java_endorsed_dirs->value(); } + static char* get_sysclasspath() { return _sun_boot_class_path->value(); } static char* get_meta_index_path() { return _meta_index_path; } static char* get_meta_index_dir() { return _meta_index_dir; } + static char* get_ext_dirs() { return _java_ext_dirs->value(); } + static char* get_appclasspath() { return _java_class_path->value(); } + static void fix_appclasspath(); // Operation modi static Mode mode() { return _mode; } diff --git a/hotspot/src/share/vm/runtime/deoptimization.cpp b/hotspot/src/share/vm/runtime/deoptimization.cpp index a7ad943cf7c..b94b7142b7e 100644 --- a/hotspot/src/share/vm/runtime/deoptimization.cpp +++ b/hotspot/src/share/vm/runtime/deoptimization.cpp @@ -191,7 +191,8 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread // It is not guaranteed that we can get such information here only // by analyzing bytecode in deoptimized frames. This is why this flag // is set during method compilation (see Compile::Process_OopMap_Node()). - bool save_oop_result = chunk->at(0)->scope()->return_oop(); + // If the previous frame was popped, we don't have a result. + bool save_oop_result = chunk->at(0)->scope()->return_oop() && !thread->popframe_forcing_deopt_reexecution(); Handle return_value; if (save_oop_result) { // Reallocation may trigger GC. If deoptimization happened on return from @@ -661,7 +662,7 @@ JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_m (iframe->interpreter_frame_expression_stack_size() == (next_mask_expression_stack_size - top_frame_expression_stack_adjustment))) || (is_top_frame && (exec_mode == Unpack_exception) && iframe->interpreter_frame_expression_stack_size() == 0) || - (is_top_frame && (exec_mode == Unpack_uncommon_trap || exec_mode == Unpack_reexecute) && + (is_top_frame && (exec_mode == Unpack_uncommon_trap || exec_mode == Unpack_reexecute || el->should_reexecute()) && (iframe->interpreter_frame_expression_stack_size() == mask.expression_stack_size() + cur_invoke_parameter_size)) )) { ttyLocker ttyl; diff --git a/hotspot/src/share/vm/runtime/fprofiler.cpp b/hotspot/src/share/vm/runtime/fprofiler.cpp index aec76268824..7f741fee561 100644 --- a/hotspot/src/share/vm/runtime/fprofiler.cpp +++ b/hotspot/src/share/vm/runtime/fprofiler.cpp @@ -629,10 +629,16 @@ class vmNode : public ProfilerNode { } vmNode(const char* name, const TickPosition where) : ProfilerNode() { - _name = name; + _name = os::strdup(name); update(where); } + ~vmNode() { + if (_name != NULL) { + os::free((void*)_name); + } + } + const char *name() const { return _name; } bool is_compiled() const { return true; } @@ -784,7 +790,7 @@ void ThreadProfiler::vm_update(const char* name, TickPosition where) { assert(index >= 0, "Must be positive"); // Note that we call strdup below since the symbol may be resource allocated if (!table[index]) { - table[index] = new (this) vmNode(os::strdup(name), where); + table[index] = new (this) vmNode(name, where); } else { ProfilerNode* prev = table[index]; for(ProfilerNode* node = prev; node; node = node->next()) { @@ -794,7 +800,7 @@ void ThreadProfiler::vm_update(const char* name, TickPosition where) { } prev = node; } - prev->set_next(new (this) vmNode(os::strdup(name), where)); + prev->set_next(new (this) vmNode(name, where)); } } diff --git a/hotspot/src/share/vm/runtime/frame.cpp b/hotspot/src/share/vm/runtime/frame.cpp index d076896f0aa..8701b9ccf63 100644 --- a/hotspot/src/share/vm/runtime/frame.cpp +++ b/hotspot/src/share/vm/runtime/frame.cpp @@ -407,7 +407,8 @@ jint frame::interpreter_frame_bci() const { address frame::interpreter_frame_bcp() const { assert(is_interpreted_frame(), "interpreted frame expected"); - return (address)*interpreter_frame_bcp_addr(); + address bcp = (address)*interpreter_frame_bcp_addr(); + return interpreter_frame_method()->bcp_from(bcp); } void frame::interpreter_frame_set_bcp(address bcp) { diff --git a/hotspot/src/share/vm/runtime/globals.cpp b/hotspot/src/share/vm/runtime/globals.cpp index 928f7134e14..b20ef79db13 100644 --- a/hotspot/src/share/vm/runtime/globals.cpp +++ b/hotspot/src/share/vm/runtime/globals.cpp @@ -131,6 +131,19 @@ void Flag::set_uint64_t(uint64_t value) { *((uint64_t*) _addr) = value; } +bool Flag::is_size_t() const { + return strcmp(_type, "size_t") == 0; +} + +size_t Flag::get_size_t() const { + return *((size_t*) _addr); +} + +void Flag::set_size_t(size_t value) { + check_writable(); + *((size_t*) _addr) = value; +} + bool Flag::is_double() const { return strcmp(_type, "double") == 0; } @@ -306,6 +319,9 @@ void Flag::print_on(outputStream* st, bool withComments) { if (is_uint64_t()) { st->print("%-16lu", get_uint64_t()); } + if (is_size_t()) { + st->print(SIZE_FORMAT_W(-16), get_size_t()); + } if (is_double()) { st->print("%-16f", get_double()); } @@ -395,6 +411,8 @@ void Flag::print_as_flag(outputStream* st) { st->print("-XX:%s=" UINTX_FORMAT, _name, get_uintx()); } else if (is_uint64_t()) { st->print("-XX:%s=" UINT64_FORMAT, _name, get_uint64_t()); + } else if (is_size_t()) { + st->print("-XX:%s=" SIZE_FORMAT, _name, get_size_t()); } else if (is_double()) { st->print("-XX:%s=%f", _name, get_double()); } else if (is_ccstr()) { @@ -723,6 +741,34 @@ void CommandLineFlagsEx::uint64_tAtPut(CommandLineFlagWithType flag, uint64_t va faddr->set_origin(origin); } +bool CommandLineFlags::size_tAt(const char* name, size_t len, size_t* value) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_size_t()) return false; + *value = result->get_size_t(); + return true; +} + +bool CommandLineFlags::size_tAtPut(const char* name, size_t len, size_t* value, Flag::Flags origin) { + Flag* result = Flag::find_flag(name, len); + if (result == NULL) return false; + if (!result->is_size_t()) return false; + size_t old_value = result->get_size_t(); + trace_flag_changed(name, old_value, *value, origin); + result->set_size_t(*value); + *value = old_value; + result->set_origin(origin); + return true; +} + +void CommandLineFlagsEx::size_tAtPut(CommandLineFlagWithType flag, size_t value, Flag::Flags origin) { + Flag* faddr = address_of_flag(flag); + guarantee(faddr != NULL && faddr->is_size_t(), "wrong flag type"); + trace_flag_changed(faddr->_name, faddr->get_size_t(), value, origin); + faddr->set_size_t(value); + faddr->set_origin(origin); +} + bool CommandLineFlags::doubleAt(const char* name, size_t len, double* value) { Flag* result = Flag::find_flag(name, len); if (result == NULL) return false; diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index 2d1805e2fb6..372f6c720ab 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -275,6 +275,10 @@ struct Flag { uint64_t get_uint64_t() const; void set_uint64_t(uint64_t value); + bool is_size_t() const; + size_t get_size_t() const; + void set_size_t(size_t value); + bool is_double() const; double get_double() const; void set_double(double value); @@ -350,7 +354,6 @@ class UIntFlagSetting { ~UIntFlagSetting() { *flag = val; } }; - class DoubleFlagSetting { double val; double* flag; @@ -359,6 +362,14 @@ class DoubleFlagSetting { ~DoubleFlagSetting() { *flag = val; } }; +class SizeTFlagSetting { + size_t val; + size_t* flag; + public: + SizeTFlagSetting(size_t& fl, size_t newValue) { flag = &fl; val = fl; fl = newValue; } + ~SizeTFlagSetting() { *flag = val; } +}; + class CommandLineFlags { public: @@ -377,6 +388,11 @@ class CommandLineFlags { static bool uintxAtPut(const char* name, size_t len, uintx* value, Flag::Flags origin); static bool uintxAtPut(const char* name, uintx* value, Flag::Flags origin) { return uintxAtPut(name, strlen(name), value, origin); } + static bool size_tAt(const char* name, size_t len, size_t* value); + static bool size_tAt(const char* name, size_t* value) { return size_tAt(name, strlen(name), value); } + static bool size_tAtPut(const char* name, size_t len, size_t* value, Flag::Flags origin); + static bool size_tAtPut(const char* name, size_t* value, Flag::Flags origin) { return size_tAtPut(name, strlen(name), value, origin); } + static bool uint64_tAt(const char* name, size_t len, uint64_t* value); static bool uint64_tAt(const char* name, uint64_t* value) { return uint64_tAt(name, strlen(name), value); } static bool uint64_tAtPut(const char* name, size_t len, uint64_t* value, Flag::Flags origin); @@ -945,11 +961,6 @@ class CommandLineFlags { diagnostic(bool, PrintNMTStatistics, false, \ "Print native memory tracking summary data if it is on") \ \ - diagnostic(bool, AutoShutdownNMT, true, \ - "Automatically shutdown native memory tracking under stress " \ - "situations. When set to false, native memory tracking tries to " \ - "stay alive at the expense of JVM performance") \ - \ diagnostic(bool, LogCompilation, false, \ "Log compilation activity in detail to LogFile") \ \ @@ -1078,6 +1089,9 @@ class CommandLineFlags { product(bool, ClassUnloading, true, \ "Do unloading of classes") \ \ + product(bool, ClassUnloadingWithConcurrentMark, true, \ + "Do unloading of classes with a concurrent marking cycle") \ + \ develop(bool, DisableStartThread, false, \ "Disable starting of additional Java threads " \ "(for debugging only)") \ @@ -2331,6 +2345,12 @@ class CommandLineFlags { notproduct(bool, TraceScavenge, false, \ "Trace scavenge") \ \ + product(bool, IgnoreEmptyClassPaths, false, \ + "Ignore empty path elements in -classpath") \ + \ + product(bool, TraceClassPaths, false, \ + "Trace processing of class paths") \ + \ product_rw(bool, TraceClassLoading, false, \ "Trace all classes loaded") \ \ @@ -2786,12 +2806,6 @@ class CommandLineFlags { product(bool, UseLoopCounter, true, \ "Increment invocation counter on backward branch") \ \ - product(bool, UseFastEmptyMethods, true, \ - "Use fast method entry code for empty methods") \ - \ - product(bool, UseFastAccessorMethods, true, \ - "Use fast method entry code for accessor methods") \ - \ product_pd(bool, UseOnStackReplacement, \ "Use on stack replacement, calls runtime if invoc. counter " \ "overflows in loop") \ @@ -3771,6 +3785,13 @@ class CommandLineFlags { product(bool, PrintSharedSpaces, false, \ "Print usage of shared spaces") \ \ + product(bool, PrintSharedArchiveAndExit, false, \ + "Print shared archive file contents") \ + \ + product(bool, PrintSharedDictionary, false, \ + "If PrintSharedArchiveAndExit is true, also print the shared " \ + "dictionary") \ + \ product(uintx, SharedReadWriteSize, NOT_LP64(12*M) LP64_ONLY(16*M), \ "Size of read-write space for metadata (in bytes)") \ \ @@ -3787,6 +3808,10 @@ class CommandLineFlags { NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0)), \ "Address to allocate shared memory region for class data") \ \ + diagnostic(bool, IgnoreUnverifiableClassesDuringDump, false, \ + "Do not quit -Xshare:dump even if we encounter unverifiable " \ + "classes. Just exclude them from the shared dictionary.") \ + \ diagnostic(bool, PrintMethodHandleStubs, false, \ "Print generated stub code for method handles") \ \ @@ -3871,14 +3896,27 @@ class CommandLineFlags { product(bool, PrintGCCause, true, \ "Include GC cause in GC logging") \ \ + experimental(intx, SurvivorAlignmentInBytes, 0, \ + "Default survivor space alignment in bytes") \ + \ product(bool , AllowNonVirtualCalls, false, \ "Obey the ACC_SUPER flag and allow invokenonvirtual calls") \ \ + product(ccstr, DumpLoadedClassList, NULL, \ + "Dump the names all loaded classes, that could be stored into " \ + "the CDS archive, in the specified file") \ + \ + product(ccstr, SharedClassListFile, NULL, \ + "Override the default CDS class list") \ + \ diagnostic(ccstr, SharedArchiveFile, NULL, \ "Override the default location of the CDS archive file") \ \ - experimental(uintx, ArrayAllocatorMallocLimit, \ - SOLARIS_ONLY(64*K) NOT_SOLARIS(max_uintx), \ + product(ccstr, ExtraSharedClassListFile, NULL, \ + "Extra classlist for building the CDS archive file") \ + \ + experimental(size_t, ArrayAllocatorMallocLimit, \ + SOLARIS_ONLY(64*K) NOT_SOLARIS((size_t)-1), \ "Allocation less than this value will be allocated " \ "using malloc. Larger allocations will use mmap.") \ \ diff --git a/hotspot/src/share/vm/runtime/globals_extension.hpp b/hotspot/src/share/vm/runtime/globals_extension.hpp index 468d4bb87fa..99c19776fe3 100644 --- a/hotspot/src/share/vm/runtime/globals_extension.hpp +++ b/hotspot/src/share/vm/runtime/globals_extension.hpp @@ -200,6 +200,7 @@ class CommandLineFlagsEx : CommandLineFlags { static void intxAtPut(CommandLineFlagWithType flag, intx value, Flag::Flags origin); static void uintxAtPut(CommandLineFlagWithType flag, uintx value, Flag::Flags origin); static void uint64_tAtPut(CommandLineFlagWithType flag, uint64_t value, Flag::Flags origin); + static void size_tAtPut(CommandLineFlagWithType flag, size_t value, Flag::Flags origin); static void doubleAtPut(CommandLineFlagWithType flag, double value, Flag::Flags origin); // Contract: Flag will make private copy of the incoming value static void ccstrAtPut(CommandLineFlagWithType flag, ccstr value, Flag::Flags origin); diff --git a/hotspot/src/share/vm/runtime/handles.hpp b/hotspot/src/share/vm/runtime/handles.hpp index e7212214a14..3eef0adbc60 100644 --- a/hotspot/src/share/vm/runtime/handles.hpp +++ b/hotspot/src/share/vm/runtime/handles.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -227,7 +227,7 @@ class HandleArea: public Arena { HandleArea* _prev; // link to outer (older) area public: // Constructor - HandleArea(HandleArea* prev) : Arena(Chunk::tiny_size) { + HandleArea(HandleArea* prev) : Arena(mtThread, Chunk::tiny_size) { debug_only(_handle_mark_nesting = 0); debug_only(_no_handle_mark_nesting = 0); _prev = prev; diff --git a/hotspot/src/share/vm/runtime/init.cpp b/hotspot/src/share/vm/runtime/init.cpp index 6c654d280e2..68c8845aaf7 100644 --- a/hotspot/src/share/vm/runtime/init.cpp +++ b/hotspot/src/share/vm/runtime/init.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,8 +34,10 @@ #include "runtime/init.hpp" #include "runtime/safepoint.hpp" #include "runtime/sharedRuntime.hpp" +#include "services/memTracker.hpp" #include "utilities/macros.hpp" + // Initialization done by VM thread in vm_init_globals() void check_ThreadShadow(); void eventlog_init(); @@ -131,6 +133,12 @@ jint init_globals() { javaClasses_init(); // must happen after vtable initialization stubRoutines_init2(); // note: StubRoutines need 2-phase init +#if INCLUDE_NMT + // Solaris stack is walkable only after stubRoutines are set up. + // On Other platforms, the stack is always walkable. + NMT_stack_walkable = true; +#endif // INCLUDE_NMT + // All the flags that get adjusted by VM_Version_init and os::init_2 // have been set so dump the flags now. if (PrintFlagsFinal) { diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp index cfecf0a5d57..cf7865ecb0b 100644 --- a/hotspot/src/share/vm/runtime/java.cpp +++ b/hotspot/src/share/vm/runtime/java.cpp @@ -57,7 +57,6 @@ #include "runtime/thread.inline.hpp" #include "runtime/timer.hpp" #include "runtime/vm_operations.hpp" -#include "services/memReporter.hpp" #include "services/memTracker.hpp" #include "trace/tracing.hpp" #include "utilities/dtrace.hpp" @@ -349,12 +348,7 @@ void print_statistics() { #endif // ENABLE_ZAP_DEAD_LOCALS // Native memory tracking data if (PrintNMTStatistics) { - if (MemTracker::is_on()) { - BaselineTTYOutputer outputer(tty); - MemTracker::print_memory_usage(outputer, K, false); - } else { - tty->print_cr("%s", MemTracker::reason()); - } + MemTracker::final_report(tty); } } @@ -390,12 +384,7 @@ void print_statistics() { // Native memory tracking data if (PrintNMTStatistics) { - if (MemTracker::is_on()) { - BaselineTTYOutputer outputer(tty); - MemTracker::print_memory_usage(outputer, K, false); - } else { - tty->print_cr("%s", MemTracker::reason()); - } + MemTracker::final_report(tty); } } @@ -441,6 +430,8 @@ extern "C" { } } +jint volatile vm_getting_terminated = 0; + // Note: before_exit() can be executed only once, if more than one threads // are trying to shutdown the VM at the same time, only one thread // can run before_exit() and all other threads must wait. @@ -471,6 +462,8 @@ void before_exit(JavaThread * thread) { } } + OrderAccess::release_store(&vm_getting_terminated, 1); + // The only difference between this and Win32's _onexit procs is that // this version is invoked before any threads get killed. ExitProc* current = exit_procs; @@ -544,10 +537,6 @@ void before_exit(JavaThread * thread) { BeforeExit_lock->notify_all(); } - // Shutdown NMT before exit. Otherwise, - // it will run into trouble when system destroys static variables. - MemTracker::shutdown(MemTracker::NMT_normal); - if (VerifyStringTableAtExit) { int fail_cnt = 0; { diff --git a/hotspot/src/share/vm/runtime/javaCalls.cpp b/hotspot/src/share/vm/runtime/javaCalls.cpp index 24f84978fe2..3cb4053f893 100644 --- a/hotspot/src/share/vm/runtime/javaCalls.cpp +++ b/hotspot/src/share/vm/runtime/javaCalls.cpp @@ -309,6 +309,10 @@ void JavaCalls::call(JavaValue* result, methodHandle method, JavaCallArguments* } void JavaCalls::call_helper(JavaValue* result, methodHandle* m, JavaCallArguments* args, TRAPS) { + // During dumping, Java execution environment is not fully initialized. Also, Java execution + // may cause undesirable side-effects in the class metadata. + assert(!DumpSharedSpaces, "must not execute Java bytecodes when dumping"); + methodHandle method = *m; JavaThread* thread = (JavaThread*)THREAD; assert(thread->is_Java_thread(), "must be called by a java thread"); diff --git a/hotspot/src/share/vm/runtime/os.cpp b/hotspot/src/share/vm/runtime/os.cpp index 6c682a703cd..3b3bc44bec0 100644 --- a/hotspot/src/share/vm/runtime/os.cpp +++ b/hotspot/src/share/vm/runtime/os.cpp @@ -52,6 +52,7 @@ #include "runtime/thread.inline.hpp" #include "runtime/vm_version.hpp" #include "services/attachListener.hpp" +#include "services/nmtCommon.hpp" #include "services/memTracker.hpp" #include "services/threadService.hpp" #include "utilities/defaultStream.hpp" @@ -516,6 +517,14 @@ char *os::strdup(const char *str, MEMFLAGS flags) { return dup_str; } +char* os::strdup_check_oom(const char* str, MEMFLAGS flags) { + char* p = os::strdup(str, flags); + if (p == NULL) { + vm_exit_out_of_memory(strlen(str) + 1, OOM_MALLOC_ERROR, "os::strdup_check_oom"); + } + return p; +} + #define paranoid 0 /* only set to 1 if you suspect checking code has bug */ @@ -553,7 +562,11 @@ static u_char* testMalloc(size_t alloc_size) { return ptr; } -void* os::malloc(size_t size, MEMFLAGS memflags, address caller) { +void* os::malloc(size_t size, MEMFLAGS flags) { + return os::malloc(size, flags, CALLER_PC); +} + +void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) { NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1)); NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size)); @@ -579,11 +592,15 @@ void* os::malloc(size_t size, MEMFLAGS memflags, address caller) { size = 1; } + // NMT support + NMT_TrackingLevel level = MemTracker::tracking_level(); + size_t nmt_header_size = MemTracker::malloc_header_size(level); + #ifndef ASSERT - const size_t alloc_size = size; + const size_t alloc_size = size + nmt_header_size; #else - const size_t alloc_size = GuardedMemory::get_total_size(size); - if (size > alloc_size) { // Check for rollover. + const size_t alloc_size = GuardedMemory::get_total_size(size + nmt_header_size); + if (size + nmt_header_size > alloc_size) { // Check for rollover. return NULL; } #endif @@ -602,7 +619,7 @@ void* os::malloc(size_t size, MEMFLAGS memflags, address caller) { return NULL; } // Wrap memory with guard - GuardedMemory guarded(ptr, size); + GuardedMemory guarded(ptr, size + nmt_header_size); ptr = guarded.get_user_ptr(); #endif if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) { @@ -615,48 +632,50 @@ void* os::malloc(size_t size, MEMFLAGS memflags, address caller) { } // we do not track guard memory - MemTracker::record_malloc((address)ptr, size, memflags, caller == 0 ? CALLER_PC : caller); - - return ptr; + return MemTracker::record_malloc((address)ptr, size, memflags, stack, level); } +void* os::realloc(void *memblock, size_t size, MEMFLAGS flags) { + return os::realloc(memblock, size, flags, CALLER_PC); +} -void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, address caller) { +void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCallStack& stack) { #ifndef ASSERT NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1)); NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size)); - MemTracker::Tracker tkr = MemTracker::get_realloc_tracker(); - void* ptr = ::realloc(memblock, size); - if (ptr != NULL) { - tkr.record((address)memblock, (address)ptr, size, memflags, - caller == 0 ? CALLER_PC : caller); - } else { - tkr.discard(); - } - return ptr; + // NMT support + void* membase = MemTracker::record_free(memblock); + NMT_TrackingLevel level = MemTracker::tracking_level(); + size_t nmt_header_size = MemTracker::malloc_header_size(level); + void* ptr = ::realloc(membase, size + nmt_header_size); + return MemTracker::record_malloc(ptr, size, memflags, stack, level); #else if (memblock == NULL) { - return os::malloc(size, memflags, (caller == 0 ? CALLER_PC : caller)); + return os::malloc(size, memflags, stack); } if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) { tty->print_cr("os::realloc caught " PTR_FORMAT, memblock); breakpoint(); } - verify_memory(memblock); + // NMT support + void* membase = MemTracker::malloc_base(memblock); + verify_memory(membase); NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap()); if (size == 0) { return NULL; } // always move the block - void* ptr = os::malloc(size, memflags, caller == 0 ? CALLER_PC : caller); + void* ptr = os::malloc(size, memflags, stack); if (PrintMalloc) { tty->print_cr("os::remalloc " SIZE_FORMAT " bytes, " PTR_FORMAT " --> " PTR_FORMAT, size, memblock, ptr); } // Copy to new memory if malloc didn't fail if ( ptr != NULL ) { - GuardedMemory guarded(memblock); - memcpy(ptr, memblock, MIN2(size, guarded.get_user_size())); - if (paranoid) verify_memory(ptr); + 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, ptr); breakpoint(); @@ -669,7 +688,6 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, address caller void os::free(void *memblock, MEMFLAGS memflags) { - address trackp = (address) memblock; NOT_PRODUCT(inc_stat_counter(&num_frees, 1)); #ifdef ASSERT if (memblock == NULL) return; @@ -677,20 +695,22 @@ void os::free(void *memblock, MEMFLAGS memflags) { if (tty != NULL) tty->print_cr("os::free caught " PTR_FORMAT, memblock); breakpoint(); } - verify_memory(memblock); + void* membase = MemTracker::record_free(memblock); + verify_memory(membase); NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap()); - GuardedMemory guarded(memblock); + GuardedMemory guarded(membase); size_t size = guarded.get_user_size(); inc_stat_counter(&free_bytes, size); - memblock = guarded.release_for_freeing(); + membase = guarded.release_for_freeing(); if (PrintMalloc && tty != NULL) { - fprintf(stderr, "os::free " SIZE_FORMAT " bytes --> " PTR_FORMAT "\n", size, (uintptr_t)memblock); + fprintf(stderr, "os::free " SIZE_FORMAT " bytes --> " PTR_FORMAT "\n", size, (uintptr_t)membase); } + ::free(membase); +#else + void* membase = MemTracker::record_free(memblock); + ::free(membase); #endif - MemTracker::record_free(trackp, memflags); - - ::free(memblock); } void os::init_random(long initval) { @@ -1478,7 +1498,7 @@ bool os::create_stack_guard_pages(char* addr, size_t bytes) { char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint) { char* result = pd_reserve_memory(bytes, addr, alignment_hint); if (result != NULL) { - MemTracker::record_virtual_memory_reserve((address)result, bytes, mtNone, CALLER_PC); + MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); } return result; @@ -1488,7 +1508,7 @@ char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint, MEMFLAGS flags) { char* result = pd_reserve_memory(bytes, addr, alignment_hint); if (result != NULL) { - MemTracker::record_virtual_memory_reserve((address)result, bytes, mtNone, CALLER_PC); + MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); MemTracker::record_virtual_memory_type((address)result, flags); } @@ -1498,7 +1518,7 @@ char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint, char* os::attempt_reserve_memory_at(size_t bytes, char* addr) { char* result = pd_attempt_reserve_memory_at(bytes, addr); if (result != NULL) { - MemTracker::record_virtual_memory_reserve((address)result, bytes, mtNone, CALLER_PC); + MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); } return result; } @@ -1538,23 +1558,29 @@ void os::commit_memory_or_exit(char* addr, size_t size, size_t alignment_hint, } bool os::uncommit_memory(char* addr, size_t bytes) { - MemTracker::Tracker tkr = MemTracker::get_virtual_memory_uncommit_tracker(); - bool res = pd_uncommit_memory(addr, bytes); - if (res) { - tkr.record((address)addr, bytes); + bool res; + if (MemTracker::tracking_level() > NMT_minimal) { + Tracker tkr = MemTracker::get_virtual_memory_uncommit_tracker(); + res = pd_uncommit_memory(addr, bytes); + if (res) { + tkr.record((address)addr, bytes); + } } else { - tkr.discard(); + res = pd_uncommit_memory(addr, bytes); } return res; } bool os::release_memory(char* addr, size_t bytes) { - MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); - bool res = pd_release_memory(addr, bytes); - if (res) { - tkr.record((address)addr, bytes); + bool res; + if (MemTracker::tracking_level() > NMT_minimal) { + Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + res = pd_release_memory(addr, bytes); + if (res) { + tkr.record((address)addr, bytes); + } } else { - tkr.discard(); + res = pd_release_memory(addr, bytes); } return res; } @@ -1565,7 +1591,7 @@ char* os::map_memory(int fd, const char* file_name, size_t file_offset, bool allow_exec) { char* result = pd_map_memory(fd, file_name, file_offset, addr, bytes, read_only, allow_exec); if (result != NULL) { - MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, mtNone, CALLER_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC); } return result; } @@ -1578,12 +1604,15 @@ char* os::remap_memory(int fd, const char* file_name, size_t file_offset, } bool os::unmap_memory(char *addr, size_t bytes) { - MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); - bool result = pd_unmap_memory(addr, bytes); - if (result) { - tkr.record((address)addr, bytes); + bool result; + if (MemTracker::tracking_level() > NMT_minimal) { + Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); + result = pd_unmap_memory(addr, bytes); + if (result) { + tkr.record((address)addr, bytes); + } } else { - tkr.discard(); + result = pd_unmap_memory(addr, bytes); } return result; } diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp index b65e7ab44c5..5029a238a00 100644 --- a/hotspot/src/share/vm/runtime/os.hpp +++ b/hotspot/src/share/vm/runtime/os.hpp @@ -65,6 +65,8 @@ class JavaThread; class Event; class DLL; class FileHandle; +class NativeCallStack; + template class GrowableArray; // %%%%% Moved ThreadState, START_FN, OSThread to new osThread.hpp. -- Rose @@ -96,9 +98,11 @@ const bool ExecMem = true; // Typedef for structured exception handling support typedef void (*java_call_t)(JavaValue* value, methodHandle* method, JavaCallArguments* args, Thread* thread); +class MallocTracker; + class os: AllStatic { friend class VMStructs; - + friend class MallocTracker; public: enum { page_sizes_max = 9 }; // Size of _page_sizes array (8 plus a sentinel) @@ -160,7 +164,10 @@ class os: AllStatic { // Override me as needed static int file_name_strcmp(const char* s1, const char* s2); + // get/unset environment variable static bool getenv(const char* name, char* buffer, int len); + static bool unsetenv(const char* name); + static bool have_special_privileges(); static jlong javaTimeMillis(); @@ -207,8 +214,13 @@ class os: AllStatic { // Interface for detecting multiprocessor system static inline bool is_MP() { +#if !INCLUDE_NMT assert(_processor_count > 0, "invalid processor count"); return _processor_count > 1 || AssumeMP; +#else + // NMT needs atomic operations before this initialization. + return true; +#endif } static julong available_memory(); static julong physical_memory(); @@ -635,15 +647,25 @@ class os: AllStatic { static void* thread_local_storage_at(int index); static void free_thread_local_storage(int index); - // Stack walk - static address get_caller_pc(int n = 0); + // Retrieve native stack frames. + // Parameter: + // stack: an array to storage stack pointers. + // frames: size of above array. + // toSkip: number of stack frames to skip at the beginning. + // Return: number of stack frames captured. + static int get_native_stack(address* stack, int size, int toSkip = 0); // General allocation (must be MT-safe) - static void* malloc (size_t size, MEMFLAGS flags, address caller_pc = 0); - static void* realloc (void *memblock, size_t size, MEMFLAGS flags, address caller_pc = 0); + static void* malloc (size_t size, MEMFLAGS flags, const NativeCallStack& stack); + static void* malloc (size_t size, MEMFLAGS flags); + static void* realloc (void *memblock, size_t size, MEMFLAGS flag, const NativeCallStack& stack); + static void* realloc (void *memblock, size_t size, MEMFLAGS flag); + static void free (void *memblock, MEMFLAGS flags = mtNone); static bool check_heap(bool force = false); // verify C heap integrity static char* strdup(const char *, MEMFLAGS flags = mtInternal); // Like strdup + // Like strdup, but exit VM when strdup() returns NULL + static char* strdup_check_oom(const char*, MEMFLAGS flags = mtInternal); #ifndef PRODUCT static julong num_mallocs; // # of calls to malloc/realloc diff --git a/hotspot/src/share/vm/runtime/reflection.cpp b/hotspot/src/share/vm/runtime/reflection.cpp index cc35fea494b..42c9ed3e8ef 100644 --- a/hotspot/src/share/vm/runtime/reflection.cpp +++ b/hotspot/src/share/vm/runtime/reflection.cpp @@ -390,7 +390,7 @@ oop Reflection::array_component_type(oop mirror, TRAPS) { return NULL; } - oop result = ArrayKlass::cast(klass)->component_mirror(); + oop result = java_lang_Class::component_mirror(mirror); #ifdef ASSERT oop result2 = NULL; if (ArrayKlass::cast(klass)->dimension() == 1) { diff --git a/hotspot/src/share/vm/runtime/safepoint.cpp b/hotspot/src/share/vm/runtime/safepoint.cpp index 4b16f6b04a8..05460b84dc0 100644 --- a/hotspot/src/share/vm/runtime/safepoint.cpp +++ b/hotspot/src/share/vm/runtime/safepoint.cpp @@ -52,7 +52,6 @@ #include "runtime/sweeper.hpp" #include "runtime/synchronizer.hpp" #include "runtime/thread.inline.hpp" -#include "services/memTracker.hpp" #include "services/runtimeService.hpp" #include "utilities/events.hpp" #include "utilities/macros.hpp" @@ -527,10 +526,6 @@ void SafepointSynchronize::do_cleanup_tasks() { TraceTime t7("purging class loader data graph", TraceSafepointCleanupTime); ClassLoaderDataGraph::purge_if_needed(); } - - if (MemTracker::is_on()) { - MemTracker::sync(); - } } diff --git a/hotspot/src/share/vm/runtime/sharedRuntimeMath.hpp b/hotspot/src/share/vm/runtime/sharedRuntimeMath.hpp index 4259227c3b3..26c9147f8b1 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntimeMath.hpp +++ b/hotspot/src/share/vm/runtime/sharedRuntimeMath.hpp @@ -27,20 +27,51 @@ #include -// VM_LITTLE_ENDIAN is #defined appropriately in the Makefiles -// [jk] this is not 100% correct because the float word order may different -// from the byte order (e.g. on ARM FPA) +// Used to access the lower/higher 32 bits of a double +typedef union { + double d; + struct { #ifdef VM_LITTLE_ENDIAN -# define __HI(x) *(1+(int*)&x) -# define __LO(x) *(int*)&x + int lo; + int hi; #else -# define __HI(x) *(int*)&x -# define __LO(x) *(1+(int*)&x) + int hi; + int lo; #endif + } split; +} DoubleIntConv; + +static inline int high(double d) { + DoubleIntConv x; + x.d = d; + return x.split.hi; +} + +static inline int low(double d) { + DoubleIntConv x; + x.d = d; + return x.split.lo; +} + +static inline void set_high(double* d, int high) { + DoubleIntConv conv; + conv.d = *d; + conv.split.hi = high; + *d = conv.d; +} + +static inline void set_low(double* d, int low) { + DoubleIntConv conv; + conv.d = *d; + conv.split.lo = low; + *d = conv.d; +} static double copysignA(double x, double y) { - __HI(x) = (__HI(x)&0x7fffffff)|(__HI(y)&0x80000000); - return x; + DoubleIntConv convX; + convX.d = x; + convX.split.hi = (convX.split.hi & 0x7fffffff) | (high(y) & 0x80000000); + return convX.d; } /* @@ -67,30 +98,32 @@ twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */ hugeX = 1.0e+300, tiny = 1.0e-300; -static double scalbnA (double x, int n) { +static double scalbnA(double x, int n) { int k,hx,lx; - hx = __HI(x); - lx = __LO(x); + hx = high(x); + lx = low(x); k = (hx&0x7ff00000)>>20; /* extract exponent */ if (k==0) { /* 0 or subnormal x */ if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */ x *= two54; - hx = __HI(x); + hx = high(x); k = ((hx&0x7ff00000)>>20) - 54; if (n< -50000) return tiny*x; /*underflow*/ } if (k==0x7ff) return x+x; /* NaN or Inf */ k = k+n; - if (k > 0x7fe) return hugeX*copysignA(hugeX,x); /* overflow */ - if (k > 0) /* normal result */ - {__HI(x) = (hx&0x800fffff)|(k<<20); return x;} + if (k > 0x7fe) return hugeX*copysignA(hugeX,x); /* overflow */ + if (k > 0) { /* normal result */ + set_high(&x, (hx&0x800fffff)|(k<<20)); + return x; + } if (k <= -54) { if (n > 50000) /* in case integer overflow in n+k */ return hugeX*copysignA(hugeX,x); /*overflow*/ else return tiny*copysignA(tiny,x); /*underflow*/ } k += 54; /* subnormal result */ - __HI(x) = (hx&0x800fffff)|(k<<20); + set_high(&x, (hx&0x800fffff)|(k<<20)); return x*twom54; } diff --git a/hotspot/src/share/vm/runtime/sharedRuntimeTrans.cpp b/hotspot/src/share/vm/runtime/sharedRuntimeTrans.cpp index 69feb28bdfb..3b8c8a3010b 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntimeTrans.cpp +++ b/hotspot/src/share/vm/runtime/sharedRuntimeTrans.cpp @@ -40,6 +40,7 @@ // generated; can not figure out how to turn down optimization for one // file in the IDE on Windows #ifdef WIN32 +# pragma warning( disable: 4748 ) // /GS can not protect parameters and local variables from local buffer overrun because optimizations are disabled in function # pragma optimize ( "", off ) #endif @@ -114,8 +115,8 @@ static double __ieee754_log(double x) { int k,hx,i,j; unsigned lx; - hx = __HI(x); /* high word of x */ - lx = __LO(x); /* low word of x */ + hx = high(x); /* high word of x */ + lx = low(x); /* low word of x */ k=0; if (hx < 0x00100000) { /* x < 2**-1022 */ @@ -123,13 +124,13 @@ static double __ieee754_log(double x) { return -two54/zero; /* log(+-0)=-inf */ if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ k -= 54; x *= two54; /* subnormal number, scale up x */ - hx = __HI(x); /* high word of x */ + hx = high(x); /* high word of x */ } if (hx >= 0x7ff00000) return x+x; k += (hx>>20)-1023; hx &= 0x000fffff; i = (hx+0x95f64)&0x100000; - __HI(x) = hx|(i^0x3ff00000); /* normalize x or x/2 */ + set_high(&x, hx|(i^0x3ff00000)); /* normalize x or x/2 */ k += (i>>20); f = x-1.0; if((0x000fffff&(2+hx))<3) { /* |f| < 2**-20 */ @@ -208,8 +209,8 @@ static double __ieee754_log10(double x) { int i,k,hx; unsigned lx; - hx = __HI(x); /* high word of x */ - lx = __LO(x); /* low word of x */ + hx = high(x); /* high word of x */ + lx = low(x); /* low word of x */ k=0; if (hx < 0x00100000) { /* x < 2**-1022 */ @@ -217,14 +218,14 @@ static double __ieee754_log10(double x) { return -two54/zero; /* log(+-0)=-inf */ if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ k -= 54; x *= two54; /* subnormal number, scale up x */ - hx = __HI(x); /* high word of x */ + hx = high(x); /* high word of x */ } if (hx >= 0x7ff00000) return x+x; k += (hx>>20)-1023; i = ((unsigned)k&0x80000000)>>31; hx = (hx&0x000fffff)|((0x3ff-i)<<20); y = (double)(k+i); - __HI(x) = hx; + set_high(&x, hx); z = y*log10_2lo + ivln10*__ieee754_log(x); return z+y*log10_2hi; } @@ -319,14 +320,14 @@ static double __ieee754_exp(double x) { int k=0,xsb; unsigned hx; - hx = __HI(x); /* high word of x */ + hx = high(x); /* high word of x */ xsb = (hx>>31)&1; /* sign bit of x */ hx &= 0x7fffffff; /* high word of |x| */ /* filter out non-finite argument */ if(hx >= 0x40862E42) { /* if |x|>=709.78... */ if(hx>=0x7ff00000) { - if(((hx&0xfffff)|__LO(x))!=0) + if(((hx&0xfffff)|low(x))!=0) return x+x; /* NaN */ else return (xsb==0)? x:0.0; /* exp(+-inf)={inf,0} */ } @@ -357,10 +358,10 @@ static double __ieee754_exp(double x) { if(k==0) return one-((x*c)/(c-2.0)-x); else y = one-((lo-(x*c)/(2.0-c))-hi); if(k >= -1021) { - __HI(y) += (k<<20); /* add k to y's exponent */ + set_high(&y, high(y) + (k<<20)); /* add k to y's exponent */ return y; } else { - __HI(y) += ((k+1000)<<20);/* add k to y's exponent */ + set_high(&y, high(y) + ((k+1000)<<20)); /* add k to y's exponent */ return y*twom1000; } } @@ -447,8 +448,8 @@ double __ieee754_pow(double x, double y) { unsigned lx,ly; i0 = ((*(int*)&one)>>29)^1; i1=1-i0; - hx = __HI(x); lx = __LO(x); - hy = __HI(y); ly = __LO(y); + hx = high(x); lx = low(x); + hy = high(y); ly = low(y); ix = hx&0x7fffffff; iy = hy&0x7fffffff; /* y==zero: x**0 = 1 */ @@ -548,14 +549,14 @@ double __ieee754_pow(double x, double y) { u = ivln2_h*t; /* ivln2_h has 21 sig. bits */ v = t*ivln2_l-w*ivln2; t1 = u+v; - __LO(t1) = 0; + set_low(&t1, 0); t2 = v-(t1-u); } else { double ss,s2,s_h,s_l,t_h,t_l; n = 0; /* take care subnormal number */ if(ix<0x00100000) - {ax *= two53; n -= 53; ix = __HI(ax); } + {ax *= two53; n -= 53; ix = high(ax); } n += ((ix)>>20)-0x3ff; j = ix&0x000fffff; /* determine interval */ @@ -563,17 +564,17 @@ double __ieee754_pow(double x, double y) { if(j<=0x3988E) k=0; /* |x|>1)|0x20000000)+0x00080000+(k<<18); + set_high(&t_h, ((ix>>1)|0x20000000)+0x00080000+(k<<18)); t_l = ax - (t_h-bp[k]); s_l = v*((u-s_h*t_h)-s_h*t_l); /* compute log(ax) */ @@ -582,32 +583,32 @@ double __ieee754_pow(double x, double y) { r += s_l*(s_h+ss); s2 = s_h*s_h; t_h = 3.0+s2+r; - __LO(t_h) = 0; + set_low(&t_h, 0); t_l = r-((t_h-3.0)-s2); /* u+v = ss*(1+...) */ u = s_h*t_h; v = s_l*t_h+t_l*ss; /* 2/(3log2)*(ss+...) */ p_h = u+v; - __LO(p_h) = 0; + set_low(&p_h, 0); p_l = v-(p_h-u); z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ z_l = cp_l*p_h+p_l*cp+dp_l[k]; /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */ t = (double)n; t1 = (((z_h+z_l)+dp_h[k])+t); - __LO(t1) = 0; + set_low(&t1, 0); t2 = z_l-(((t1-t)-dp_h[k])-z_h); } /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ y1 = y; - __LO(y1) = 0; + set_low(&y1, 0); p_l = (y-y1)*t1+y*t2; p_h = y1*t1; z = p_l+p_h; - j = __HI(z); - i = __LO(z); + j = high(z); + i = low(z); if (j>=0x40900000) { /* z >= 1024 */ if(((j-0x40900000)|i)!=0) /* if z > 1024 */ return s*hugeX*hugeX; /* overflow */ @@ -631,13 +632,13 @@ double __ieee754_pow(double x, double y) { n = j+(0x00100000>>(k+1)); k = ((n&0x7fffffff)>>20)-0x3ff; /* new k for n */ t = zeroX; - __HI(t) = (n&~(0x000fffff>>k)); + set_high(&t, (n&~(0x000fffff>>k))); n = ((n&0x000fffff)|0x00100000)>>(20-k); if(j<0) n = -n; p_h -= t; } t = p_l+p_h; - __LO(t) = 0; + set_low(&t, 0); u = t*lg2_h; v = (p_l-(t-p_h))*lg2+t*lg2_l; z = u+v; @@ -646,10 +647,10 @@ double __ieee754_pow(double x, double y) { t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); r = (z*t1)/(t1-two)-(w+z*w); z = one-(r-z); - j = __HI(z); + j = high(z); j += (n<<20); if((j>>20)<=0) z = scalbnA(z,n); /* subnormal output */ - else __HI(z) += (n<<20); + else set_high(&z, high(z) + (n<<20)); return s*z; } diff --git a/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp b/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp index 84f35aa9317..8acd7401585 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp +++ b/hotspot/src/share/vm/runtime/sharedRuntimeTrig.cpp @@ -519,7 +519,7 @@ static double __kernel_sin(double x, double y, int iy) { double z,r,v; int ix; - ix = __HI(x)&0x7fffffff; /* high word of x */ + ix = high(x)&0x7fffffff; /* high word of x */ if(ix<0x3e400000) /* |x| < 2**-27 */ {if((int)x==0) return x;} /* generate inexact */ z = x*x; @@ -574,9 +574,9 @@ C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ static double __kernel_cos(double x, double y) { - double a,h,z,r,qx; + double a,h,z,r,qx=0; int ix; - ix = __HI(x)&0x7fffffff; /* ix = |x|'s high word*/ + ix = high(x)&0x7fffffff; /* ix = |x|'s high word*/ if(ix<0x3e400000) { /* if x < 2**27 */ if(((int)x)==0) return one; /* generate inexact */ } @@ -588,8 +588,8 @@ static double __kernel_cos(double x, double y) if(ix > 0x3fe90000) { /* x > 0.78125 */ qx = 0.28125; } else { - __HI(qx) = ix-0x00200000; /* x/4 */ - __LO(qx) = 0; + set_high(&qx, ix-0x00200000); /* x/4 */ + set_low(&qx, 0); } h = 0.5*z-qx; a = one-qx; @@ -654,11 +654,11 @@ static double __kernel_tan(double x, double y, int iy) { double z,r,v,w,s; int ix,hx; - hx = __HI(x); /* high word of x */ + hx = high(x); /* high word of x */ ix = hx&0x7fffffff; /* high word of |x| */ if(ix<0x3e300000) { /* x < 2**-28 */ if((int)x==0) { /* generate inexact */ - if (((ix | __LO(x)) | (iy + 1)) == 0) + if (((ix | low(x)) | (iy + 1)) == 0) return one / fabsd(x); else { if (iy == 1) @@ -667,10 +667,10 @@ static double __kernel_tan(double x, double y, int iy) double a, t; z = w = x + y; - __LO(z) = 0; + set_low(&z, 0); v = y - (z - x); t = a = -one / w; - __LO(t) = 0; + set_low(&t, 0); s = one + t * z; return t + a * (s + t * v); } @@ -705,10 +705,10 @@ static double __kernel_tan(double x, double y, int iy) /* compute -1.0/(x+r) accurately */ double a,t; z = w; - __LO(z) = 0; + set_low(&z, 0); v = r-(z - x); /* z+v = r+x */ t = a = -1.0/w; /* a = -1.0/w */ - __LO(t) = 0; + set_low(&t, 0); s = 1.0+t*z; return t+a*(s+t*v); } @@ -757,7 +757,7 @@ JRT_LEAF(jdouble, SharedRuntime::dsin(jdouble x)) int n, ix; /* High word of x. */ - ix = __HI(x); + ix = high(x); /* |x| ~< pi/4 */ ix &= 0x7fffffff; @@ -815,7 +815,7 @@ JRT_LEAF(jdouble, SharedRuntime::dcos(jdouble x)) int n, ix; /* High word of x. */ - ix = __HI(x); + ix = high(x); /* |x| ~< pi/4 */ ix &= 0x7fffffff; @@ -872,7 +872,7 @@ JRT_LEAF(jdouble, SharedRuntime::dtan(jdouble x)) int n, ix; /* High word of x. */ - ix = __HI(x); + ix = high(x); /* |x| ~< pi/4 */ ix &= 0x7fffffff; diff --git a/hotspot/src/share/vm/runtime/stubRoutines.cpp b/hotspot/src/share/vm/runtime/stubRoutines.cpp index 6febb5bcee9..f35cce9f5e9 100644 --- a/hotspot/src/share/vm/runtime/stubRoutines.cpp +++ b/hotspot/src/share/vm/runtime/stubRoutines.cpp @@ -135,6 +135,8 @@ address StubRoutines::_sha512_implCompressMB = NULL; address StubRoutines::_updateBytesCRC32 = NULL; address StubRoutines::_crc_table_adr = NULL; +address StubRoutines::_multiplyToLen = NULL; + double (* StubRoutines::_intrinsic_log )(double) = NULL; double (* StubRoutines::_intrinsic_log10 )(double) = NULL; double (* StubRoutines::_intrinsic_exp )(double) = NULL; diff --git a/hotspot/src/share/vm/runtime/stubRoutines.hpp b/hotspot/src/share/vm/runtime/stubRoutines.hpp index 46d93a52916..60f24843abf 100644 --- a/hotspot/src/share/vm/runtime/stubRoutines.hpp +++ b/hotspot/src/share/vm/runtime/stubRoutines.hpp @@ -202,6 +202,8 @@ class StubRoutines: AllStatic { static address _updateBytesCRC32; static address _crc_table_adr; + static address _multiplyToLen; + // These are versions of the java.lang.Math methods which perform // the same operations as the intrinsic version. They are used for // constant folding in the compiler to ensure equivalence. If the @@ -358,6 +360,8 @@ class StubRoutines: AllStatic { static address updateBytesCRC32() { return _updateBytesCRC32; } static address crc_table_addr() { return _crc_table_adr; } + static address multiplyToLen() {return _multiplyToLen; } + static address select_fill_function(BasicType t, bool aligned, const char* &name); static address zero_aligned_words() { return _zero_aligned_words; } diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index 08785805729..9956ed28846 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -297,8 +297,7 @@ void Thread::record_stack_base_and_size() { #if INCLUDE_NMT // record thread's native stack, stack grows downward address stack_low_addr = stack_base() - stack_size(); - MemTracker::record_thread_stack(stack_low_addr, stack_size(), this, - CURRENT_PC); + MemTracker::record_thread_stack(stack_low_addr, stack_size()); #endif // INCLUDE_NMT } @@ -316,7 +315,7 @@ Thread::~Thread() { #if INCLUDE_NMT if (_stack_base != NULL) { address low_stack_addr = stack_base() - stack_size(); - MemTracker::release_thread_stack(low_stack_addr, stack_size(), this); + MemTracker::release_thread_stack(low_stack_addr, stack_size()); #ifdef ASSERT set_stack_base(NULL); #endif @@ -1425,9 +1424,6 @@ void JavaThread::initialize() { set_monitor_chunks(NULL); set_next(NULL); set_thread_state(_thread_new); -#if INCLUDE_NMT - set_recorder(NULL); -#endif _terminated = _not_terminated; _privileged_stack_top = NULL; _array_for_gc = NULL; @@ -1503,7 +1499,6 @@ JavaThread::JavaThread(bool is_attaching_via_jni) : _jni_attach_state = _not_attaching_via_jni; } assert(deferred_card_mark().is_empty(), "Default MemRegion ctor"); - _safepoint_visible = false; } bool JavaThread::reguard_stack(address cur_sp) { @@ -1566,7 +1561,6 @@ JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread : os::java_thread; os::create_thread(this, thr_type, stack_sz); - _safepoint_visible = false; // The _osthread may be NULL here because we ran out of memory (too many threads active). // We need to throw and OutOfMemoryError - however we cannot do this here because the caller // may hold a lock and all locks must be unlocked before throwing the exception (throwing @@ -1584,13 +1578,6 @@ JavaThread::~JavaThread() { tty->print_cr("terminate thread %p", this); } - // By now, this thread should already be invisible to safepoint, - // and its per-thread recorder also collected. - assert(!is_safepoint_visible(), "wrong state"); -#if INCLUDE_NMT - assert(get_recorder() == NULL, "Already collected"); -#endif // INCLUDE_NMT - // JSR166 -- return the parker to the free list Parker::Release(_parker); _parker = NULL; @@ -3359,11 +3346,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // initialize TLS ThreadLocalStorage::init(); - // Bootstrap native memory tracking, so it can start recording memory - // activities before worker thread is started. This is the first phase - // of bootstrapping, VM is currently running in single-thread mode. - MemTracker::bootstrap_single_thread(); - // Initialize output stream logging ostream_init_log(); @@ -3414,9 +3396,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // Initialize Java-Level synchronization subsystem ObjectMonitor::Initialize(); - // Second phase of bootstrapping, VM is about entering multi-thread mode - MemTracker::bootstrap_multi_thread(); - // Initialize global modules jint status = init_globals(); if (status != JNI_OK) { @@ -3438,9 +3417,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // real raw monitor. VM is setup enough here for raw monitor enter. JvmtiExport::transition_pending_onload_raw_monitors(); - // Fully start NMT - MemTracker::start(); - // Create the VMThread { TraceTime timer("Start VMThread", TraceStartupTime); VMThread::create(); @@ -3995,8 +3971,6 @@ void Threads::add(JavaThread* p, bool force_daemon) { daemon = false; } - p->set_safepoint_visible(true); - ThreadService::add_thread(p, daemon); // Possible GC point. @@ -4042,13 +4016,6 @@ void Threads::remove(JavaThread* p) { // to do callbacks into the safepoint code. However, the safepoint code is not aware // of this thread since it is removed from the queue. p->set_terminated_value(); - - // Now, this thread is not visible to safepoint - p->set_safepoint_visible(false); - // once the thread becomes safepoint invisible, we can not use its per-thread - // recorder. And Threads::do_threads() no longer walks this thread, so we have - // to release its per-thread recorder here. - MemTracker::thread_exiting(p); } // unlock Threads_lock // Since Events::log uses a lock, we grab it outside the Threads_lock diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp index 7df1c0f3cbc..332fd8666f8 100644 --- a/hotspot/src/share/vm/runtime/thread.hpp +++ b/hotspot/src/share/vm/runtime/thread.hpp @@ -43,10 +43,6 @@ #include "runtime/unhandledOops.hpp" #include "utilities/macros.hpp" -#if INCLUDE_NMT -#include "services/memRecorder.hpp" -#endif // INCLUDE_NMT - #include "trace/traceBackend.hpp" #include "trace/traceMacros.hpp" #include "utilities/exceptions.hpp" @@ -1036,16 +1032,6 @@ class JavaThread: public Thread { bool do_not_unlock_if_synchronized() { return _do_not_unlock_if_synchronized; } void set_do_not_unlock_if_synchronized(bool val) { _do_not_unlock_if_synchronized = val; } -#if INCLUDE_NMT - // native memory tracking - inline MemRecorder* get_recorder() const { return (MemRecorder*)_recorder; } - inline void set_recorder(MemRecorder* rc) { _recorder = rc; } - - private: - // per-thread memory recorder - MemRecorder* volatile _recorder; -#endif // INCLUDE_NMT - // Suspend/resume support for JavaThread private: inline void set_ext_suspended(); @@ -1485,19 +1471,6 @@ public: return result; } - // NMT (Native memory tracking) support. - // This flag helps NMT to determine if this JavaThread will be blocked - // at safepoint. If not, ThreadCritical is needed for writing memory records. - // JavaThread is only safepoint visible when it is in Threads' thread list, - // it is not visible until it is added to the list and becomes invisible - // once it is removed from the list. - public: - bool is_safepoint_visible() const { return _safepoint_visible; } - void set_safepoint_visible(bool visible) { _safepoint_visible = visible; } - private: - bool _safepoint_visible; - - // Static operations public: // Returns the running thread as a JavaThread static inline JavaThread* current(); diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index 3ff41705843..d3ad9bd9b88 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -52,6 +52,7 @@ #include "interpreter/bytecodes.hpp" #include "interpreter/interpreter.hpp" #include "memory/allocation.hpp" +#include "memory/allocation.inline.hpp" #include "memory/cardTableRS.hpp" #include "memory/defNewGeneration.hpp" #include "memory/freeBlockDictionary.hpp" @@ -93,6 +94,7 @@ #include "runtime/globals.hpp" #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" +#include "runtime/os.hpp" #include "runtime/perfMemory.hpp" #include "runtime/serviceThread.hpp" #include "runtime/sharedRuntime.hpp" @@ -272,7 +274,6 @@ typedef TwoOopHashtable SymbolTwoOopHashtable; volatile_nonstatic_field(ArrayKlass, _higher_dimension, Klass*) \ volatile_nonstatic_field(ArrayKlass, _lower_dimension, Klass*) \ nonstatic_field(ArrayKlass, _vtable_len, int) \ - nonstatic_field(ArrayKlass, _component_mirror, oop) \ nonstatic_field(CompiledICHolder, _holder_method, Method*) \ nonstatic_field(CompiledICHolder, _holder_klass, Klass*) \ nonstatic_field(ConstantPool, _tags, Array*) \ @@ -312,7 +313,7 @@ typedef TwoOopHashtable SymbolTwoOopHashtable; nonstatic_field(InstanceKlass, _jni_ids, JNIid*) \ nonstatic_field(InstanceKlass, _osr_nmethods_head, nmethod*) \ nonstatic_field(InstanceKlass, _breakpoints, BreakpointInfo*) \ - nonstatic_field(InstanceKlass, _generic_signature_index, u2) \ + nonstatic_field(InstanceKlass, _generic_signature_index, u2) \ nonstatic_field(InstanceKlass, _methods_jmethod_ids, jmethodID*) \ volatile_nonstatic_field(InstanceKlass, _idnum_allocated_count, u2) \ nonstatic_field(InstanceKlass, _annotations, Annotations*) \ @@ -660,6 +661,7 @@ typedef TwoOopHashtable SymbolTwoOopHashtable; static_field(SystemDictionary, WK_KLASS(StackOverflowError_klass), Klass*) \ static_field(SystemDictionary, WK_KLASS(ProtectionDomain_klass), Klass*) \ static_field(SystemDictionary, WK_KLASS(AccessControlContext_klass), Klass*) \ + static_field(SystemDictionary, WK_KLASS(SecureClassLoader_klass), Klass*) \ static_field(SystemDictionary, WK_KLASS(Reference_klass), Klass*) \ static_field(SystemDictionary, WK_KLASS(SoftReference_klass), Klass*) \ static_field(SystemDictionary, WK_KLASS(WeakReference_klass), Klass*) \ @@ -808,6 +810,7 @@ typedef TwoOopHashtable SymbolTwoOopHashtable; static_field(StubRoutines, _cipherBlockChaining_decryptAESCrypt, address) \ static_field(StubRoutines, _updateBytesCRC32, address) \ static_field(StubRoutines, _crc_table_adr, address) \ + static_field(StubRoutines, _multiplyToLen, address) \ \ /*****************/ \ /* SharedRuntime */ \ @@ -3296,14 +3299,14 @@ static int recursiveFindType(VMTypeEntry* origtypes, const char* typeName, bool } } if (strstr(typeName, " const") == typeName + len - 6) { - char * s = strdup(typeName); + char * s = os::strdup_check_oom(typeName); s[len - 6] = '\0'; // tty->print_cr("checking \"%s\" for \"%s\"", s, typeName); if (recursiveFindType(origtypes, s, true) == 1) { - free(s); + os::free(s); return 1; } - free(s); + os::free(s); } if (!isRecurse) { tty->print_cr("type \"%s\" not found", typeName); diff --git a/hotspot/src/share/vm/services/allocationSite.hpp b/hotspot/src/share/vm/services/allocationSite.hpp new file mode 100644 index 00000000000..07f8595f815 --- /dev/null +++ b/hotspot/src/share/vm/services/allocationSite.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_SERVICES_ALLOCATION_SITE_HPP +#define SHARE_VM_SERVICES_ALLOCATION_SITE_HPP + +#include "memory/allocation.hpp" +#include "utilities/nativeCallStack.hpp" + +// Allocation site represents a code path that makes a memory +// allocation +template class AllocationSite VALUE_OBJ_CLASS_SPEC { + private: + NativeCallStack _call_stack; + E e; + public: + AllocationSite(const NativeCallStack& stack) : _call_stack(stack) { } + int hash() const { return _call_stack.hash(); } + bool equals(const NativeCallStack& stack) const { + return _call_stack.equals(stack); + } + + bool equals(const AllocationSite& other) const { + return other.equals(_call_stack); + } + + const NativeCallStack* call_stack() const { + return &_call_stack; + } + + // Information regarding this allocation + E* data() { return &e; } + const E* peek() const { return &e; } +}; + +#endif // SHARE_VM_SERVICES_ALLOCATION_SITE_HPP diff --git a/hotspot/src/share/vm/services/attachListener.cpp b/hotspot/src/share/vm/services/attachListener.cpp index 2321f084dc5..ecd59d4c11d 100644 --- a/hotspot/src/share/vm/services/attachListener.cpp +++ b/hotspot/src/share/vm/services/attachListener.cpp @@ -320,6 +320,25 @@ static jint set_uint64_t_flag(const char* name, AttachOperation* op, outputStrea return res? JNI_OK : JNI_ERR; } +// set a size_t global flag using value from AttachOperation +static jint set_size_t_flag(const char* name, AttachOperation* op, outputStream* out) { + size_t value; + const char* arg1; + if ((arg1 = op->arg(1)) != NULL) { + int n = sscanf(arg1, SIZE_FORMAT, &value); + if (n != 1) { + out->print_cr("flag value must be an unsigned integer"); + return JNI_ERR; + } + } + bool res = CommandLineFlags::size_tAtPut((char*)name, &value, Flag::ATTACH_ON_DEMAND); + if (! res) { + out->print_cr("setting flag %s failed", name); + } + + return res? JNI_OK : JNI_ERR; +} + // set a string global flag using value from AttachOperation static jint set_ccstr_flag(const char* name, AttachOperation* op, outputStream* out) { const char* value; @@ -356,6 +375,8 @@ static jint set_flag(AttachOperation* op, outputStream* out) { return set_uintx_flag(name, op, out); } else if (f->is_uint64_t()) { return set_uint64_t_flag(name, op, out); + } else if (f->is_size_t()) { + return set_size_t_flag(name, op, out); } else if (f->is_ccstr()) { return set_ccstr_flag(name, op, out); } else { diff --git a/hotspot/src/share/vm/services/mallocSiteTable.cpp b/hotspot/src/share/vm/services/mallocSiteTable.cpp new file mode 100644 index 00000000000..ec11cfabe69 --- /dev/null +++ b/hotspot/src/share/vm/services/mallocSiteTable.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#include "precompiled.hpp" + + +#include "memory/allocation.inline.hpp" +#include "runtime/atomic.hpp" +#include "services/mallocSiteTable.hpp" + +/* + * Early os::malloc() calls come from initializations of static variables, long before entering any + * VM code. Upon the arrival of the first os::malloc() call, malloc site hashtable has to be + * initialized, along with the allocation site for the hashtable entries. + * To ensure that malloc site hashtable can be initialized without triggering any additional os::malloc() + * call, the hashtable bucket array and hashtable entry allocation site have to be static. + * It is not a problem for hashtable bucket, since it is an array of pointer type, C runtime just + * allocates a block memory and zero the memory for it. + * But for hashtable entry allocation site object, things get tricky. C runtime not only allocates + * memory for it, but also calls its constructor at some later time. If we initialize the allocation site + * at the first os::malloc() call, the object will be reinitialized when its constructor is called + * by C runtime. + * To workaround above issue, we declare a static size_t array with the size of the CallsiteHashtableEntry, + * the memory is used to instantiate CallsiteHashtableEntry for the hashtable entry allocation site. + * Given it is a primitive type array, C runtime will do nothing other than assign the memory block for the variable, + * which is exactly what we want. + * The same trick is also applied to create NativeCallStack object for CallsiteHashtableEntry memory allocation. + * + * Note: C++ object usually aligns to particular alignment, depends on compiler implementation, we declare + * the memory as size_t arrays, to ensure the memory is aligned to native machine word alignment. + */ + +// Reserve enough memory for NativeCallStack and MallocSiteHashtableEntry objects +size_t MallocSiteTable::_hash_entry_allocation_stack[CALC_OBJ_SIZE_IN_TYPE(NativeCallStack, size_t)]; +size_t MallocSiteTable::_hash_entry_allocation_site[CALC_OBJ_SIZE_IN_TYPE(MallocSiteHashtableEntry, size_t)]; + +// Malloc site hashtable buckets +MallocSiteHashtableEntry* MallocSiteTable::_table[MallocSiteTable::table_size]; + +// concurrent access counter +volatile int MallocSiteTable::_access_count = 0; + +// Tracking hashtable contention +NOT_PRODUCT(int MallocSiteTable::_peak_count = 0;) + + +/* + * Initialize malloc site table. + * Hashtable entry is malloc'd, so it can cause infinite recursion. + * To avoid above problem, we pre-initialize a hash entry for + * this allocation site. + * The method is called during C runtime static variable initialization + * time, it is in single-threaded mode from JVM perspective. + */ +bool MallocSiteTable::initialize() { + assert(sizeof(_hash_entry_allocation_stack) >= sizeof(NativeCallStack), "Sanity Check"); + assert(sizeof(_hash_entry_allocation_site) >= sizeof(MallocSiteHashtableEntry), + "Sanity Check"); + assert((size_t)table_size <= MAX_MALLOCSITE_TABLE_SIZE, "Hashtable overflow"); + + // Fake the call stack for hashtable entry allocation + assert(NMT_TrackingStackDepth > 1, "At least one tracking stack"); + + // Create pseudo call stack for hashtable entry allocation + address pc[3]; + if (NMT_TrackingStackDepth >= 3) { + pc[2] = (address)MallocSiteTable::allocation_at; + } + if (NMT_TrackingStackDepth >= 2) { + pc[1] = (address)MallocSiteTable::lookup_or_add; + } + pc[0] = (address)MallocSiteTable::new_entry; + + // Instantiate NativeCallStack object, have to use placement new operator. (see comments above) + NativeCallStack* stack = ::new ((void*)_hash_entry_allocation_stack) + NativeCallStack(pc, MIN2(((int)(sizeof(pc) / sizeof(address))), ((int)NMT_TrackingStackDepth))); + + // Instantiate hash entry for hashtable entry allocation callsite + MallocSiteHashtableEntry* entry = ::new ((void*)_hash_entry_allocation_site) + MallocSiteHashtableEntry(*stack); + + // Add the allocation site to hashtable. + int index = hash_to_index(stack->hash()); + _table[index] = entry; + + return true; +} + +// Walks entries in the hashtable. +// It stops walk if the walker returns false. +bool MallocSiteTable::walk(MallocSiteWalker* walker) { + MallocSiteHashtableEntry* head; + for (int index = 0; index < table_size; index ++) { + head = _table[index]; + while (head != NULL) { + if (!walker->do_malloc_site(head->peek())) { + return false; + } + head = (MallocSiteHashtableEntry*)head->next(); + } + } + return true; +} + +/* + * The hashtable does not have deletion policy on individual entry, + * and each linked list node is inserted via compare-and-swap, + * so each linked list is stable, the contention only happens + * at the end of linked list. + * This method should not return NULL under normal circumstance. + * If NULL is returned, it indicates: + * 1. Out of memory, it cannot allocate new hash entry. + * 2. Overflow hash bucket. + * Under any of above circumstances, caller should handle the situation. + */ +MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, size_t* bucket_idx, + size_t* pos_idx) { + int index = hash_to_index(key.hash()); + assert(index >= 0, "Negative index"); + *bucket_idx = (size_t)index; + *pos_idx = 0; + + // First entry for this hash bucket + if (_table[index] == NULL) { + MallocSiteHashtableEntry* entry = new_entry(key); + // OOM check + if (entry == NULL) return NULL; + + // swap in the head + if (Atomic::cmpxchg_ptr((void*)entry, (volatile void *)&_table[index], NULL) == NULL) { + return entry->data(); + } + + delete entry; + } + + MallocSiteHashtableEntry* head = _table[index]; + while (head != NULL && (*pos_idx) <= MAX_BUCKET_LENGTH) { + MallocSite* site = head->data(); + if (site->equals(key)) { + // found matched entry + return head->data(); + } + + if (head->next() == NULL && (*pos_idx) < MAX_BUCKET_LENGTH) { + MallocSiteHashtableEntry* entry = new_entry(key); + // OOM check + if (entry == NULL) return NULL; + if (head->atomic_insert(entry)) { + (*pos_idx) ++; + return entry->data(); + } + // contended, other thread won + delete entry; + } + head = (MallocSiteHashtableEntry*)head->next(); + (*pos_idx) ++; + } + return NULL; +} + +// Access malloc site +MallocSite* MallocSiteTable::malloc_site(size_t bucket_idx, size_t pos_idx) { + assert(bucket_idx < table_size, "Invalid bucket index"); + MallocSiteHashtableEntry* head = _table[bucket_idx]; + for (size_t index = 0; index < pos_idx && head != NULL; + index ++, head = (MallocSiteHashtableEntry*)head->next()); + assert(head != NULL, "Invalid position index"); + return head->data(); +} + +// Allocates MallocSiteHashtableEntry object. Special call stack +// (pre-installed allocation site) has to be used to avoid infinite +// recursion. +MallocSiteHashtableEntry* MallocSiteTable::new_entry(const NativeCallStack& key) { + void* p = AllocateHeap(sizeof(MallocSiteHashtableEntry), mtNMT, + *hash_entry_allocation_stack(), AllocFailStrategy::RETURN_NULL); + return ::new (p) MallocSiteHashtableEntry(key); +} + +void MallocSiteTable::reset() { + for (int index = 0; index < table_size; index ++) { + MallocSiteHashtableEntry* head = _table[index]; + _table[index] = NULL; + delete_linked_list(head); + } +} + +void MallocSiteTable::delete_linked_list(MallocSiteHashtableEntry* head) { + MallocSiteHashtableEntry* p; + while (head != NULL) { + p = head; + head = (MallocSiteHashtableEntry*)head->next(); + if (p != (MallocSiteHashtableEntry*)_hash_entry_allocation_site) { + delete p; + } + } +} + +void MallocSiteTable::shutdown() { + AccessLock locker(&_access_count); + locker.exclusiveLock(); + reset(); +} + +bool MallocSiteTable::walk_malloc_site(MallocSiteWalker* walker) { + assert(walker != NULL, "NuLL walker"); + AccessLock locker(&_access_count); + if (locker.sharedLock()) { + NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);) + return walk(walker); + } + return false; +} + + +void MallocSiteTable::AccessLock::exclusiveLock() { + jint target; + jint val; + + assert(_lock_state != ExclusiveLock, "Can only call once"); + assert(*_lock >= 0, "Can not content exclusive lock"); + + // make counter negative to block out shared locks + do { + val = *_lock; + target = _MAGIC_ + *_lock; + } while (Atomic::cmpxchg(target, _lock, val) != val); + + // wait for all readers to exit + while (*_lock != _MAGIC_) { +#ifdef _WINDOWS + os::naked_short_sleep(1); +#else + os::naked_yield(); +#endif + } + _lock_state = ExclusiveLock; +} + + diff --git a/hotspot/src/share/vm/services/mallocSiteTable.hpp b/hotspot/src/share/vm/services/mallocSiteTable.hpp new file mode 100644 index 00000000000..cff98161449 --- /dev/null +++ b/hotspot/src/share/vm/services/mallocSiteTable.hpp @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_SERVICES_MALLOC_SITE_TABLE_HPP +#define SHARE_VM_SERVICES_MALLOC_SITE_TABLE_HPP + +#if INCLUDE_NMT + +#include "memory/allocation.hpp" +#include "runtime/atomic.hpp" +#include "services/allocationSite.hpp" +#include "services/mallocTracker.hpp" +#include "services/nmtCommon.hpp" +#include "utilities/nativeCallStack.hpp" + +// MallocSite represents a code path that eventually calls +// os::malloc() to allocate memory +class MallocSite : public AllocationSite { + public: + MallocSite() : + AllocationSite(NativeCallStack::EMPTY_STACK) { } + + MallocSite(const NativeCallStack& stack) : + AllocationSite(stack) { } + + void allocate(size_t size) { data()->allocate(size); } + void deallocate(size_t size) { data()->deallocate(size); } + + // Memory allocated from this code path + size_t size() const { return peek()->size(); } + // The number of calls were made + size_t count() const { return peek()->count(); } +}; + +// Malloc site hashtable entry +class MallocSiteHashtableEntry : public CHeapObj { + private: + MallocSite _malloc_site; + MallocSiteHashtableEntry* _next; + + public: + MallocSiteHashtableEntry() : _next(NULL) { } + + MallocSiteHashtableEntry(NativeCallStack stack): + _malloc_site(stack), _next(NULL) { } + + inline const MallocSiteHashtableEntry* next() const { + return _next; + } + + // Insert an entry atomically. + // Return true if the entry is inserted successfully. + // The operation can be failed due to contention from other thread. + bool atomic_insert(const MallocSiteHashtableEntry* entry) { + return (Atomic::cmpxchg_ptr((void*)entry, (volatile void*)&_next, + NULL) == NULL); + } + + void set_callsite(const MallocSite& site) { + _malloc_site = site; + } + + inline const MallocSite* peek() const { return &_malloc_site; } + inline MallocSite* data() { return &_malloc_site; } + + inline long hash() const { return _malloc_site.hash(); } + inline bool equals(const NativeCallStack& stack) const { + return _malloc_site.equals(stack); + } + // Allocation/deallocation on this allocation site + inline void allocate(size_t size) { _malloc_site.allocate(size); } + inline void deallocate(size_t size) { _malloc_site.deallocate(size); } + // Memory counters + inline size_t size() const { return _malloc_site.size(); } + inline size_t count() const { return _malloc_site.count(); } +}; + +// The walker walks every entry on MallocSiteTable +class MallocSiteWalker : public StackObj { + public: + virtual bool do_malloc_site(const MallocSite* e) { return false; } +}; + +/* + * Native memory tracking call site table. + * The table is only needed when detail tracking is enabled. + */ +class MallocSiteTable : AllStatic { + private: + // The number of hash bucket in this hashtable. The number should + // be tuned if malloc activities changed significantly. + // The statistics data can be obtained via Jcmd + // jcmd VM.native_memory statistics. + + // Currently, (number of buckets / number of entires) ratio is + // about 1 / 6 + enum { + table_base_size = 128, // The base size is calculated from statistics to give + // table ratio around 1:6 + table_size = (table_base_size * NMT_TrackingStackDepth - 1) + }; + + + // This is a very special lock, that allows multiple shared accesses (sharedLock), but + // once exclusive access (exclusiveLock) is requested, all shared accesses are + // rejected forever. + class AccessLock : public StackObj { + enum LockState { + NoLock, + SharedLock, + ExclusiveLock + }; + + private: + // A very large negative number. The only possibility to "overflow" + // this number is when there are more than -min_jint threads in + // this process, which is not going to happen in foreseeable future. + const static int _MAGIC_ = min_jint; + + LockState _lock_state; + volatile int* _lock; + public: + AccessLock(volatile int* lock) : + _lock(lock), _lock_state(NoLock) { + } + + ~AccessLock() { + if (_lock_state == SharedLock) { + Atomic::dec((volatile jint*)_lock); + } + } + // Acquire shared lock. + // Return true if shared access is granted. + inline bool sharedLock() { + jint res = Atomic::add(1, _lock); + if (res < 0) { + Atomic::add(-1, _lock); + return false; + } + _lock_state = SharedLock; + return true; + } + // Acquire exclusive lock + void exclusiveLock(); + }; + + public: + static bool initialize(); + static void shutdown(); + + NOT_PRODUCT(static int access_peak_count() { return _peak_count; }) + + // Number of hash buckets + static inline int hash_buckets() { return (int)table_size; } + + // Access and copy a call stack from this table. Shared lock should be + // acquired before access the entry. + static inline bool access_stack(NativeCallStack& stack, size_t bucket_idx, + size_t pos_idx) { + AccessLock locker(&_access_count); + if (locker.sharedLock()) { + NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);) + MallocSite* site = malloc_site(bucket_idx, pos_idx); + if (site != NULL) { + stack = *site->call_stack(); + return true; + } + } + return false; + } + + // Record a new allocation from specified call path. + // Return true if the allocation is recorded successfully, bucket_idx + // and pos_idx are also updated to indicate the entry where the allocation + // information was recorded. + // Return false only occurs under rare scenarios: + // 1. out of memory + // 2. overflow hash bucket + static inline bool allocation_at(const NativeCallStack& stack, size_t size, + size_t* bucket_idx, size_t* pos_idx) { + AccessLock locker(&_access_count); + if (locker.sharedLock()) { + NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);) + MallocSite* site = lookup_or_add(stack, bucket_idx, pos_idx); + if (site != NULL) site->allocate(size); + return site != NULL; + } + return false; + } + + // Record memory deallocation. bucket_idx and pos_idx indicate where the allocation + // information was recorded. + static inline bool deallocation_at(size_t size, size_t bucket_idx, size_t pos_idx) { + AccessLock locker(&_access_count); + if (locker.sharedLock()) { + NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);) + MallocSite* site = malloc_site(bucket_idx, pos_idx); + if (site != NULL) { + site->deallocate(size); + return true; + } + } + return false; + } + + // Walk this table. + static bool walk_malloc_site(MallocSiteWalker* walker); + + private: + static MallocSiteHashtableEntry* new_entry(const NativeCallStack& key); + static void reset(); + + // Delete a bucket linked list + static void delete_linked_list(MallocSiteHashtableEntry* head); + + static MallocSite* lookup_or_add(const NativeCallStack& key, size_t* bucket_idx, size_t* pos_idx); + static MallocSite* malloc_site(size_t bucket_idx, size_t pos_idx); + static bool walk(MallocSiteWalker* walker); + + static inline int hash_to_index(int hash) { + hash = (hash > 0) ? hash : (-hash); + return (hash % table_size); + } + + static inline const NativeCallStack* hash_entry_allocation_stack() { + return (NativeCallStack*)_hash_entry_allocation_stack; + } + + private: + // Counter for counting concurrent access + static volatile int _access_count; + + // The callsite hashtable. It has to be a static table, + // since malloc call can come from C runtime linker. + static MallocSiteHashtableEntry* _table[table_size]; + + + // Reserve enough memory for placing the objects + + // The memory for hashtable entry allocation stack object + static size_t _hash_entry_allocation_stack[CALC_OBJ_SIZE_IN_TYPE(NativeCallStack, size_t)]; + // The memory for hashtable entry allocation callsite object + static size_t _hash_entry_allocation_site[CALC_OBJ_SIZE_IN_TYPE(MallocSiteHashtableEntry, size_t)]; + NOT_PRODUCT(static int _peak_count;) +}; + +#endif // INCLUDE_NMT +#endif // SHARE_VM_SERVICES_MALLOC_SITE_TABLE_HPP diff --git a/hotspot/src/share/vm/services/mallocTracker.cpp b/hotspot/src/share/vm/services/mallocTracker.cpp new file mode 100644 index 00000000000..d60dd383576 --- /dev/null +++ b/hotspot/src/share/vm/services/mallocTracker.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#include "precompiled.hpp" + +#include "runtime/atomic.hpp" +#include "runtime/atomic.inline.hpp" +#include "services/mallocSiteTable.hpp" +#include "services/mallocTracker.hpp" +#include "services/mallocTracker.inline.hpp" +#include "services/memTracker.hpp" + +size_t MallocMemorySummary::_snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)]; + +// Total malloc'd memory amount +size_t MallocMemorySnapshot::total() const { + size_t amount = 0; + for (int index = 0; index < mt_number_of_types; index ++) { + amount += _malloc[index].malloc_size(); + } + amount += _tracking_header.size() + total_arena(); + return amount; +} + +// Total malloc'd memory used by arenas +size_t MallocMemorySnapshot::total_arena() const { + size_t amount = 0; + for (int index = 0; index < mt_number_of_types; index ++) { + amount += _malloc[index].arena_size(); + } + return amount; +} + + +void MallocMemorySnapshot::reset() { + _tracking_header.reset(); + for (int index = 0; index < mt_number_of_types; index ++) { + _malloc[index].reset(); + } +} + +// Make adjustment by subtracting chunks used by arenas +// from total chunks to get total free chunck size +void MallocMemorySnapshot::make_adjustment() { + size_t arena_size = total_arena(); + int chunk_idx = NMTUtil::flag_to_index(mtChunk); + _malloc[chunk_idx].record_free(arena_size); +} + + +void MallocMemorySummary::initialize() { + assert(sizeof(_snapshot) >= sizeof(MallocMemorySnapshot), "Sanity Check"); + // Uses placement new operator to initialize static area. + ::new ((void*)_snapshot)MallocMemorySnapshot(); +} + +void MallocHeader::release() const { + // Tracking already shutdown, no housekeeping is needed anymore + if (MemTracker::tracking_level() <= NMT_minimal) return; + + MallocMemorySummary::record_free(size(), flags()); + MallocMemorySummary::record_free_malloc_header(sizeof(MallocHeader)); + if (tracking_level() == NMT_detail) { + MallocSiteTable::deallocation_at(size(), _bucket_idx, _pos_idx); + } +} + +bool MallocHeader::record_malloc_site(const NativeCallStack& stack, size_t size, + size_t* bucket_idx, size_t* pos_idx) const { + bool ret = MallocSiteTable::allocation_at(stack, size, bucket_idx, pos_idx); + + // Something went wrong, could be OOM or overflow malloc site table. + // We want to keep tracking data under OOM circumstance, so transition to + // summary tracking. + if (!ret) { + MemTracker::transition_to(NMT_summary); + } + return ret; +} + +bool MallocHeader::get_stack(NativeCallStack& stack) const { + return MallocSiteTable::access_stack(stack, _bucket_idx, _pos_idx); +} + +bool MallocTracker::initialize(NMT_TrackingLevel level) { + if (level >= NMT_summary) { + MallocMemorySummary::initialize(); + } + + if (level == NMT_detail) { + return MallocSiteTable::initialize(); + } + return true; +} + +bool MallocTracker::transition(NMT_TrackingLevel from, NMT_TrackingLevel to) { + assert(from != NMT_off, "Can not transition from off state"); + assert(to != NMT_off, "Can not transition to off state"); + if (from == NMT_minimal) { + MallocMemorySummary::reset(); + } + + if (to == NMT_detail) { + assert(from == NMT_minimal || from == NMT_summary, "Just check"); + return MallocSiteTable::initialize(); + } else if (from == NMT_detail) { + assert(to == NMT_minimal || to == NMT_summary, "Just check"); + MallocSiteTable::shutdown(); + } + return true; +} + +// Record a malloc memory allocation +void* MallocTracker::record_malloc(void* malloc_base, size_t size, MEMFLAGS flags, + const NativeCallStack& stack, NMT_TrackingLevel level) { + void* memblock; // the address for user data + MallocHeader* header = NULL; + + if (malloc_base == NULL) { + return NULL; + } + + // Check malloc size, size has to <= MAX_MALLOC_SIZE. This is only possible on 32-bit + // systems, when malloc size >= 1GB, but is is safe to assume it won't happen. + if (size > MAX_MALLOC_SIZE) { + fatal("Should not use malloc for big memory block, use virtual memory instead"); + } + // Uses placement global new operator to initialize malloc header + switch(level) { + case NMT_off: + return malloc_base; + case NMT_minimal: { + MallocHeader* hdr = ::new (malloc_base) MallocHeader(); + break; + } + case NMT_summary: { + header = ::new (malloc_base) MallocHeader(size, flags); + break; + } + case NMT_detail: { + header = ::new (malloc_base) MallocHeader(size, flags, stack); + break; + } + default: + ShouldNotReachHere(); + } + memblock = (void*)((char*)malloc_base + sizeof(MallocHeader)); + + // The alignment check: 8 bytes alignment for 32 bit systems. + // 16 bytes alignment for 64-bit systems. + assert(((size_t)memblock & (sizeof(size_t) * 2 - 1)) == 0, "Alignment check"); + + // Sanity check + assert(get_memory_tracking_level(memblock) == level, + "Wrong tracking level"); + +#ifdef ASSERT + if (level > NMT_minimal) { + // Read back + assert(get_size(memblock) == size, "Wrong size"); + assert(get_flags(memblock) == flags, "Wrong flags"); + } +#endif + + return memblock; +} + +void* MallocTracker::record_free(void* memblock) { + // Never turned on + if (MemTracker::tracking_level() == NMT_off || + memblock == NULL) { + return memblock; + } + MallocHeader* header = malloc_header(memblock); + header->release(); + + return (void*)header; +} + + diff --git a/hotspot/src/share/vm/services/mallocTracker.hpp b/hotspot/src/share/vm/services/mallocTracker.hpp new file mode 100644 index 00000000000..17c58529482 --- /dev/null +++ b/hotspot/src/share/vm/services/mallocTracker.hpp @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_SERVICES_MALLOC_TRACKER_HPP +#define SHARE_VM_SERVICES_MALLOC_TRACKER_HPP + +#if INCLUDE_NMT + +#include "memory/allocation.hpp" +#include "runtime/atomic.hpp" +#include "services/nmtCommon.hpp" +#include "utilities/nativeCallStack.hpp" + +/* + * This counter class counts memory allocation and deallocation, + * records total memory allocation size and number of allocations. + * The counters are updated atomically. + */ +class MemoryCounter VALUE_OBJ_CLASS_SPEC { + private: + size_t _count; + size_t _size; + + DEBUG_ONLY(size_t _peak_count;) + DEBUG_ONLY(size_t _peak_size; ) + + public: + MemoryCounter() : _count(0), _size(0) { + DEBUG_ONLY(_peak_count = 0;) + DEBUG_ONLY(_peak_size = 0;) + } + + // Reset counters + void reset() { + _size = 0; + _count = 0; + DEBUG_ONLY(_peak_size = 0;) + DEBUG_ONLY(_peak_count = 0;) + } + + inline void allocate(size_t sz) { + Atomic::add(1, (volatile MemoryCounterType*)&_count); + if (sz > 0) { + Atomic::add((MemoryCounterType)sz, (volatile MemoryCounterType*)&_size); + DEBUG_ONLY(_peak_size = MAX2(_peak_size, _size)); + } + DEBUG_ONLY(_peak_count = MAX2(_peak_count, _count);) + } + + inline void deallocate(size_t sz) { + assert(_count > 0, "Negative counter"); + assert(_size >= sz, "Negative size"); + Atomic::add(-1, (volatile MemoryCounterType*)&_count); + if (sz > 0) { + Atomic::add(-(MemoryCounterType)sz, (volatile MemoryCounterType*)&_size); + } + } + + inline void resize(long sz) { + if (sz != 0) { + Atomic::add((MemoryCounterType)sz, (volatile MemoryCounterType*)&_size); + DEBUG_ONLY(_peak_size = MAX2(_size, _peak_size);) + } + } + + inline size_t count() const { return _count; } + inline size_t size() const { return _size; } + DEBUG_ONLY(inline size_t peak_count() const { return _peak_count; }) + DEBUG_ONLY(inline size_t peak_size() const { return _peak_size; }) + +}; + +/* + * Malloc memory used by a particular subsystem. + * It includes the memory acquired through os::malloc() + * call and arena's backing memory. + */ +class MallocMemory VALUE_OBJ_CLASS_SPEC { + private: + MemoryCounter _malloc; + MemoryCounter _arena; + + public: + MallocMemory() { } + + inline void record_malloc(size_t sz) { + _malloc.allocate(sz); + } + + inline void record_free(size_t sz) { + _malloc.deallocate(sz); + } + + inline void record_new_arena() { + _arena.allocate(0); + } + + inline void record_arena_free() { + _arena.deallocate(0); + } + + inline void record_arena_size_change(long sz) { + _arena.resize(sz); + } + + void reset() { + _malloc.reset(); + _arena.reset(); + } + + inline size_t malloc_size() const { return _malloc.size(); } + inline size_t malloc_count() const { return _malloc.count();} + inline size_t arena_size() const { return _arena.size(); } + inline size_t arena_count() const { return _arena.count(); } + + DEBUG_ONLY(inline const MemoryCounter& malloc_counter() const { return _malloc; }) + DEBUG_ONLY(inline const MemoryCounter& arena_counter() const { return _arena; }) +}; + +class MallocMemorySummary; + +// A snapshot of malloc'd memory, includes malloc memory +// usage by types and memory used by tracking itself. +class MallocMemorySnapshot : public ResourceObj { + friend class MallocMemorySummary; + + private: + MallocMemory _malloc[mt_number_of_types]; + MemoryCounter _tracking_header; + + + public: + inline MallocMemory* by_type(MEMFLAGS flags) { + int index = NMTUtil::flag_to_index(flags); + return &_malloc[index]; + } + + inline MallocMemory* by_index(int index) { + assert(index >= 0, "Index out of bound"); + assert(index < mt_number_of_types, "Index out of bound"); + return &_malloc[index]; + } + + inline MemoryCounter* malloc_overhead() { + return &_tracking_header; + } + + // Total malloc'd memory amount + size_t total() const; + // Total malloc'd memory used by arenas + size_t total_arena() const; + + inline size_t thread_count() const { + MallocMemorySnapshot* s = const_cast(this); + return s->by_type(mtThreadStack)->malloc_count(); + } + + void reset(); + + void copy_to(MallocMemorySnapshot* s) { + s->_tracking_header = _tracking_header; + for (int index = 0; index < mt_number_of_types; index ++) { + s->_malloc[index] = _malloc[index]; + } + } + + // Make adjustment by subtracting chunks used by arenas + // from total chunks to get total free chunk size + void make_adjustment(); +}; + +/* + * This class is for collecting malloc statistics at summary level + */ +class MallocMemorySummary : AllStatic { + private: + // Reserve memory for placement of MallocMemorySnapshot object + static size_t _snapshot[CALC_OBJ_SIZE_IN_TYPE(MallocMemorySnapshot, size_t)]; + + public: + static void initialize(); + + static inline void record_malloc(size_t size, MEMFLAGS flag) { + as_snapshot()->by_type(flag)->record_malloc(size); + } + + static inline void record_free(size_t size, MEMFLAGS flag) { + as_snapshot()->by_type(flag)->record_free(size); + } + + static inline void record_new_arena(MEMFLAGS flag) { + as_snapshot()->by_type(flag)->record_new_arena(); + } + + static inline void record_arena_free(MEMFLAGS flag) { + as_snapshot()->by_type(flag)->record_arena_free(); + } + + static inline void record_arena_size_change(long size, MEMFLAGS flag) { + as_snapshot()->by_type(flag)->record_arena_size_change(size); + } + + static void snapshot(MallocMemorySnapshot* s) { + as_snapshot()->copy_to(s); + s->make_adjustment(); + } + + // Record memory used by malloc tracking header + static inline void record_new_malloc_header(size_t sz) { + as_snapshot()->malloc_overhead()->allocate(sz); + } + + static inline void record_free_malloc_header(size_t sz) { + as_snapshot()->malloc_overhead()->deallocate(sz); + } + + // The memory used by malloc tracking headers + static inline size_t tracking_overhead() { + return as_snapshot()->malloc_overhead()->size(); + } + + // Reset all counters to zero + static void reset() { + as_snapshot()->reset(); + } + + static MallocMemorySnapshot* as_snapshot() { + return (MallocMemorySnapshot*)_snapshot; + } +}; + + +/* + * Malloc tracking header. + * To satisfy malloc alignment requirement, NMT uses 2 machine words for tracking purpose, + * which ensures 8-bytes alignment on 32-bit systems and 16-bytes on 64-bit systems (Product build). + */ + +class MallocHeader VALUE_OBJ_CLASS_SPEC { +#ifdef _LP64 + size_t _size : 62; + size_t _level : 2; + size_t _flags : 8; + size_t _pos_idx : 16; + size_t _bucket_idx: 40; +#define MAX_MALLOCSITE_TABLE_SIZE ((size_t)1 << 40) +#define MAX_BUCKET_LENGTH ((size_t)(1 << 16)) +#define MAX_MALLOC_SIZE (((size_t)1 << 62) - 1) +#else + size_t _size : 30; + size_t _level : 2; + size_t _flags : 8; + size_t _pos_idx : 8; + size_t _bucket_idx: 16; +#define MAX_MALLOCSITE_TABLE_SIZE ((size_t)(1 << 16)) +#define MAX_BUCKET_LENGTH ((size_t)(1 << 8)) +// Max malloc size = 1GB - 1 on 32 bit system, such has total 4GB memory +#define MAX_MALLOC_SIZE ((size_t)(1 << 30) - 1) +#endif // _LP64 + + public: + // Summary tracking header + MallocHeader(size_t size, MEMFLAGS flags) { + assert(sizeof(MallocHeader) == sizeof(void*) * 2, + "Wrong header size"); + + _level = NMT_summary; + _flags = flags; + set_size(size); + MallocMemorySummary::record_malloc(size, flags); + MallocMemorySummary::record_new_malloc_header(sizeof(MallocHeader)); + } + // Detail tracking header + MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack) { + assert(sizeof(MallocHeader) == sizeof(void*) * 2, + "Wrong header size"); + + _level = NMT_detail; + _flags = flags; + set_size(size); + size_t bucket_idx; + size_t pos_idx; + if (record_malloc_site(stack, size, &bucket_idx, &pos_idx)) { + assert(bucket_idx <= MAX_MALLOCSITE_TABLE_SIZE, "Overflow bucket index"); + assert(pos_idx <= MAX_BUCKET_LENGTH, "Overflow bucket position index"); + _bucket_idx = bucket_idx; + _pos_idx = pos_idx; + } + MallocMemorySummary::record_malloc(size, flags); + MallocMemorySummary::record_new_malloc_header(sizeof(MallocHeader)); + } + // Minimal tracking header + MallocHeader() { + assert(sizeof(MallocHeader) == sizeof(void*) * 2, + "Wrong header size"); + + _level = (unsigned short)NMT_minimal; + } + + inline NMT_TrackingLevel tracking_level() const { + return (NMT_TrackingLevel)_level; + } + + inline size_t size() const { return _size; } + inline MEMFLAGS flags() const { return (MEMFLAGS)_flags; } + bool get_stack(NativeCallStack& stack) const; + + // Cleanup tracking information before the memory is released. + void release() const; + + private: + inline void set_size(size_t size) { + assert(size <= MAX_MALLOC_SIZE, "Malloc size too large, should use virtual memory?"); + _size = size; + } + bool record_malloc_site(const NativeCallStack& stack, size_t size, + size_t* bucket_idx, size_t* pos_idx) const; +}; + + +// Main class called from MemTracker to track malloc activities +class MallocTracker : AllStatic { + public: + // Initialize malloc tracker for specific tracking level + static bool initialize(NMT_TrackingLevel level); + + static bool transition(NMT_TrackingLevel from, NMT_TrackingLevel to); + + // malloc tracking header size for specific tracking level + static inline size_t malloc_header_size(NMT_TrackingLevel level) { + return (level == NMT_off) ? 0 : sizeof(MallocHeader); + } + + // Parameter name convention: + // memblock : the beginning address for user data + // malloc_base: the beginning address that includes malloc tracking header + // + // The relationship: + // memblock = (char*)malloc_base + sizeof(nmt header) + // + + // Record malloc on specified memory block + static void* record_malloc(void* malloc_base, size_t size, MEMFLAGS flags, + const NativeCallStack& stack, NMT_TrackingLevel level); + + // Record free on specified memory block + static void* record_free(void* memblock); + + // Get tracking level of specified memory block + static inline NMT_TrackingLevel get_memory_tracking_level(void* memblock); + + + // Offset memory address to header address + static inline void* get_base(void* memblock); + static inline void* get_base(void* memblock, NMT_TrackingLevel level) { + if (memblock == NULL || level == NMT_off) return memblock; + return (char*)memblock - malloc_header_size(level); + } + + // Get memory size + static inline size_t get_size(void* memblock) { + MallocHeader* header = malloc_header(memblock); + assert(header->tracking_level() >= NMT_summary, + "Wrong tracking level"); + return header->size(); + } + + // Get memory type + static inline MEMFLAGS get_flags(void* memblock) { + MallocHeader* header = malloc_header(memblock); + assert(header->tracking_level() >= NMT_summary, + "Wrong tracking level"); + return header->flags(); + } + + // Get header size + static inline size_t get_header_size(void* memblock) { + return (memblock == NULL) ? 0 : sizeof(MallocHeader); + } + + static inline void record_new_arena(MEMFLAGS flags) { + MallocMemorySummary::record_new_arena(flags); + } + + static inline void record_arena_free(MEMFLAGS flags) { + MallocMemorySummary::record_arena_free(flags); + } + + static inline void record_arena_size_change(int size, MEMFLAGS flags) { + MallocMemorySummary::record_arena_size_change(size, flags); + } + private: + static inline MallocHeader* malloc_header(void *memblock) { + assert(memblock != NULL, "NULL pointer"); + MallocHeader* header = (MallocHeader*)((char*)memblock - sizeof(MallocHeader)); + assert(header->tracking_level() >= NMT_minimal, "Bad header"); + return header; + } +}; + +#endif // INCLUDE_NMT + + +#endif //SHARE_VM_SERVICES_MALLOC_TRACKER_HPP diff --git a/hotspot/src/share/vm/services/mallocTracker.inline.hpp b/hotspot/src/share/vm/services/mallocTracker.inline.hpp new file mode 100644 index 00000000000..338295b6566 --- /dev/null +++ b/hotspot/src/share/vm/services/mallocTracker.inline.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_SERVICES_MALLOC_TRACKER_INLINE_HPP +#define SHARE_VM_SERVICES_MALLOC_TRACKER_INLINE_HPP + +#include "services/mallocTracker.hpp" +#include "services/memTracker.hpp" + +inline NMT_TrackingLevel MallocTracker::get_memory_tracking_level(void* memblock) { + assert(memblock != NULL, "Sanity check"); + if (MemTracker::tracking_level() == NMT_off) return NMT_off; + MallocHeader* header = malloc_header(memblock); + return header->tracking_level(); +} + +inline void* MallocTracker::get_base(void* memblock){ + return get_base(memblock, MemTracker::tracking_level()); +} + +#endif // SHARE_VM_SERVICES_MALLOC_TRACKER_INLINE_HPP + diff --git a/hotspot/src/share/vm/services/management.cpp b/hotspot/src/share/vm/services/management.cpp index 2d6f2ff1d23..53096acc584 100644 --- a/hotspot/src/share/vm/services/management.cpp +++ b/hotspot/src/share/vm/services/management.cpp @@ -1696,6 +1696,9 @@ bool add_global_entry(JNIEnv* env, Handle name, jmmVMGlobal *global, Flag *flag, } else if (flag->is_uint64_t()) { global->value.j = (jlong)flag->get_uint64_t(); global->type = JMM_VMGLOBAL_TYPE_JLONG; + } else if (flag->is_size_t()) { + global->value.j = (jlong)flag->get_size_t(); + global->type = JMM_VMGLOBAL_TYPE_JLONG; } else if (flag->is_ccstr()) { Handle str = java_lang_String::create_from_str(flag->get_ccstr(), CHECK_false); global->value.l = (jobject)JNIHandles::make_local(env, str()); @@ -1851,6 +1854,9 @@ JVM_ENTRY(void, jmm_SetVMGlobal(JNIEnv *env, jstring flag_name, jvalue new_value } else if (flag->is_uint64_t()) { uint64_t uvalue = (uint64_t)new_value.j; succeed = CommandLineFlags::uint64_tAtPut(name, &uvalue, Flag::MANAGEMENT); + } else if (flag->is_size_t()) { + size_t svalue = (size_t)new_value.j; + succeed = CommandLineFlags::size_tAtPut(name, &svalue, Flag::MANAGEMENT); } else if (flag->is_ccstr()) { oop str = JNIHandles::resolve_external_guard(new_value.l); if (str == NULL) { @@ -1914,7 +1920,7 @@ void ThreadTimesClosure::do_thread(Thread* thread) { ResourceMark rm(THREAD); // thread->name() uses ResourceArea assert(thread->name() != NULL, "All threads should have a name"); - _names_chars[_count] = strdup(thread->name()); + _names_chars[_count] = os::strdup(thread->name()); _times->long_at_put(_count, os::is_thread_cpu_time_supported() ? os::thread_cpu_time(thread) : -1); _count++; @@ -1932,7 +1938,7 @@ void ThreadTimesClosure::do_unlocked() { ThreadTimesClosure::~ThreadTimesClosure() { for (int i = 0; i < _count; i++) { - free(_names_chars[i]); + os::free(_names_chars[i]); } FREE_C_HEAP_ARRAY(char *, _names_chars, mtInternal); } diff --git a/hotspot/src/share/vm/services/memBaseline.cpp b/hotspot/src/share/vm/services/memBaseline.cpp index 62e51873c7b..02b4924760b 100644 --- a/hotspot/src/share/vm/services/memBaseline.cpp +++ b/hotspot/src/share/vm/services/memBaseline.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,471 +22,279 @@ * */ #include "precompiled.hpp" + #include "memory/allocation.hpp" #include "runtime/safepoint.hpp" #include "runtime/thread.inline.hpp" #include "services/memBaseline.hpp" #include "services/memTracker.hpp" +/* + * Sizes are sorted in descenting order for reporting + */ +int compare_malloc_size(const MallocSite& s1, const MallocSite& s2) { + if (s1.size() == s2.size()) { + return 0; + } else if (s1.size() > s2.size()) { + return -1; + } else { + return 1; + } +} -MemType2Name MemBaseline::MemType2NameMap[NUMBER_OF_MEMORY_TYPE] = { - {mtJavaHeap, "Java Heap"}, - {mtClass, "Class"}, - {mtThreadStack,"Thread Stack"}, - {mtThread, "Thread"}, - {mtCode, "Code"}, - {mtGC, "GC"}, - {mtCompiler, "Compiler"}, - {mtInternal, "Internal"}, - {mtOther, "Other"}, - {mtSymbol, "Symbol"}, - {mtNMT, "Memory Tracking"}, - {mtTracing, "Tracing"}, - {mtChunk, "Pooled Free Chunks"}, - {mtClassShared,"Shared spaces for classes"}, - {mtTest, "Test"}, - {mtNone, "Unknown"} // It can happen when type tagging records are lagging - // behind + +int compare_virtual_memory_size(const VirtualMemoryAllocationSite& s1, + const VirtualMemoryAllocationSite& s2) { + if (s1.reserved() == s2.reserved()) { + return 0; + } else if (s1.reserved() > s2.reserved()) { + return -1; + } else { + return 1; + } +} + +// Sort into allocation site addresses order for baseline comparison +int compare_malloc_site(const MallocSite& s1, const MallocSite& s2) { + return s1.call_stack()->compare(*s2.call_stack()); +} + + +int compare_virtual_memory_site(const VirtualMemoryAllocationSite& s1, + const VirtualMemoryAllocationSite& s2) { + return s1.call_stack()->compare(*s2.call_stack()); +} + +/* + * Walker to walk malloc allocation site table + */ +class MallocAllocationSiteWalker : public MallocSiteWalker { + private: + SortedLinkedList _malloc_sites; + size_t _count; + + // Entries in MallocSiteTable with size = 0 and count = 0, + // when the malloc site is not longer there. + public: + MallocAllocationSiteWalker() : _count(0) { } + + inline size_t count() const { return _count; } + + LinkedList* malloc_sites() { + return &_malloc_sites; + } + + bool do_malloc_site(const MallocSite* site) { + if (site->size() >= MemBaseline::SIZE_THRESHOLD) { + if (_malloc_sites.add(*site) != NULL) { + _count++; + return true; + } else { + return false; // OOM + } + } else { + // malloc site does not meet threshold, ignore and continue + return true; + } + } }; -MemBaseline::MemBaseline() { - _baselined = false; - - for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) { - _malloc_data[index].set_type(MemType2NameMap[index]._flag); - _vm_data[index].set_type(MemType2NameMap[index]._flag); - _arena_data[index].set_type(MemType2NameMap[index]._flag); - } - - _malloc_cs = NULL; - _vm_cs = NULL; - _vm_map = NULL; - - _number_of_classes = 0; - _number_of_threads = 0; +// Compare virtual memory region's base address +int compare_virtual_memory_base(const ReservedMemoryRegion& r1, const ReservedMemoryRegion& r2) { + return r1.compare(r2); } +// Walk all virtual memory regions for baselining +class VirtualMemoryAllocationWalker : public VirtualMemoryWalker { + private: + SortedLinkedList + _virtual_memory_regions; + size_t _count; -void MemBaseline::clear() { - if (_malloc_cs != NULL) { - delete _malloc_cs; - _malloc_cs = NULL; - } + public: + VirtualMemoryAllocationWalker() : _count(0) { } - if (_vm_cs != NULL) { - delete _vm_cs; - _vm_cs = NULL; - } - - if (_vm_map != NULL) { - delete _vm_map; - _vm_map = NULL; - } - - reset(); -} - - -void MemBaseline::reset() { - _baselined = false; - _total_vm_reserved = 0; - _total_vm_committed = 0; - _total_malloced = 0; - _number_of_classes = 0; - - if (_malloc_cs != NULL) _malloc_cs->clear(); - if (_vm_cs != NULL) _vm_cs->clear(); - if (_vm_map != NULL) _vm_map->clear(); - - for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) { - _malloc_data[index].clear(); - _vm_data[index].clear(); - _arena_data[index].clear(); - } -} - -MemBaseline::~MemBaseline() { - clear(); -} - -// baseline malloc'd memory records, generate overall summary and summaries by -// memory types -bool MemBaseline::baseline_malloc_summary(const MemPointerArray* malloc_records) { - MemPointerArrayIteratorImpl malloc_itr((MemPointerArray*)malloc_records); - MemPointerRecord* malloc_ptr = (MemPointerRecord*)malloc_itr.current(); - size_t used_arena_size = 0; - int index; - while (malloc_ptr != NULL) { - index = flag2index(FLAGS_TO_MEMORY_TYPE(malloc_ptr->flags())); - size_t size = malloc_ptr->size(); - if (malloc_ptr->is_arena_memory_record()) { - // We do have anonymous arenas, they are either used as value objects, - // which are embedded inside other objects, or used as stack objects. - _arena_data[index].inc(size); - used_arena_size += size; - } else { - _total_malloced += size; - _malloc_data[index].inc(size); - if (malloc_ptr->is_arena_record()) { - // see if arena memory record present - MemPointerRecord* next_malloc_ptr = (MemPointerRecordEx*)malloc_itr.peek_next(); - if (next_malloc_ptr != NULL && next_malloc_ptr->is_arena_memory_record()) { - assert(next_malloc_ptr->is_memory_record_of_arena(malloc_ptr), - "Arena records do not match"); - size = next_malloc_ptr->size(); - _arena_data[index].inc(size); - used_arena_size += size; - malloc_itr.next(); - } - } - } - malloc_ptr = (MemPointerRecordEx*)malloc_itr.next(); - } - - // substract used arena size to get size of arena chunk in free list - index = flag2index(mtChunk); - _malloc_data[index].reduce(used_arena_size); - // we really don't know how many chunks in free list, so just set to - // 0 - _malloc_data[index].overwrite_counter(0); - - return true; -} - -// check if there is a safepoint in progress, if so, block the thread -// for the safepoint -void MemBaseline::check_safepoint(JavaThread* thr) { - if (SafepointSynchronize::is_synchronizing()) { - // grab and drop the SR_lock to honor the safepoint protocol - MutexLocker ml(thr->SR_lock()); - } -} - -// baseline mmap'd memory records, generate overall summary and summaries by -// memory types -bool MemBaseline::baseline_vm_summary(const MemPointerArray* vm_records) { - MemPointerArrayIteratorImpl vm_itr((MemPointerArray*)vm_records); - VMMemRegion* vm_ptr = (VMMemRegion*)vm_itr.current(); - int index; - while (vm_ptr != NULL) { - if (vm_ptr->is_reserved_region()) { - index = flag2index(FLAGS_TO_MEMORY_TYPE(vm_ptr->flags())); - // we use the number of thread stack to count threads - if (IS_MEMORY_TYPE(vm_ptr->flags(), mtThreadStack)) { - _number_of_threads ++; - } - _total_vm_reserved += vm_ptr->size(); - _vm_data[index].inc(vm_ptr->size(), 0); - } else { - _total_vm_committed += vm_ptr->size(); - _vm_data[index].inc(0, vm_ptr->size()); - } - vm_ptr = (VMMemRegion*)vm_itr.next(); - } - return true; -} - -// baseline malloc'd memory by callsites, but only the callsites with memory allocation -// over 1KB are stored. -bool MemBaseline::baseline_malloc_details(const MemPointerArray* malloc_records) { - assert(MemTracker::track_callsite(), "detail tracking is off"); - - MemPointerArrayIteratorImpl malloc_itr(const_cast(malloc_records)); - MemPointerRecordEx* malloc_ptr = (MemPointerRecordEx*)malloc_itr.current(); - MallocCallsitePointer malloc_callsite; - - // initailize malloc callsite array - if (_malloc_cs == NULL) { - _malloc_cs = new (std::nothrow) MemPointerArrayImpl(64); - // out of native memory - if (_malloc_cs == NULL || _malloc_cs->out_of_memory()) { - return false; - } - } else { - _malloc_cs->clear(); - } - - MemPointerArray* malloc_data = const_cast(malloc_records); - - // sort into callsite pc order. Details are aggregated by callsites - malloc_data->sort((FN_SORT)malloc_sort_by_pc); - bool ret = true; - - // baseline memory that is totaled over 1 KB - while (malloc_ptr != NULL) { - if (!MemPointerRecord::is_arena_memory_record(malloc_ptr->flags())) { - // skip thread stacks - if (!IS_MEMORY_TYPE(malloc_ptr->flags(), mtThreadStack)) { - if (malloc_callsite.addr() != malloc_ptr->pc()) { - if ((malloc_callsite.amount()/K) > 0) { - if (!_malloc_cs->append(&malloc_callsite)) { - ret = false; - break; - } - } - malloc_callsite = MallocCallsitePointer(malloc_ptr->pc()); - } - malloc_callsite.inc(malloc_ptr->size()); - } - } - malloc_ptr = (MemPointerRecordEx*)malloc_itr.next(); - } - - // restore to address order. Snapshot malloc data is maintained in memory - // address order. - malloc_data->sort((FN_SORT)malloc_sort_by_addr); - - if (!ret) { - return false; - } - // deal with last record - if (malloc_callsite.addr() != 0 && (malloc_callsite.amount()/K) > 0) { - if (!_malloc_cs->append(&malloc_callsite)) { - return false; - } - } - return true; -} - -// baseline mmap'd memory by callsites -bool MemBaseline::baseline_vm_details(const MemPointerArray* vm_records) { - assert(MemTracker::track_callsite(), "detail tracking is off"); - - VMCallsitePointer vm_callsite; - VMCallsitePointer* cur_callsite = NULL; - MemPointerArrayIteratorImpl vm_itr((MemPointerArray*)vm_records); - VMMemRegionEx* vm_ptr = (VMMemRegionEx*)vm_itr.current(); - - // initialize virtual memory map array - if (_vm_map == NULL) { - _vm_map = new (std::nothrow) MemPointerArrayImpl(vm_records->length()); - if (_vm_map == NULL || _vm_map->out_of_memory()) { - return false; - } - } else { - _vm_map->clear(); - } - - // initialize virtual memory callsite array - if (_vm_cs == NULL) { - _vm_cs = new (std::nothrow) MemPointerArrayImpl(64); - if (_vm_cs == NULL || _vm_cs->out_of_memory()) { - return false; - } - } else { - _vm_cs->clear(); - } - - // consolidate virtual memory data - VMMemRegionEx* reserved_rec = NULL; - VMMemRegionEx* committed_rec = NULL; - - // vm_ptr is coming in increasing base address order - while (vm_ptr != NULL) { - if (vm_ptr->is_reserved_region()) { - // consolidate reserved memory regions for virtual memory map. - // The criteria for consolidation is: - // 1. two adjacent reserved memory regions - // 2. belong to the same memory type - // 3. reserved from the same callsite - if (reserved_rec == NULL || - reserved_rec->base() + reserved_rec->size() != vm_ptr->addr() || - FLAGS_TO_MEMORY_TYPE(reserved_rec->flags()) != FLAGS_TO_MEMORY_TYPE(vm_ptr->flags()) || - reserved_rec->pc() != vm_ptr->pc()) { - if (!_vm_map->append(vm_ptr)) { + bool do_allocation_site(const ReservedMemoryRegion* rgn) { + if (rgn->size() >= MemBaseline::SIZE_THRESHOLD) { + if (_virtual_memory_regions.add(*rgn) != NULL) { + _count ++; + return true; + } else { return false; } - // inserted reserved region, we need the pointer to the element in virtual - // memory map array. - reserved_rec = (VMMemRegionEx*)_vm_map->at(_vm_map->length() - 1); - } else { - reserved_rec->expand_region(vm_ptr->addr(), vm_ptr->size()); } - - if (cur_callsite != NULL && !_vm_cs->append(cur_callsite)) { - return false; - } - vm_callsite = VMCallsitePointer(vm_ptr->pc()); - cur_callsite = &vm_callsite; - vm_callsite.inc(vm_ptr->size(), 0); - } else { - // consolidate committed memory regions for virtual memory map - // The criterial is: - // 1. two adjacent committed memory regions - // 2. committed from the same callsite - if (committed_rec == NULL || - committed_rec->base() + committed_rec->size() != vm_ptr->addr() || - committed_rec->pc() != vm_ptr->pc()) { - if (!_vm_map->append(vm_ptr)) { - return false; - } - committed_rec = (VMMemRegionEx*)_vm_map->at(_vm_map->length() - 1); - } else { - committed_rec->expand_region(vm_ptr->addr(), vm_ptr->size()); - } - vm_callsite.inc(0, vm_ptr->size()); - } - vm_ptr = (VMMemRegionEx*)vm_itr.next(); + return true; } - // deal with last record - if (cur_callsite != NULL && !_vm_cs->append(cur_callsite)) { + + LinkedList* virtual_memory_allocations() { + return &_virtual_memory_regions; + } +}; + + +bool MemBaseline::baseline_summary() { + MallocMemorySummary::snapshot(&_malloc_memory_snapshot); + VirtualMemorySummary::snapshot(&_virtual_memory_snapshot); + return true; +} + +bool MemBaseline::baseline_allocation_sites() { + // Malloc allocation sites + MallocAllocationSiteWalker malloc_walker; + if (!MallocSiteTable::walk_malloc_site(&malloc_walker)) { return false; } - // sort it into callsite pc order. Details are aggregated by callsites - _vm_cs->sort((FN_SORT)bl_vm_sort_by_pc); + _malloc_sites.move(malloc_walker.malloc_sites()); + // The malloc sites are collected in size order + _malloc_sites_order = by_size; - // walk the array to consolidate record by pc - MemPointerArrayIteratorImpl itr(_vm_cs); - VMCallsitePointer* callsite_rec = (VMCallsitePointer*)itr.current(); - VMCallsitePointer* next_rec = (VMCallsitePointer*)itr.next(); - while (next_rec != NULL) { - assert(callsite_rec != NULL, "Sanity check"); - if (next_rec->addr() == callsite_rec->addr()) { - callsite_rec->inc(next_rec->reserved_amount(), next_rec->committed_amount()); - itr.remove(); - next_rec = (VMCallsitePointer*)itr.current(); - } else { - callsite_rec = next_rec; - next_rec = (VMCallsitePointer*)itr.next(); - } + // Virtual memory allocation sites + VirtualMemoryAllocationWalker virtual_memory_walker; + if (!VirtualMemoryTracker::walk_virtual_memory(&virtual_memory_walker)) { + return false; + } + + // Virtual memory allocations are collected in call stack order + _virtual_memory_allocations.move(virtual_memory_walker.virtual_memory_allocations()); + + if (!aggregate_virtual_memory_allocation_sites()) { + return false; + } + // Virtual memory allocation sites are aggregrated in call stack order + _virtual_memory_sites_order = by_address; + + return true; +} + +bool MemBaseline::baseline(bool summaryOnly) { + reset(); + + _class_count = InstanceKlass::number_of_instance_classes(); + + if (!baseline_summary()) { + return false; + } + + _baseline_type = Summary_baselined; + + // baseline details + if (!summaryOnly && + MemTracker::tracking_level() == NMT_detail) { + baseline_allocation_sites(); + _baseline_type = Detail_baselined; } return true; } -// baseline a snapshot. If summary_only = false, memory usages aggregated by -// callsites are also baselined. -// The method call can be lengthy, especially when detail tracking info is -// requested. So the method checks for safepoint explicitly. -bool MemBaseline::baseline(MemSnapshot& snapshot, bool summary_only) { - Thread* THREAD = Thread::current(); - assert(THREAD->is_Java_thread(), "must be a JavaThread"); - MutexLocker snapshot_locker(snapshot._lock); - reset(); - _baselined = baseline_malloc_summary(snapshot._alloc_ptrs); - if (_baselined) { - check_safepoint((JavaThread*)THREAD); - _baselined = baseline_vm_summary(snapshot._vm_ptrs); - } - _number_of_classes = snapshot.number_of_classes(); +int compare_allocation_site(const VirtualMemoryAllocationSite& s1, + const VirtualMemoryAllocationSite& s2) { + return s1.call_stack()->compare(*s2.call_stack()); +} - if (!summary_only && MemTracker::track_callsite() && _baselined) { - check_safepoint((JavaThread*)THREAD); - _baselined = baseline_malloc_details(snapshot._alloc_ptrs); - if (_baselined) { - check_safepoint((JavaThread*)THREAD); - _baselined = baseline_vm_details(snapshot._vm_ptrs); +bool MemBaseline::aggregate_virtual_memory_allocation_sites() { + SortedLinkedList allocation_sites; + + VirtualMemoryAllocationIterator itr = virtual_memory_allocations(); + const ReservedMemoryRegion* rgn; + VirtualMemoryAllocationSite* site; + while ((rgn = itr.next()) != NULL) { + VirtualMemoryAllocationSite tmp(*rgn->call_stack()); + site = allocation_sites.find(tmp); + if (site == NULL) { + LinkedListNode* node = + allocation_sites.add(tmp); + if (node == NULL) return false; + site = node->data(); } - } - return _baselined; -} - - -int MemBaseline::flag2index(MEMFLAGS flag) const { - for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) { - if (MemType2NameMap[index]._flag == flag) { - return index; - } - } - assert(false, "no type"); - return -1; -} - -const char* MemBaseline::type2name(MEMFLAGS type) { - for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) { - if (MemType2NameMap[index]._flag == type) { - return MemType2NameMap[index]._name; - } - } - assert(false, err_msg("bad type %x", type)); - return NULL; -} - - -MemBaseline& MemBaseline::operator=(const MemBaseline& other) { - _total_malloced = other._total_malloced; - _total_vm_reserved = other._total_vm_reserved; - _total_vm_committed = other._total_vm_committed; - - _baselined = other._baselined; - _number_of_classes = other._number_of_classes; - - for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) { - _malloc_data[index] = other._malloc_data[index]; - _vm_data[index] = other._vm_data[index]; - _arena_data[index] = other._arena_data[index]; + site->reserve_memory(rgn->size()); + site->commit_memory(rgn->committed_size()); } - if (MemTracker::track_callsite()) { - assert(_malloc_cs != NULL && _vm_cs != NULL, "out of memory"); - assert(other._malloc_cs != NULL && other._vm_cs != NULL, - "not properly baselined"); - _malloc_cs->clear(); - _vm_cs->clear(); - int index; - for (index = 0; index < other._malloc_cs->length(); index ++) { - _malloc_cs->append(other._malloc_cs->at(index)); - } + _virtual_memory_sites.move(&allocation_sites); + return true; +} - for (index = 0; index < other._vm_cs->length(); index ++) { - _vm_cs->append(other._vm_cs->at(index)); - } +MallocSiteIterator MemBaseline::malloc_sites(SortingOrder order) { + assert(!_malloc_sites.is_empty(), "Not detail baseline"); + switch(order) { + case by_size: + malloc_sites_to_size_order(); + break; + case by_site: + malloc_sites_to_allocation_site_order(); + break; + case by_address: + default: + ShouldNotReachHere(); } - return *this; + return MallocSiteIterator(_malloc_sites.head()); } -/* compare functions for sorting */ - -// sort snapshot malloc'd records in callsite pc order -int MemBaseline::malloc_sort_by_pc(const void* p1, const void* p2) { - assert(MemTracker::track_callsite(),"Just check"); - const MemPointerRecordEx* mp1 = (const MemPointerRecordEx*)p1; - const MemPointerRecordEx* mp2 = (const MemPointerRecordEx*)p2; - return UNSIGNED_COMPARE(mp1->pc(), mp2->pc()); -} - -// sort baselined malloc'd records in size order -int MemBaseline::bl_malloc_sort_by_size(const void* p1, const void* p2) { - assert(MemTracker::is_on(), "Just check"); - const MallocCallsitePointer* mp1 = (const MallocCallsitePointer*)p1; - const MallocCallsitePointer* mp2 = (const MallocCallsitePointer*)p2; - return UNSIGNED_COMPARE(mp2->amount(), mp1->amount()); -} - -// sort baselined malloc'd records in callsite pc order -int MemBaseline::bl_malloc_sort_by_pc(const void* p1, const void* p2) { - assert(MemTracker::is_on(), "Just check"); - const MallocCallsitePointer* mp1 = (const MallocCallsitePointer*)p1; - const MallocCallsitePointer* mp2 = (const MallocCallsitePointer*)p2; - return UNSIGNED_COMPARE(mp1->addr(), mp2->addr()); +VirtualMemorySiteIterator MemBaseline::virtual_memory_sites(SortingOrder order) { + assert(!_virtual_memory_sites.is_empty(), "Not detail baseline"); + switch(order) { + case by_size: + virtual_memory_sites_to_size_order(); + break; + case by_site: + virtual_memory_sites_to_reservation_site_order(); + break; + case by_address: + default: + ShouldNotReachHere(); + } + return VirtualMemorySiteIterator(_virtual_memory_sites.head()); } -// sort baselined mmap'd records in size (reserved size) order -int MemBaseline::bl_vm_sort_by_size(const void* p1, const void* p2) { - assert(MemTracker::is_on(), "Just check"); - const VMCallsitePointer* mp1 = (const VMCallsitePointer*)p1; - const VMCallsitePointer* mp2 = (const VMCallsitePointer*)p2; - return UNSIGNED_COMPARE(mp2->reserved_amount(), mp1->reserved_amount()); +// Sorting allocations sites in different orders +void MemBaseline::malloc_sites_to_size_order() { + if (_malloc_sites_order != by_size) { + SortedLinkedList tmp; + + // Add malloc sites to sorted linked list to sort into size order + tmp.move(&_malloc_sites); + _malloc_sites.set_head(tmp.head()); + tmp.set_head(NULL); + _malloc_sites_order = by_size; + } } -// sort baselined mmap'd records in callsite pc order -int MemBaseline::bl_vm_sort_by_pc(const void* p1, const void* p2) { - assert(MemTracker::is_on(), "Just check"); - const VMCallsitePointer* mp1 = (const VMCallsitePointer*)p1; - const VMCallsitePointer* mp2 = (const VMCallsitePointer*)p2; - return UNSIGNED_COMPARE(mp1->addr(), mp2->addr()); +void MemBaseline::malloc_sites_to_allocation_site_order() { + if (_malloc_sites_order != by_site) { + SortedLinkedList tmp; + // Add malloc sites to sorted linked list to sort into site (address) order + tmp.move(&_malloc_sites); + _malloc_sites.set_head(tmp.head()); + tmp.set_head(NULL); + _malloc_sites_order = by_site; + } } +void MemBaseline::virtual_memory_sites_to_size_order() { + if (_virtual_memory_sites_order != by_size) { + SortedLinkedList tmp; -// sort snapshot malloc'd records in memory block address order -int MemBaseline::malloc_sort_by_addr(const void* p1, const void* p2) { - assert(MemTracker::is_on(), "Just check"); - const MemPointerRecord* mp1 = (const MemPointerRecord*)p1; - const MemPointerRecord* mp2 = (const MemPointerRecord*)p2; - int delta = UNSIGNED_COMPARE(mp1->addr(), mp2->addr()); - assert(p1 == p2 || delta != 0, "dup pointer"); - return delta; + tmp.move(&_virtual_memory_sites); + + _virtual_memory_sites.set_head(tmp.head()); + tmp.set_head(NULL); + _virtual_memory_sites_order = by_size; + } +} + +void MemBaseline::virtual_memory_sites_to_reservation_site_order() { + if (_virtual_memory_sites_order != by_size) { + SortedLinkedList tmp; + + tmp.move(&_virtual_memory_sites); + + _virtual_memory_sites.set_head(tmp.head()); + tmp.set_head(NULL); + + _virtual_memory_sites_order = by_size; + } } diff --git a/hotspot/src/share/vm/services/memBaseline.hpp b/hotspot/src/share/vm/services/memBaseline.hpp index 864c6cc5724..8272da49646 100644 --- a/hotspot/src/share/vm/services/memBaseline.hpp +++ b/hotspot/src/share/vm/services/memBaseline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,425 +25,181 @@ #ifndef SHARE_VM_SERVICES_MEM_BASELINE_HPP #define SHARE_VM_SERVICES_MEM_BASELINE_HPP +#if INCLUDE_NMT + #include "memory/allocation.hpp" #include "runtime/mutex.hpp" -#include "services/memPtr.hpp" -#include "services/memSnapshot.hpp" +#include "services/mallocSiteTable.hpp" +#include "services/mallocTracker.hpp" +#include "services/nmtCommon.hpp" +#include "services/virtualMemoryTracker.hpp" +#include "utilities/linkedlist.hpp" -// compare unsigned number -#define UNSIGNED_COMPARE(a, b) ((a > b) ? 1 : ((a == b) ? 0 : -1)) +typedef LinkedListIterator MallocSiteIterator; +typedef LinkedListIterator VirtualMemorySiteIterator; +typedef LinkedListIterator VirtualMemoryAllocationIterator; /* - * MallocCallsitePointer and VMCallsitePointer are used - * to baseline memory blocks with their callsite information. - * They are only available when detail tracking is turned - * on. - */ - -/* baselined malloc record aggregated by callsite */ -class MallocCallsitePointer : public MemPointer { - private: - size_t _count; // number of malloc invocation from this callsite - size_t _amount; // total amount of memory malloc-ed from this callsite - - public: - MallocCallsitePointer() { - _count = 0; - _amount = 0; - } - - MallocCallsitePointer(address pc) : MemPointer(pc) { - _count = 0; - _amount = 0; - } - - MallocCallsitePointer& operator=(const MallocCallsitePointer& p) { - MemPointer::operator=(p); - _count = p.count(); - _amount = p.amount(); - return *this; - } - - inline void inc(size_t size) { - _count ++; - _amount += size; - }; - - inline size_t count() const { - return _count; - } - - inline size_t amount() const { - return _amount; - } -}; - -// baselined virtual memory record aggregated by callsite -class VMCallsitePointer : public MemPointer { - private: - size_t _count; // number of invocation from this callsite - size_t _reserved_amount; // total reserved amount - size_t _committed_amount; // total committed amount - - public: - VMCallsitePointer() { - _count = 0; - _reserved_amount = 0; - _committed_amount = 0; - } - - VMCallsitePointer(address pc) : MemPointer(pc) { - _count = 0; - _reserved_amount = 0; - _committed_amount = 0; - } - - VMCallsitePointer& operator=(const VMCallsitePointer& p) { - MemPointer::operator=(p); - _count = p.count(); - _reserved_amount = p.reserved_amount(); - _committed_amount = p.committed_amount(); - return *this; - } - - inline void inc(size_t reserved, size_t committed) { - _count ++; - _reserved_amount += reserved; - _committed_amount += committed; - } - - inline size_t count() const { - return _count; - } - - inline size_t reserved_amount() const { - return _reserved_amount; - } - - inline size_t committed_amount() const { - return _committed_amount; - } -}; - -// maps a memory type flag to readable name -typedef struct _memType2Name { - MEMFLAGS _flag; - const char* _name; -} MemType2Name; - - -// This class aggregates malloc'd records by memory type -class MallocMem VALUE_OBJ_CLASS_SPEC { - private: - MEMFLAGS _type; - - size_t _count; - size_t _amount; - - public: - MallocMem() { - _type = mtNone; - _count = 0; - _amount = 0; - } - - MallocMem(MEMFLAGS flags) { - assert(HAS_VALID_MEMORY_TYPE(flags), "no type"); - _type = FLAGS_TO_MEMORY_TYPE(flags); - _count = 0; - _amount = 0; - } - - inline void set_type(MEMFLAGS flag) { - _type = flag; - } - - inline void clear() { - _count = 0; - _amount = 0; - _type = mtNone; - } - - MallocMem& operator=(const MallocMem& m) { - assert(_type == m.type(), "different type"); - _count = m.count(); - _amount = m.amount(); - return *this; - } - - inline void inc(size_t amt) { - _amount += amt; - _count ++; - } - - inline void reduce(size_t amt) { - assert(_amount >= amt, "Just check"); - _amount -= amt; - } - - inline void overwrite_counter(size_t count) { - _count = count; - } - - inline MEMFLAGS type() const { - return _type; - } - - inline bool is_type(MEMFLAGS flags) const { - return FLAGS_TO_MEMORY_TYPE(flags) == _type; - } - - inline size_t count() const { - return _count; - } - - inline size_t amount() const { - return _amount; - } -}; - -// This class records live arena's memory usage -class ArenaMem : public MallocMem { - public: - ArenaMem(MEMFLAGS typeflag): MallocMem(typeflag) { - } - ArenaMem() { } -}; - -// This class aggregates virtual memory by its memory type -class VMMem VALUE_OBJ_CLASS_SPEC { - private: - MEMFLAGS _type; - - size_t _count; - size_t _reserved_amount; - size_t _committed_amount; - - public: - VMMem() { - _type = mtNone; - _count = 0; - _reserved_amount = 0; - _committed_amount = 0; - } - - VMMem(MEMFLAGS flags) { - assert(HAS_VALID_MEMORY_TYPE(flags), "no type"); - _type = FLAGS_TO_MEMORY_TYPE(flags); - _count = 0; - _reserved_amount = 0; - _committed_amount = 0; - } - - inline void clear() { - _type = mtNone; - _count = 0; - _reserved_amount = 0; - _committed_amount = 0; - } - - inline void set_type(MEMFLAGS flags) { - _type = FLAGS_TO_MEMORY_TYPE(flags); - } - - VMMem& operator=(const VMMem& m) { - assert(_type == m.type(), "different type"); - - _count = m.count(); - _reserved_amount = m.reserved_amount(); - _committed_amount = m.committed_amount(); - return *this; - } - - - inline MEMFLAGS type() const { - return _type; - } - - inline bool is_type(MEMFLAGS flags) const { - return FLAGS_TO_MEMORY_TYPE(flags) == _type; - } - - inline void inc(size_t reserved_amt, size_t committed_amt) { - _reserved_amount += reserved_amt; - _committed_amount += committed_amt; - _count ++; - } - - inline size_t count() const { - return _count; - } - - inline size_t reserved_amount() const { - return _reserved_amount; - } - - inline size_t committed_amount() const { - return _committed_amount; - } -}; - - - -#define NUMBER_OF_MEMORY_TYPE (mt_number_of_types + 1) - -class BaselineReporter; -class BaselineComparisonReporter; - -/* - * This class baselines current memory snapshot. - * A memory baseline summarizes memory usage by memory type, - * aggregates memory usage by callsites when detail tracking - * is on. + * Baseline a memory snapshot */ class MemBaseline VALUE_OBJ_CLASS_SPEC { - friend class BaselineReporter; - friend class BaselineComparisonReporter; + public: + enum BaselineThreshold { + SIZE_THRESHOLD = K // Only allocation size over this threshold will be baselined. + }; + + enum BaselineType { + Not_baselined, + Summary_baselined, + Detail_baselined + }; + + enum SortingOrder { + by_address, // by memory address + by_size, // by memory size + by_site // by call site where the memory is allocated from + }; private: - // overall summaries - size_t _total_malloced; - size_t _total_vm_reserved; - size_t _total_vm_committed; - size_t _number_of_classes; - size_t _number_of_threads; + // Summary information + MallocMemorySnapshot _malloc_memory_snapshot; + VirtualMemorySnapshot _virtual_memory_snapshot; - // if it has properly baselined - bool _baselined; + size_t _class_count; - // we categorize memory into three categories within the memory type - MallocMem _malloc_data[NUMBER_OF_MEMORY_TYPE]; - VMMem _vm_data[NUMBER_OF_MEMORY_TYPE]; - ArenaMem _arena_data[NUMBER_OF_MEMORY_TYPE]; + // Allocation sites information + // Malloc allocation sites + LinkedListImpl _malloc_sites; - // memory records that aggregate memory usage by callsites. - // only available when detail tracking is on. - MemPointerArray* _malloc_cs; - MemPointerArray* _vm_cs; - // virtual memory map - MemPointerArray* _vm_map; + // All virtual memory allocations + LinkedListImpl _virtual_memory_allocations; - private: - static MemType2Name MemType2NameMap[NUMBER_OF_MEMORY_TYPE]; + // Virtual memory allocations by allocation sites, always in by_address + // order + LinkedListImpl _virtual_memory_sites; - private: - // should not use copy constructor - MemBaseline(MemBaseline& copy) { ShouldNotReachHere(); } + SortingOrder _malloc_sites_order; + SortingOrder _virtual_memory_sites_order; - // check and block at a safepoint - static inline void check_safepoint(JavaThread* thr); + BaselineType _baseline_type; public: // create a memory baseline - MemBaseline(); - - ~MemBaseline(); - - inline bool baselined() const { - return _baselined; + MemBaseline(): + _baseline_type(Not_baselined), + _class_count(0) { } - MemBaseline& operator=(const MemBaseline& other); + ~MemBaseline() { + reset(); + } + + bool baseline(bool summaryOnly = true); + + BaselineType baseline_type() const { return _baseline_type; } + + MallocMemorySnapshot* malloc_memory_snapshot() { + return &_malloc_memory_snapshot; + } + + VirtualMemorySnapshot* virtual_memory_snapshot() { + return &_virtual_memory_snapshot; + } + + MallocSiteIterator malloc_sites(SortingOrder order); + VirtualMemorySiteIterator virtual_memory_sites(SortingOrder order); + + // Virtual memory allocation iterator always returns in virtual memory + // base address order. + VirtualMemoryAllocationIterator virtual_memory_allocations() { + assert(!_virtual_memory_allocations.is_empty(), "Not detail baseline"); + return VirtualMemoryAllocationIterator(_virtual_memory_allocations.head()); + } + + // Total reserved memory = total malloc'd memory + total reserved virtual + // memory + size_t total_reserved_memory() const { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + size_t amount = _malloc_memory_snapshot.total() + + _virtual_memory_snapshot.total_reserved(); + return amount; + } + + // Total committed memory = total malloc'd memory + total committed + // virtual memory + size_t total_committed_memory() const { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + size_t amount = _malloc_memory_snapshot.total() + + _virtual_memory_snapshot.total_committed(); + return amount; + } + + size_t total_arena_memory() const { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + return _malloc_memory_snapshot.total_arena(); + } + + size_t malloc_tracking_overhead() const { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + MemBaseline* bl = const_cast(this); + return bl->_malloc_memory_snapshot.malloc_overhead()->size(); + } + + MallocMemory* malloc_memory(MEMFLAGS flag) { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + return _malloc_memory_snapshot.by_type(flag); + } + + VirtualMemory* virtual_memory(MEMFLAGS flag) { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + return _virtual_memory_snapshot.by_type(flag); + } + + + size_t class_count() const { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + return _class_count; + } + + size_t thread_count() const { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + return _malloc_memory_snapshot.thread_count(); + } // reset the baseline for reuse - void clear(); + void reset() { + _baseline_type = Not_baselined; + _malloc_memory_snapshot.reset(); + _virtual_memory_snapshot.reset(); + _class_count = 0; - // baseline the snapshot - bool baseline(MemSnapshot& snapshot, bool summary_only = true); - - bool baseline(const MemPointerArray* malloc_records, - const MemPointerArray* vm_records, - bool summary_only = true); - - // total malloc'd memory of specified memory type - inline size_t malloc_amount(MEMFLAGS flag) const { - return _malloc_data[flag2index(flag)].amount(); + _malloc_sites.clear(); + _virtual_memory_sites.clear(); + _virtual_memory_allocations.clear(); } - // number of malloc'd memory blocks of specified memory type - inline size_t malloc_count(MEMFLAGS flag) const { - return _malloc_data[flag2index(flag)].count(); - } - // total memory used by arenas of specified memory type - inline size_t arena_amount(MEMFLAGS flag) const { - return _arena_data[flag2index(flag)].amount(); - } - // number of arenas of specified memory type - inline size_t arena_count(MEMFLAGS flag) const { - return _arena_data[flag2index(flag)].count(); - } - // total reserved memory of specified memory type - inline size_t reserved_amount(MEMFLAGS flag) const { - return _vm_data[flag2index(flag)].reserved_amount(); - } - // total committed memory of specified memory type - inline size_t committed_amount(MEMFLAGS flag) const { - return _vm_data[flag2index(flag)].committed_amount(); - } - // total memory (malloc'd + mmap'd + arena) of specified - // memory type - inline size_t total_amount(MEMFLAGS flag) const { - int index = flag2index(flag); - return _malloc_data[index].amount() + - _vm_data[index].reserved_amount() + - _arena_data[index].amount(); - } - - /* overall summaries */ - - // total malloc'd memory in snapshot - inline size_t total_malloc_amount() const { - return _total_malloced; - } - // total mmap'd memory in snapshot - inline size_t total_reserved_amount() const { - return _total_vm_reserved; - } - // total committed memory in snapshot - inline size_t total_committed_amount() const { - return _total_vm_committed; - } - // number of loaded classes - inline size_t number_of_classes() const { - return _number_of_classes; - } - // number of running threads - inline size_t number_of_threads() const { - return _number_of_threads; - } - // lookup human readable name of a memory type - static const char* type2name(MEMFLAGS type); private: - // convert memory flag to the index to mapping table - int flag2index(MEMFLAGS flag) const; + // Baseline summary information + bool baseline_summary(); - // reset baseline values - void reset(); + // Baseline allocation sites (detail tracking only) + bool baseline_allocation_sites(); - // summarize the records in global snapshot - bool baseline_malloc_summary(const MemPointerArray* malloc_records); - bool baseline_vm_summary(const MemPointerArray* vm_records); - bool baseline_malloc_details(const MemPointerArray* malloc_records); - bool baseline_vm_details(const MemPointerArray* vm_records); + // Aggregate virtual memory allocation by allocation sites + bool aggregate_virtual_memory_allocation_sites(); - // print a line of malloc'd memory aggregated by callsite - void print_malloc_callsite(outputStream* st, address pc, size_t size, - size_t count, int diff_amt, int diff_count) const; - // print a line of mmap'd memory aggregated by callsite - void print_vm_callsite(outputStream* st, address pc, size_t rsz, - size_t csz, int diff_rsz, int diff_csz) const; + // Sorting allocation sites in different orders + // Sort allocation sites in size order + void malloc_sites_to_size_order(); + // Sort allocation sites in call site address order + void malloc_sites_to_allocation_site_order(); - // sorting functions for raw records - static int malloc_sort_by_pc(const void* p1, const void* p2); - static int malloc_sort_by_addr(const void* p1, const void* p2); - - private: - // sorting functions for baselined records - static int bl_malloc_sort_by_size(const void* p1, const void* p2); - static int bl_vm_sort_by_size(const void* p1, const void* p2); - static int bl_malloc_sort_by_pc(const void* p1, const void* p2); - static int bl_vm_sort_by_pc(const void* p1, const void* p2); + // Sort allocation sites in reserved size order + void virtual_memory_sites_to_size_order(); + // Sort allocation sites in call site address order + void virtual_memory_sites_to_reservation_site_order(); }; +#endif // INCLUDE_NMT #endif // SHARE_VM_SERVICES_MEM_BASELINE_HPP diff --git a/hotspot/src/share/vm/services/memPtr.hpp b/hotspot/src/share/vm/services/memPtr.hpp deleted file mode 100644 index cae454cc318..00000000000 --- a/hotspot/src/share/vm/services/memPtr.hpp +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (c) 2012, 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. - * - */ - -#ifndef SHARE_VM_SERVICES_MEM_PTR_HPP -#define SHARE_VM_SERVICES_MEM_PTR_HPP - -#include "memory/allocation.hpp" -#include "runtime/os.hpp" -#include "runtime/safepoint.hpp" - -/* - * global sequence generator that generates sequence numbers to serialize - * memory records. - */ -class SequenceGenerator : AllStatic { - public: - static jint next(); - - // peek last sequence number - static jint peek() { - return _seq_number; - } - - // reset sequence number - static void reset() { - assert(SafepointSynchronize::is_at_safepoint(), "Safepoint required"); - _seq_number = 1; - _generation ++; - }; - - static unsigned long current_generation() { return _generation; } - NOT_PRODUCT(static jint max_seq_num() { return _max_seq_number; }) - - private: - static volatile jint _seq_number; - static volatile unsigned long _generation; - NOT_PRODUCT(static jint _max_seq_number; ) -}; - -/* - * followings are the classes that are used to hold memory activity records in different stages. - * MemPointer - * |--------MemPointerRecord - * | - * |----MemPointerRecordEx - * | | - * | |-------SeqMemPointerRecordEx - * | - * |----SeqMemPointerRecord - * | - * |----VMMemRegion - * | - * |-----VMMemRegionEx - * - * - * prefix 'Seq' - sequenced, the record contains a sequence number - * surfix 'Ex' - extension, the record contains a caller's pc - * - * per-thread recorder : SeqMemPointerRecord(Ex) - * snapshot staging : SeqMemPointerRecord(Ex) - * snapshot : MemPointerRecord(Ex) and VMMemRegion(Ex) - * - */ - -/* - * class that wraps an address to a memory block, - * the memory pointer either points to a malloc'd - * memory block, or a mmap'd memory block - */ -class MemPointer VALUE_OBJ_CLASS_SPEC { - public: - MemPointer(): _addr(0) { } - MemPointer(address addr): _addr(addr) { } - - MemPointer(const MemPointer& copy_from) { - _addr = copy_from.addr(); - } - - inline address addr() const { - return _addr; - } - - inline operator address() const { - return addr(); - } - - inline bool operator == (const MemPointer& other) const { - return addr() == other.addr(); - } - - inline MemPointer& operator = (const MemPointer& other) { - _addr = other.addr(); - return *this; - } - - protected: - inline void set_addr(address addr) { _addr = addr; } - - protected: - // memory address - address _addr; -}; - -/* MemPointerRecord records an activityand associated - * attributes on a memory block. - */ -class MemPointerRecord : public MemPointer { - private: - MEMFLAGS _flags; - size_t _size; - -public: - /* extension of MemoryType enum - * see share/vm/memory/allocation.hpp for details. - * - * The tag values are associated to sorting orders, so be - * careful if changes are needed. - * The allocation records should be sorted ahead of tagging - * records, which in turn ahead of deallocation records - */ - enum MemPointerTags { - tag_alloc = 0x0001, // malloc or reserve record - tag_commit = 0x0002, // commit record - tag_type = 0x0003, // tag virtual memory to a memory type - tag_uncommit = 0x0004, // uncommit record - tag_release = 0x0005, // free or release record - tag_size = 0x0006, // arena size - tag_masks = 0x0007, // all tag bits - vmBit = 0x0008 - }; - - /* helper functions to interpret the tagging flags */ - - inline static bool is_allocation_record(MEMFLAGS flags) { - return (flags & tag_masks) == tag_alloc; - } - - inline static bool is_deallocation_record(MEMFLAGS flags) { - return (flags & tag_masks) == tag_release; - } - - inline static bool is_arena_record(MEMFLAGS flags) { - return (flags & (otArena | tag_size)) == otArena; - } - - inline static bool is_arena_memory_record(MEMFLAGS flags) { - return (flags & (otArena | tag_size)) == (otArena | tag_size); - } - - inline static bool is_virtual_memory_record(MEMFLAGS flags) { - return (flags & vmBit) != 0; - } - - inline static bool is_virtual_memory_reserve_record(MEMFLAGS flags) { - return (flags & 0x0F) == (tag_alloc | vmBit); - } - - inline static bool is_virtual_memory_commit_record(MEMFLAGS flags) { - return (flags & 0x0F) == (tag_commit | vmBit); - } - - inline static bool is_virtual_memory_uncommit_record(MEMFLAGS flags) { - return (flags & 0x0F) == (tag_uncommit | vmBit); - } - - inline static bool is_virtual_memory_release_record(MEMFLAGS flags) { - return (flags & 0x0F) == (tag_release | vmBit); - } - - inline static bool is_virtual_memory_type_record(MEMFLAGS flags) { - return (flags & 0x0F) == (tag_type | vmBit); - } - - /* tagging flags */ - inline static MEMFLAGS malloc_tag() { return tag_alloc; } - inline static MEMFLAGS free_tag() { return tag_release; } - inline static MEMFLAGS arena_size_tag() { return tag_size | otArena; } - inline static MEMFLAGS virtual_memory_tag() { return vmBit; } - inline static MEMFLAGS virtual_memory_reserve_tag() { return (tag_alloc | vmBit); } - inline static MEMFLAGS virtual_memory_commit_tag() { return (tag_commit | vmBit); } - inline static MEMFLAGS virtual_memory_uncommit_tag(){ return (tag_uncommit | vmBit); } - inline static MEMFLAGS virtual_memory_release_tag() { return (tag_release | vmBit); } - inline static MEMFLAGS virtual_memory_type_tag() { return (tag_type | vmBit); } - - public: - MemPointerRecord(): _size(0), _flags(mtNone) { } - - MemPointerRecord(address addr, MEMFLAGS memflags, size_t size = 0): - MemPointer(addr), _flags(memflags), _size(size) { } - - MemPointerRecord(const MemPointerRecord& copy_from): - MemPointer(copy_from), _flags(copy_from.flags()), - _size(copy_from.size()) { - } - - /* MemPointerRecord is not sequenced, it always return - * 0 to indicate non-sequenced - */ - virtual jint seq() const { return 0; } - - inline size_t size() const { return _size; } - inline void set_size(size_t size) { _size = size; } - - inline MEMFLAGS flags() const { return _flags; } - inline void set_flags(MEMFLAGS flags) { _flags = flags; } - - MemPointerRecord& operator= (const MemPointerRecord& ptr) { - MemPointer::operator=(ptr); - _flags = ptr.flags(); -#ifdef ASSERT - if (IS_ARENA_OBJ(_flags)) { - assert(!is_vm_pointer(), "wrong flags"); - assert((_flags & ot_masks) == otArena, "wrong flags"); - } -#endif - _size = ptr.size(); - return *this; - } - - // if the pointer represents a malloc-ed memory address - inline bool is_malloced_pointer() const { - return !is_vm_pointer(); - } - - // if the pointer represents a virtual memory address - inline bool is_vm_pointer() const { - return is_virtual_memory_record(_flags); - } - - // if this record records a 'malloc' or virtual memory - // 'reserve' call - inline bool is_allocation_record() const { - return is_allocation_record(_flags); - } - - // if this record records a size information of an arena - inline bool is_arena_memory_record() const { - return is_arena_memory_record(_flags); - } - - // if this pointer represents an address to an arena object - inline bool is_arena_record() const { - return is_arena_record(_flags); - } - - // if this record represents a size information of specific arena - inline bool is_memory_record_of_arena(const MemPointerRecord* arena_rc) { - assert(is_arena_memory_record(), "not size record"); - assert(arena_rc->is_arena_record(), "not arena record"); - return (arena_rc->addr() + sizeof(void*)) == addr(); - } - - // if this record records a 'free' or virtual memory 'free' call - inline bool is_deallocation_record() const { - return is_deallocation_record(_flags); - } - - // if this record records a virtual memory 'commit' call - inline bool is_commit_record() const { - return is_virtual_memory_commit_record(_flags); - } - - // if this record records a virtual memory 'uncommit' call - inline bool is_uncommit_record() const { - return is_virtual_memory_uncommit_record(_flags); - } - - // if this record is a tagging record of a virtual memory block - inline bool is_type_tagging_record() const { - return is_virtual_memory_type_record(_flags); - } - - // if the two memory pointer records actually represent the same - // memory block - inline bool is_same_region(const MemPointerRecord* other) const { - return (addr() == other->addr() && size() == other->size()); - } - - // if this memory region fully contains another one - inline bool contains_region(const MemPointerRecord* other) const { - return contains_region(other->addr(), other->size()); - } - - // if this memory region fully contains specified memory range - inline bool contains_region(address add, size_t sz) const { - return (addr() <= add && addr() + size() >= add + sz); - } - - inline bool contains_address(address add) const { - return (addr() <= add && addr() + size() > add); - } - - // if this memory region overlaps another region - inline bool overlaps_region(const MemPointerRecord* other) const { - assert(other != NULL, "Just check"); - assert(size() > 0 && other->size() > 0, "empty range"); - return contains_address(other->addr()) || - contains_address(other->addr() + other->size() - 1) || // exclude end address - other->contains_address(addr()) || - other->contains_address(addr() + size() - 1); // exclude end address - } - -}; - -// MemPointerRecordEx also records callsite pc, from where -// the memory block is allocated -class MemPointerRecordEx : public MemPointerRecord { - private: - address _pc; // callsite pc - - public: - MemPointerRecordEx(): _pc(0) { } - - MemPointerRecordEx(address addr, MEMFLAGS memflags, size_t size = 0, address pc = 0): - MemPointerRecord(addr, memflags, size), _pc(pc) {} - - MemPointerRecordEx(const MemPointerRecordEx& copy_from): - MemPointerRecord(copy_from), _pc(copy_from.pc()) {} - - inline address pc() const { return _pc; } - - void init(const MemPointerRecordEx* mpe) { - MemPointerRecord::operator=(*mpe); - _pc = mpe->pc(); - } - - void init(const MemPointerRecord* mp) { - MemPointerRecord::operator=(*mp); - _pc = 0; - } -}; - -// a virtual memory region. The region can represent a reserved -// virtual memory region or a committed memory region -class VMMemRegion : public MemPointerRecord { -public: - VMMemRegion() { } - - void init(const MemPointerRecord* mp) { - assert(mp->is_vm_pointer(), "Sanity check"); - _addr = mp->addr(); - set_size(mp->size()); - set_flags(mp->flags()); - } - - VMMemRegion& operator=(const VMMemRegion& other) { - MemPointerRecord::operator=(other); - return *this; - } - - inline bool is_reserved_region() const { - return is_allocation_record(); - } - - inline bool is_committed_region() const { - return is_commit_record(); - } - - /* base address of this virtual memory range */ - inline address base() const { - return addr(); - } - - /* tag this virtual memory range to the specified memory type */ - inline void tag(MEMFLAGS f) { - set_flags(flags() | (f & mt_masks)); - } - - // expand this region to also cover specified range. - // The range has to be on either end of the memory region. - void expand_region(address addr, size_t sz) { - if (addr < base()) { - assert(addr + sz == base(), "Sanity check"); - _addr = addr; - set_size(size() + sz); - } else { - assert(base() + size() == addr, "Sanity check"); - set_size(size() + sz); - } - } - - // exclude the specified address range from this region. - // The excluded memory range has to be on either end of this memory - // region. - inline void exclude_region(address add, size_t sz) { - assert(is_reserved_region() || is_committed_region(), "Sanity check"); - assert(addr() != NULL && size() != 0, "Sanity check"); - assert(add >= addr() && add < addr() + size(), "Sanity check"); - assert(add == addr() || (add + sz) == (addr() + size()), - "exclude in the middle"); - if (add == addr()) { - set_addr(add + sz); - set_size(size() - sz); - } else { - set_size(size() - sz); - } - } -}; - -class VMMemRegionEx : public VMMemRegion { - private: - jint _seq; // sequence number - - public: - VMMemRegionEx(): _pc(0) { } - - void init(const MemPointerRecordEx* mpe) { - VMMemRegion::init(mpe); - _pc = mpe->pc(); - } - - void init(const MemPointerRecord* mpe) { - VMMemRegion::init(mpe); - _pc = 0; - } - - VMMemRegionEx& operator=(const VMMemRegionEx& other) { - VMMemRegion::operator=(other); - _pc = other.pc(); - return *this; - } - - inline address pc() const { return _pc; } - private: - address _pc; -}; - -/* - * Sequenced memory record - */ -class SeqMemPointerRecord : public MemPointerRecord { - private: - jint _seq; // sequence number - - public: - SeqMemPointerRecord(): _seq(0){ } - - SeqMemPointerRecord(address addr, MEMFLAGS flags, size_t size, jint seq) - : MemPointerRecord(addr, flags, size), _seq(seq) { - } - - SeqMemPointerRecord(const SeqMemPointerRecord& copy_from) - : MemPointerRecord(copy_from) { - _seq = copy_from.seq(); - } - - SeqMemPointerRecord& operator= (const SeqMemPointerRecord& ptr) { - MemPointerRecord::operator=(ptr); - _seq = ptr.seq(); - return *this; - } - - inline jint seq() const { - return _seq; - } -}; - - - -class SeqMemPointerRecordEx : public MemPointerRecordEx { - private: - jint _seq; // sequence number - - public: - SeqMemPointerRecordEx(): _seq(0) { } - - SeqMemPointerRecordEx(address addr, MEMFLAGS flags, size_t size, - jint seq, address pc): - MemPointerRecordEx(addr, flags, size, pc), _seq(seq) { - } - - SeqMemPointerRecordEx(const SeqMemPointerRecordEx& copy_from) - : MemPointerRecordEx(copy_from) { - _seq = copy_from.seq(); - } - - SeqMemPointerRecordEx& operator= (const SeqMemPointerRecordEx& ptr) { - MemPointerRecordEx::operator=(ptr); - _seq = ptr.seq(); - return *this; - } - - inline jint seq() const { - return _seq; - } -}; - -#endif // SHARE_VM_SERVICES_MEM_PTR_HPP diff --git a/hotspot/src/share/vm/services/memPtrArray.hpp b/hotspot/src/share/vm/services/memPtrArray.hpp deleted file mode 100644 index b3e48f6868e..00000000000 --- a/hotspot/src/share/vm/services/memPtrArray.hpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ -#ifndef SHARE_VM_UTILITIES_MEM_PTR_ARRAY_HPP -#define SHARE_VM_UTILITIES_MEM_PTR_ARRAY_HPP - -#include "memory/allocation.hpp" -#include "services/memPtr.hpp" - -class MemPtr; -class MemRecorder; -class ArenaInfo; -class MemSnapshot; - -extern "C" { - typedef int (*FN_SORT)(const void *, const void *); -} - - -// Memory pointer array interface. This array is used by NMT to hold -// various memory block information. -// The memory pointer arrays are usually walked with their iterators. - -class MemPointerArray : public CHeapObj { - public: - virtual ~MemPointerArray() { } - - // return true if it can not allocate storage for the data - virtual bool out_of_memory() const = 0; - virtual bool is_empty() const = 0; - virtual bool is_full() = 0; - virtual int length() const = 0; - virtual void clear() = 0; - virtual bool append(MemPointer* ptr) = 0; - virtual bool insert_at(MemPointer* ptr, int pos) = 0; - virtual bool remove_at(int pos) = 0; - virtual MemPointer* at(int index) const = 0; - virtual void sort(FN_SORT fn) = 0; - virtual size_t instance_size() const = 0; - virtual bool shrink() = 0; - - NOT_PRODUCT(virtual int capacity() const = 0;) -}; - -// Iterator interface -class MemPointerArrayIterator VALUE_OBJ_CLASS_SPEC { - public: - // return the pointer at current position - virtual MemPointer* current() const = 0; - // return the next pointer and advance current position - virtual MemPointer* next() = 0; - // return next pointer without advancing current position - virtual MemPointer* peek_next() const = 0; - // return previous pointer without changing current position - virtual MemPointer* peek_prev() const = 0; - // remove the pointer at current position - virtual void remove() = 0; - // insert the pointer at current position - virtual bool insert(MemPointer* ptr) = 0; - // insert specified element after current position and - // move current position to newly inserted position - virtual bool insert_after(MemPointer* ptr) = 0; -}; - -// implementation class -class MemPointerArrayIteratorImpl : public MemPointerArrayIterator { - protected: - MemPointerArray* _array; - int _pos; - - public: - MemPointerArrayIteratorImpl(MemPointerArray* arr) { - assert(arr != NULL, "Parameter check"); - _array = arr; - _pos = 0; - } - - virtual MemPointer* current() const { - if (_pos < _array->length()) { - return _array->at(_pos); - } - return NULL; - } - - virtual MemPointer* next() { - if (_pos + 1 < _array->length()) { - return _array->at(++_pos); - } - _pos = _array->length(); - return NULL; - } - - virtual MemPointer* peek_next() const { - if (_pos + 1 < _array->length()) { - return _array->at(_pos + 1); - } - return NULL; - } - - virtual MemPointer* peek_prev() const { - if (_pos > 0) { - return _array->at(_pos - 1); - } - return NULL; - } - - virtual void remove() { - if (_pos < _array->length()) { - _array->remove_at(_pos); - } - } - - virtual bool insert(MemPointer* ptr) { - return _array->insert_at(ptr, _pos); - } - - virtual bool insert_after(MemPointer* ptr) { - if (_array->insert_at(ptr, _pos + 1)) { - _pos ++; - return true; - } - return false; - } -}; - - - -// Memory pointer array implementation. -// This implementation implements expandable array -#define DEFAULT_PTR_ARRAY_SIZE 1024 - -template class MemPointerArrayImpl : public MemPointerArray { - private: - int _max_size; - int _size; - bool _init_elements; - E* _data; - - public: - MemPointerArrayImpl(int initial_size = DEFAULT_PTR_ARRAY_SIZE, bool init_elements = true): - _max_size(initial_size), _size(0), _init_elements(init_elements) { - _data = (E*)raw_allocate(sizeof(E), initial_size); - if (_init_elements) { - for (int index = 0; index < _max_size; index ++) { - ::new ((void*)&_data[index]) E(); - } - } - } - - virtual ~MemPointerArrayImpl() { - if (_data != NULL) { - raw_free(_data); - } - } - - public: - bool out_of_memory() const { - return (_data == NULL); - } - - size_t instance_size() const { - return sizeof(MemPointerArrayImpl) + _max_size * sizeof(E); - } - - bool is_empty() const { - assert(_data != NULL, "Just check"); - return _size == 0; - } - - bool is_full() { - assert(_data != NULL, "Just check"); - if (_size < _max_size) { - return false; - } else { - return !expand_array(); - } - } - - int length() const { - assert(_data != NULL, "Just check"); - return _size; - } - - NOT_PRODUCT(int capacity() const { return _max_size; }) - - void clear() { - assert(_data != NULL, "Just check"); - _size = 0; - } - - bool append(MemPointer* ptr) { - assert(_data != NULL, "Just check"); - if (is_full()) { - return false; - } - _data[_size ++] = *(E*)ptr; - return true; - } - - bool insert_at(MemPointer* ptr, int pos) { - assert(_data != NULL, "Just check"); - if (is_full()) { - return false; - } - for (int index = _size; index > pos; index --) { - _data[index] = _data[index - 1]; - } - _data[pos] = *(E*)ptr; - _size ++; - return true; - } - - bool remove_at(int pos) { - assert(_data != NULL, "Just check"); - if (_size <= pos && pos >= 0) { - return false; - } - -- _size; - - for (int index = pos; index < _size; index ++) { - _data[index] = _data[index + 1]; - } - return true; - } - - MemPointer* at(int index) const { - assert(_data != NULL, "Just check"); - assert(index >= 0 && index < _size, "illegal index"); - return &_data[index]; - } - - bool shrink() { - float used = ((float)_size) / ((float)_max_size); - if (used < 0.40) { - E* old_ptr = _data; - int new_size = ((_max_size) / (2 * DEFAULT_PTR_ARRAY_SIZE) + 1) * DEFAULT_PTR_ARRAY_SIZE; - _data = (E*)raw_reallocate(_data, sizeof(E), new_size); - if (_data == NULL) { - _data = old_ptr; - return false; - } else { - _max_size = new_size; - return true; - } - } - return false; - } - - void sort(FN_SORT fn) { - assert(_data != NULL, "Just check"); - qsort((void*)_data, _size, sizeof(E), fn); - } - - private: - bool expand_array() { - assert(_data != NULL, "Not yet allocated"); - E* old_ptr = _data; - if ((_data = (E*)raw_reallocate((void*)_data, sizeof(E), - _max_size + DEFAULT_PTR_ARRAY_SIZE)) == NULL) { - _data = old_ptr; - return false; - } else { - _max_size += DEFAULT_PTR_ARRAY_SIZE; - if (_init_elements) { - for (int index = _size; index < _max_size; index ++) { - ::new ((void*)&_data[index]) E(); - } - } - return true; - } - } - - void* raw_allocate(size_t elementSize, int items) { - return os::malloc(elementSize * items, mtNMT); - } - - void* raw_reallocate(void* ptr, size_t elementSize, int items) { - return os::realloc(ptr, elementSize * items, mtNMT); - } - - void raw_free(void* ptr) { - os::free(ptr, mtNMT); - } -}; - -#endif // SHARE_VM_UTILITIES_MEM_PTR_ARRAY_HPP diff --git a/hotspot/src/share/vm/services/memRecorder.cpp b/hotspot/src/share/vm/services/memRecorder.cpp deleted file mode 100644 index b8707f58360..00000000000 --- a/hotspot/src/share/vm/services/memRecorder.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2012, 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. - * - */ - -#include "precompiled.hpp" - -#include "runtime/atomic.inline.hpp" -#include "services/memBaseline.hpp" -#include "services/memRecorder.hpp" -#include "services/memPtr.hpp" -#include "services/memTracker.hpp" - -MemPointer* SequencedRecordIterator::next_record() { - MemPointerRecord* itr_cur = (MemPointerRecord*)_itr.current(); - if (itr_cur == NULL) { - return itr_cur; - } - - MemPointerRecord* itr_next = (MemPointerRecord*)_itr.next(); - - // don't collapse virtual memory records - while (itr_next != NULL && !itr_cur->is_vm_pointer() && - !itr_next->is_vm_pointer() && - same_kind(itr_cur, itr_next)) { - itr_cur = itr_next; - itr_next = (MemPointerRecord*)_itr.next(); - } - - return itr_cur; -} - - -volatile jint MemRecorder::_instance_count = 0; - -MemRecorder::MemRecorder() { - assert(MemTracker::is_on(), "Native memory tracking is off"); - Atomic::inc(&_instance_count); - set_generation(); - - if (MemTracker::track_callsite()) { - _pointer_records = new (std::nothrow)FixedSizeMemPointerArray(); - } else { - _pointer_records = new (std::nothrow)FixedSizeMemPointerArray(); - } - _next = NULL; - - - if (_pointer_records != NULL) { - // recode itself - address pc = CURRENT_PC; - record((address)this, (MemPointerRecord::malloc_tag()|mtNMT|otNMTRecorder), - sizeof(MemRecorder), SequenceGenerator::next(), pc); - record((address)_pointer_records, (MemPointerRecord::malloc_tag()|mtNMT|otNMTRecorder), - _pointer_records->instance_size(), SequenceGenerator::next(), pc); - } -} - -MemRecorder::~MemRecorder() { - if (_pointer_records != NULL) { - if (MemTracker::is_on()) { - MemTracker::record_free((address)_pointer_records, mtNMT); - MemTracker::record_free((address)this, mtNMT); - } - delete _pointer_records; - } - // delete all linked recorders - while (_next != NULL) { - MemRecorder* tmp = _next; - _next = _next->next(); - tmp->set_next(NULL); - delete tmp; - } - Atomic::dec(&_instance_count); -} - -// Sorting order: -// 1. memory block address -// 2. mem pointer record tags -// 3. sequence number -int MemRecorder::sort_record_fn(const void* e1, const void* e2) { - const MemPointerRecord* p1 = (const MemPointerRecord*)e1; - const MemPointerRecord* p2 = (const MemPointerRecord*)e2; - int delta = UNSIGNED_COMPARE(p1->addr(), p2->addr()); - if (delta == 0) { - int df = UNSIGNED_COMPARE((p1->flags() & MemPointerRecord::tag_masks), - (p2->flags() & MemPointerRecord::tag_masks)); - if (df == 0) { - assert(p1->seq() != p2->seq(), "dup seq"); - return p1->seq() - p2->seq(); - } else { - return df; - } - } else { - return delta; - } -} - -bool MemRecorder::record(address p, MEMFLAGS flags, size_t size, jint seq, address pc) { - assert(seq > 0, "No sequence number"); -#ifdef ASSERT - if (MemPointerRecord::is_virtual_memory_record(flags)) { - assert((flags & MemPointerRecord::tag_masks) != 0, "bad virtual memory record"); - } else { - assert((flags & MemPointerRecord::tag_masks) == MemPointerRecord::malloc_tag() || - (flags & MemPointerRecord::tag_masks) == MemPointerRecord::free_tag() || - IS_ARENA_OBJ(flags), - "bad malloc record"); - } - // a recorder should only hold records within the same generation - unsigned long cur_generation = SequenceGenerator::current_generation(); - assert(cur_generation == _generation, - "this thread did not enter sync point"); -#endif - - if (MemTracker::track_callsite()) { - SeqMemPointerRecordEx ap(p, flags, size, seq, pc); - debug_only(check_dup_seq(ap.seq());) - return _pointer_records->append(&ap); - } else { - SeqMemPointerRecord ap(p, flags, size, seq); - debug_only(check_dup_seq(ap.seq());) - return _pointer_records->append(&ap); - } -} - - // iterator for alloc pointers -SequencedRecordIterator MemRecorder::pointer_itr() { - assert(_pointer_records != NULL, "just check"); - _pointer_records->sort((FN_SORT)sort_record_fn); - return SequencedRecordIterator(_pointer_records); -} - - -void MemRecorder::set_generation() { - _generation = SequenceGenerator::current_generation(); -} - -#ifdef ASSERT - -void MemRecorder::check_dup_seq(jint seq) const { - MemPointerArrayIteratorImpl itr(_pointer_records); - MemPointerRecord* rc = (MemPointerRecord*)itr.current(); - while (rc != NULL) { - assert(rc->seq() != seq, "dup seq"); - rc = (MemPointerRecord*)itr.next(); - } -} - -#endif diff --git a/hotspot/src/share/vm/services/memRecorder.hpp b/hotspot/src/share/vm/services/memRecorder.hpp deleted file mode 100644 index 4329dad0267..00000000000 --- a/hotspot/src/share/vm/services/memRecorder.hpp +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2012, 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. - * - */ - -#ifndef SHARE_VM_SERVICES_MEM_RECORDER_HPP -#define SHARE_VM_SERVICES_MEM_RECORDER_HPP - -#include "memory/allocation.hpp" -#include "runtime/os.hpp" -#include "services/memPtrArray.hpp" - -class MemSnapshot; -class MemTracker; -class MemTrackWorker; - -// Fixed size memory pointer array implementation -template class FixedSizeMemPointerArray : - public MemPointerArray { - // This implementation is for memory recorder only - friend class MemRecorder; - - private: - E _data[SIZE]; - int _size; - - protected: - FixedSizeMemPointerArray(bool init_elements = false): - _size(0){ - if (init_elements) { - for (int index = 0; index < SIZE; index ++) { - ::new ((void*)&_data[index]) E(); - } - } - } - - void* operator new(size_t size, const std::nothrow_t& nothrow_constant) throw() { - // the instance is part of memRecorder, needs to be tagged with 'otNMTRecorder' - // to avoid recursion - return os::malloc(size, (mtNMT | otNMTRecorder)); - } - - void* operator new(size_t size) throw() { - assert(false, "use nothrow version"); - return NULL; - } - - void operator delete(void* p) { - os::free(p, (mtNMT | otNMTRecorder)); - } - - // instance size - inline size_t instance_size() const { - return sizeof(FixedSizeMemPointerArray); - } - - NOT_PRODUCT(int capacity() const { return SIZE; }) - - public: - // implementation of public interface - bool out_of_memory() const { return false; } - bool is_empty() const { return _size == 0; } - bool is_full() { return length() >= SIZE; } - int length() const { return _size; } - - void clear() { - _size = 0; - } - - bool append(MemPointer* ptr) { - if (is_full()) return false; - _data[_size ++] = *(E*)ptr; - return true; - } - - virtual bool insert_at(MemPointer* p, int pos) { - assert(false, "append only"); - return false; - } - - virtual bool remove_at(int pos) { - assert(false, "not supported"); - return false; - } - - MemPointer* at(int index) const { - assert(index >= 0 && index < length(), - "parameter check"); - return ((E*)&_data[index]); - } - - void sort(FN_SORT fn) { - qsort((void*)_data, _size, sizeof(E), fn); - } - - bool shrink() { - return false; - } -}; - - -// This iterator requires pre-sorted MemPointerArray, which is sorted by: -// 1. address -// 2. allocation type -// 3. sequence number -// During the array walking, iterator collapses pointers with the same -// address and allocation type, and only returns the one with highest -// sequence number. -// -// This is read-only iterator, update methods are asserted. -class SequencedRecordIterator : public MemPointerArrayIterator { - private: - MemPointerArrayIteratorImpl _itr; - MemPointer* _cur; - - public: - SequencedRecordIterator(const MemPointerArray* arr): - _itr(const_cast(arr)) { - _cur = next_record(); - } - - SequencedRecordIterator(const SequencedRecordIterator& itr): - _itr(itr._itr) { - _cur = next_record(); - } - - // return the pointer at current position - virtual MemPointer* current() const { - return _cur; - }; - - // return the next pointer and advance current position - virtual MemPointer* next() { - _cur = next_record(); - return _cur; - } - - // return the next pointer without advancing current position - virtual MemPointer* peek_next() const { - assert(false, "not implemented"); - return NULL; - - } - // return the previous pointer without changing current position - virtual MemPointer* peek_prev() const { - assert(false, "not implemented"); - return NULL; - } - - // remove the pointer at current position - virtual void remove() { - assert(false, "read-only iterator"); - }; - // insert the pointer at current position - virtual bool insert(MemPointer* ptr) { - assert(false, "read-only iterator"); - return false; - } - - virtual bool insert_after(MemPointer* ptr) { - assert(false, "read-only iterator"); - return false; - } - private: - // collapse the 'same kind' of records, and return this 'kind' of - // record with highest sequence number - MemPointer* next_record(); - - // Test if the two records are the same kind: the same memory block and allocation - // type. - inline bool same_kind(const MemPointerRecord* p1, const MemPointerRecord* p2) const { - assert(!p1->is_vm_pointer() && !p2->is_vm_pointer(), "malloc pointer only"); - return (p1->addr() == p2->addr() && - (p1->flags() &MemPointerRecord::tag_masks) == - (p2->flags() & MemPointerRecord::tag_masks)); - } -}; - - - -#define DEFAULT_RECORDER_PTR_ARRAY_SIZE 512 - -class MemRecorder : public CHeapObj { - friend class MemSnapshot; - friend class MemTracker; - friend class MemTrackWorker; - friend class GenerationData; - - protected: - // the array that holds memory records - MemPointerArray* _pointer_records; - - private: - // used for linked list - MemRecorder* _next; - // active recorder can only record a certain generation data - unsigned long _generation; - - protected: - _NOINLINE_ MemRecorder(); - ~MemRecorder(); - - // record a memory operation - bool record(address addr, MEMFLAGS flags, size_t size, jint seq, address caller_pc = 0); - - // linked list support - inline void set_next(MemRecorder* rec) { - _next = rec; - } - - inline MemRecorder* next() const { - return _next; - } - - // if the recorder is full - inline bool is_full() const { - assert(_pointer_records != NULL, "just check"); - return _pointer_records->is_full(); - } - - // if running out of memory when initializing recorder's internal - // data - inline bool out_of_memory() const { - return (_pointer_records == NULL || - _pointer_records->out_of_memory()); - } - - inline void clear() { - assert(_pointer_records != NULL, "Just check"); - _pointer_records->clear(); - } - - SequencedRecordIterator pointer_itr(); - - // return the generation of this recorder which it belongs to - unsigned long get_generation() const { return _generation; } - protected: - // number of MemRecorder instance - static volatile jint _instance_count; - - private: - // sorting function, sort records into following order - // 1. memory address - // 2. allocation type - // 3. sequence number - static int sort_record_fn(const void* e1, const void* e2); - - debug_only(void check_dup_seq(jint seq) const;) - void set_generation(); -}; - -#endif // SHARE_VM_SERVICES_MEM_RECORDER_HPP diff --git a/hotspot/src/share/vm/services/memReporter.cpp b/hotspot/src/share/vm/services/memReporter.cpp index 305693dad08..0269619178f 100644 --- a/hotspot/src/share/vm/services/memReporter.cpp +++ b/hotspot/src/share/vm/services/memReporter.cpp @@ -22,618 +22,595 @@ * */ #include "precompiled.hpp" -#include "classfile/systemDictionary.hpp" -#include "runtime/os.hpp" + +#include "memory/allocation.hpp" +#include "services/mallocTracker.hpp" #include "services/memReporter.hpp" -#include "services/memPtrArray.hpp" -#include "services/memTracker.hpp" +#include "services/virtualMemoryTracker.hpp" +#include "utilities/globalDefinitions.hpp" -PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC +size_t MemReporterBase::reserved_total(const MallocMemory* malloc, const VirtualMemory* vm) const { + return malloc->malloc_size() + malloc->arena_size() + vm->reserved(); +} -const char* BaselineOutputer::memory_unit(size_t scale) { - switch(scale) { - case K: return "KB"; - case M: return "MB"; - case G: return "GB"; +size_t MemReporterBase::committed_total(const MallocMemory* malloc, const VirtualMemory* vm) const { + return malloc->malloc_size() + malloc->arena_size() + vm->committed(); +} + +void MemReporterBase::print_total(size_t reserved, size_t committed) const { + const char* scale = current_scale(); + output()->print("reserved=" SIZE_FORMAT "%s, committed=" SIZE_FORMAT "%s", + amount_in_current_scale(reserved), scale, amount_in_current_scale(committed), scale); +} + +void MemReporterBase::print_malloc(size_t amount, size_t count) const { + const char* scale = current_scale(); + outputStream* out = output(); + out->print("(malloc=" SIZE_FORMAT "%s", + amount_in_current_scale(amount), scale); + + if (count > 0) { + out->print(" #" SIZE_FORMAT "", count); } - ShouldNotReachHere(); - return NULL; + + out->print(")"); +} + +void MemReporterBase::print_virtual_memory(size_t reserved, size_t committed) const { + const char* scale = current_scale(); + output()->print("(mmap: reserved=" SIZE_FORMAT "%s, committed=" SIZE_FORMAT "%s)", + amount_in_current_scale(reserved), scale, amount_in_current_scale(committed), scale); +} + +void MemReporterBase::print_malloc_line(size_t amount, size_t count) const { + output()->print("%28s", " "); + print_malloc(amount, count); + output()->print_cr(" "); +} + +void MemReporterBase::print_virtual_memory_line(size_t reserved, size_t committed) const { + output()->print("%28s", " "); + print_virtual_memory(reserved, committed); + output()->print_cr(" "); +} + +void MemReporterBase::print_arena_line(size_t amount, size_t count) const { + const char* scale = current_scale(); + output()->print_cr("%27s (arena=" SIZE_FORMAT "%s #" SIZE_FORMAT ")", " ", + amount_in_current_scale(amount), scale, count); +} + +void MemReporterBase::print_virtual_memory_region(const char* type, address base, size_t size) const { + const char* scale = current_scale(); + output()->print("[" PTR_FORMAT " - " PTR_FORMAT "] %s " SIZE_FORMAT "%s", + p2i(base), p2i(base + size), type, amount_in_current_scale(size), scale); } -void BaselineReporter::report_baseline(const MemBaseline& baseline, bool summary_only) { - assert(MemTracker::is_on(), "Native memory tracking is off"); - _outputer.start(scale()); - _outputer.total_usage( - amount_in_current_scale(baseline.total_malloc_amount() + baseline.total_reserved_amount()), - amount_in_current_scale(baseline.total_malloc_amount() + baseline.total_committed_amount())); +void MemSummaryReporter::report() { + const char* scale = current_scale(); + outputStream* out = output(); + size_t total_reserved_amount = _malloc_snapshot->total() + + _vm_snapshot->total_reserved(); + size_t total_committed_amount = _malloc_snapshot->total() + + _vm_snapshot->total_committed(); - _outputer.num_of_classes(baseline.number_of_classes()); - _outputer.num_of_threads(baseline.number_of_threads()); + // Overall total + out->print_cr("\nNative Memory Tracking:\n"); + out->print("Total: "); + print_total(total_reserved_amount, total_committed_amount); + out->print("\n"); - report_summaries(baseline); - if (!summary_only && MemTracker::track_callsite()) { - report_virtual_memory_map(baseline); - report_callsites(baseline); + // Summary by memory type + for (int index = 0; index < mt_number_of_types; index ++) { + MEMFLAGS flag = NMTUtil::index_to_flag(index); + // thread stack is reported as part of thread category + if (flag == mtThreadStack) continue; + MallocMemory* malloc_memory = _malloc_snapshot->by_type(flag); + VirtualMemory* virtual_memory = _vm_snapshot->by_type(flag); + + report_summary_of_type(flag, malloc_memory, virtual_memory); } - _outputer.done(); } -void BaselineReporter::report_summaries(const MemBaseline& baseline) { - _outputer.start_category_summary(); - MEMFLAGS type; +void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag, + MallocMemory* malloc_memory, VirtualMemory* virtual_memory) { - for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) { - type = MemBaseline::MemType2NameMap[index]._flag; - _outputer.category_summary(type, - amount_in_current_scale(baseline.reserved_amount(type)), - amount_in_current_scale(baseline.committed_amount(type)), - amount_in_current_scale(baseline.malloc_amount(type)), - baseline.malloc_count(type), - amount_in_current_scale(baseline.arena_amount(type)), - baseline.arena_count(type)); + size_t reserved_amount = reserved_total (malloc_memory, virtual_memory); + size_t committed_amount = committed_total(malloc_memory, virtual_memory); + + // Count thread's native stack in "Thread" category + if (flag == mtThread) { + const VirtualMemory* thread_stack_usage = + (const VirtualMemory*)_vm_snapshot->by_type(mtThreadStack); + reserved_amount += thread_stack_usage->reserved(); + committed_amount += thread_stack_usage->committed(); + } else if (flag == mtNMT) { + // Count malloc headers in "NMT" category + reserved_amount += _malloc_snapshot->malloc_overhead()->size(); + committed_amount += _malloc_snapshot->malloc_overhead()->size(); } - _outputer.done_category_summary(); -} + if (amount_in_current_scale(reserved_amount) > 0) { + outputStream* out = output(); + const char* scale = current_scale(); + out->print("-%26s (", NMTUtil::flag_to_name(flag)); + print_total(reserved_amount, committed_amount); + out->print_cr(")"); -void BaselineReporter::report_virtual_memory_map(const MemBaseline& baseline) { - _outputer.start_virtual_memory_map(); - MemBaseline* pBL = const_cast(&baseline); - MemPointerArrayIteratorImpl itr = MemPointerArrayIteratorImpl(pBL->_vm_map); - VMMemRegionEx* rgn = (VMMemRegionEx*)itr.current(); - while (rgn != NULL) { - if (rgn->is_reserved_region()) { - _outputer.reserved_memory_region(FLAGS_TO_MEMORY_TYPE(rgn->flags()), - rgn->base(), rgn->base() + rgn->size(), amount_in_current_scale(rgn->size()), rgn->pc()); - } else { - _outputer.committed_memory_region(rgn->base(), rgn->base() + rgn->size(), - amount_in_current_scale(rgn->size()), rgn->pc()); + if (flag == mtClass) { + // report class count + out->print_cr("%27s (classes #" SIZE_FORMAT ")", " ", _class_count); + } else if (flag == mtThread) { + // report thread count + out->print_cr("%27s (thread #" SIZE_FORMAT ")", " ", _malloc_snapshot->thread_count()); + const VirtualMemory* thread_stack_usage = + _vm_snapshot->by_type(mtThreadStack); + out->print("%27s (stack: ", " "); + print_total(thread_stack_usage->reserved(), thread_stack_usage->committed()); + out->print_cr(")"); } - rgn = (VMMemRegionEx*)itr.next(); - } - _outputer.done_virtual_memory_map(); + // report malloc'd memory + if (amount_in_current_scale(malloc_memory->malloc_size()) > 0) { + // We don't know how many arena chunks are in used, so don't report the count + size_t count = (flag == mtChunk) ? 0 : malloc_memory->malloc_count(); + print_malloc_line(malloc_memory->malloc_size(), count); + } + + if (amount_in_current_scale(virtual_memory->reserved()) > 0) { + print_virtual_memory_line(virtual_memory->reserved(), virtual_memory->committed()); + } + + if (amount_in_current_scale(malloc_memory->arena_size()) > 0) { + print_arena_line(malloc_memory->arena_size(), malloc_memory->arena_count()); + } + + if (flag == mtNMT && + amount_in_current_scale(_malloc_snapshot->malloc_overhead()->size()) > 0) { + out->print_cr("%27s (tracking overhead=" SIZE_FORMAT "%s)", " ", + amount_in_current_scale(_malloc_snapshot->malloc_overhead()->size()), scale); + } + + out->print_cr(" "); + } } -void BaselineReporter::report_callsites(const MemBaseline& baseline) { - _outputer.start_callsite(); - MemBaseline* pBL = const_cast(&baseline); +void MemDetailReporter::report_detail() { + // Start detail report + outputStream* out = output(); + out->print_cr("Details:\n"); - pBL->_malloc_cs->sort((FN_SORT)MemBaseline::bl_malloc_sort_by_size); - pBL->_vm_cs->sort((FN_SORT)MemBaseline::bl_vm_sort_by_size); - - // walk malloc callsites - MemPointerArrayIteratorImpl malloc_itr(pBL->_malloc_cs); - MallocCallsitePointer* malloc_callsite = - (MallocCallsitePointer*)malloc_itr.current(); - while (malloc_callsite != NULL) { - _outputer.malloc_callsite(malloc_callsite->addr(), - amount_in_current_scale(malloc_callsite->amount()), malloc_callsite->count()); - malloc_callsite = (MallocCallsitePointer*)malloc_itr.next(); - } - - // walk virtual memory callsite - MemPointerArrayIteratorImpl vm_itr(pBL->_vm_cs); - VMCallsitePointer* vm_callsite = (VMCallsitePointer*)vm_itr.current(); - while (vm_callsite != NULL) { - _outputer.virtual_memory_callsite(vm_callsite->addr(), - amount_in_current_scale(vm_callsite->reserved_amount()), - amount_in_current_scale(vm_callsite->committed_amount())); - vm_callsite = (VMCallsitePointer*)vm_itr.next(); - } - pBL->_malloc_cs->sort((FN_SORT)MemBaseline::bl_malloc_sort_by_pc); - pBL->_vm_cs->sort((FN_SORT)MemBaseline::bl_vm_sort_by_pc); - _outputer.done_callsite(); + report_malloc_sites(); + report_virtual_memory_allocation_sites(); } -void BaselineReporter::diff_baselines(const MemBaseline& cur, const MemBaseline& prev, - bool summary_only) { - assert(MemTracker::is_on(), "Native memory tracking is off"); - _outputer.start(scale()); - size_t total_reserved = cur.total_malloc_amount() + cur.total_reserved_amount(); - size_t total_committed = cur.total_malloc_amount() + cur.total_committed_amount(); +void MemDetailReporter::report_malloc_sites() { + MallocSiteIterator malloc_itr = _baseline.malloc_sites(MemBaseline::by_size); + if (malloc_itr.is_empty()) return; - _outputer.diff_total_usage( - amount_in_current_scale(total_reserved), amount_in_current_scale(total_committed), - diff_in_current_scale(total_reserved, (prev.total_malloc_amount() + prev.total_reserved_amount())), - diff_in_current_scale(total_committed, (prev.total_committed_amount() + prev.total_malloc_amount()))); + outputStream* out = output(); - _outputer.diff_num_of_classes(cur.number_of_classes(), - diff(cur.number_of_classes(), prev.number_of_classes())); - _outputer.diff_num_of_threads(cur.number_of_threads(), - diff(cur.number_of_threads(), prev.number_of_threads())); + const MallocSite* malloc_site; + while ((malloc_site = malloc_itr.next()) != NULL) { + // Don't report if size is too small + if (amount_in_current_scale(malloc_site->size()) == 0) + continue; - diff_summaries(cur, prev); - if (!summary_only && MemTracker::track_callsite()) { - diff_callsites(cur, prev); + const NativeCallStack* stack = malloc_site->call_stack(); + stack->print_on(out); + out->print("%29s", " "); + print_malloc(malloc_site->size(), malloc_site->count()); + out->print_cr("\n"); } - _outputer.done(); } -void BaselineReporter::diff_summaries(const MemBaseline& cur, const MemBaseline& prev) { - _outputer.start_category_summary(); - MEMFLAGS type; +void MemDetailReporter::report_virtual_memory_allocation_sites() { + VirtualMemorySiteIterator virtual_memory_itr = + _baseline.virtual_memory_sites(MemBaseline::by_size); - for (int index = 0; index < NUMBER_OF_MEMORY_TYPE; index ++) { - type = MemBaseline::MemType2NameMap[index]._flag; - _outputer.diff_category_summary(type, - amount_in_current_scale(cur.reserved_amount(type)), - amount_in_current_scale(cur.committed_amount(type)), - amount_in_current_scale(cur.malloc_amount(type)), - cur.malloc_count(type), - amount_in_current_scale(cur.arena_amount(type)), - cur.arena_count(type), - diff_in_current_scale(cur.reserved_amount(type), prev.reserved_amount(type)), - diff_in_current_scale(cur.committed_amount(type), prev.committed_amount(type)), - diff_in_current_scale(cur.malloc_amount(type), prev.malloc_amount(type)), - diff(cur.malloc_count(type), prev.malloc_count(type)), - diff_in_current_scale(cur.arena_amount(type), prev.arena_amount(type)), - diff(cur.arena_count(type), prev.arena_count(type))); + if (virtual_memory_itr.is_empty()) return; + + outputStream* out = output(); + const VirtualMemoryAllocationSite* virtual_memory_site; + + while ((virtual_memory_site = virtual_memory_itr.next()) != NULL) { + // Don't report if size is too small + if (amount_in_current_scale(virtual_memory_site->reserved()) == 0) + continue; + + const NativeCallStack* stack = virtual_memory_site->call_stack(); + stack->print_on(out); + out->print("%28s (", " "); + print_total(virtual_memory_site->reserved(), virtual_memory_site->committed()); + out->print_cr(")\n"); } - - _outputer.done_category_summary(); } -void BaselineReporter::diff_callsites(const MemBaseline& cur, const MemBaseline& prev) { - _outputer.start_callsite(); - MemBaseline* pBL_cur = const_cast(&cur); - MemBaseline* pBL_prev = const_cast(&prev); - // walk malloc callsites - MemPointerArrayIteratorImpl cur_malloc_itr(pBL_cur->_malloc_cs); - MemPointerArrayIteratorImpl prev_malloc_itr(pBL_prev->_malloc_cs); +void MemDetailReporter::report_virtual_memory_map() { + // Virtual memory map always in base address order + VirtualMemoryAllocationIterator itr = _baseline.virtual_memory_allocations(); + const ReservedMemoryRegion* rgn; - MallocCallsitePointer* cur_malloc_callsite = - (MallocCallsitePointer*)cur_malloc_itr.current(); - MallocCallsitePointer* prev_malloc_callsite = - (MallocCallsitePointer*)prev_malloc_itr.current(); + output()->print_cr("Virtual memory map:"); + while ((rgn = itr.next()) != NULL) { + report_virtual_memory_region(rgn); + } +} - while (cur_malloc_callsite != NULL || prev_malloc_callsite != NULL) { - if (prev_malloc_callsite == NULL) { - assert(cur_malloc_callsite != NULL, "sanity check"); - // this is a new callsite - _outputer.diff_malloc_callsite(cur_malloc_callsite->addr(), - amount_in_current_scale(cur_malloc_callsite->amount()), - cur_malloc_callsite->count(), - diff_in_current_scale(cur_malloc_callsite->amount(), 0), - diff(cur_malloc_callsite->count(), 0)); - cur_malloc_callsite = (MallocCallsitePointer*)cur_malloc_itr.next(); - } else if (cur_malloc_callsite == NULL) { - assert(prev_malloc_callsite != NULL, "Sanity check"); - // this callsite is already gone - _outputer.diff_malloc_callsite(prev_malloc_callsite->addr(), - 0, 0, - diff_in_current_scale(0, prev_malloc_callsite->amount()), - diff(0, prev_malloc_callsite->count())); - prev_malloc_callsite = (MallocCallsitePointer*)prev_malloc_itr.next(); +void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* reserved_rgn) { + assert(reserved_rgn != NULL, "NULL pointer"); + + // Don't report if size is too small + if (amount_in_current_scale(reserved_rgn->size()) == 0) return; + + outputStream* out = output(); + const char* scale = current_scale(); + const NativeCallStack* stack = reserved_rgn->call_stack(); + bool all_committed = reserved_rgn->all_committed(); + const char* region_type = (all_committed ? "reserved and committed" : "reserved"); + out->print_cr(" "); + print_virtual_memory_region(region_type, reserved_rgn->base(), reserved_rgn->size()); + out->print(" for %s", NMTUtil::flag_to_name(reserved_rgn->flag())); + if (stack->is_empty()) { + out->print_cr(" "); + } else { + out->print_cr(" from"); + stack->print_on(out, 4); + } + + if (all_committed) return; + + CommittedRegionIterator itr = reserved_rgn->iterate_committed_regions(); + const CommittedMemoryRegion* committed_rgn; + while ((committed_rgn = itr.next()) != NULL) { + // Don't report if size is too small + if (amount_in_current_scale(committed_rgn->size()) == 0) continue; + stack = committed_rgn->call_stack(); + out->print("\n\t"); + print_virtual_memory_region("committed", committed_rgn->base(), committed_rgn->size()); + if (stack->is_empty()) { + out->print_cr(" "); } else { - assert(cur_malloc_callsite != NULL, "Sanity check"); - assert(prev_malloc_callsite != NULL, "Sanity check"); - if (cur_malloc_callsite->addr() < prev_malloc_callsite->addr()) { - // this is a new callsite - _outputer.diff_malloc_callsite(cur_malloc_callsite->addr(), - amount_in_current_scale(cur_malloc_callsite->amount()), - cur_malloc_callsite->count(), - diff_in_current_scale(cur_malloc_callsite->amount(), 0), - diff(cur_malloc_callsite->count(), 0)); - cur_malloc_callsite = (MallocCallsitePointer*)cur_malloc_itr.next(); - } else if (cur_malloc_callsite->addr() > prev_malloc_callsite->addr()) { - // this callsite is already gone - _outputer.diff_malloc_callsite(prev_malloc_callsite->addr(), - 0, 0, - diff_in_current_scale(0, prev_malloc_callsite->amount()), - diff(0, prev_malloc_callsite->count())); - prev_malloc_callsite = (MallocCallsitePointer*)prev_malloc_itr.next(); + out->print_cr(" from"); + stack->print_on(out, 12); + } + } +} + +void MemSummaryDiffReporter::report_diff() { + const char* scale = current_scale(); + outputStream* out = output(); + out->print_cr("\nNative Memory Tracking:\n"); + + // Overall diff + out->print("Total: "); + print_virtual_memory_diff(_current_baseline.total_reserved_memory(), + _current_baseline.total_committed_memory(), _early_baseline.total_reserved_memory(), + _early_baseline.total_committed_memory()); + + out->print_cr("\n"); + + // Summary diff by memory type + for (int index = 0; index < mt_number_of_types; index ++) { + MEMFLAGS flag = NMTUtil::index_to_flag(index); + // thread stack is reported as part of thread category + if (flag == mtThreadStack) continue; + diff_summary_of_type(flag, _early_baseline.malloc_memory(flag), + _early_baseline.virtual_memory(flag), _current_baseline.malloc_memory(flag), + _current_baseline.virtual_memory(flag)); + } +} + +void MemSummaryDiffReporter::print_malloc_diff(size_t current_amount, size_t current_count, + size_t early_amount, size_t early_count) const { + const char* scale = current_scale(); + outputStream* out = output(); + + out->print("malloc=" SIZE_FORMAT "%s", amount_in_current_scale(current_amount), scale); + long amount_diff = diff_in_current_scale(current_amount, early_amount); + if (amount_diff != 0) { + out->print(" %+ld%s", amount_diff, scale); + } + if (current_count > 0) { + out->print(" #" SIZE_FORMAT "", current_count); + if (current_count != early_count) { + out->print(" %+d", (int)(current_count - early_count)); + } + } +} + +void MemSummaryDiffReporter::print_arena_diff(size_t current_amount, size_t current_count, + size_t early_amount, size_t early_count) const { + const char* scale = current_scale(); + outputStream* out = output(); + out->print("arena=" SIZE_FORMAT "%s", amount_in_current_scale(current_amount), scale); + if (diff_in_current_scale(current_amount, early_amount) != 0) { + out->print(" %+ld", diff_in_current_scale(current_amount, early_amount)); + } + + out->print(" #" SIZE_FORMAT "", current_count); + if (current_count != early_count) { + out->print(" %+d", (int)(current_count - early_count)); + } +} + +void MemSummaryDiffReporter::print_virtual_memory_diff(size_t current_reserved, size_t current_committed, + size_t early_reserved, size_t early_committed) const { + const char* scale = current_scale(); + outputStream* out = output(); + out->print("reserved=" SIZE_FORMAT "%s", amount_in_current_scale(current_reserved), scale); + long reserved_diff = diff_in_current_scale(current_reserved, early_reserved); + if (reserved_diff != 0) { + out->print(" %+ld%s", reserved_diff, scale); + } + + out->print(", committed=" SIZE_FORMAT "%s", amount_in_current_scale(current_committed), scale); + long committed_diff = diff_in_current_scale(current_committed, early_committed); + if (committed_diff != 0) { + out->print(" %+ld%s", committed_diff, scale); + } +} + + +void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag, const MallocMemory* early_malloc, + const VirtualMemory* early_vm, const MallocMemory* current_malloc, + const VirtualMemory* current_vm) const { + + outputStream* out = output(); + const char* scale = current_scale(); + + // Total reserved and committed memory in current baseline + size_t current_reserved_amount = reserved_total (current_malloc, current_vm); + size_t current_committed_amount = committed_total(current_malloc, current_vm); + + // Total reserved and committed memory in early baseline + size_t early_reserved_amount = reserved_total(early_malloc, early_vm); + size_t early_committed_amount = committed_total(early_malloc, early_vm); + + // Adjust virtual memory total + if (flag == mtThread) { + const VirtualMemory* early_thread_stack_usage = + _early_baseline.virtual_memory(mtThreadStack); + const VirtualMemory* current_thread_stack_usage = + _current_baseline.virtual_memory(mtThreadStack); + + early_reserved_amount += early_thread_stack_usage->reserved(); + early_committed_amount += early_thread_stack_usage->committed(); + + current_reserved_amount += current_thread_stack_usage->reserved(); + current_committed_amount += current_thread_stack_usage->committed(); + } else if (flag == mtNMT) { + early_reserved_amount += _early_baseline.malloc_tracking_overhead(); + early_committed_amount += _early_baseline.malloc_tracking_overhead(); + + current_reserved_amount += _current_baseline.malloc_tracking_overhead(); + current_committed_amount += _current_baseline.malloc_tracking_overhead(); + } + + if (amount_in_current_scale(current_reserved_amount) > 0 || + diff_in_current_scale(current_reserved_amount, early_reserved_amount) != 0) { + + // print summary line + out->print("-%26s (", NMTUtil::flag_to_name(flag)); + print_virtual_memory_diff(current_reserved_amount, current_committed_amount, + early_reserved_amount, early_committed_amount); + out->print_cr(")"); + + // detail lines + if (flag == mtClass) { + // report class count + out->print("%27s (classes #" SIZE_FORMAT "", " ", _current_baseline.class_count()); + int class_count_diff = (int)(_current_baseline.class_count() - + _early_baseline.class_count()); + if (_current_baseline.class_count() != _early_baseline.class_count()) { + out->print(" %+d", (int)(_current_baseline.class_count() - _early_baseline.class_count())); + } + out->print_cr(")"); + } else if (flag == mtThread) { + // report thread count + out->print("%27s (thread #" SIZE_FORMAT "", " ", _current_baseline.thread_count()); + int thread_count_diff = (int)(_current_baseline.thread_count() - + _early_baseline.thread_count()); + if (thread_count_diff != 0) { + out->print(" %+d", thread_count_diff); + } + out->print_cr(")"); + + // report thread stack + const VirtualMemory* current_thread_stack = + _current_baseline.virtual_memory(mtThreadStack); + const VirtualMemory* early_thread_stack = + _early_baseline.virtual_memory(mtThreadStack); + + out->print("%27s (stack: ", " "); + print_virtual_memory_diff(current_thread_stack->reserved(), current_thread_stack->committed(), + early_thread_stack->reserved(), early_thread_stack->committed()); + out->print_cr(")"); + } + + // Report malloc'd memory + size_t current_malloc_amount = current_malloc->malloc_size(); + size_t early_malloc_amount = early_malloc->malloc_size(); + if (amount_in_current_scale(current_malloc_amount) > 0 || + diff_in_current_scale(current_malloc_amount, early_malloc_amount) != 0) { + out->print("%28s(", " "); + print_malloc_diff(current_malloc_amount, (flag == mtChunk) ? 0 : current_malloc->malloc_count(), + early_malloc_amount, early_malloc->malloc_count()); + out->print_cr(")"); + } + + // Report virtual memory + if (amount_in_current_scale(current_vm->reserved()) > 0 || + diff_in_current_scale(current_vm->reserved(), early_vm->reserved()) != 0) { + out->print("%27s (mmap: ", " "); + print_virtual_memory_diff(current_vm->reserved(), current_vm->committed(), + early_vm->reserved(), early_vm->committed()); + out->print_cr(")"); + } + + // Report arena memory + if (amount_in_current_scale(current_malloc->arena_size()) > 0 || + diff_in_current_scale(current_malloc->arena_size(), early_malloc->arena_size()) != 0) { + out->print("%28s(", " "); + print_arena_diff(current_malloc->arena_size(), current_malloc->arena_count(), + early_malloc->arena_size(), early_malloc->arena_count()); + out->print_cr(")"); + } + + // Report native memory tracking overhead + if (flag == mtNMT) { + size_t current_tracking_overhead = amount_in_current_scale(_current_baseline.malloc_tracking_overhead()); + size_t early_tracking_overhead = amount_in_current_scale(_early_baseline.malloc_tracking_overhead()); + + out->print("%27s (tracking overhead=" SIZE_FORMAT "%s", " ", + amount_in_current_scale(_current_baseline.malloc_tracking_overhead()), scale); + + long overhead_diff = diff_in_current_scale(_current_baseline.malloc_tracking_overhead(), + _early_baseline.malloc_tracking_overhead()); + if (overhead_diff != 0) { + out->print(" %+ld%s", overhead_diff, scale); + } + out->print_cr(")"); + } + out->print_cr(" "); + } +} + +void MemDetailDiffReporter::report_diff() { + MemSummaryDiffReporter::report_diff(); + diff_malloc_sites(); + diff_virtual_memory_sites(); +} + +void MemDetailDiffReporter::diff_malloc_sites() const { + MallocSiteIterator early_itr = _early_baseline.malloc_sites(MemBaseline::by_site); + MallocSiteIterator current_itr = _current_baseline.malloc_sites(MemBaseline::by_site); + + const MallocSite* early_site = early_itr.next(); + const MallocSite* current_site = current_itr.next(); + + while (early_site != NULL || current_site != NULL) { + if (early_site == NULL) { + new_malloc_site(current_site); + current_site = current_itr.next(); + } else if (current_site == NULL) { + old_malloc_site(early_site); + early_site = early_itr.next(); + } else { + int compVal = current_site->call_stack()->compare(*early_site->call_stack()); + if (compVal < 0) { + new_malloc_site(current_site); + current_site = current_itr.next(); + } else if (compVal > 0) { + old_malloc_site(early_site); + early_site = early_itr.next(); } else { - // the same callsite - _outputer.diff_malloc_callsite(cur_malloc_callsite->addr(), - amount_in_current_scale(cur_malloc_callsite->amount()), - cur_malloc_callsite->count(), - diff_in_current_scale(cur_malloc_callsite->amount(), prev_malloc_callsite->amount()), - diff(cur_malloc_callsite->count(), prev_malloc_callsite->count())); - cur_malloc_callsite = (MallocCallsitePointer*)cur_malloc_itr.next(); - prev_malloc_callsite = (MallocCallsitePointer*)prev_malloc_itr.next(); + diff_malloc_site(early_site, current_site); + early_site = early_itr.next(); + current_site = current_itr.next(); } } } - - // walk virtual memory callsite - MemPointerArrayIteratorImpl cur_vm_itr(pBL_cur->_vm_cs); - MemPointerArrayIteratorImpl prev_vm_itr(pBL_prev->_vm_cs); - VMCallsitePointer* cur_vm_callsite = (VMCallsitePointer*)cur_vm_itr.current(); - VMCallsitePointer* prev_vm_callsite = (VMCallsitePointer*)prev_vm_itr.current(); - while (cur_vm_callsite != NULL || prev_vm_callsite != NULL) { - if (prev_vm_callsite == NULL || cur_vm_callsite->addr() < prev_vm_callsite->addr()) { - // this is a new callsite - _outputer.diff_virtual_memory_callsite(cur_vm_callsite->addr(), - amount_in_current_scale(cur_vm_callsite->reserved_amount()), - amount_in_current_scale(cur_vm_callsite->committed_amount()), - diff_in_current_scale(cur_vm_callsite->reserved_amount(), 0), - diff_in_current_scale(cur_vm_callsite->committed_amount(), 0)); - cur_vm_callsite = (VMCallsitePointer*)cur_vm_itr.next(); - } else if (cur_vm_callsite == NULL || cur_vm_callsite->addr() > prev_vm_callsite->addr()) { - // this callsite is already gone - _outputer.diff_virtual_memory_callsite(prev_vm_callsite->addr(), - amount_in_current_scale(0), - amount_in_current_scale(0), - diff_in_current_scale(0, prev_vm_callsite->reserved_amount()), - diff_in_current_scale(0, prev_vm_callsite->committed_amount())); - prev_vm_callsite = (VMCallsitePointer*)prev_vm_itr.next(); - } else { // the same callsite - _outputer.diff_virtual_memory_callsite(cur_vm_callsite->addr(), - amount_in_current_scale(cur_vm_callsite->reserved_amount()), - amount_in_current_scale(cur_vm_callsite->committed_amount()), - diff_in_current_scale(cur_vm_callsite->reserved_amount(), prev_vm_callsite->reserved_amount()), - diff_in_current_scale(cur_vm_callsite->committed_amount(), prev_vm_callsite->committed_amount())); - cur_vm_callsite = (VMCallsitePointer*)cur_vm_itr.next(); - prev_vm_callsite = (VMCallsitePointer*)prev_vm_itr.next(); - } - } - - _outputer.done_callsite(); } -size_t BaselineReporter::amount_in_current_scale(size_t amt) const { - return (size_t)(((float)amt/(float)_scale) + 0.5); -} +void MemDetailDiffReporter::diff_virtual_memory_sites() const { + VirtualMemorySiteIterator early_itr = _early_baseline.virtual_memory_sites(MemBaseline::by_site); + VirtualMemorySiteIterator current_itr = _current_baseline.virtual_memory_sites(MemBaseline::by_site); -int BaselineReporter::diff_in_current_scale(size_t value1, size_t value2) const { - return (int)(((float)value1 - (float)value2)/((float)_scale) + 0.5); -} + const VirtualMemoryAllocationSite* early_site = early_itr.next(); + const VirtualMemoryAllocationSite* current_site = current_itr.next(); -int BaselineReporter::diff(size_t value1, size_t value2) const { - return ((int)value1 - (int)value2); -} - -void BaselineTTYOutputer::start(size_t scale, bool report_diff) { - _scale = scale; - _output->print_cr(" "); - _output->print_cr("Native Memory Tracking:"); - _output->print_cr(" "); -} - -void BaselineTTYOutputer::done() { - -} - -void BaselineTTYOutputer::total_usage(size_t total_reserved, size_t total_committed) { - const char* unit = memory_unit(_scale); - _output->print_cr("Total: reserved=%d%s, committed=%d%s", - total_reserved, unit, total_committed, unit); -} - -void BaselineTTYOutputer::start_category_summary() { - _output->print_cr(" "); -} - -/** - * report a summary of memory type - */ -void BaselineTTYOutputer::category_summary(MEMFLAGS type, - size_t reserved_amt, size_t committed_amt, size_t malloc_amt, - size_t malloc_count, size_t arena_amt, size_t arena_count) { - - // we report mtThreadStack under mtThread category - if (type == mtThreadStack) { - assert(malloc_amt == 0 && malloc_count == 0 && arena_amt == 0, - "Just check"); - _thread_stack_reserved = reserved_amt; - _thread_stack_committed = committed_amt; - } else { - const char* unit = memory_unit(_scale); - size_t total_reserved = (reserved_amt + malloc_amt + arena_amt); - size_t total_committed = (committed_amt + malloc_amt + arena_amt); - if (type == mtThread) { - total_reserved += _thread_stack_reserved; - total_committed += _thread_stack_committed; - } - - if (total_reserved > 0) { - _output->print_cr("-%26s (reserved=%d%s, committed=%d%s)", - MemBaseline::type2name(type), total_reserved, unit, - total_committed, unit); - - if (type == mtClass) { - _output->print_cr("%27s (classes #%d)", " ", _num_of_classes); - } else if (type == mtThread) { - _output->print_cr("%27s (thread #%d)", " ", _num_of_threads); - _output->print_cr("%27s (stack: reserved=%d%s, committed=%d%s)", " ", - _thread_stack_reserved, unit, _thread_stack_committed, unit); - } - - if (malloc_amt > 0) { - if (type != mtChunk) { - _output->print_cr("%27s (malloc=%d%s, #%d)", " ", malloc_amt, unit, - malloc_count); - } else { - _output->print_cr("%27s (malloc=%d%s)", " ", malloc_amt, unit); - } - } - - if (reserved_amt > 0) { - _output->print_cr("%27s (mmap: reserved=%d%s, committed=%d%s)", - " ", reserved_amt, unit, committed_amt, unit); - } - - if (arena_amt > 0) { - _output->print_cr("%27s (arena=%d%s, #%d)", " ", arena_amt, unit, arena_count); - } - - _output->print_cr(" "); - } - } -} - -void BaselineTTYOutputer::done_category_summary() { - _output->print_cr(" "); -} - - -void BaselineTTYOutputer::start_virtual_memory_map() { - _output->print_cr("Virtual memory map:"); -} - -void BaselineTTYOutputer::reserved_memory_region(MEMFLAGS type, address base, address end, - size_t size, address pc) { - const char* unit = memory_unit(_scale); - char buf[128]; - int offset; - _output->print_cr(" "); - _output->print_cr("[" PTR_FORMAT " - " PTR_FORMAT "] reserved %d%s for %s", base, end, size, unit, - MemBaseline::type2name(type)); - if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) { - _output->print_cr("\t\tfrom [%s+0x%x]", buf, offset); - } -} - -void BaselineTTYOutputer::committed_memory_region(address base, address end, size_t size, address pc) { - const char* unit = memory_unit(_scale); - char buf[128]; - int offset; - _output->print("\t[" PTR_FORMAT " - " PTR_FORMAT "] committed %d%s", base, end, size, unit); - if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) { - _output->print_cr(" from [%s+0x%x]", buf, offset); - } -} - -void BaselineTTYOutputer::done_virtual_memory_map() { - _output->print_cr(" "); -} - - - -void BaselineTTYOutputer::start_callsite() { - _output->print_cr("Details:"); - _output->print_cr(" "); -} - -void BaselineTTYOutputer::done_callsite() { - _output->print_cr(" "); -} - -void BaselineTTYOutputer::malloc_callsite(address pc, size_t malloc_amt, - size_t malloc_count) { - if (malloc_amt > 0) { - const char* unit = memory_unit(_scale); - char buf[128]; - int offset; - if (pc == 0) { - _output->print("[BOOTSTRAP]%18s", " "); - } else if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) { - _output->print_cr("[" PTR_FORMAT "] %s+0x%x", pc, buf, offset); - _output->print("%28s", " "); + while (early_site != NULL || current_site != NULL) { + if (early_site == NULL) { + new_virtual_memory_site(current_site); + current_site = current_itr.next(); + } else if (current_site == NULL) { + old_virtual_memory_site(early_site); + early_site = early_itr.next(); } else { - _output->print("[" PTR_FORMAT "]%18s", pc, " "); + int compVal = current_site->call_stack()->compare(*early_site->call_stack()); + if (compVal < 0) { + new_virtual_memory_site(current_site); + current_site = current_itr.next(); + } else if (compVal > 0) { + old_virtual_memory_site(early_site); + early_site = early_itr.next(); + } else { + diff_virtual_memory_site(early_site, current_site); + early_site = early_itr.next(); + current_site = current_itr.next(); + } } - - _output->print_cr("(malloc=%d%s #%d)", malloc_amt, unit, malloc_count); - _output->print_cr(" "); } } -void BaselineTTYOutputer::virtual_memory_callsite(address pc, size_t reserved_amt, - size_t committed_amt) { - if (reserved_amt > 0) { - const char* unit = memory_unit(_scale); - char buf[128]; - int offset; - if (pc == 0) { - _output->print("[BOOTSTRAP]%18s", " "); - } else if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) { - _output->print_cr("[" PTR_FORMAT "] %s+0x%x", pc, buf, offset); - _output->print("%28s", " "); - } else { - _output->print("[" PTR_FORMAT "]%18s", pc, " "); - } - _output->print_cr("(mmap: reserved=%d%s, committed=%d%s)", - reserved_amt, unit, committed_amt, unit); - _output->print_cr(" "); - } +void MemDetailDiffReporter::new_malloc_site(const MallocSite* malloc_site) const { + diff_malloc_site(malloc_site->call_stack(), malloc_site->size(), malloc_site->count(), + 0, 0); } -void BaselineTTYOutputer::diff_total_usage(size_t total_reserved, - size_t total_committed, int reserved_diff, int committed_diff) { - const char* unit = memory_unit(_scale); - _output->print_cr("Total: reserved=%d%s %+d%s, committed=%d%s %+d%s", - total_reserved, unit, reserved_diff, unit, total_committed, unit, - committed_diff, unit); +void MemDetailDiffReporter::old_malloc_site(const MallocSite* malloc_site) const { + diff_malloc_site(malloc_site->call_stack(), 0, 0, malloc_site->size(), + malloc_site->count()); } -void BaselineTTYOutputer::diff_category_summary(MEMFLAGS type, - size_t cur_reserved_amt, size_t cur_committed_amt, - size_t cur_malloc_amt, size_t cur_malloc_count, - size_t cur_arena_amt, size_t cur_arena_count, - int reserved_diff, int committed_diff, int malloc_diff, - int malloc_count_diff, int arena_diff, int arena_count_diff) { +void MemDetailDiffReporter::diff_malloc_site(const MallocSite* early, + const MallocSite* current) const { + diff_malloc_site(current->call_stack(), current->size(), current->count(), + early->size(), early->count()); +} - if (type == mtThreadStack) { - assert(cur_malloc_amt == 0 && cur_malloc_count == 0 && - cur_arena_amt == 0, "Just check"); - _thread_stack_reserved = cur_reserved_amt; - _thread_stack_committed = cur_committed_amt; - _thread_stack_reserved_diff = reserved_diff; - _thread_stack_committed_diff = committed_diff; - } else { - const char* unit = memory_unit(_scale); - size_t total_reserved = (cur_reserved_amt + cur_malloc_amt + cur_arena_amt); - // nothing to report in this category - if (total_reserved == 0) { +void MemDetailDiffReporter::diff_malloc_site(const NativeCallStack* stack, size_t current_size, + size_t current_count, size_t early_size, size_t early_count) const { + outputStream* out = output(); + + assert(stack != NULL, "NULL stack"); + + if (diff_in_current_scale(current_size, early_size) == 0) { return; - } - int diff_reserved = (reserved_diff + malloc_diff + arena_diff); - - // category summary - _output->print("-%26s (reserved=%d%s", MemBaseline::type2name(type), - total_reserved, unit); - - if (diff_reserved != 0) { - _output->print(" %+d%s", diff_reserved, unit); - } - - size_t total_committed = cur_committed_amt + cur_malloc_amt + cur_arena_amt; - _output->print(", committed=%d%s", total_committed, unit); - - int total_committed_diff = committed_diff + malloc_diff + arena_diff; - if (total_committed_diff != 0) { - _output->print(" %+d%s", total_committed_diff, unit); - } - - _output->print_cr(")"); - - // special cases - if (type == mtClass) { - _output->print("%27s (classes #%d", " ", _num_of_classes); - if (_num_of_classes_diff != 0) { - _output->print(" %+d", _num_of_classes_diff); - } - _output->print_cr(")"); - } else if (type == mtThread) { - // thread count - _output->print("%27s (thread #%d", " ", _num_of_threads); - if (_num_of_threads_diff != 0) { - _output->print_cr(" %+d)", _num_of_threads_diff); - } else { - _output->print_cr(")"); - } - _output->print("%27s (stack: reserved=%d%s", " ", _thread_stack_reserved, unit); - if (_thread_stack_reserved_diff != 0) { - _output->print(" %+d%s", _thread_stack_reserved_diff, unit); - } - - _output->print(", committed=%d%s", _thread_stack_committed, unit); - if (_thread_stack_committed_diff != 0) { - _output->print(" %+d%s",_thread_stack_committed_diff, unit); - } - - _output->print_cr(")"); - } - - // malloc'd memory - if (cur_malloc_amt > 0) { - _output->print("%27s (malloc=%d%s", " ", cur_malloc_amt, unit); - if (malloc_diff != 0) { - _output->print(" %+d%s", malloc_diff, unit); - } - if (type != mtChunk) { - _output->print(", #%d", cur_malloc_count); - if (malloc_count_diff) { - _output->print(" %+d", malloc_count_diff); - } - } - _output->print_cr(")"); - } - - // mmap'd memory - if (cur_reserved_amt > 0) { - _output->print("%27s (mmap: reserved=%d%s", " ", cur_reserved_amt, unit); - if (reserved_diff != 0) { - _output->print(" %+d%s", reserved_diff, unit); - } - - _output->print(", committed=%d%s", cur_committed_amt, unit); - if (committed_diff != 0) { - _output->print(" %+d%s", committed_diff, unit); - } - _output->print_cr(")"); - } - - // arena memory - if (cur_arena_amt > 0) { - _output->print("%27s (arena=%d%s", " ", cur_arena_amt, unit); - if (arena_diff != 0) { - _output->print(" %+d%s", arena_diff, unit); - } - _output->print(", #%d", cur_arena_count); - if (arena_count_diff != 0) { - _output->print(" %+d", arena_count_diff); - } - _output->print_cr(")"); - } - - _output->print_cr(" "); } + + stack->print_on(out); + out->print("%28s (", " "); + print_malloc_diff(current_size, current_count, + early_size, early_count); + + out->print_cr(")\n"); } -void BaselineTTYOutputer::diff_malloc_callsite(address pc, - size_t cur_malloc_amt, size_t cur_malloc_count, - int malloc_diff, int malloc_count_diff) { - if (malloc_diff != 0) { - const char* unit = memory_unit(_scale); - char buf[128]; - int offset; - if (pc == 0) { - _output->print_cr("[BOOTSTRAP]%18s", " "); - } else { - if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) { - _output->print_cr("[" PTR_FORMAT "] %s+0x%x", pc, buf, offset); - _output->print("%28s", " "); - } else { - _output->print("[" PTR_FORMAT "]%18s", pc, " "); - } - } - _output->print("(malloc=%d%s", cur_malloc_amt, unit); - if (malloc_diff != 0) { - _output->print(" %+d%s", malloc_diff, unit); - } - _output->print(", #%d", cur_malloc_count); - if (malloc_count_diff != 0) { - _output->print(" %+d", malloc_count_diff); - } - _output->print_cr(")"); - _output->print_cr(" "); - } +void MemDetailDiffReporter::new_virtual_memory_site(const VirtualMemoryAllocationSite* site) const { + diff_virtual_memory_site(site->call_stack(), site->reserved(), site->committed(), 0, 0); } -void BaselineTTYOutputer::diff_virtual_memory_callsite(address pc, - size_t cur_reserved_amt, size_t cur_committed_amt, - int reserved_diff, int committed_diff) { - if (reserved_diff != 0 || committed_diff != 0) { - const char* unit = memory_unit(_scale); - char buf[64]; - int offset; - if (pc == 0) { - _output->print_cr("[BOOSTRAP]%18s", " "); - } else { - if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) { - _output->print_cr("[" PTR_FORMAT "] %s+0x%x", pc, buf, offset); - _output->print("%28s", " "); - } else { - _output->print("[" PTR_FORMAT "]%18s", pc, " "); - } - } - - _output->print("(mmap: reserved=%d%s", cur_reserved_amt, unit); - if (reserved_diff != 0) { - _output->print(" %+d%s", reserved_diff, unit); - } - _output->print(", committed=%d%s", cur_committed_amt, unit); - if (committed_diff != 0) { - _output->print(" %+d%s", committed_diff, unit); - } - _output->print_cr(")"); - _output->print_cr(" "); - } +void MemDetailDiffReporter::old_virtual_memory_site(const VirtualMemoryAllocationSite* site) const { + diff_virtual_memory_site(site->call_stack(), 0, 0, site->reserved(), site->committed()); } + +void MemDetailDiffReporter::diff_virtual_memory_site(const VirtualMemoryAllocationSite* early, + const VirtualMemoryAllocationSite* current) const { + diff_virtual_memory_site(current->call_stack(), current->reserved(), current->committed(), + early->reserved(), early->committed()); +} + +void MemDetailDiffReporter::diff_virtual_memory_site(const NativeCallStack* stack, size_t current_reserved, + size_t current_committed, size_t early_reserved, size_t early_committed) const { + outputStream* out = output(); + + // no change + if (diff_in_current_scale(current_reserved, early_reserved) == 0 && + diff_in_current_scale(current_committed, early_committed) == 0) { + return; + } + + stack->print_on(out); + out->print("%28s (mmap: ", " "); + print_virtual_memory_diff(current_reserved, current_committed, + early_reserved, early_committed); + + out->print_cr(")\n"); + } + diff --git a/hotspot/src/share/vm/services/memReporter.hpp b/hotspot/src/share/vm/services/memReporter.hpp index 805559b6fba..239fb945b33 100644 --- a/hotspot/src/share/vm/services/memReporter.hpp +++ b/hotspot/src/share/vm/services/memReporter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,262 +25,217 @@ #ifndef SHARE_VM_SERVICES_MEM_REPORTER_HPP #define SHARE_VM_SERVICES_MEM_REPORTER_HPP -#include "runtime/mutexLocker.hpp" -#include "services/memBaseline.hpp" -#include "services/memTracker.hpp" -#include "utilities/ostream.hpp" -#include "utilities/macros.hpp" - #if INCLUDE_NMT +#include "oops/instanceKlass.hpp" +#include "services/memBaseline.hpp" +#include "services/nmtCommon.hpp" +#include "services/mallocTracker.hpp" +#include "services/virtualMemoryTracker.hpp" + /* - * MemBaselineReporter reports data to this outputer class, - * ReportOutputer is responsible for format, store and redirect - * the data to the final destination. - */ -class BaselineOutputer : public StackObj { + * Base class that provides helpers +*/ +class MemReporterBase : public StackObj { + private: + size_t _scale; // report in this scale + outputStream* _output; // destination + public: - // start to report memory usage in specified scale. - // if report_diff = true, the reporter reports baseline comparison - // information. + MemReporterBase(outputStream* out = NULL, size_t scale = K) + : _scale(scale) { + _output = (out == NULL) ? tty : out; + } - virtual void start(size_t scale, bool report_diff = false) = 0; - // Done reporting - virtual void done() = 0; + protected: + inline outputStream* output() const { + return _output; + } + // Current reporting scale + inline const char* current_scale() const { + return NMTUtil::scale_name(_scale); + } + // Convert memory amount in bytes to current reporting scale + inline size_t amount_in_current_scale(size_t amount) const { + return NMTUtil::amount_in_scale(amount, _scale); + } - /* report baseline summary information */ - virtual void total_usage(size_t total_reserved, - size_t total_committed) = 0; - virtual void num_of_classes(size_t classes) = 0; - virtual void num_of_threads(size_t threads) = 0; + // Convert diff amount in bytes to current reporting scale + inline long diff_in_current_scale(size_t s1, size_t s2) const { + long amount = (long)(s1 - s2); + long scale = (long)_scale; + amount = (amount > 0) ? (amount + scale / 2) : (amount - scale / 2); + return amount / scale; + } - virtual void thread_info(size_t stack_reserved_amt, size_t stack_committed_amt) = 0; - - /* report baseline summary comparison */ - virtual void diff_total_usage(size_t total_reserved, - size_t total_committed, - int reserved_diff, - int committed_diff) = 0; - virtual void diff_num_of_classes(size_t classes, int diff) = 0; - virtual void diff_num_of_threads(size_t threads, int diff) = 0; - - virtual void diff_thread_info(size_t stack_reserved, size_t stack_committed, - int stack_reserved_diff, int stack_committed_diff) = 0; + // Helper functions + // Calculate total reserved and committed amount + size_t reserved_total(const MallocMemory* malloc, const VirtualMemory* vm) const; + size_t committed_total(const MallocMemory* malloc, const VirtualMemory* vm) const; - /* - * memory summary by memory types. - * for each memory type, following summaries are reported: - * - reserved amount, committed amount - * - malloc'd amount, malloc count - * - arena amount, arena count - */ + // Print summary total, malloc and virtual memory + void print_total(size_t reserved, size_t committed) const; + void print_malloc(size_t amount, size_t count) const; + void print_virtual_memory(size_t reserved, size_t committed) const; - // start reporting memory summary by memory type - virtual void start_category_summary() = 0; + void print_malloc_line(size_t amount, size_t count) const; + void print_virtual_memory_line(size_t reserved, size_t committed) const; + void print_arena_line(size_t amount, size_t count) const; - virtual void category_summary(MEMFLAGS type, size_t reserved_amt, - size_t committed_amt, - size_t malloc_amt, size_t malloc_count, - size_t arena_amt, size_t arena_count) = 0; - - virtual void diff_category_summary(MEMFLAGS type, size_t cur_reserved_amt, - size_t cur_committed_amt, - size_t cur_malloc_amt, size_t cur_malloc_count, - size_t cur_arena_amt, size_t cur_arena_count, - int reserved_diff, int committed_diff, int malloc_diff, - int malloc_count_diff, int arena_diff, - int arena_count_diff) = 0; - - virtual void done_category_summary() = 0; - - virtual void start_virtual_memory_map() = 0; - virtual void reserved_memory_region(MEMFLAGS type, address base, address end, size_t size, address pc) = 0; - virtual void committed_memory_region(address base, address end, size_t size, address pc) = 0; - virtual void done_virtual_memory_map() = 0; - - /* - * Report callsite information - */ - virtual void start_callsite() = 0; - virtual void malloc_callsite(address pc, size_t malloc_amt, size_t malloc_count) = 0; - virtual void virtual_memory_callsite(address pc, size_t reserved_amt, size_t committed_amt) = 0; - - virtual void diff_malloc_callsite(address pc, size_t cur_malloc_amt, size_t cur_malloc_count, - int malloc_diff, int malloc_count_diff) = 0; - virtual void diff_virtual_memory_callsite(address pc, size_t cur_reserved_amt, size_t cur_committed_amt, - int reserved_diff, int committed_diff) = 0; - - virtual void done_callsite() = 0; - - // return current scale in "KB", "MB" or "GB" - static const char* memory_unit(size_t scale); + void print_virtual_memory_region(const char* type, address base, size_t size) const; }; /* - * This class reports processed data from a baseline or - * the changes between the two baseline. + * The class is for generating summary tracking report. */ -class BaselineReporter : public StackObj { +class MemSummaryReporter : public MemReporterBase { private: - BaselineOutputer& _outputer; - size_t _scale; + MallocMemorySnapshot* _malloc_snapshot; + VirtualMemorySnapshot* _vm_snapshot; + size_t _class_count; public: - // construct a reporter that reports memory usage - // in specified scale - BaselineReporter(BaselineOutputer& outputer, size_t scale = K): - _outputer(outputer) { - _scale = scale; + // Report summary tracking data from global snapshots directly. + // This constructor is used for final reporting and hs_err reporting. + MemSummaryReporter(MallocMemorySnapshot* malloc_snapshot, + VirtualMemorySnapshot* vm_snapshot, outputStream* output, + size_t class_count = 0, size_t scale = K) : + MemReporterBase(output, scale), + _malloc_snapshot(malloc_snapshot), + _vm_snapshot(vm_snapshot) { + if (class_count == 0) { + _class_count = InstanceKlass::number_of_instance_classes(); + } else { + _class_count = class_count; + } } - virtual void report_baseline(const MemBaseline& baseline, bool summary_only = false); - virtual void diff_baselines(const MemBaseline& cur, const MemBaseline& prev, - bool summary_only = false); + // This constructor is for normal reporting from a recent baseline. + MemSummaryReporter(MemBaseline& baseline, outputStream* output, + size_t scale = K) : MemReporterBase(output, scale), + _malloc_snapshot(baseline.malloc_memory_snapshot()), + _vm_snapshot(baseline.virtual_memory_snapshot()), + _class_count(baseline.class_count()) { } - void set_scale(size_t scale); - size_t scale() const { return _scale; } + // Generate summary report + virtual void report(); private: - void report_summaries(const MemBaseline& baseline); - void report_virtual_memory_map(const MemBaseline& baseline); - void report_callsites(const MemBaseline& baseline); - - void diff_summaries(const MemBaseline& cur, const MemBaseline& prev); - void diff_callsites(const MemBaseline& cur, const MemBaseline& prev); - - // calculate memory size in current memory scale - size_t amount_in_current_scale(size_t amt) const; - // diff two unsigned values in current memory scale - int diff_in_current_scale(size_t value1, size_t value2) const; - // diff two unsigned value - int diff(size_t value1, size_t value2) const; + // Report summary for each memory type + void report_summary_of_type(MEMFLAGS type, MallocMemory* malloc_memory, + VirtualMemory* virtual_memory); }; /* - * tty output implementation. Native memory tracking - * DCmd uses this outputer. + * The class is for generating detail tracking report. */ -class BaselineTTYOutputer : public BaselineOutputer { +class MemDetailReporter : public MemSummaryReporter { private: - size_t _scale; - - size_t _num_of_classes; - size_t _num_of_threads; - size_t _thread_stack_reserved; - size_t _thread_stack_committed; - - int _num_of_classes_diff; - int _num_of_threads_diff; - int _thread_stack_reserved_diff; - int _thread_stack_committed_diff; - - outputStream* _output; + MemBaseline& _baseline; public: - BaselineTTYOutputer(outputStream* st) { - _scale = K; - _num_of_classes = 0; - _num_of_threads = 0; - _thread_stack_reserved = 0; - _thread_stack_committed = 0; - _num_of_classes_diff = 0; - _num_of_threads_diff = 0; - _thread_stack_reserved_diff = 0; - _thread_stack_committed_diff = 0; - _output = st; + MemDetailReporter(MemBaseline& baseline, outputStream* output, size_t scale = K) : + MemSummaryReporter(baseline, output, scale), + _baseline(baseline) { } + + // Generate detail report. + // The report contains summary and detail sections. + virtual void report() { + MemSummaryReporter::report(); + report_virtual_memory_map(); + report_detail(); } - // begin reporting memory usage in specified scale - void start(size_t scale, bool report_diff = false); - // done reporting - void done(); + private: + // Report detail tracking data. + void report_detail(); + // Report virtual memory map + void report_virtual_memory_map(); + // Report malloc allocation sites + void report_malloc_sites(); + // Report virtual memory reservation sites + void report_virtual_memory_allocation_sites(); - // total memory usage - void total_usage(size_t total_reserved, - size_t total_committed); - // report total loaded classes - void num_of_classes(size_t classes) { - _num_of_classes = classes; - } - - void num_of_threads(size_t threads) { - _num_of_threads = threads; - } - - void thread_info(size_t stack_reserved_amt, size_t stack_committed_amt) { - _thread_stack_reserved = stack_reserved_amt; - _thread_stack_committed = stack_committed_amt; - } - - void diff_total_usage(size_t total_reserved, - size_t total_committed, - int reserved_diff, - int committed_diff); - - void diff_num_of_classes(size_t classes, int diff) { - _num_of_classes = classes; - _num_of_classes_diff = diff; - } - - void diff_num_of_threads(size_t threads, int diff) { - _num_of_threads = threads; - _num_of_threads_diff = diff; - } - - void diff_thread_info(size_t stack_reserved_amt, size_t stack_committed_amt, - int stack_reserved_diff, int stack_committed_diff) { - _thread_stack_reserved = stack_reserved_amt; - _thread_stack_committed = stack_committed_amt; - _thread_stack_reserved_diff = stack_reserved_diff; - _thread_stack_committed_diff = stack_committed_diff; - } - - /* - * Report memory summary categoriuzed by memory types. - * For each memory type, following summaries are reported: - * - reserved amount, committed amount - * - malloc-ed amount, malloc count - * - arena amount, arena count - */ - // start reporting memory summary by memory type - void start_category_summary(); - void category_summary(MEMFLAGS type, size_t reserved_amt, size_t committed_amt, - size_t malloc_amt, size_t malloc_count, - size_t arena_amt, size_t arena_count); - - void diff_category_summary(MEMFLAGS type, size_t cur_reserved_amt, - size_t cur_committed_amt, - size_t cur_malloc_amt, size_t cur_malloc_count, - size_t cur_arena_amt, size_t cur_arena_count, - int reserved_diff, int committed_diff, int malloc_diff, - int malloc_count_diff, int arena_diff, - int arena_count_diff); - - void done_category_summary(); - - // virtual memory map - void start_virtual_memory_map(); - void reserved_memory_region(MEMFLAGS type, address base, address end, size_t size, address pc); - void committed_memory_region(address base, address end, size_t size, address pc); - void done_virtual_memory_map(); - - - /* - * Report callsite information - */ - void start_callsite(); - void malloc_callsite(address pc, size_t malloc_amt, size_t malloc_count); - void virtual_memory_callsite(address pc, size_t reserved_amt, size_t committed_amt); - - void diff_malloc_callsite(address pc, size_t cur_malloc_amt, size_t cur_malloc_count, - int malloc_diff, int malloc_count_diff); - void diff_virtual_memory_callsite(address pc, size_t cur_reserved_amt, size_t cur_committed_amt, - int reserved_diff, int committed_diff); - - void done_callsite(); + // Report a virtual memory region + void report_virtual_memory_region(const ReservedMemoryRegion* rgn); }; +/* + * The class is for generating summary comparison report. + * It compares current memory baseline against an early baseline. + */ +class MemSummaryDiffReporter : public MemReporterBase { + protected: + MemBaseline& _early_baseline; + MemBaseline& _current_baseline; + + public: + MemSummaryDiffReporter(MemBaseline& early_baseline, MemBaseline& current_baseline, + outputStream* output, size_t scale = K) : MemReporterBase(output, scale), + _early_baseline(early_baseline), _current_baseline(current_baseline) { + assert(early_baseline.baseline_type() != MemBaseline::Not_baselined, "Not baselined"); + assert(current_baseline.baseline_type() != MemBaseline::Not_baselined, "Not baselined"); + } + + // Generate summary comparison report + virtual void report_diff(); + + private: + // report the comparison of each memory type + void diff_summary_of_type(MEMFLAGS type, + const MallocMemory* early_malloc, const VirtualMemory* early_vm, + const MallocMemory* current_malloc, const VirtualMemory* current_vm) const; + + protected: + void print_malloc_diff(size_t current_amount, size_t current_count, + size_t early_amount, size_t early_count) const; + void print_virtual_memory_diff(size_t current_reserved, size_t current_committed, + size_t early_reserved, size_t early_committed) const; + void print_arena_diff(size_t current_amount, size_t current_count, + size_t early_amount, size_t early_count) const; +}; + +/* + * The class is for generating detail comparison report. + * It compares current memory baseline against an early baseline, + * both baselines have to be detail baseline. + */ +class MemDetailDiffReporter : public MemSummaryDiffReporter { + public: + MemDetailDiffReporter(MemBaseline& early_baseline, MemBaseline& current_baseline, + outputStream* output, size_t scale = K) : + MemSummaryDiffReporter(early_baseline, current_baseline, output, scale) { } + + // Generate detail comparison report + virtual void report_diff(); + + // Malloc allocation site comparison + void diff_malloc_sites() const; + // Virutal memory reservation site comparison + void diff_virtual_memory_sites() const; + + // New malloc allocation site in recent baseline + void new_malloc_site (const MallocSite* site) const; + // The malloc allocation site is not in recent baseline + void old_malloc_site (const MallocSite* site) const; + // Compare malloc allocation site, it is in both baselines + void diff_malloc_site(const MallocSite* early, const MallocSite* current) const; + + // New virtual memory allocation site in recent baseline + void new_virtual_memory_site (const VirtualMemoryAllocationSite* callsite) const; + // The virtual memory allocation site is not in recent baseline + void old_virtual_memory_site (const VirtualMemoryAllocationSite* callsite) const; + // Compare virtual memory allocation site, it is in both baseline + void diff_virtual_memory_site(const VirtualMemoryAllocationSite* early, + const VirtualMemoryAllocationSite* current) const; + + void diff_malloc_site(const NativeCallStack* stack, size_t current_size, + size_t currrent_count, size_t early_size, size_t early_count) const; + void diff_virtual_memory_site(const NativeCallStack* stack, size_t current_reserved, + size_t current_committed, size_t early_reserved, size_t early_committed) const; +}; #endif // INCLUDE_NMT -#endif // SHARE_VM_SERVICES_MEM_REPORTER_HPP +#endif + diff --git a/hotspot/src/share/vm/services/memSnapshot.cpp b/hotspot/src/share/vm/services/memSnapshot.cpp deleted file mode 100644 index 8f5ca4f4195..00000000000 --- a/hotspot/src/share/vm/services/memSnapshot.cpp +++ /dev/null @@ -1,748 +0,0 @@ -/* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "runtime/mutexLocker.hpp" -#include "utilities/decoder.hpp" -#include "services/memBaseline.hpp" -#include "services/memPtr.hpp" -#include "services/memPtrArray.hpp" -#include "services/memSnapshot.hpp" -#include "services/memTracker.hpp" - -PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC - -#ifdef ASSERT - -void decode_pointer_record(MemPointerRecord* rec) { - tty->print("Pointer: [" PTR_FORMAT " - " PTR_FORMAT "] size = %d bytes", rec->addr(), - rec->addr() + rec->size(), (int)rec->size()); - tty->print(" type = %s", MemBaseline::type2name(FLAGS_TO_MEMORY_TYPE(rec->flags()))); - if (rec->is_vm_pointer()) { - if (rec->is_allocation_record()) { - tty->print_cr(" (reserve)"); - } else if (rec->is_commit_record()) { - tty->print_cr(" (commit)"); - } else if (rec->is_uncommit_record()) { - tty->print_cr(" (uncommit)"); - } else if (rec->is_deallocation_record()) { - tty->print_cr(" (release)"); - } else { - tty->print_cr(" (tag)"); - } - } else { - if (rec->is_arena_memory_record()) { - tty->print_cr(" (arena size)"); - } else if (rec->is_allocation_record()) { - tty->print_cr(" (malloc)"); - } else { - tty->print_cr(" (free)"); - } - } - if (MemTracker::track_callsite()) { - char buf[1024]; - address pc = ((MemPointerRecordEx*)rec)->pc(); - if (pc != NULL && os::dll_address_to_function_name(pc, buf, sizeof(buf), NULL)) { - tty->print_cr("\tfrom %s", buf); - } else { - tty->print_cr("\tcould not decode pc = " PTR_FORMAT "", pc); - } - } -} - -void decode_vm_region_record(VMMemRegion* rec) { - tty->print("VM Region [" PTR_FORMAT " - " PTR_FORMAT "]", rec->addr(), - rec->addr() + rec->size()); - tty->print(" type = %s", MemBaseline::type2name(FLAGS_TO_MEMORY_TYPE(rec->flags()))); - if (rec->is_allocation_record()) { - tty->print_cr(" (reserved)"); - } else if (rec->is_commit_record()) { - tty->print_cr(" (committed)"); - } else { - ShouldNotReachHere(); - } - if (MemTracker::track_callsite()) { - char buf[1024]; - address pc = ((VMMemRegionEx*)rec)->pc(); - if (pc != NULL && os::dll_address_to_function_name(pc, buf, sizeof(buf), NULL)) { - tty->print_cr("\tfrom %s", buf); - } else { - tty->print_cr("\tcould not decode pc = " PTR_FORMAT "", pc); - } - - } -} - -#endif - - -bool VMMemPointerIterator::insert_record(MemPointerRecord* rec) { - VMMemRegionEx new_rec; - assert(rec->is_allocation_record() || rec->is_commit_record(), - "Sanity check"); - if (MemTracker::track_callsite()) { - new_rec.init((MemPointerRecordEx*)rec); - } else { - new_rec.init(rec); - } - return insert(&new_rec); -} - -bool VMMemPointerIterator::insert_record_after(MemPointerRecord* rec) { - VMMemRegionEx new_rec; - assert(rec->is_allocation_record() || rec->is_commit_record(), - "Sanity check"); - if (MemTracker::track_callsite()) { - new_rec.init((MemPointerRecordEx*)rec); - } else { - new_rec.init(rec); - } - return insert_after(&new_rec); -} - -// we don't consolidate reserved regions, since they may be categorized -// in different types. -bool VMMemPointerIterator::add_reserved_region(MemPointerRecord* rec) { - assert(rec->is_allocation_record(), "Sanity check"); - VMMemRegion* reserved_region = (VMMemRegion*)current(); - - // we don't have anything yet - if (reserved_region == NULL) { - return insert_record(rec); - } - - assert(reserved_region->is_reserved_region(), "Sanity check"); - // duplicated records - if (reserved_region->is_same_region(rec)) { - return true; - } - // Overlapping stack regions indicate that a JNI thread failed to - // detach from the VM before exiting. This leaks the JavaThread object. - if (CheckJNICalls) { - guarantee(FLAGS_TO_MEMORY_TYPE(reserved_region->flags()) != mtThreadStack || - !reserved_region->overlaps_region(rec), - "Attached JNI thread exited without being detached"); - } - // otherwise, we should not have overlapping reserved regions - assert(FLAGS_TO_MEMORY_TYPE(reserved_region->flags()) == mtThreadStack || - reserved_region->base() > rec->addr(), "Just check: locate()"); - assert(FLAGS_TO_MEMORY_TYPE(reserved_region->flags()) == mtThreadStack || - !reserved_region->overlaps_region(rec), "overlapping reserved regions"); - - return insert_record(rec); -} - -// we do consolidate committed regions -bool VMMemPointerIterator::add_committed_region(MemPointerRecord* rec) { - assert(rec->is_commit_record(), "Sanity check"); - VMMemRegion* reserved_rgn = (VMMemRegion*)current(); - assert(reserved_rgn->is_reserved_region() && reserved_rgn->contains_region(rec), - "Sanity check"); - - // thread's native stack is always marked as "committed", ignore - // the "commit" operation for creating stack guard pages - if (FLAGS_TO_MEMORY_TYPE(reserved_rgn->flags()) == mtThreadStack && - FLAGS_TO_MEMORY_TYPE(rec->flags()) != mtThreadStack) { - return true; - } - - // if the reserved region has any committed regions - VMMemRegion* committed_rgn = (VMMemRegion*)next(); - while (committed_rgn != NULL && committed_rgn->is_committed_region()) { - // duplicated commit records - if(committed_rgn->contains_region(rec)) { - return true; - } else if (committed_rgn->overlaps_region(rec)) { - // overlaps front part - if (rec->addr() < committed_rgn->addr()) { - committed_rgn->expand_region(rec->addr(), - committed_rgn->addr() - rec->addr()); - } else { - // overlaps tail part - address committed_rgn_end = committed_rgn->addr() + - committed_rgn->size(); - assert(committed_rgn_end < rec->addr() + rec->size(), - "overlap tail part"); - committed_rgn->expand_region(committed_rgn_end, - (rec->addr() + rec->size()) - committed_rgn_end); - } - } else if (committed_rgn->base() + committed_rgn->size() == rec->addr()) { - // adjunct each other - committed_rgn->expand_region(rec->addr(), rec->size()); - VMMemRegion* next_reg = (VMMemRegion*)next(); - // see if we can consolidate next committed region - if (next_reg != NULL && next_reg->is_committed_region() && - next_reg->base() == committed_rgn->base() + committed_rgn->size()) { - committed_rgn->expand_region(next_reg->base(), next_reg->size()); - // delete merged region - remove(); - } - return true; - } else if (committed_rgn->base() > rec->addr()) { - // found the location, insert this committed region - return insert_record(rec); - } - committed_rgn = (VMMemRegion*)next(); - } - return insert_record(rec); -} - -bool VMMemPointerIterator::remove_uncommitted_region(MemPointerRecord* rec) { - assert(rec->is_uncommit_record(), "sanity check"); - VMMemRegion* cur; - cur = (VMMemRegion*)current(); - assert(cur->is_reserved_region() && cur->contains_region(rec), - "Sanity check"); - // thread's native stack is always marked as "committed", ignore - // the "commit" operation for creating stack guard pages - if (FLAGS_TO_MEMORY_TYPE(cur->flags()) == mtThreadStack && - FLAGS_TO_MEMORY_TYPE(rec->flags()) != mtThreadStack) { - return true; - } - - cur = (VMMemRegion*)next(); - while (cur != NULL && cur->is_committed_region()) { - // region already uncommitted, must be due to duplicated record - if (cur->addr() >= rec->addr() + rec->size()) { - break; - } else if (cur->contains_region(rec)) { - // uncommit whole region - if (cur->is_same_region(rec)) { - remove(); - break; - } else if (rec->addr() == cur->addr() || - rec->addr() + rec->size() == cur->addr() + cur->size()) { - // uncommitted from either end of current memory region. - cur->exclude_region(rec->addr(), rec->size()); - break; - } else { // split the committed region and release the middle - address high_addr = cur->addr() + cur->size(); - size_t sz = high_addr - rec->addr(); - cur->exclude_region(rec->addr(), sz); - sz = high_addr - (rec->addr() + rec->size()); - if (MemTracker::track_callsite()) { - MemPointerRecordEx tmp(rec->addr() + rec->size(), cur->flags(), sz, - ((VMMemRegionEx*)cur)->pc()); - return insert_record_after(&tmp); - } else { - MemPointerRecord tmp(rec->addr() + rec->size(), cur->flags(), sz); - return insert_record_after(&tmp); - } - } - } - cur = (VMMemRegion*)next(); - } - - // we may not find committed record due to duplicated records - return true; -} - -bool VMMemPointerIterator::remove_released_region(MemPointerRecord* rec) { - assert(rec->is_deallocation_record(), "Sanity check"); - VMMemRegion* cur = (VMMemRegion*)current(); - assert(cur->is_reserved_region() && cur->contains_region(rec), - "Sanity check"); - if (rec->is_same_region(cur)) { - - // In snapshot, the virtual memory records are sorted in following orders: - // 1. virtual memory's base address - // 2. virtual memory reservation record, followed by commit records within this reservation. - // The commit records are also in base address order. - // When a reserved region is released, we want to remove the reservation record and all - // commit records following it. -#ifdef ASSERT - address low_addr = cur->addr(); - address high_addr = low_addr + cur->size(); -#endif - // remove virtual memory reservation record - remove(); - // remove committed regions within above reservation - VMMemRegion* next_region = (VMMemRegion*)current(); - while (next_region != NULL && next_region->is_committed_region()) { - assert(next_region->addr() >= low_addr && - next_region->addr() + next_region->size() <= high_addr, - "Range check"); - remove(); - next_region = (VMMemRegion*)current(); - } - } else if (rec->addr() == cur->addr() || - rec->addr() + rec->size() == cur->addr() + cur->size()) { - // released region is at either end of this region - cur->exclude_region(rec->addr(), rec->size()); - assert(check_reserved_region(), "Integrity check"); - } else { // split the reserved region and release the middle - address high_addr = cur->addr() + cur->size(); - size_t sz = high_addr - rec->addr(); - cur->exclude_region(rec->addr(), sz); - sz = high_addr - rec->addr() - rec->size(); - if (MemTracker::track_callsite()) { - MemPointerRecordEx tmp(rec->addr() + rec->size(), cur->flags(), sz, - ((VMMemRegionEx*)cur)->pc()); - bool ret = insert_reserved_region(&tmp); - assert(!ret || check_reserved_region(), "Integrity check"); - return ret; - } else { - MemPointerRecord tmp(rec->addr() + rec->size(), cur->flags(), sz); - bool ret = insert_reserved_region(&tmp); - assert(!ret || check_reserved_region(), "Integrity check"); - return ret; - } - } - return true; -} - -bool VMMemPointerIterator::insert_reserved_region(MemPointerRecord* rec) { - // skip all 'commit' records associated with previous reserved region - VMMemRegion* p = (VMMemRegion*)next(); - while (p != NULL && p->is_committed_region() && - p->base() + p->size() < rec->addr()) { - p = (VMMemRegion*)next(); - } - return insert_record(rec); -} - -bool VMMemPointerIterator::split_reserved_region(VMMemRegion* rgn, address new_rgn_addr, size_t new_rgn_size) { - assert(rgn->contains_region(new_rgn_addr, new_rgn_size), "Not fully contained"); - address pc = (MemTracker::track_callsite() ? ((VMMemRegionEx*)rgn)->pc() : NULL); - if (rgn->base() == new_rgn_addr) { // new region is at the beginning of the region - size_t sz = rgn->size() - new_rgn_size; - // the original region becomes 'new' region - rgn->exclude_region(new_rgn_addr + new_rgn_size, sz); - // remaining becomes next region - MemPointerRecordEx next_rgn(new_rgn_addr + new_rgn_size, rgn->flags(), sz, pc); - return insert_reserved_region(&next_rgn); - } else if (rgn->base() + rgn->size() == new_rgn_addr + new_rgn_size) { - rgn->exclude_region(new_rgn_addr, new_rgn_size); - MemPointerRecordEx next_rgn(new_rgn_addr, rgn->flags(), new_rgn_size, pc); - return insert_reserved_region(&next_rgn); - } else { - // the orginal region will be split into three - address rgn_high_addr = rgn->base() + rgn->size(); - // first region - rgn->exclude_region(new_rgn_addr, (rgn_high_addr - new_rgn_addr)); - // the second region is the new region - MemPointerRecordEx new_rgn(new_rgn_addr, rgn->flags(), new_rgn_size, pc); - if (!insert_reserved_region(&new_rgn)) return false; - // the remaining region - MemPointerRecordEx rem_rgn(new_rgn_addr + new_rgn_size, rgn->flags(), - rgn_high_addr - (new_rgn_addr + new_rgn_size), pc); - return insert_reserved_region(&rem_rgn); - } -} - -static int sort_in_seq_order(const void* p1, const void* p2) { - assert(p1 != NULL && p2 != NULL, "Sanity check"); - const MemPointerRecord* mp1 = (MemPointerRecord*)p1; - const MemPointerRecord* mp2 = (MemPointerRecord*)p2; - return (mp1->seq() - mp2->seq()); -} - -bool StagingArea::init() { - if (MemTracker::track_callsite()) { - _malloc_data = new (std::nothrow)MemPointerArrayImpl(); - _vm_data = new (std::nothrow)MemPointerArrayImpl(); - } else { - _malloc_data = new (std::nothrow)MemPointerArrayImpl(); - _vm_data = new (std::nothrow)MemPointerArrayImpl(); - } - - if (_malloc_data != NULL && _vm_data != NULL && - !_malloc_data->out_of_memory() && - !_vm_data->out_of_memory()) { - return true; - } else { - if (_malloc_data != NULL) delete _malloc_data; - if (_vm_data != NULL) delete _vm_data; - _malloc_data = NULL; - _vm_data = NULL; - return false; - } -} - - -VMRecordIterator StagingArea::virtual_memory_record_walker() { - MemPointerArray* arr = vm_data(); - // sort into seq number order - arr->sort((FN_SORT)sort_in_seq_order); - return VMRecordIterator(arr); -} - - -MemSnapshot::MemSnapshot() { - if (MemTracker::track_callsite()) { - _alloc_ptrs = new (std::nothrow) MemPointerArrayImpl(); - _vm_ptrs = new (std::nothrow)MemPointerArrayImpl(64, true); - } else { - _alloc_ptrs = new (std::nothrow) MemPointerArrayImpl(); - _vm_ptrs = new (std::nothrow)MemPointerArrayImpl(64, true); - } - - _staging_area.init(); - _lock = new (std::nothrow) Mutex(Monitor::max_nonleaf - 1, "memSnapshotLock"); - NOT_PRODUCT(_untracked_count = 0;) - _number_of_classes = 0; -} - -MemSnapshot::~MemSnapshot() { - assert(MemTracker::shutdown_in_progress(), "native memory tracking still on"); - { - MutexLockerEx locker(_lock); - if (_alloc_ptrs != NULL) { - delete _alloc_ptrs; - _alloc_ptrs = NULL; - } - - if (_vm_ptrs != NULL) { - delete _vm_ptrs; - _vm_ptrs = NULL; - } - } - - if (_lock != NULL) { - delete _lock; - _lock = NULL; - } -} - - -void MemSnapshot::copy_seq_pointer(MemPointerRecord* dest, const MemPointerRecord* src) { - assert(dest != NULL && src != NULL, "Just check"); - assert(dest->addr() == src->addr(), "Just check"); - assert(dest->seq() > 0 && src->seq() > 0, "not sequenced"); - - if (MemTracker::track_callsite()) { - *(SeqMemPointerRecordEx*)dest = *(SeqMemPointerRecordEx*)src; - } else { - *(SeqMemPointerRecord*)dest = *(SeqMemPointerRecord*)src; - } -} - -void MemSnapshot::assign_pointer(MemPointerRecord*dest, const MemPointerRecord* src) { - assert(src != NULL && dest != NULL, "Just check"); - assert(dest->seq() == 0 && src->seq() >0, "cast away sequence"); - - if (MemTracker::track_callsite()) { - *(MemPointerRecordEx*)dest = *(MemPointerRecordEx*)src; - } else { - *(MemPointerRecord*)dest = *(MemPointerRecord*)src; - } -} - -// merge a recorder to the staging area -bool MemSnapshot::merge(MemRecorder* rec) { - assert(rec != NULL && !rec->out_of_memory(), "Just check"); - - SequencedRecordIterator itr(rec->pointer_itr()); - - MutexLockerEx lock(_lock, true); - MemPointerIterator malloc_staging_itr(_staging_area.malloc_data()); - MemPointerRecord* incoming_rec = (MemPointerRecord*) itr.current(); - MemPointerRecord* matched_rec; - - while (incoming_rec != NULL) { - if (incoming_rec->is_vm_pointer()) { - // we don't do anything with virtual memory records during merge - if (!_staging_area.vm_data()->append(incoming_rec)) { - return false; - } - } else { - // locate matched record and/or also position the iterator to proper - // location for this incoming record. - matched_rec = (MemPointerRecord*)malloc_staging_itr.locate(incoming_rec->addr()); - // we have not seen this memory block in this generation, - // so just add to staging area - if (matched_rec == NULL) { - if (!malloc_staging_itr.insert(incoming_rec)) { - return false; - } - } else if (incoming_rec->addr() == matched_rec->addr()) { - // whoever has higher sequence number wins - if (incoming_rec->seq() > matched_rec->seq()) { - copy_seq_pointer(matched_rec, incoming_rec); - } - } else if (incoming_rec->addr() < matched_rec->addr()) { - if (!malloc_staging_itr.insert(incoming_rec)) { - return false; - } - } else { - ShouldNotReachHere(); - } - } - incoming_rec = (MemPointerRecord*)itr.next(); - } - NOT_PRODUCT(void check_staging_data();) - return true; -} - - -// promote data to next generation -bool MemSnapshot::promote(int number_of_classes) { - assert(_alloc_ptrs != NULL && _vm_ptrs != NULL, "Just check"); - assert(_staging_area.malloc_data() != NULL && _staging_area.vm_data() != NULL, - "Just check"); - MutexLockerEx lock(_lock, true); - - MallocRecordIterator malloc_itr = _staging_area.malloc_record_walker(); - bool promoted = false; - if (promote_malloc_records(&malloc_itr)) { - VMRecordIterator vm_itr = _staging_area.virtual_memory_record_walker(); - if (promote_virtual_memory_records(&vm_itr)) { - promoted = true; - } - } - - NOT_PRODUCT(check_malloc_pointers();) - _staging_area.clear(); - _number_of_classes = number_of_classes; - return promoted; -} - -bool MemSnapshot::promote_malloc_records(MemPointerArrayIterator* itr) { - MemPointerIterator malloc_snapshot_itr(_alloc_ptrs); - MemPointerRecord* new_rec = (MemPointerRecord*)itr->current(); - MemPointerRecord* matched_rec; - while (new_rec != NULL) { - matched_rec = (MemPointerRecord*)malloc_snapshot_itr.locate(new_rec->addr()); - // found matched memory block - if (matched_rec != NULL && new_rec->addr() == matched_rec->addr()) { - // snapshot already contains 'live' records - assert(matched_rec->is_allocation_record() || matched_rec->is_arena_memory_record(), - "Sanity check"); - // update block states - if (new_rec->is_allocation_record()) { - assign_pointer(matched_rec, new_rec); - } else if (new_rec->is_arena_memory_record()) { - if (new_rec->size() == 0) { - // remove size record once size drops to 0 - malloc_snapshot_itr.remove(); - } else { - assign_pointer(matched_rec, new_rec); - } - } else { - // a deallocation record - assert(new_rec->is_deallocation_record(), "Sanity check"); - // an arena record can be followed by a size record, we need to remove both - if (matched_rec->is_arena_record()) { - MemPointerRecord* next = (MemPointerRecord*)malloc_snapshot_itr.peek_next(); - if (next != NULL && next->is_arena_memory_record() && - next->is_memory_record_of_arena(matched_rec)) { - malloc_snapshot_itr.remove(); - } - } - // the memory is deallocated, remove related record(s) - malloc_snapshot_itr.remove(); - } - } else { - // don't insert size 0 record - if (new_rec->is_arena_memory_record() && new_rec->size() == 0) { - new_rec = NULL; - } - - if (new_rec != NULL) { - if (new_rec->is_allocation_record() || new_rec->is_arena_memory_record()) { - if (matched_rec != NULL && new_rec->addr() > matched_rec->addr()) { - if (!malloc_snapshot_itr.insert_after(new_rec)) { - return false; - } - } else { - if (!malloc_snapshot_itr.insert(new_rec)) { - return false; - } - } - } -#ifndef PRODUCT - else if (!has_allocation_record(new_rec->addr())) { - // NMT can not track some startup memory, which is allocated before NMT is on - _untracked_count ++; - } -#endif - } - } - new_rec = (MemPointerRecord*)itr->next(); - } - return true; -} - -bool MemSnapshot::promote_virtual_memory_records(MemPointerArrayIterator* itr) { - VMMemPointerIterator vm_snapshot_itr(_vm_ptrs); - MemPointerRecord* new_rec = (MemPointerRecord*)itr->current(); - VMMemRegion* reserved_rec; - while (new_rec != NULL) { - assert(new_rec->is_vm_pointer(), "Sanity check"); - - // locate a reserved region that contains the specified address, or - // the nearest reserved region has base address just above the specified - // address - reserved_rec = (VMMemRegion*)vm_snapshot_itr.locate(new_rec->addr()); - if (reserved_rec != NULL && reserved_rec->contains_region(new_rec)) { - // snapshot can only have 'live' records - assert(reserved_rec->is_reserved_region(), "Sanity check"); - if (new_rec->is_allocation_record()) { - if (!reserved_rec->is_same_region(new_rec)) { - // only deal with split a bigger reserved region into smaller regions. - // So far, CDS is the only use case. - if (!vm_snapshot_itr.split_reserved_region(reserved_rec, new_rec->addr(), new_rec->size())) { - return false; - } - } - } else if (new_rec->is_uncommit_record()) { - if (!vm_snapshot_itr.remove_uncommitted_region(new_rec)) { - return false; - } - } else if (new_rec->is_commit_record()) { - // insert or expand existing committed region to cover this - // newly committed region - if (!vm_snapshot_itr.add_committed_region(new_rec)) { - return false; - } - } else if (new_rec->is_deallocation_record()) { - // release part or all memory region - if (!vm_snapshot_itr.remove_released_region(new_rec)) { - return false; - } - } else if (new_rec->is_type_tagging_record()) { - // tag this reserved virtual memory range to a memory type. Can not re-tag a memory range - // to different type. - assert(FLAGS_TO_MEMORY_TYPE(reserved_rec->flags()) == mtNone || - FLAGS_TO_MEMORY_TYPE(reserved_rec->flags()) == FLAGS_TO_MEMORY_TYPE(new_rec->flags()), - "Sanity check"); - reserved_rec->tag(new_rec->flags()); - } else { - ShouldNotReachHere(); - } - } else { - /* - * The assertion failure indicates mis-matched virtual memory records. The likely - * scenario is, that some virtual memory operations are not going through os::xxxx_memory() - * api, which have to be tracked manually. (perfMemory is an example). - */ - assert(new_rec->is_allocation_record(), "Sanity check"); - if (!vm_snapshot_itr.add_reserved_region(new_rec)) { - return false; - } - } - new_rec = (MemPointerRecord*)itr->next(); - } - return true; -} - -#ifndef PRODUCT -void MemSnapshot::print_snapshot_stats(outputStream* st) { - st->print_cr("Snapshot:"); - st->print_cr("\tMalloced: %d/%d [%5.2f%%] %dKB", _alloc_ptrs->length(), _alloc_ptrs->capacity(), - (100.0 * (float)_alloc_ptrs->length()) / (float)_alloc_ptrs->capacity(), _alloc_ptrs->instance_size()/K); - - st->print_cr("\tVM: %d/%d [%5.2f%%] %dKB", _vm_ptrs->length(), _vm_ptrs->capacity(), - (100.0 * (float)_vm_ptrs->length()) / (float)_vm_ptrs->capacity(), _vm_ptrs->instance_size()/K); - - st->print_cr("\tMalloc staging Area: %d/%d [%5.2f%%] %dKB", _staging_area.malloc_data()->length(), - _staging_area.malloc_data()->capacity(), - (100.0 * (float)_staging_area.malloc_data()->length()) / (float)_staging_area.malloc_data()->capacity(), - _staging_area.malloc_data()->instance_size()/K); - - st->print_cr("\tVirtual memory staging Area: %d/%d [%5.2f%%] %dKB", _staging_area.vm_data()->length(), - _staging_area.vm_data()->capacity(), - (100.0 * (float)_staging_area.vm_data()->length()) / (float)_staging_area.vm_data()->capacity(), - _staging_area.vm_data()->instance_size()/K); - - st->print_cr("\tUntracked allocation: %d", _untracked_count); -} - -void MemSnapshot::check_malloc_pointers() { - MemPointerArrayIteratorImpl mItr(_alloc_ptrs); - MemPointerRecord* p = (MemPointerRecord*)mItr.current(); - MemPointerRecord* prev = NULL; - while (p != NULL) { - if (prev != NULL) { - assert(p->addr() >= prev->addr(), "sorting order"); - } - prev = p; - p = (MemPointerRecord*)mItr.next(); - } -} - -bool MemSnapshot::has_allocation_record(address addr) { - MemPointerArrayIteratorImpl itr(_staging_area.malloc_data()); - MemPointerRecord* cur = (MemPointerRecord*)itr.current(); - while (cur != NULL) { - if (cur->addr() == addr && cur->is_allocation_record()) { - return true; - } - cur = (MemPointerRecord*)itr.next(); - } - return false; -} -#endif // PRODUCT - -#ifdef ASSERT -void MemSnapshot::check_staging_data() { - MemPointerArrayIteratorImpl itr(_staging_area.malloc_data()); - MemPointerRecord* cur = (MemPointerRecord*)itr.current(); - MemPointerRecord* next = (MemPointerRecord*)itr.next(); - while (next != NULL) { - assert((next->addr() > cur->addr()) || - ((next->flags() & MemPointerRecord::tag_masks) > - (cur->flags() & MemPointerRecord::tag_masks)), - "sorting order"); - cur = next; - next = (MemPointerRecord*)itr.next(); - } - - MemPointerArrayIteratorImpl vm_itr(_staging_area.vm_data()); - cur = (MemPointerRecord*)vm_itr.current(); - while (cur != NULL) { - assert(cur->is_vm_pointer(), "virtual memory pointer only"); - cur = (MemPointerRecord*)vm_itr.next(); - } -} - -void MemSnapshot::dump_all_vm_pointers() { - MemPointerArrayIteratorImpl itr(_vm_ptrs); - VMMemRegion* ptr = (VMMemRegion*)itr.current(); - tty->print_cr("dump virtual memory pointers:"); - while (ptr != NULL) { - if (ptr->is_committed_region()) { - tty->print("\t"); - } - tty->print("[" PTR_FORMAT " - " PTR_FORMAT "] [%x]", ptr->addr(), - (ptr->addr() + ptr->size()), ptr->flags()); - - if (MemTracker::track_callsite()) { - VMMemRegionEx* ex = (VMMemRegionEx*)ptr; - if (ex->pc() != NULL) { - char buf[1024]; - if (os::dll_address_to_function_name(ex->pc(), buf, sizeof(buf), NULL)) { - tty->print_cr("\t%s", buf); - } else { - tty->cr(); - } - } - } - - ptr = (VMMemRegion*)itr.next(); - } - tty->flush(); -} -#endif // ASSERT - diff --git a/hotspot/src/share/vm/services/memSnapshot.hpp b/hotspot/src/share/vm/services/memSnapshot.hpp deleted file mode 100644 index f7d07568c6d..00000000000 --- a/hotspot/src/share/vm/services/memSnapshot.hpp +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (c) 2012, 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. - * - */ - -#ifndef SHARE_VM_SERVICES_MEM_SNAPSHOT_HPP -#define SHARE_VM_SERVICES_MEM_SNAPSHOT_HPP - -#include "memory/allocation.hpp" -#include "runtime/mutex.hpp" -#include "runtime/mutexLocker.hpp" -#include "services/memBaseline.hpp" -#include "services/memPtrArray.hpp" - -// Snapshot pointer array iterator - -// The pointer array contains malloc-ed pointers -class MemPointerIterator : public MemPointerArrayIteratorImpl { - public: - MemPointerIterator(MemPointerArray* arr): - MemPointerArrayIteratorImpl(arr) { - assert(arr != NULL, "null array"); - } - -#ifdef ASSERT - virtual bool is_dup_pointer(const MemPointer* ptr1, - const MemPointer* ptr2) const { - MemPointerRecord* p1 = (MemPointerRecord*)ptr1; - MemPointerRecord* p2 = (MemPointerRecord*)ptr2; - - if (p1->addr() != p2->addr()) return false; - if ((p1->flags() & MemPointerRecord::tag_masks) != - (p2->flags() & MemPointerRecord::tag_masks)) { - return false; - } - // we do see multiple commit/uncommit on the same memory, it is ok - return (p1->flags() & MemPointerRecord::tag_masks) == MemPointerRecord::tag_alloc || - (p1->flags() & MemPointerRecord::tag_masks) == MemPointerRecord::tag_release; - } - - virtual bool insert(MemPointer* ptr) { - if (_pos > 0) { - MemPointer* p1 = (MemPointer*)ptr; - MemPointer* p2 = (MemPointer*)_array->at(_pos - 1); - assert(!is_dup_pointer(p1, p2), - err_msg("duplicated pointer, flag = [%x]", (unsigned int)((MemPointerRecord*)p1)->flags())); - } - if (_pos < _array->length() -1) { - MemPointer* p1 = (MemPointer*)ptr; - MemPointer* p2 = (MemPointer*)_array->at(_pos + 1); - assert(!is_dup_pointer(p1, p2), - err_msg("duplicated pointer, flag = [%x]", (unsigned int)((MemPointerRecord*)p1)->flags())); - } - return _array->insert_at(ptr, _pos); - } - - virtual bool insert_after(MemPointer* ptr) { - if (_pos > 0) { - MemPointer* p1 = (MemPointer*)ptr; - MemPointer* p2 = (MemPointer*)_array->at(_pos - 1); - assert(!is_dup_pointer(p1, p2), - err_msg("duplicated pointer, flag = [%x]", (unsigned int)((MemPointerRecord*)p1)->flags())); - } - if (_pos < _array->length() - 1) { - MemPointer* p1 = (MemPointer*)ptr; - MemPointer* p2 = (MemPointer*)_array->at(_pos + 1); - - assert(!is_dup_pointer(p1, p2), - err_msg("duplicated pointer, flag = [%x]", (unsigned int)((MemPointerRecord*)p1)->flags())); - } - if (_array->insert_at(ptr, _pos + 1)) { - _pos ++; - return true; - } - return false; - } -#endif - - virtual MemPointer* locate(address addr) { - MemPointer* cur = current(); - while (cur != NULL && cur->addr() < addr) { - cur = next(); - } - return cur; - } -}; - -class VMMemPointerIterator : public MemPointerIterator { - public: - VMMemPointerIterator(MemPointerArray* arr): - MemPointerIterator(arr) { - } - - // locate an existing reserved memory region that contains specified address, - // or the reserved region just above this address, where the incoming - // reserved region should be inserted. - virtual MemPointer* locate(address addr) { - reset(); - VMMemRegion* reg = (VMMemRegion*)current(); - while (reg != NULL) { - if (reg->is_reserved_region()) { - if (reg->contains_address(addr) || addr < reg->base()) { - return reg; - } - } - reg = (VMMemRegion*)next(); - } - return NULL; - } - - // following methods update virtual memory in the context - // of 'current' position, which is properly positioned by - // callers via locate method. - bool add_reserved_region(MemPointerRecord* rec); - bool add_committed_region(MemPointerRecord* rec); - bool remove_uncommitted_region(MemPointerRecord* rec); - bool remove_released_region(MemPointerRecord* rec); - - // split a reserved region to create a new memory region with specified base and size - bool split_reserved_region(VMMemRegion* rgn, address new_rgn_addr, size_t new_rgn_size); - private: - bool insert_record(MemPointerRecord* rec); - bool insert_record_after(MemPointerRecord* rec); - - bool insert_reserved_region(MemPointerRecord* rec); - - // reset current position - inline void reset() { _pos = 0; } -#ifdef ASSERT - // check integrity of records on current reserved memory region. - bool check_reserved_region() { - VMMemRegion* reserved_region = (VMMemRegion*)current(); - assert(reserved_region != NULL && reserved_region->is_reserved_region(), - "Sanity check"); - // all committed regions that follow current reserved region, should all - // belong to the reserved region. - VMMemRegion* next_region = (VMMemRegion*)next(); - for (; next_region != NULL && next_region->is_committed_region(); - next_region = (VMMemRegion*)next() ) { - if(!reserved_region->contains_region(next_region)) { - return false; - } - } - return true; - } - - virtual bool is_dup_pointer(const MemPointer* ptr1, - const MemPointer* ptr2) const { - VMMemRegion* p1 = (VMMemRegion*)ptr1; - VMMemRegion* p2 = (VMMemRegion*)ptr2; - - if (p1->addr() != p2->addr()) return false; - if ((p1->flags() & MemPointerRecord::tag_masks) != - (p2->flags() & MemPointerRecord::tag_masks)) { - return false; - } - // we do see multiple commit/uncommit on the same memory, it is ok - return (p1->flags() & MemPointerRecord::tag_masks) == MemPointerRecord::tag_alloc || - (p1->flags() & MemPointerRecord::tag_masks) == MemPointerRecord::tag_release; - } -#endif -}; - -class MallocRecordIterator : public MemPointerArrayIterator { - private: - MemPointerArrayIteratorImpl _itr; - - - - public: - MallocRecordIterator(MemPointerArray* arr) : _itr(arr) { - } - - virtual MemPointer* current() const { -#ifdef ASSERT - MemPointer* cur_rec = _itr.current(); - if (cur_rec != NULL) { - MemPointer* prev_rec = _itr.peek_prev(); - MemPointer* next_rec = _itr.peek_next(); - assert(prev_rec == NULL || prev_rec->addr() < cur_rec->addr(), "Sorting order"); - assert(next_rec == NULL || next_rec->addr() > cur_rec->addr(), "Sorting order"); - } -#endif - return _itr.current(); - } - virtual MemPointer* next() { - MemPointerRecord* next_rec = (MemPointerRecord*)_itr.next(); - // arena memory record is a special case, which we have to compare - // sequence number against its associated arena record. - if (next_rec != NULL && next_rec->is_arena_memory_record()) { - MemPointerRecord* prev_rec = (MemPointerRecord*)_itr.peek_prev(); - // if there is an associated arena record, it has to be previous - // record because of sorting order (by address) - NMT generates a pseudo address - // for arena's size record by offsetting arena's address, that guarantees - // the order of arena record and it's size record. - if (prev_rec != NULL && prev_rec->is_arena_record() && - next_rec->is_memory_record_of_arena(prev_rec)) { - if (prev_rec->seq() > next_rec->seq()) { - // Skip this arena memory record - // Two scenarios: - // - if the arena record is an allocation record, this early - // size record must be leftover by previous arena, - // and the last size record should have size = 0. - // - if the arena record is a deallocation record, this - // size record should be its cleanup record, which should - // also have size = 0. In other world, arena alway reset - // its size before gone (see Arena's destructor) - assert(next_rec->size() == 0, "size not reset"); - return _itr.next(); - } else { - assert(prev_rec->is_allocation_record(), - "Arena size record ahead of allocation record"); - } - } - } - return next_rec; - } - - MemPointer* peek_next() const { ShouldNotReachHere(); return NULL; } - MemPointer* peek_prev() const { ShouldNotReachHere(); return NULL; } - void remove() { ShouldNotReachHere(); } - bool insert(MemPointer* ptr) { ShouldNotReachHere(); return false; } - bool insert_after(MemPointer* ptr) { ShouldNotReachHere(); return false; } -}; - -// collapse duplicated records. Eliminating duplicated records here, is much -// cheaper than during promotion phase. However, it does have limitation - it -// can only eliminate duplicated records within the generation, there are -// still chances seeing duplicated records during promotion. -// We want to use the record with higher sequence number, because it has -// more accurate callsite pc. -class VMRecordIterator : public MemPointerArrayIterator { - private: - MemPointerArrayIteratorImpl _itr; - - public: - VMRecordIterator(MemPointerArray* arr) : _itr(arr) { - MemPointerRecord* cur = (MemPointerRecord*)_itr.current(); - MemPointerRecord* next = (MemPointerRecord*)_itr.peek_next(); - while (next != NULL) { - assert(cur != NULL, "Sanity check"); - assert(((SeqMemPointerRecord*)next)->seq() > ((SeqMemPointerRecord*)cur)->seq(), - "pre-sort order"); - - if (is_duplicated_record(cur, next)) { - _itr.next(); - next = (MemPointerRecord*)_itr.peek_next(); - } else { - break; - } - } - } - - virtual MemPointer* current() const { - return _itr.current(); - } - - // get next record, but skip the duplicated records - virtual MemPointer* next() { - MemPointerRecord* cur = (MemPointerRecord*)_itr.next(); - MemPointerRecord* next = (MemPointerRecord*)_itr.peek_next(); - while (next != NULL) { - assert(cur != NULL, "Sanity check"); - assert(((SeqMemPointerRecord*)next)->seq() > ((SeqMemPointerRecord*)cur)->seq(), - "pre-sort order"); - - if (is_duplicated_record(cur, next)) { - _itr.next(); - cur = next; - next = (MemPointerRecord*)_itr.peek_next(); - } else { - break; - } - } - return cur; - } - - MemPointer* peek_next() const { ShouldNotReachHere(); return NULL; } - MemPointer* peek_prev() const { ShouldNotReachHere(); return NULL; } - void remove() { ShouldNotReachHere(); } - bool insert(MemPointer* ptr) { ShouldNotReachHere(); return false; } - bool insert_after(MemPointer* ptr) { ShouldNotReachHere(); return false; } - - private: - bool is_duplicated_record(MemPointerRecord* p1, MemPointerRecord* p2) const { - bool ret = (p1->addr() == p2->addr() && p1->size() == p2->size() && p1->flags() == p2->flags()); - assert(!(ret && FLAGS_TO_MEMORY_TYPE(p1->flags()) == mtThreadStack), "dup on stack record"); - return ret; - } -}; - -class StagingArea VALUE_OBJ_CLASS_SPEC { - private: - MemPointerArray* _malloc_data; - MemPointerArray* _vm_data; - - public: - StagingArea() : _malloc_data(NULL), _vm_data(NULL) { - init(); - } - - ~StagingArea() { - if (_malloc_data != NULL) delete _malloc_data; - if (_vm_data != NULL) delete _vm_data; - } - - MallocRecordIterator malloc_record_walker() { - return MallocRecordIterator(malloc_data()); - } - - VMRecordIterator virtual_memory_record_walker(); - - bool init(); - void clear() { - assert(_malloc_data != NULL && _vm_data != NULL, "Just check"); - _malloc_data->shrink(); - _malloc_data->clear(); - _vm_data->clear(); - } - - inline MemPointerArray* malloc_data() { return _malloc_data; } - inline MemPointerArray* vm_data() { return _vm_data; } -}; - -class MemBaseline; -class MemSnapshot : public CHeapObj { - private: - // the following two arrays contain records of all known lived memory blocks - // live malloc-ed memory pointers - MemPointerArray* _alloc_ptrs; - // live virtual memory pointers - MemPointerArray* _vm_ptrs; - - StagingArea _staging_area; - - // the lock to protect this snapshot - Monitor* _lock; - - // the number of instance classes - int _number_of_classes; - - NOT_PRODUCT(size_t _untracked_count;) - friend class MemBaseline; - - public: - MemSnapshot(); - virtual ~MemSnapshot(); - - // if we are running out of native memory - bool out_of_memory() { - return (_alloc_ptrs == NULL || - _staging_area.malloc_data() == NULL || - _staging_area.vm_data() == NULL || - _vm_ptrs == NULL || _lock == NULL || - _alloc_ptrs->out_of_memory() || - _vm_ptrs->out_of_memory()); - } - - // merge a per-thread memory recorder into staging area - bool merge(MemRecorder* rec); - // promote staged data to snapshot - bool promote(int number_of_classes); - - int number_of_classes() const { return _number_of_classes; } - - void wait(long timeout) { - assert(_lock != NULL, "Just check"); - MonitorLockerEx locker(_lock); - locker.wait(true, timeout); - } - - NOT_PRODUCT(void print_snapshot_stats(outputStream* st);) - NOT_PRODUCT(void check_staging_data();) - NOT_PRODUCT(void check_malloc_pointers();) - NOT_PRODUCT(bool has_allocation_record(address addr);) - // dump all virtual memory pointers in snapshot - DEBUG_ONLY( void dump_all_vm_pointers();) - - private: - // copy sequenced pointer from src to dest - void copy_seq_pointer(MemPointerRecord* dest, const MemPointerRecord* src); - // assign a sequenced pointer to non-sequenced pointer - void assign_pointer(MemPointerRecord*dest, const MemPointerRecord* src); - - bool promote_malloc_records(MemPointerArrayIterator* itr); - bool promote_virtual_memory_records(MemPointerArrayIterator* itr); -}; - -#endif // SHARE_VM_SERVICES_MEM_SNAPSHOT_HPP diff --git a/hotspot/src/share/vm/services/memTrackWorker.cpp b/hotspot/src/share/vm/services/memTrackWorker.cpp deleted file mode 100644 index 7bf18eb273c..00000000000 --- a/hotspot/src/share/vm/services/memTrackWorker.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "runtime/threadCritical.hpp" -#include "services/memTracker.hpp" -#include "services/memTrackWorker.hpp" -#include "utilities/decoder.hpp" -#include "utilities/vmError.hpp" - - -void GenerationData::reset() { - _number_of_classes = 0; - while (_recorder_list != NULL) { - MemRecorder* tmp = _recorder_list; - _recorder_list = _recorder_list->next(); - MemTracker::release_thread_recorder(tmp); - } -} - -MemTrackWorker::MemTrackWorker(MemSnapshot* snapshot): _snapshot(snapshot) { - // create thread uses cgc thread type for now. We should revisit - // the option, or create new thread type. - _has_error = !os::create_thread(this, os::cgc_thread); - set_name("MemTrackWorker"); - - // initial generation circuit buffer - if (!has_error()) { - _head = _tail = 0; - for(int index = 0; index < MAX_GENERATIONS; index ++) { - ::new ((void*)&_gen[index]) GenerationData(); - } - } - NOT_PRODUCT(_sync_point_count = 0;) - NOT_PRODUCT(_merge_count = 0;) - NOT_PRODUCT(_last_gen_in_use = 0;) -} - -MemTrackWorker::~MemTrackWorker() { - for (int index = 0; index < MAX_GENERATIONS; index ++) { - _gen[index].reset(); - } -} - -void* MemTrackWorker::operator new(size_t size) throw() { - assert(false, "use nothrow version"); - return NULL; -} - -void* MemTrackWorker::operator new(size_t size, const std::nothrow_t& nothrow_constant) throw() { - return allocate(size, false, mtNMT); -} - -void MemTrackWorker::start() { - os::start_thread(this); -} - -/* - * Native memory tracking worker thread loop: - * 1. merge one generation of memory recorders to staging area - * 2. promote staging data to memory snapshot - * - * This thread can run through safepoint. - */ - -void MemTrackWorker::run() { - assert(MemTracker::is_on(), "native memory tracking is off"); - this->initialize_thread_local_storage(); - this->record_stack_base_and_size(); - assert(_snapshot != NULL, "Worker should not be started"); - MemRecorder* rec; - unsigned long processing_generation = 0; - bool worker_idle = false; - - while (!MemTracker::shutdown_in_progress()) { - NOT_PRODUCT(_last_gen_in_use = generations_in_use();) - { - // take a recorder from earliest generation in buffer - ThreadCritical tc; - rec = _gen[_head].next_recorder(); - } - if (rec != NULL) { - if (rec->get_generation() != processing_generation || worker_idle) { - processing_generation = rec->get_generation(); - worker_idle = false; - MemTracker::set_current_processing_generation(processing_generation); - } - - // merge the recorder into staging area - if (!_snapshot->merge(rec)) { - MemTracker::shutdown(MemTracker::NMT_out_of_memory); - } else { - NOT_PRODUCT(_merge_count ++;) - } - MemTracker::release_thread_recorder(rec); - } else { - // no more recorder to merge, promote staging area - // to snapshot - if (_head != _tail) { - long number_of_classes; - { - ThreadCritical tc; - if (_gen[_head].has_more_recorder() || _head == _tail) { - continue; - } - number_of_classes = _gen[_head].number_of_classes(); - _gen[_head].reset(); - - // done with this generation, increment _head pointer - _head = (_head + 1) % MAX_GENERATIONS; - } - // promote this generation data to snapshot - if (!_snapshot->promote(number_of_classes)) { - // failed to promote, means out of memory - MemTracker::shutdown(MemTracker::NMT_out_of_memory); - } - } else { - // worker thread is idle - worker_idle = true; - MemTracker::report_worker_idle(); - _snapshot->wait(1000); - ThreadCritical tc; - // check if more data arrived - if (!_gen[_head].has_more_recorder()) { - _gen[_head].add_recorders(MemTracker::get_pending_recorders()); - } - } - } - } - assert(MemTracker::shutdown_in_progress(), "just check"); - - // transits to final shutdown - MemTracker::final_shutdown(); -} - -// at synchronization point, where 'safepoint visible' Java threads are blocked -// at a safepoint, and the rest of threads are blocked on ThreadCritical lock. -// The caller MemTracker::sync() already takes ThreadCritical before calling this -// method. -// -// Following tasks are performed: -// 1. add all recorders in pending queue to current generation -// 2. increase generation - -void MemTrackWorker::at_sync_point(MemRecorder* rec, int number_of_classes) { - NOT_PRODUCT(_sync_point_count ++;) - assert(count_recorder(rec) <= MemRecorder::_instance_count, - "pending queue has infinite loop"); - - bool out_of_generation_buffer = false; - // check shutdown state inside ThreadCritical - if (MemTracker::shutdown_in_progress()) return; - - _gen[_tail].set_number_of_classes(number_of_classes); - // append the recorders to the end of the generation - _gen[_tail].add_recorders(rec); - assert(count_recorder(_gen[_tail].peek()) <= MemRecorder::_instance_count, - "after add to current generation has infinite loop"); - // we have collected all recorders for this generation. If there is data, - // we need to increment _tail to start a new generation. - if (_gen[_tail].has_more_recorder() || _head == _tail) { - _tail = (_tail + 1) % MAX_GENERATIONS; - out_of_generation_buffer = (_tail == _head); - } - - if (out_of_generation_buffer) { - MemTracker::shutdown(MemTracker::NMT_out_of_generation); - } -} - -#ifndef PRODUCT -int MemTrackWorker::count_recorder(const MemRecorder* head) { - int count = 0; - while(head != NULL) { - count ++; - head = head->next(); - } - return count; -} - -int MemTrackWorker::count_pending_recorders() const { - int count = 0; - for (int index = 0; index < MAX_GENERATIONS; index ++) { - MemRecorder* head = _gen[index].peek(); - if (head != NULL) { - count += count_recorder(head); - } - } - return count; -} -#endif diff --git a/hotspot/src/share/vm/services/memTrackWorker.hpp b/hotspot/src/share/vm/services/memTrackWorker.hpp deleted file mode 100644 index ee45244e324..00000000000 --- a/hotspot/src/share/vm/services/memTrackWorker.hpp +++ /dev/null @@ -1,118 +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. - * - */ - -#ifndef SHARE_VM_SERVICES_MEM_TRACK_WORKER_HPP -#define SHARE_VM_SERVICES_MEM_TRACK_WORKER_HPP - -#include "memory/allocation.hpp" -#include "runtime/thread.hpp" -#include "services/memRecorder.hpp" - -// Maximum MAX_GENERATIONS generation data can be tracked. -#define MAX_GENERATIONS 512 - -class GenerationData VALUE_OBJ_CLASS_SPEC { - private: - int _number_of_classes; - MemRecorder* _recorder_list; - - public: - GenerationData(): _number_of_classes(0), _recorder_list(NULL) { } - - inline int number_of_classes() const { return _number_of_classes; } - inline void set_number_of_classes(long num) { _number_of_classes = num; } - - inline MemRecorder* next_recorder() { - if (_recorder_list == NULL) { - return NULL; - } else { - MemRecorder* tmp = _recorder_list; - _recorder_list = _recorder_list->next(); - return tmp; - } - } - - inline bool has_more_recorder() const { - return (_recorder_list != NULL); - } - - // add recorders to this generation - void add_recorders(MemRecorder* head) { - if (head != NULL) { - if (_recorder_list == NULL) { - _recorder_list = head; - } else { - MemRecorder* tmp = _recorder_list; - for (; tmp->next() != NULL; tmp = tmp->next()); - tmp->set_next(head); - } - } - } - - void reset(); - - NOT_PRODUCT(MemRecorder* peek() const { return _recorder_list; }) -}; - -class MemTrackWorker : public NamedThread { - private: - // circular buffer. This buffer contains generation data to be merged into global - // snaphsot. - // Each slot holds a generation - GenerationData _gen[MAX_GENERATIONS]; - int _head, _tail; // head and tail pointers to above circular buffer - - bool _has_error; - - MemSnapshot* _snapshot; - - public: - MemTrackWorker(MemSnapshot* snapshot); - ~MemTrackWorker(); - _NOINLINE_ void* operator new(size_t size) throw(); - _NOINLINE_ void* operator new(size_t size, const std::nothrow_t& nothrow_constant) throw(); - - void start(); - void run(); - - inline bool has_error() const { return _has_error; } - - // task at synchronization point - void at_sync_point(MemRecorder* pending_recorders, int number_of_classes); - - // for debugging purpose, they are not thread safe. - NOT_PRODUCT(static int count_recorder(const MemRecorder* head);) - NOT_PRODUCT(int count_pending_recorders() const;) - - NOT_PRODUCT(int _sync_point_count;) - NOT_PRODUCT(int _merge_count;) - NOT_PRODUCT(int _last_gen_in_use;) - - // how many generations are queued - inline int generations_in_use() const { - return (_tail >= _head ? (_tail - _head + 1) : (MAX_GENERATIONS - (_head - _tail) + 1)); - } -}; - -#endif // SHARE_VM_SERVICES_MEM_TRACK_WORKER_HPP diff --git a/hotspot/src/share/vm/services/memTracker.cpp b/hotspot/src/share/vm/services/memTracker.cpp index 190c004b374..24644c8cc67 100644 --- a/hotspot/src/share/vm/services/memTracker.cpp +++ b/hotspot/src/share/vm/services/memTracker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, 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,862 +23,315 @@ */ #include "precompiled.hpp" -#include "oops/instanceKlass.hpp" -#include "runtime/atomic.inline.hpp" -#include "runtime/interfaceSupport.hpp" -#include "runtime/mutexLocker.hpp" -#include "runtime/safepoint.hpp" -#include "runtime/threadCritical.hpp" -#include "runtime/thread.inline.hpp" -#include "runtime/vm_operations.hpp" -#include "services/memPtr.hpp" +#include "runtime/mutex.hpp" +#include "services/memBaseline.hpp" #include "services/memReporter.hpp" +#include "services/mallocTracker.inline.hpp" #include "services/memTracker.hpp" -#include "utilities/decoder.hpp" #include "utilities/defaultStream.hpp" -#include "utilities/globalDefinitions.hpp" -bool NMT_track_callsite = false; +#ifdef SOLARIS + volatile bool NMT_stack_walkable = false; +#else + volatile bool NMT_stack_walkable = true; +#endif -// walk all 'known' threads at NMT sync point, and collect their recorders -void SyncThreadRecorderClosure::do_thread(Thread* thread) { - assert(SafepointSynchronize::is_at_safepoint(), "Safepoint required"); - if (thread->is_Java_thread()) { - JavaThread* javaThread = (JavaThread*)thread; - MemRecorder* recorder = javaThread->get_recorder(); - if (recorder != NULL) { - MemTracker::enqueue_pending_recorder(recorder); - javaThread->set_recorder(NULL); +volatile NMT_TrackingLevel MemTracker::_tracking_level = NMT_unknown; +NMT_TrackingLevel MemTracker::_cmdline_tracking_level = NMT_unknown; + +MemBaseline MemTracker::_baseline; +Mutex* MemTracker::_query_lock = NULL; +bool MemTracker::_is_nmt_env_valid = true; + + +NMT_TrackingLevel MemTracker::init_tracking_level() { + NMT_TrackingLevel level = NMT_off; + char buf[64]; + char nmt_option[64]; + jio_snprintf(buf, sizeof(buf), "NMT_LEVEL_%d", os::current_process_id()); + if (os::getenv(buf, nmt_option, sizeof(nmt_option))) { + if (strcmp(nmt_option, "summary") == 0) { + level = NMT_summary; + } else if (strcmp(nmt_option, "detail") == 0) { +#if PLATFORM_NATIVE_STACK_WALKING_SUPPORTED + level = NMT_detail; +#else + level = NMT_summary; +#endif // PLATFORM_NATIVE_STACK_WALKING_SUPPORTED + } else if (strcmp(nmt_option, "off") != 0) { + // The option value is invalid + _is_nmt_env_valid = false; } + + // Remove the environment variable to avoid leaking to child processes + os::unsetenv(buf); } - _thread_count ++; + + // Construct NativeCallStack::EMPTY_STACK. It may get constructed twice, + // but it is benign, the results are the same. + ::new ((void*)&NativeCallStack::EMPTY_STACK) NativeCallStack(0, false); + + if (!MallocTracker::initialize(level) || + !VirtualMemoryTracker::initialize(level)) { + level = NMT_off; + } + return level; } +void MemTracker::init() { + NMT_TrackingLevel level = tracking_level(); + if (level >= NMT_summary) { + if (!VirtualMemoryTracker::late_initialize(level)) { + shutdown(); + return; + } + _query_lock = new (std::nothrow) Mutex(Monitor::max_nonleaf, "NMT_queryLock"); + // Already OOM. It is unlikely, but still have to handle it. + if (_query_lock == NULL) { + shutdown(); + } + } +} -MemRecorder* volatile MemTracker::_global_recorder = NULL; -MemSnapshot* MemTracker::_snapshot = NULL; -MemBaseline MemTracker::_baseline; -Mutex* MemTracker::_query_lock = NULL; -MemRecorder* volatile MemTracker::_merge_pending_queue = NULL; -MemRecorder* volatile MemTracker::_pooled_recorders = NULL; -MemTrackWorker* MemTracker::_worker_thread = NULL; -int MemTracker::_sync_point_skip_count = 0; -MemTracker::NMTLevel MemTracker::_tracking_level = MemTracker::NMT_off; -volatile MemTracker::NMTStates MemTracker::_state = NMT_uninited; -MemTracker::ShutdownReason MemTracker::_reason = NMT_shutdown_none; -int MemTracker::_thread_count = 255; -volatile jint MemTracker::_pooled_recorder_count = 0; -volatile unsigned long MemTracker::_processing_generation = 0; -volatile bool MemTracker::_worker_thread_idle = false; -volatile jint MemTracker::_pending_op_count = 0; -volatile bool MemTracker::_slowdown_calling_thread = false; -debug_only(intx MemTracker::_main_thread_tid = 0;) -NOT_PRODUCT(volatile jint MemTracker::_pending_recorder_count = 0;) - -void MemTracker::init_tracking_options(const char* option_line) { - _tracking_level = NMT_off; - if (strcmp(option_line, "=summary") == 0) { - _tracking_level = NMT_summary; - } else if (strcmp(option_line, "=detail") == 0) { - // detail relies on a stack-walking ability that may not - // be available depending on platform and/or compiler flags -#if PLATFORM_NATIVE_STACK_WALKING_SUPPORTED - _tracking_level = NMT_detail; -#else +bool MemTracker::check_launcher_nmt_support(const char* value) { + if (strcmp(value, "=detail") == 0) { +#if !PLATFORM_NATIVE_STACK_WALKING_SUPPORTED jio_fprintf(defaultStream::error_stream(), "NMT detail is not supported on this platform. Using NMT summary instead.\n"); - _tracking_level = NMT_summary; + if (MemTracker::tracking_level() != NMT_summary) { + return false; + } +#else + if (MemTracker::tracking_level() != NMT_detail) { + return false; + } #endif - } else if (strcmp(option_line, "=off") != 0) { - vm_exit_during_initialization("Syntax error, expecting -XX:NativeMemoryTracking=[off|summary|detail]", NULL); - } -} - -// first phase of bootstrapping, when VM is still in single-threaded mode. -void MemTracker::bootstrap_single_thread() { - if (_tracking_level > NMT_off) { - assert(_state == NMT_uninited, "wrong state"); - - // NMT is not supported with UseMallocOnly is on. NMT can NOT - // handle the amount of malloc data without significantly impacting - // runtime performance when this flag is on. - if (UseMallocOnly) { - shutdown(NMT_use_malloc_only); - return; + } else if (strcmp(value, "=summary") == 0) { + if (MemTracker::tracking_level() != NMT_summary) { + return false; } - - _query_lock = new (std::nothrow) Mutex(Monitor::max_nonleaf, "NMT_queryLock"); - if (_query_lock == NULL) { - shutdown(NMT_out_of_memory); - return; + } else if (strcmp(value, "=off") == 0) { + if (MemTracker::tracking_level() != NMT_off) { + return false; } - - debug_only(_main_thread_tid = os::current_thread_id();) - _state = NMT_bootstrapping_single_thread; - NMT_track_callsite = (_tracking_level == NMT_detail && can_walk_stack()); - } -} - -// second phase of bootstrapping, when VM is about to or already entered multi-theaded mode. -void MemTracker::bootstrap_multi_thread() { - if (_tracking_level > NMT_off && _state == NMT_bootstrapping_single_thread) { - // create nmt lock for multi-thread execution - assert(_main_thread_tid == os::current_thread_id(), "wrong thread"); - _state = NMT_bootstrapping_multi_thread; - NMT_track_callsite = (_tracking_level == NMT_detail && can_walk_stack()); - } -} - -// fully start nmt -void MemTracker::start() { - // Native memory tracking is off from command line option - if (_tracking_level == NMT_off || shutdown_in_progress()) return; - - assert(_main_thread_tid == os::current_thread_id(), "wrong thread"); - assert(_state == NMT_bootstrapping_multi_thread, "wrong state"); - - _snapshot = new (std::nothrow)MemSnapshot(); - if (_snapshot != NULL) { - if (!_snapshot->out_of_memory() && start_worker(_snapshot)) { - _state = NMT_started; - NMT_track_callsite = (_tracking_level == NMT_detail && can_walk_stack()); - return; - } - - delete _snapshot; - _snapshot = NULL; - } - - // fail to start native memory tracking, shut it down - shutdown(NMT_initialization); -} - -/** - * Shutting down native memory tracking. - * We can not shutdown native memory tracking immediately, so we just - * setup shutdown pending flag, every native memory tracking component - * should orderly shut itself down. - * - * The shutdown sequences: - * 1. MemTracker::shutdown() sets MemTracker to shutdown pending state - * 2. Worker thread calls MemTracker::final_shutdown(), which transites - * MemTracker to final shutdown state. - * 3. At sync point, MemTracker does final cleanup, before sets memory - * tracking level to off to complete shutdown. - */ -void MemTracker::shutdown(ShutdownReason reason) { - if (_tracking_level == NMT_off) return; - - if (_state <= NMT_bootstrapping_single_thread) { - // we still in single thread mode, there is not contention - _state = NMT_shutdown_pending; - _reason = reason; } else { - // we want to know who initialized shutdown - if ((jint)NMT_started == Atomic::cmpxchg((jint)NMT_shutdown_pending, - (jint*)&_state, (jint)NMT_started)) { - _reason = reason; - } - } -} - -// final phase of shutdown -void MemTracker::final_shutdown() { - // delete all pending recorders and pooled recorders - delete_all_pending_recorders(); - delete_all_pooled_recorders(); - - { - // shared baseline and snapshot are the only objects needed to - // create query results - MutexLockerEx locker(_query_lock, true); - // cleanup baseline data and snapshot - _baseline.clear(); - delete _snapshot; - _snapshot = NULL; + _is_nmt_env_valid = false; } - // shutdown shared decoder instance, since it is only - // used by native memory tracking so far. - Decoder::shutdown(); - - MemTrackWorker* worker = NULL; - { - ThreadCritical tc; - // can not delete worker inside the thread critical - if (_worker_thread != NULL && Thread::current() == _worker_thread) { - worker = _worker_thread; - _worker_thread = NULL; - } - } - if (worker != NULL) { - delete worker; - } - _state = NMT_final_shutdown; -} - -// delete all pooled recorders -void MemTracker::delete_all_pooled_recorders() { - // free all pooled recorders - MemRecorder* volatile cur_head = _pooled_recorders; - if (cur_head != NULL) { - MemRecorder* null_ptr = NULL; - while (cur_head != NULL && (void*)cur_head != Atomic::cmpxchg_ptr((void*)null_ptr, - (void*)&_pooled_recorders, (void*)cur_head)) { - cur_head = _pooled_recorders; - } - if (cur_head != NULL) { - delete cur_head; - _pooled_recorder_count = 0; - } - } -} - -// delete all recorders in pending queue -void MemTracker::delete_all_pending_recorders() { - // free all pending recorders - MemRecorder* pending_head = get_pending_recorders(); - if (pending_head != NULL) { - delete pending_head; - } -} - -/* - * retrieve per-thread recorder of specified thread. - * if thread == NULL, it means global recorder - */ -MemRecorder* MemTracker::get_thread_recorder(JavaThread* thread) { - if (shutdown_in_progress()) return NULL; - - MemRecorder* rc; - if (thread == NULL) { - rc = _global_recorder; - } else { - rc = thread->get_recorder(); - } - - if (rc != NULL && rc->is_full()) { - enqueue_pending_recorder(rc); - rc = NULL; - } - - if (rc == NULL) { - rc = get_new_or_pooled_instance(); - if (thread == NULL) { - _global_recorder = rc; - } else { - thread->set_recorder(rc); - } - } - return rc; -} - -/* - * get a per-thread recorder from pool, or create a new one if - * there is not one available. - */ -MemRecorder* MemTracker::get_new_or_pooled_instance() { - MemRecorder* cur_head = const_cast (_pooled_recorders); - if (cur_head == NULL) { - MemRecorder* rec = new (std::nothrow)MemRecorder(); - if (rec == NULL || rec->out_of_memory()) { - shutdown(NMT_out_of_memory); - if (rec != NULL) { - delete rec; - rec = NULL; - } - } - return rec; - } else { - MemRecorder* next_head = cur_head->next(); - if ((void*)cur_head != Atomic::cmpxchg_ptr((void*)next_head, (void*)&_pooled_recorders, - (void*)cur_head)) { - return get_new_or_pooled_instance(); - } - cur_head->set_next(NULL); - Atomic::dec(&_pooled_recorder_count); - cur_head->set_generation(); - return cur_head; - } -} - -/* - * retrieve all recorders in pending queue, and empty the queue - */ -MemRecorder* MemTracker::get_pending_recorders() { - MemRecorder* cur_head = const_cast(_merge_pending_queue); - MemRecorder* null_ptr = NULL; - while ((void*)cur_head != Atomic::cmpxchg_ptr((void*)null_ptr, (void*)&_merge_pending_queue, - (void*)cur_head)) { - cur_head = const_cast(_merge_pending_queue); - } - NOT_PRODUCT(Atomic::store(0, &_pending_recorder_count)); - return cur_head; -} - -/* - * release a recorder to recorder pool. - */ -void MemTracker::release_thread_recorder(MemRecorder* rec) { - assert(rec != NULL, "null recorder"); - // we don't want to pool too many recorders - rec->set_next(NULL); - if (shutdown_in_progress() || _pooled_recorder_count > _thread_count * 2) { - delete rec; - return; - } - - rec->clear(); - MemRecorder* cur_head = const_cast(_pooled_recorders); - rec->set_next(cur_head); - while ((void*)cur_head != Atomic::cmpxchg_ptr((void*)rec, (void*)&_pooled_recorders, - (void*)cur_head)) { - cur_head = const_cast(_pooled_recorders); - rec->set_next(cur_head); - } - Atomic::inc(&_pooled_recorder_count); -} - -// write a record to proper recorder. No lock can be taken from this method -// down. -void MemTracker::write_tracking_record(address addr, MEMFLAGS flags, - size_t size, jint seq, address pc, JavaThread* thread) { - - MemRecorder* rc = get_thread_recorder(thread); - if (rc != NULL) { - rc->record(addr, flags, size, seq, pc); - } -} - -/** - * enqueue a recorder to pending queue - */ -void MemTracker::enqueue_pending_recorder(MemRecorder* rec) { - assert(rec != NULL, "null recorder"); - - // we are shutting down, so just delete it - if (shutdown_in_progress()) { - rec->set_next(NULL); - delete rec; - return; - } - - MemRecorder* cur_head = const_cast(_merge_pending_queue); - rec->set_next(cur_head); - while ((void*)cur_head != Atomic::cmpxchg_ptr((void*)rec, (void*)&_merge_pending_queue, - (void*)cur_head)) { - cur_head = const_cast(_merge_pending_queue); - rec->set_next(cur_head); - } - NOT_PRODUCT(Atomic::inc(&_pending_recorder_count);) -} - -/* - * The method is called at global safepoint - * during it synchronization process. - * 1. enqueue all JavaThreads' per-thread recorders - * 2. enqueue global recorder - * 3. retrieve all pending recorders - * 4. reset global sequence number generator - * 5. call worker's sync - */ -#define MAX_SAFEPOINTS_TO_SKIP 128 -#define SAFE_SEQUENCE_THRESHOLD 30 -#define HIGH_GENERATION_THRESHOLD 60 -#define MAX_RECORDER_THREAD_RATIO 30 -#define MAX_RECORDER_PER_THREAD 100 - -void MemTracker::sync() { - assert(_tracking_level > NMT_off, "NMT is not enabled"); - assert(SafepointSynchronize::is_at_safepoint(), "Safepoint required"); - - // Some GC tests hit large number of safepoints in short period of time - // without meaningful activities. We should prevent going to - // sync point in these cases, which can potentially exhaust generation buffer. - // Here is the factots to determine if we should go into sync point: - // 1. not to overflow sequence number - // 2. if we are in danger to overflow generation buffer - // 3. how many safepoints we already skipped sync point - if (_state == NMT_started) { - // worker thread is not ready, no one can manage generation - // buffer, so skip this safepoint - if (_worker_thread == NULL) return; - - if (_sync_point_skip_count < MAX_SAFEPOINTS_TO_SKIP) { - int per_seq_in_use = SequenceGenerator::peek() * 100 / max_jint; - int per_gen_in_use = _worker_thread->generations_in_use() * 100 / MAX_GENERATIONS; - if (per_seq_in_use < SAFE_SEQUENCE_THRESHOLD && per_gen_in_use >= HIGH_GENERATION_THRESHOLD) { - _sync_point_skip_count ++; - return; - } - } - { - // This method is running at safepoint, with ThreadCritical lock, - // it should guarantee that NMT is fully sync-ed. - ThreadCritical tc; - - // We can NOT execute NMT sync-point if there are pending tracking ops. - if (_pending_op_count == 0) { - SequenceGenerator::reset(); - _sync_point_skip_count = 0; - - // walk all JavaThreads to collect recorders - SyncThreadRecorderClosure stc; - Threads::threads_do(&stc); - - _thread_count = stc.get_thread_count(); - MemRecorder* pending_recorders = get_pending_recorders(); - - if (_global_recorder != NULL) { - _global_recorder->set_next(pending_recorders); - pending_recorders = _global_recorder; - _global_recorder = NULL; - } - - // see if NMT has too many outstanding recorder instances, it usually - // means that worker thread is lagging behind in processing them. - if (!AutoShutdownNMT) { - _slowdown_calling_thread = (MemRecorder::_instance_count > MAX_RECORDER_THREAD_RATIO * _thread_count); - } else { - // If auto shutdown is on, enforce MAX_RECORDER_PER_THREAD threshold to prevent OOM - if (MemRecorder::_instance_count >= _thread_count * MAX_RECORDER_PER_THREAD) { - shutdown(NMT_out_of_memory); - } - } - - // check _worker_thread with lock to avoid racing condition - if (_worker_thread != NULL) { - _worker_thread->at_sync_point(pending_recorders, InstanceKlass::number_of_instance_classes()); - } - assert(SequenceGenerator::peek() == 1, "Should not have memory activities during sync-point"); - } else { - _sync_point_skip_count ++; - } - } - } - - // now, it is the time to shut whole things off - if (_state == NMT_final_shutdown) { - // walk all JavaThreads to delete all recorders - SyncThreadRecorderClosure stc; - Threads::threads_do(&stc); - // delete global recorder - { - ThreadCritical tc; - if (_global_recorder != NULL) { - delete _global_recorder; - _global_recorder = NULL; - } - } - MemRecorder* pending_recorders = get_pending_recorders(); - if (pending_recorders != NULL) { - delete pending_recorders; - } - // try at a later sync point to ensure MemRecorder instance drops to zero to - // completely shutdown NMT - if (MemRecorder::_instance_count == 0) { - _state = NMT_shutdown; - _tracking_level = NMT_off; - } - } -} - -/* - * Start worker thread. - */ -bool MemTracker::start_worker(MemSnapshot* snapshot) { - assert(_worker_thread == NULL && _snapshot != NULL, "Just Check"); - _worker_thread = new (std::nothrow) MemTrackWorker(snapshot); - if (_worker_thread == NULL) { - return false; - } else if (_worker_thread->has_error()) { - delete _worker_thread; - _worker_thread = NULL; - return false; - } - _worker_thread->start(); return true; } -/* - * We need to collect a JavaThread's per-thread recorder - * before it exits. - */ -void MemTracker::thread_exiting(JavaThread* thread) { - if (is_on()) { - MemRecorder* rec = thread->get_recorder(); - if (rec != NULL) { - enqueue_pending_recorder(rec); - thread->set_recorder(NULL); +bool MemTracker::verify_nmt_option() { + return _is_nmt_env_valid; +} + +void* MemTracker::malloc_base(void* memblock) { + return MallocTracker::get_base(memblock); +} + +void Tracker::record(address addr, size_t size) { + if (MemTracker::tracking_level() < NMT_summary) return; + switch(_type) { + case uncommit: + VirtualMemoryTracker::remove_uncommitted_region(addr, size); + break; + case release: + VirtualMemoryTracker::remove_released_region(addr, size); + break; + default: + ShouldNotReachHere(); + } +} + + +// Shutdown can only be issued via JCmd, and NMT JCmd is serialized +// by lock +void MemTracker::shutdown() { + // We can only shutdown NMT to minimal tracking level if it is + // ever on. + if (tracking_level () > NMT_minimal) { + transition_to(NMT_minimal); + } +} + +bool MemTracker::transition_to(NMT_TrackingLevel level) { + NMT_TrackingLevel current_level = tracking_level(); + + if (current_level == level) { + return true; + } else if (current_level > level) { + // Downgrade tracking level, we want to lower the tracking + // level first + _tracking_level = level; + // Make _tracking_level visible immediately. + OrderAccess::fence(); + VirtualMemoryTracker::transition(current_level, level); + MallocTracker::transition(current_level, level); + + if (level == NMT_minimal) _baseline.reset(); + } else { + VirtualMemoryTracker::transition(current_level, level); + MallocTracker::transition(current_level, level); + + _tracking_level = level; + // Make _tracking_level visible immediately. + OrderAccess::fence(); + } + + return true; +} + +void MemTracker::final_report(outputStream* output) { + assert(output != NULL, "No output stream"); + if (tracking_level() >= NMT_summary) { + MallocMemorySnapshot* malloc_memory_snapshot = + MallocMemorySummary::as_snapshot(); + malloc_memory_snapshot->make_adjustment(); + + VirtualMemorySnapshot* virtual_memory_snapshot = + VirtualMemorySummary::as_snapshot(); + + MemSummaryReporter rptr(malloc_memory_snapshot, + virtual_memory_snapshot, output); + rptr.report(); + // shutdown NMT, the data no longer accurate + shutdown(); + } +} + +// This is a walker to gather malloc site hashtable statistics, +// the result is used for tuning. +class StatisticsWalker : public MallocSiteWalker { + private: + enum Threshold { + // aggregates statistics over this threshold into one + // line item. + report_threshold = 20 + }; + + private: + // Number of allocation sites that have all memory freed + int _empty_entries; + // Total number of allocation sites, include empty sites + int _total_entries; + // Number of captured call stack distribution + int _stack_depth_distribution[NMT_TrackingStackDepth]; + // Hash distribution + int _hash_distribution[report_threshold]; + // Number of hash buckets that have entries over the threshold + int _bucket_over_threshold; + + // The hash bucket that walker is currently walking + int _current_hash_bucket; + // The length of current hash bucket + int _current_bucket_length; + // Number of hash buckets that are not empty + int _used_buckets; + // Longest hash bucket length + int _longest_bucket_length; + + public: + StatisticsWalker() : _empty_entries(0), _total_entries(0) { + int index = 0; + for (index = 0; index < NMT_TrackingStackDepth; index ++) { + _stack_depth_distribution[index] = 0; } + for (index = 0; index < report_threshold; index ++) { + _hash_distribution[index] = 0; + } + _bucket_over_threshold = 0; + _longest_bucket_length = 0; + _current_hash_bucket = -1; + _current_bucket_length = 0; + _used_buckets = 0; } -} -// baseline current memory snapshot -bool MemTracker::baseline() { - MutexLocker lock(_query_lock); - MemSnapshot* snapshot = get_snapshot(); - if (snapshot != NULL) { - return _baseline.baseline(*snapshot, false); - } - return false; -} + virtual bool do_malloc_site(const MallocSite* e) { + if (e->size() == 0) _empty_entries ++; + _total_entries ++; -// print memory usage from current snapshot -bool MemTracker::print_memory_usage(BaselineOutputer& out, size_t unit, bool summary_only) { - MemBaseline baseline; - MutexLocker lock(_query_lock); - MemSnapshot* snapshot = get_snapshot(); - if (snapshot != NULL && baseline.baseline(*snapshot, summary_only)) { - BaselineReporter reporter(out, unit); - reporter.report_baseline(baseline, summary_only); + // stack depth distrubution + int frames = e->call_stack()->frames(); + _stack_depth_distribution[frames - 1] ++; + + // hash distribution + int hash_bucket = e->hash() % MallocSiteTable::hash_buckets(); + if (_current_hash_bucket == -1) { + _current_hash_bucket = hash_bucket; + _current_bucket_length = 1; + } else if (_current_hash_bucket == hash_bucket) { + _current_bucket_length ++; + } else { + record_bucket_length(_current_bucket_length); + _current_hash_bucket = hash_bucket; + _current_bucket_length = 1; + } return true; } - return false; -} -// Whitebox API for blocking until the current generation of NMT data has been merged -bool MemTracker::wbtest_wait_for_data_merge() { - // NMT can't be shutdown while we're holding _query_lock - MutexLocker lock(_query_lock); - assert(_worker_thread != NULL, "Invalid query"); - // the generation at query time, so NMT will spin till this generation is processed - unsigned long generation_at_query_time = SequenceGenerator::current_generation(); - unsigned long current_processing_generation = _processing_generation; - // if generation counter overflown - bool generation_overflown = (generation_at_query_time < current_processing_generation); - long generations_to_wrap = MAX_UNSIGNED_LONG - current_processing_generation; - // spin - while (!shutdown_in_progress()) { - if (!generation_overflown) { - if (current_processing_generation > generation_at_query_time) { - return true; + // walk completed + void completed() { + record_bucket_length(_current_bucket_length); + } + + void report_statistics(outputStream* out) { + int index; + out->print_cr("Malloc allocation site table:"); + out->print_cr("\tTotal entries: %d", _total_entries); + out->print_cr("\tEmpty entries: %d (%2.2f%%)", _empty_entries, ((float)_empty_entries * 100) / _total_entries); + out->print_cr(" "); + out->print_cr("Hash distribution:"); + if (_used_buckets < MallocSiteTable::hash_buckets()) { + out->print_cr("empty bucket: %d", (MallocSiteTable::hash_buckets() - _used_buckets)); + } + for (index = 0; index < report_threshold; index ++) { + if (_hash_distribution[index] != 0) { + if (index == 0) { + out->print_cr(" %d entry: %d", 1, _hash_distribution[0]); + } else if (index < 9) { // single digit + out->print_cr(" %d entries: %d", (index + 1), _hash_distribution[index]); + } else { + out->print_cr(" %d entries: %d", (index + 1), _hash_distribution[index]); + } } + } + if (_bucket_over_threshold > 0) { + out->print_cr(" >%d entries: %d", report_threshold, _bucket_over_threshold); + } + out->print_cr("most entries: %d", _longest_bucket_length); + out->print_cr(" "); + out->print_cr("Call stack depth distribution:"); + for (index = 0; index < NMT_TrackingStackDepth; index ++) { + if (_stack_depth_distribution[index] > 0) { + out->print_cr("\t%d: %d", index + 1, _stack_depth_distribution[index]); + } + } + } + + private: + void record_bucket_length(int length) { + _used_buckets ++; + if (length <= report_threshold) { + _hash_distribution[length - 1] ++; } else { - assert(generations_to_wrap >= 0, "Sanity check"); - long current_generations_to_wrap = MAX_UNSIGNED_LONG - current_processing_generation; - assert(current_generations_to_wrap >= 0, "Sanity check"); - // to overflow an unsigned long should take long time, so to_wrap check should be sufficient - if (current_generations_to_wrap > generations_to_wrap && - current_processing_generation > generation_at_query_time) { - return true; - } + _bucket_over_threshold ++; } - - // if worker thread is idle, but generation is not advancing, that means - // there is not safepoint to let NMT advance generation, force one. - if (_worker_thread_idle) { - VM_ForceSafepoint vfs; - VMThread::execute(&vfs); - } - MemSnapshot* snapshot = get_snapshot(); - if (snapshot == NULL) { - return false; - } - snapshot->wait(1000); - current_processing_generation = _processing_generation; - } - // We end up here if NMT is shutting down before our data has been merged - return false; -} - -// compare memory usage between current snapshot and baseline -bool MemTracker::compare_memory_usage(BaselineOutputer& out, size_t unit, bool summary_only) { - MutexLocker lock(_query_lock); - if (_baseline.baselined()) { - MemBaseline baseline; - MemSnapshot* snapshot = get_snapshot(); - if (snapshot != NULL && baseline.baseline(*snapshot, summary_only)) { - BaselineReporter reporter(out, unit); - reporter.diff_baselines(baseline, _baseline, summary_only); - return true; - } - } - return false; -} - -#ifndef PRODUCT -void MemTracker::walk_stack(int toSkip, char* buf, int len) { - int cur_len = 0; - char tmp[1024]; - address pc; - - while (cur_len < len) { - pc = os::get_caller_pc(toSkip + 1); - if (pc != NULL && os::dll_address_to_function_name(pc, tmp, sizeof(tmp), NULL)) { - jio_snprintf(&buf[cur_len], (len - cur_len), "%s\n", tmp); - cur_len = (int)strlen(buf); - } else { - buf[cur_len] = '\0'; - break; - } - toSkip ++; - } -} - -void MemTracker::print_tracker_stats(outputStream* st) { - st->print_cr("\nMemory Tracker Stats:"); - st->print_cr("\tMax sequence number = %d", SequenceGenerator::max_seq_num()); - st->print_cr("\tthead count = %d", _thread_count); - st->print_cr("\tArena instance = %d", Arena::_instance_count); - st->print_cr("\tpooled recorder count = %d", _pooled_recorder_count); - st->print_cr("\tqueued recorder count = %d", _pending_recorder_count); - st->print_cr("\tmemory recorder instance count = %d", MemRecorder::_instance_count); - if (_worker_thread != NULL) { - st->print_cr("\tWorker thread:"); - st->print_cr("\t\tSync point count = %d", _worker_thread->_sync_point_count); - st->print_cr("\t\tpending recorder count = %d", _worker_thread->count_pending_recorders()); - st->print_cr("\t\tmerge count = %d", _worker_thread->_merge_count); - } else { - st->print_cr("\tWorker thread is not started"); - } - st->print_cr(" "); - - if (_snapshot != NULL) { - _snapshot->print_snapshot_stats(st); - } else { - st->print_cr("No snapshot"); - } -} -#endif - - -// Tracker Implementation - -/* - * Create a tracker. - * This is a fairly complicated constructor, as it has to make two important decisions: - * 1) Does it need to take ThreadCritical lock to write tracking record - * 2) Does it need to pre-reserve a sequence number for the tracking record - * - * The rules to determine if ThreadCritical is needed: - * 1. When nmt is in single-threaded bootstrapping mode, no lock is needed as VM - * still in single thread mode. - * 2. For all threads other than JavaThread, ThreadCritical is needed - * to write to recorders to global recorder. - * 3. For JavaThreads that are no longer visible by safepoint, also - * need to take ThreadCritical and records are written to global - * recorders, since these threads are NOT walked by Threads.do_thread(). - * 4. JavaThreads that are running in safepoint-safe states do not stop - * for safepoints, ThreadCritical lock should be taken to write - * memory records. - * 5. JavaThreads that are running in VM state do not need any lock and - * records are written to per-thread recorders. - * 6. For a thread has yet to attach VM 'Thread', they need to take - * ThreadCritical to write to global recorder. - * - * The memory operations that need pre-reserve sequence numbers: - * The memory operations that "release" memory blocks and the - * operations can fail, need to pre-reserve sequence number. They - * are realloc, uncommit and release. - * - * The reason for pre-reserve sequence number, is to prevent race condition: - * Thread 1 Thread 2 - * - * - * - * - * if Thread 2 happens to obtain the memory address Thread 1 just released, - * then NMT can mistakenly report the memory is free. - * - * Noticeably, free() does not need pre-reserve sequence number, because the call - * does not fail, so we can alway write "release" record before the memory is actaully - * freed. - * - * For realloc, uncommit and release, following coding pattern should be used: - * - * MemTracker::Tracker tkr = MemTracker::get_realloc_tracker(); - * ptr = ::realloc(...); - * if (ptr == NULL) { - * tkr.record(...) - * } else { - * tkr.discard(); - * } - * - * MemTracker::Tracker tkr = MemTracker::get_virtual_memory_uncommit_tracker(); - * if (uncommit(...)) { - * tkr.record(...); - * } else { - * tkr.discard(); - * } - * - * MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); - * if (release(...)) { - * tkr.record(...); - * } else { - * tkr.discard(); - * } - * - * Since pre-reserved sequence number is only good for the generation that it is acquired, - * when there is pending Tracker that reserved sequence number, NMT sync-point has - * to be skipped to prevent from advancing generation. This is done by inc and dec - * MemTracker::_pending_op_count, when MemTracker::_pending_op_count > 0, NMT sync-point is skipped. - * Not all pre-reservation of sequence number will increment pending op count. For JavaThreads - * that honor safepoints, safepoint can not occur during the memory operations, so the - * pre-reserved sequence number won't cross the generation boundry. - */ -MemTracker::Tracker::Tracker(MemoryOperation op, Thread* thr) { - _op = NoOp; - _seq = 0; - if (MemTracker::is_on()) { - _java_thread = NULL; - _op = op; - - // figure out if ThreadCritical lock is needed to write this operation - // to MemTracker - if (MemTracker::is_single_threaded_bootstrap()) { - thr = NULL; - } else if (thr == NULL) { - // don't use Thread::current(), since it is possible that - // the calling thread has yet to attach to VM 'Thread', - // which will result assertion failure - thr = ThreadLocalStorage::thread(); - } - - if (thr != NULL) { - // Check NMT load - MemTracker::check_NMT_load(thr); - - if (thr->is_Java_thread() && ((JavaThread*)thr)->is_safepoint_visible()) { - _java_thread = (JavaThread*)thr; - JavaThreadState state = _java_thread->thread_state(); - // JavaThreads that are safepoint safe, can run through safepoint, - // so ThreadCritical is needed to ensure no threads at safepoint create - // new records while the records are being gathered and the sequence number is changing - _need_thread_critical_lock = - SafepointSynchronize::safepoint_safe(_java_thread, state); - } else { - _need_thread_critical_lock = true; - } - } else { - _need_thread_critical_lock - = !MemTracker::is_single_threaded_bootstrap(); - } - - // see if we need to pre-reserve sequence number for this operation - if (_op == Realloc || _op == Uncommit || _op == Release) { - if (_need_thread_critical_lock) { - ThreadCritical tc; - MemTracker::inc_pending_op_count(); - _seq = SequenceGenerator::next(); - } else { - // for the threads that honor safepoints, no safepoint can occur - // during the lifespan of tracker, so we don't need to increase - // pending op count. - _seq = SequenceGenerator::next(); - } - } - } -} - -void MemTracker::Tracker::discard() { - if (MemTracker::is_on() && _seq != 0) { - if (_need_thread_critical_lock) { - ThreadCritical tc; - MemTracker::dec_pending_op_count(); - } - _seq = 0; - } -} - - -void MemTracker::Tracker::record(address old_addr, address new_addr, size_t size, - MEMFLAGS flags, address pc) { - assert(old_addr != NULL && new_addr != NULL, "Sanity check"); - assert(_op == Realloc || _op == NoOp, "Wrong call"); - if (MemTracker::is_on() && NMT_CAN_TRACK(flags) && _op != NoOp && !MemTracker::shutdown_in_progress()) { - assert(_seq > 0, "Need pre-reserve sequence number"); - if (_need_thread_critical_lock) { - ThreadCritical tc; - // free old address, use pre-reserved sequence number - MemTracker::write_tracking_record(old_addr, MemPointerRecord::free_tag(), - 0, _seq, pc, _java_thread); - MemTracker::write_tracking_record(new_addr, flags | MemPointerRecord::malloc_tag(), - size, SequenceGenerator::next(), pc, _java_thread); - // decrement MemTracker pending_op_count - MemTracker::dec_pending_op_count(); - } else { - // free old address, use pre-reserved sequence number - MemTracker::write_tracking_record(old_addr, MemPointerRecord::free_tag(), - 0, _seq, pc, _java_thread); - MemTracker::write_tracking_record(new_addr, flags | MemPointerRecord::malloc_tag(), - size, SequenceGenerator::next(), pc, _java_thread); - } - _seq = 0; - } -} - -void MemTracker::Tracker::record(address addr, size_t size, MEMFLAGS flags, address pc) { - // OOM already? - if (addr == NULL) return; - - if (MemTracker::is_on() && NMT_CAN_TRACK(flags) && _op != NoOp && !MemTracker::shutdown_in_progress()) { - bool pre_reserved_seq = (_seq != 0); - address pc = CALLER_CALLER_PC; - MEMFLAGS orig_flags = flags; - - // or the tagging flags - switch(_op) { - case Malloc: - flags |= MemPointerRecord::malloc_tag(); - break; - case Free: - flags = MemPointerRecord::free_tag(); - break; - case Realloc: - fatal("Use the other Tracker::record()"); - break; - case Reserve: - case ReserveAndCommit: - flags |= MemPointerRecord::virtual_memory_reserve_tag(); - break; - case Commit: - flags = MemPointerRecord::virtual_memory_commit_tag(); - break; - case Type: - flags |= MemPointerRecord::virtual_memory_type_tag(); - break; - case Uncommit: - assert(pre_reserved_seq, "Need pre-reserve sequence number"); - flags = MemPointerRecord::virtual_memory_uncommit_tag(); - break; - case Release: - assert(pre_reserved_seq, "Need pre-reserve sequence number"); - flags = MemPointerRecord::virtual_memory_release_tag(); - break; - case ArenaSize: - // a bit of hack here, add a small postive offset to arena - // address for its size record, so the size record is sorted - // right after arena record. - flags = MemPointerRecord::arena_size_tag(); - addr += sizeof(void*); - break; - case StackRelease: - flags = MemPointerRecord::virtual_memory_release_tag(); - break; - default: - ShouldNotReachHere(); - } - - // write memory tracking record - if (_need_thread_critical_lock) { - ThreadCritical tc; - if (_seq == 0) _seq = SequenceGenerator::next(); - MemTracker::write_tracking_record(addr, flags, size, _seq, pc, _java_thread); - if (_op == ReserveAndCommit) { - MemTracker::write_tracking_record(addr, orig_flags | MemPointerRecord::virtual_memory_commit_tag(), - size, SequenceGenerator::next(), pc, _java_thread); - } - if (pre_reserved_seq) MemTracker::dec_pending_op_count(); - } else { - if (_seq == 0) _seq = SequenceGenerator::next(); - MemTracker::write_tracking_record(addr, flags, size, _seq, pc, _java_thread); - if (_op == ReserveAndCommit) { - MemTracker::write_tracking_record(addr, orig_flags | MemPointerRecord::virtual_memory_commit_tag(), - size, SequenceGenerator::next(), pc, _java_thread); - } - } - _seq = 0; + _longest_bucket_length = MAX2(_longest_bucket_length, length); } +}; + + +void MemTracker::tuning_statistics(outputStream* out) { + // NMT statistics + StatisticsWalker walker; + MallocSiteTable::walk_malloc_site(&walker); + walker.completed(); + + out->print_cr("Native Memory Tracking Statistics:"); + out->print_cr("Malloc allocation site table size: %d", MallocSiteTable::hash_buckets()); + out->print_cr(" Tracking stack depth: %d", NMT_TrackingStackDepth); + NOT_PRODUCT(out->print_cr("Peak concurrent access: %d", MallocSiteTable::access_peak_count());) + out->print_cr(" "); + walker.report_statistics(out); } diff --git a/hotspot/src/share/vm/services/memTracker.hpp b/hotspot/src/share/vm/services/memTracker.hpp index 8573c1f6530..de43b9f2b0e 100644 --- a/hotspot/src/share/vm/services/memTracker.hpp +++ b/hotspot/src/share/vm/services/memTracker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,566 +25,288 @@ #ifndef SHARE_VM_SERVICES_MEM_TRACKER_HPP #define SHARE_VM_SERVICES_MEM_TRACKER_HPP -#include "utilities/macros.hpp" +#include "services/nmtCommon.hpp" +#include "utilities/nativeCallStack.hpp" + #if !INCLUDE_NMT -#include "utilities/ostream.hpp" +#define CURRENT_PC NativeCallStack::EMPTY_STACK +#define CALLER_PC NativeCallStack::EMPTY_STACK -class BaselineOutputer : public StackObj { - -}; - -class BaselineTTYOutputer : public BaselineOutputer { - public: - BaselineTTYOutputer(outputStream* st) { } +class Tracker : public StackObj { + public: + Tracker() { } + void record(address addr, size_t size) { } }; class MemTracker : AllStatic { - public: - enum ShutdownReason { - NMT_shutdown_none, // no shutdown requested - NMT_shutdown_user, // user requested shutdown - NMT_normal, // normal shutdown, process exit - NMT_out_of_memory, // shutdown due to out of memory - NMT_initialization, // shutdown due to initialization failure - NMT_use_malloc_only, // can not combine NMT with UseMallocOnly flag - NMT_error_reporting, // shutdown by vmError::report_and_die() - NMT_out_of_generation, // running out of generation queue - NMT_sequence_overflow // overflow the sequence number - }; + public: + static inline NMT_TrackingLevel tracking_level() { return NMT_off; } + static inline void shutdown() { } + static inline void init() { } + static bool check_launcher_nmt_support(const char* value) { return true; } + static bool verify_nmt_option() { return true; } - class Tracker { - public: - void discard() { } + static inline void* record_malloc(void* mem_base, size_t size, MEMFLAGS flag, + const NativeCallStack& stack, NMT_TrackingLevel level) { return mem_base; } + static inline size_t malloc_header_size(NMT_TrackingLevel level) { return 0; } + static inline size_t malloc_header_size(void* memblock) { return 0; } + static inline void* malloc_base(void* memblock) { return memblock; } + static inline void* record_free(void* memblock) { return memblock; } - void record(address addr, size_t size = 0, MEMFLAGS flags = mtNone, address pc = NULL) { } - void record(address old_addr, address new_addr, size_t size, - MEMFLAGS flags, address pc = NULL) { } - }; + static inline void record_new_arena(MEMFLAGS flag) { } + static inline void record_arena_free(MEMFLAGS flag) { } + static inline void record_arena_size_change(int diff, MEMFLAGS flag) { } + static inline void record_virtual_memory_reserve(void* addr, size_t size, const NativeCallStack& stack, + MEMFLAGS flag = mtNone) { } + 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() { } + 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) { } - private: - static Tracker _tkr; - - - public: - static inline void init_tracking_options(const char* option_line) { } - static inline bool is_on() { return false; } - static const char* reason() { return "Native memory tracking is not implemented"; } - static inline bool can_walk_stack() { return false; } - - static inline void bootstrap_single_thread() { } - static inline void bootstrap_multi_thread() { } - static inline void start() { } - - static inline void record_malloc(address addr, size_t size, MEMFLAGS flags, - address pc = 0, Thread* thread = NULL) { } - static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) { } - static inline void record_arena_size(address addr, size_t size) { } - static inline void record_virtual_memory_reserve(address addr, size_t size, - MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { } - static inline void record_virtual_memory_reserve_and_commit(address addr, size_t size, - MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { } - static inline void record_virtual_memory_commit(address addr, size_t size, - address pc = 0, Thread* thread = NULL) { } - static inline void record_virtual_memory_release(address addr, size_t size, - Thread* thread = NULL) { } - static inline void record_virtual_memory_type(address base, MEMFLAGS flags, - Thread* thread = NULL) { } - static inline Tracker get_realloc_tracker() { return _tkr; } - static inline Tracker get_virtual_memory_uncommit_tracker() { return _tkr; } - static inline Tracker get_virtual_memory_release_tracker() { return _tkr; } - static inline bool baseline() { return false; } - static inline bool has_baseline() { return false; } - - static inline void set_autoShutdown(bool value) { } - static void shutdown(ShutdownReason reason) { } - static inline bool shutdown_in_progress() { return false; } - static bool print_memory_usage(BaselineOutputer& out, size_t unit, - bool summary_only = true) { return false; } - static bool compare_memory_usage(BaselineOutputer& out, size_t unit, - bool summary_only = true) { return false; } - - static bool wbtest_wait_for_data_merge() { return false; } - - static inline void sync() { } - static inline void thread_exiting(JavaThread* thread) { } + static void final_report(outputStream*) { } }; - -#else // !INCLUDE_NMT - -#include "memory/allocation.hpp" -#include "runtime/globals.hpp" -#include "runtime/mutex.hpp" -#include "runtime/os.hpp" -#include "runtime/thread.hpp" -#include "services/memPtr.hpp" -#include "services/memRecorder.hpp" -#include "services/memSnapshot.hpp" -#include "services/memTrackWorker.hpp" - -extern bool NMT_track_callsite; - -#ifndef MAX_UNSIGNED_LONG -#define MAX_UNSIGNED_LONG (unsigned long)(-1) -#endif - -#ifdef ASSERT - #define DEBUG_CALLER_PC (NMT_track_callsite ? os::get_caller_pc(2) : 0) #else - #define DEBUG_CALLER_PC 0 -#endif -// The thread closure walks threads to collect per-thread -// memory recorders at NMT sync point -class SyncThreadRecorderClosure : public ThreadClosure { - private: - int _thread_count; +#include "runtime/atomic.hpp" +#include "runtime/threadCritical.hpp" +#include "services/mallocTracker.hpp" +#include "services/virtualMemoryTracker.hpp" + +extern volatile bool NMT_stack_walkable; + +#define CURRENT_PC ((MemTracker::tracking_level() == NMT_detail && NMT_stack_walkable) ? \ + NativeCallStack(0, true) : NativeCallStack::EMPTY_STACK) +#define CALLER_PC ((MemTracker::tracking_level() == NMT_detail && NMT_stack_walkable) ? \ + NativeCallStack(1, true) : NativeCallStack::EMPTY_STACK) + +class MemBaseline; +class Mutex; + +// Tracker is used for guarding 'release' semantics of virtual memory operation, to avoid +// the other thread obtains and records the same region that is just 'released' by current +// thread but before it can record the operation. +class Tracker : public StackObj { + public: + enum TrackerType { + uncommit, + release + }; public: - SyncThreadRecorderClosure() { - _thread_count =0; - } - - void do_thread(Thread* thread); - int get_thread_count() const { - return _thread_count; - } + Tracker(enum TrackerType type) : _type(type) { } + void record(address addr, size_t size); + private: + enum TrackerType _type; + // Virtual memory tracking data structures are protected by ThreadCritical lock. + ThreadCritical _tc; }; -class BaselineOutputer; -class MemSnapshot; -class MemTrackWorker; -class Thread; -/* - * MemTracker is the 'gate' class to native memory tracking runtime. - */ class MemTracker : AllStatic { - friend class GenerationData; - friend class MemTrackWorker; - friend class MemSnapshot; - friend class SyncThreadRecorderClosure; - - // NMT state - enum NMTStates { - NMT_uninited, // not yet initialized - NMT_bootstrapping_single_thread, // bootstrapping, VM is in single thread mode - NMT_bootstrapping_multi_thread, // bootstrapping, VM is about to enter multi-thread mode - NMT_started, // NMT fully started - NMT_shutdown_pending, // shutdown pending - NMT_final_shutdown, // in final phase of shutdown - NMT_shutdown // shutdown - }; - public: - class Tracker : public StackObj { - friend class MemTracker; - public: - enum MemoryOperation { - NoOp, // no op - Malloc, // malloc - Realloc, // realloc - Free, // free - Reserve, // virtual memory reserve - Commit, // virtual memory commit - ReserveAndCommit, // virtual memory reserve and commit - StackAlloc = ReserveAndCommit, // allocate thread stack - Type, // assign virtual memory type - Uncommit, // virtual memory uncommit - Release, // virtual memory release - ArenaSize, // set arena size - StackRelease // release thread stack - }; - - - protected: - Tracker(MemoryOperation op, Thread* thr = NULL); - - public: - void discard(); - - void record(address addr, size_t size = 0, MEMFLAGS flags = mtNone, address pc = NULL); - void record(address old_addr, address new_addr, size_t size, - MEMFLAGS flags, address pc = NULL); - - private: - bool _need_thread_critical_lock; - JavaThread* _java_thread; - MemoryOperation _op; // memory operation - jint _seq; // reserved sequence number - }; - - - public: - // native memory tracking level - enum NMTLevel { - NMT_off, // native memory tracking is off - NMT_summary, // don't track callsite - NMT_detail // track callsite also - }; - - enum ShutdownReason { - NMT_shutdown_none, // no shutdown requested - NMT_shutdown_user, // user requested shutdown - NMT_normal, // normal shutdown, process exit - NMT_out_of_memory, // shutdown due to out of memory - NMT_initialization, // shutdown due to initialization failure - NMT_use_malloc_only, // can not combine NMT with UseMallocOnly flag - NMT_error_reporting, // shutdown by vmError::report_and_die() - NMT_out_of_generation, // running out of generation queue - NMT_sequence_overflow // overflow the sequence number - }; - - public: - // initialize NMT tracking level from command line options, called - // from VM command line parsing code - static void init_tracking_options(const char* option_line); - - // if NMT is enabled to record memory activities - static inline bool is_on() { - return (_tracking_level >= NMT_summary && - _state >= NMT_bootstrapping_single_thread); - } - - static inline enum NMTLevel tracking_level() { + static inline NMT_TrackingLevel tracking_level() { + if (_tracking_level == NMT_unknown) { + // No fencing is needed here, since JVM is in single-threaded + // mode. + _tracking_level = init_tracking_level(); + _cmdline_tracking_level = _tracking_level; + } return _tracking_level; } - // user readable reason for shutting down NMT - static const char* reason() { - switch(_reason) { - case NMT_shutdown_none: - return "Native memory tracking is not enabled"; - case NMT_shutdown_user: - return "Native memory tracking has been shutdown by user"; - case NMT_normal: - return "Native memory tracking has been shutdown due to process exiting"; - case NMT_out_of_memory: - return "Native memory tracking has been shutdown due to out of native memory"; - case NMT_initialization: - return "Native memory tracking failed to initialize"; - case NMT_error_reporting: - return "Native memory tracking has been shutdown due to error reporting"; - case NMT_out_of_generation: - return "Native memory tracking has been shutdown due to running out of generation buffer"; - case NMT_sequence_overflow: - return "Native memory tracking has been shutdown due to overflow the sequence number"; - case NMT_use_malloc_only: - return "Native memory tracking is not supported when UseMallocOnly is on"; - default: - ShouldNotReachHere(); - return NULL; + // A late initialization, for the stuff(s) can not be + // done in init_tracking_level(), which can NOT malloc + // any memory. + static void init(); + + // Shutdown native memory tracking + static void shutdown(); + + // Verify native memory tracking command line option. + // This check allows JVM to detect if compatible launcher + // is used. + // If an incompatible launcher is used, NMT may not be + // able to start, even it is enabled by command line option. + // A warning message should be given if it is encountered. + static bool check_launcher_nmt_support(const char* value); + + // This method checks native memory tracking environment + // variable value passed by launcher. + // Launcher only obligated to pass native memory tracking + // option value, but not obligated to validate the value, + // and launcher has option to discard native memory tracking + // option from the command line once it sets up the environment + // variable, so NMT has to catch the bad value here. + static bool verify_nmt_option(); + + // Transition the tracking level to specified level + static bool transition_to(NMT_TrackingLevel level); + + static inline void* record_malloc(void* mem_base, size_t size, MEMFLAGS flag, + const NativeCallStack& stack, NMT_TrackingLevel level) { + return MallocTracker::record_malloc(mem_base, size, flag, stack, level); + } + + static inline size_t malloc_header_size(NMT_TrackingLevel level) { + return MallocTracker::malloc_header_size(level); + } + + static size_t malloc_header_size(void* memblock) { + if (tracking_level() != NMT_off) { + return MallocTracker::get_header_size(memblock); + } + return 0; + } + + // To malloc base address, which is the starting address + // of malloc tracking header if tracking is enabled. + // Otherwise, it returns the same address. + static void* malloc_base(void* memblock); + + // Record malloc free and return malloc base address + static inline void* record_free(void* memblock) { + return MallocTracker::record_free(memblock); + } + + + // Record creation of an arena + static inline void record_new_arena(MEMFLAGS flag) { + if (tracking_level() < NMT_summary) return; + MallocTracker::record_new_arena(flag); + } + + // Record destruction of an arena + static inline void record_arena_free(MEMFLAGS flag) { + if (tracking_level() < NMT_summary) return; + MallocTracker::record_arena_free(flag); + } + + // Record arena size change. Arena size is the size of all arena + // chuncks that backing up the arena. + static inline void record_arena_size_change(int diff, MEMFLAGS flag) { + if (tracking_level() < NMT_summary) return; + MallocTracker::record_arena_size_change(diff, flag); + } + + static inline void record_virtual_memory_reserve(void* addr, size_t size, const NativeCallStack& stack, + MEMFLAGS flag = mtNone) { + if (tracking_level() < NMT_summary) return; + if (addr != NULL) { + ThreadCritical tc; + // Recheck to avoid potential racing during NMT shutdown + if (tracking_level() < NMT_summary) return; + VirtualMemoryTracker::add_reserved_region((address)addr, size, stack, flag); } } - // test if we can walk native stack - static bool can_walk_stack() { - // native stack is not walkable during bootstrapping on sparc -#if defined(SPARC) - return (_state == NMT_started); -#else - return (_state >= NMT_bootstrapping_single_thread && _state <= NMT_started); -#endif - } - - // if native memory tracking tracks callsite - static inline bool track_callsite() { return _tracking_level == NMT_detail; } - - // NMT automatically shuts itself down under extreme situation by default. - // When the value is set to false, NMT will try its best to stay alive, - // even it has to slow down VM. - static inline void set_autoShutdown(bool value) { - AutoShutdownNMT = value; - if (AutoShutdownNMT && _slowdown_calling_thread) { - _slowdown_calling_thread = false; + static inline void record_virtual_memory_reserve_and_commit(void* addr, size_t size, + const NativeCallStack& stack, MEMFLAGS flag = mtNone) { + if (tracking_level() < NMT_summary) return; + if (addr != NULL) { + ThreadCritical tc; + if (tracking_level() < NMT_summary) return; + VirtualMemoryTracker::add_reserved_region((address)addr, size, + stack, flag, true); } } - // shutdown native memory tracking capability. Native memory tracking - // can be shutdown by VM when it encounters low memory scenarios. - // Memory tracker should gracefully shutdown itself, and preserve the - // latest memory statistics for post morten diagnosis. - static void shutdown(ShutdownReason reason); - - // if there is shutdown requested - static inline bool shutdown_in_progress() { - return (_state >= NMT_shutdown_pending); - } - - // bootstrap native memory tracking, so it can start to collect raw data - // before worker thread can start - - // the first phase of bootstrapping, when VM still in single-threaded mode - static void bootstrap_single_thread(); - // the second phase of bootstrapping, VM is about or already in multi-threaded mode - static void bootstrap_multi_thread(); - - - // start() has to be called when VM still in single thread mode, but after - // command line option parsing is done. - static void start(); - - // record a 'malloc' call - static inline void record_malloc(address addr, size_t size, MEMFLAGS flags, - address pc = 0, Thread* thread = NULL) { - Tracker tkr(Tracker::Malloc, thread); - tkr.record(addr, size, flags, pc); - } - // record a 'free' call - static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) { - Tracker tkr(Tracker::Free, thread); - tkr.record(addr, 0, flags, DEBUG_CALLER_PC); - } - - static inline void record_arena_size(address addr, size_t size) { - Tracker tkr(Tracker::ArenaSize); - tkr.record(addr, size); - } - - // record a virtual memory 'reserve' call - static inline void record_virtual_memory_reserve(address addr, size_t size, - MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { - assert(size > 0, "Sanity check"); - Tracker tkr(Tracker::Reserve, thread); - tkr.record(addr, size, flags, pc); - } - - static inline void record_thread_stack(address addr, size_t size, Thread* thr, - address pc = 0) { - Tracker tkr(Tracker::StackAlloc, thr); - tkr.record(addr, size, mtThreadStack, pc); - } - - static inline void release_thread_stack(address addr, size_t size, Thread* thr) { - Tracker tkr(Tracker::StackRelease, thr); - tkr.record(addr, size, mtThreadStack, DEBUG_CALLER_PC); - } - - // record a virtual memory 'commit' call - static inline void record_virtual_memory_commit(address addr, size_t size, - address pc, Thread* thread = NULL) { - Tracker tkr(Tracker::Commit, thread); - tkr.record(addr, size, mtNone, pc); - } - - static inline void record_virtual_memory_reserve_and_commit(address addr, size_t size, - MEMFLAGS flags, address pc, Thread* thread = NULL) { - Tracker tkr(Tracker::ReserveAndCommit, thread); - tkr.record(addr, size, flags, pc); - } - - static inline void record_virtual_memory_release(address addr, size_t size, - Thread* thread = NULL) { - if (is_on()) { - Tracker tkr(Tracker::Release, thread); - tkr.record(addr, size); + static inline void record_virtual_memory_commit(void* addr, size_t size, + const NativeCallStack& stack) { + if (tracking_level() < NMT_summary) return; + if (addr != NULL) { + ThreadCritical tc; + if (tracking_level() < NMT_summary) return; + VirtualMemoryTracker::add_committed_region((address)addr, size, stack); } } - // record memory type on virtual memory base address - static inline void record_virtual_memory_type(address base, MEMFLAGS flags, - Thread* thread = NULL) { - Tracker tkr(Tracker::Type); - tkr.record(base, 0, flags); - } - - // Get memory trackers for memory operations that can result race conditions. - // The memory tracker has to be obtained before realloc, virtual memory uncommit - // and virtual memory release, and call tracker.record() method if operation - // succeeded, or tracker.discard() to abort the tracking. - static inline Tracker get_realloc_tracker() { - return Tracker(Tracker::Realloc); - } - static inline Tracker get_virtual_memory_uncommit_tracker() { - return Tracker(Tracker::Uncommit); + assert(tracking_level() >= NMT_summary, "Check by caller"); + return Tracker(Tracker::uncommit); } static inline Tracker get_virtual_memory_release_tracker() { - return Tracker(Tracker::Release); + assert(tracking_level() >= NMT_summary, "Check by caller"); + return Tracker(Tracker::release); } - - // create memory baseline of current memory snapshot - static bool baseline(); - // is there a memory baseline - static bool has_baseline() { - return _baseline.baselined(); - } - - // print memory usage from current snapshot - static bool print_memory_usage(BaselineOutputer& out, size_t unit, - bool summary_only = true); - // compare memory usage between current snapshot and baseline - static bool compare_memory_usage(BaselineOutputer& out, size_t unit, - bool summary_only = true); - - // the version for whitebox testing support, it ensures that all memory - // activities before this method call, are reflected in the snapshot - // database. - static bool wbtest_wait_for_data_merge(); - - // sync is called within global safepoint to synchronize nmt data - static void sync(); - - // called when a thread is about to exit - static void thread_exiting(JavaThread* thread); - - // retrieve global snapshot - static MemSnapshot* get_snapshot() { - if (shutdown_in_progress()) { - return NULL; - } - return _snapshot; - } - - // print tracker stats - NOT_PRODUCT(static void print_tracker_stats(outputStream* st);) - NOT_PRODUCT(static void walk_stack(int toSkip, char* buf, int len);) - - private: - // start native memory tracking worker thread - static bool start_worker(MemSnapshot* snapshot); - - // called by worker thread to complete shutdown process - static void final_shutdown(); - - protected: - // retrieve per-thread recorder of the specified thread. - // if the recorder is full, it will be enqueued to overflow - // queue, a new recorder is acquired from recorder pool or a - // new instance is created. - // when thread == NULL, it means global recorder - static MemRecorder* get_thread_recorder(JavaThread* thread); - - // per-thread recorder pool - static void release_thread_recorder(MemRecorder* rec); - static void delete_all_pooled_recorders(); - - // pending recorder queue. Recorders are queued to pending queue - // when they are overflowed or collected at nmt sync point. - static void enqueue_pending_recorder(MemRecorder* rec); - static MemRecorder* get_pending_recorders(); - static void delete_all_pending_recorders(); - - // write a memory tracking record in recorder - static void write_tracking_record(address addr, MEMFLAGS type, - size_t size, jint seq, address pc, JavaThread* thread); - - static bool is_single_threaded_bootstrap() { - return _state == NMT_bootstrapping_single_thread; - } - - static void check_NMT_load(Thread* thr) { - assert(thr != NULL, "Sanity check"); - if (_slowdown_calling_thread && thr != _worker_thread) { -#ifdef _WINDOWS - // On Windows, os::NakedYield() does not work as well - // as short sleep. - os::naked_short_sleep(1); -#else - os::naked_yield(); -#endif + static inline void record_virtual_memory_type(void* addr, MEMFLAGS flag) { + if (tracking_level() < NMT_summary) return; + if (addr != NULL) { + ThreadCritical tc; + if (tracking_level() < NMT_summary) return; + VirtualMemoryTracker::set_reserved_region_type((address)addr, flag); } } - static void inc_pending_op_count() { - Atomic::inc(&_pending_op_count); + static inline void record_thread_stack(void* addr, size_t size) { + if (tracking_level() < NMT_summary) return; + if (addr != NULL) { + // uses thread stack malloc slot for book keeping number of threads + MallocMemorySummary::record_malloc(0, mtThreadStack); + record_virtual_memory_reserve_and_commit(addr, size, CALLER_PC, mtThreadStack); + } } - static void dec_pending_op_count() { - Atomic::dec(&_pending_op_count); - assert(_pending_op_count >= 0, "Sanity check"); + static inline void release_thread_stack(void* addr, size_t size) { + if (tracking_level() < NMT_summary) return; + if (addr != NULL) { + // uses thread stack malloc slot for book keeping number of threads + MallocMemorySummary::record_free(0, mtThreadStack); + ThreadCritical tc; + if (tracking_level() < NMT_summary) return; + VirtualMemoryTracker::remove_released_region((address)addr, size); + } } + // Query lock is used to synchronize the access to tracking data. + // So far, it is only used by JCmd query, but it may be used by + // other tools. + static inline Mutex* query_lock() { return _query_lock; } + + // Make a final report and shutdown. + // This function generates summary report without creating snapshots, + // to avoid additional memory allocation. It uses native memory summary + // counters, and makes adjustment to them, once the adjustment is made, + // the counters are no longer accurate. As the result, this function + // should only be used for final reporting before shutting down. + static void final_report(outputStream*); + + // Stored baseline + static inline MemBaseline& get_baseline() { + return _baseline; + } + + static NMT_TrackingLevel cmdline_tracking_level() { + return _cmdline_tracking_level; + } + + static void tuning_statistics(outputStream* out); private: - // retrieve a pooled memory record or create new one if there is not - // one available - static MemRecorder* get_new_or_pooled_instance(); - static void create_memory_record(address addr, MEMFLAGS type, - size_t size, address pc, Thread* thread); - static void create_record_in_recorder(address addr, MEMFLAGS type, - size_t size, address pc, JavaThread* thread); - - static void set_current_processing_generation(unsigned long generation) { - _worker_thread_idle = false; - _processing_generation = generation; - } - - static void report_worker_idle() { - _worker_thread_idle = true; - } + static NMT_TrackingLevel init_tracking_level(); private: - // global memory snapshot - static MemSnapshot* _snapshot; - - // a memory baseline of snapshot + // Tracking level + static volatile NMT_TrackingLevel _tracking_level; + // If NMT option value passed by launcher through environment + // variable is valid + static bool _is_nmt_env_valid; + // command line tracking level + static NMT_TrackingLevel _cmdline_tracking_level; + // Stored baseline static MemBaseline _baseline; - - // query lock + // Query lock static Mutex* _query_lock; - - // a thread can start to allocate memory before it is attached - // to VM 'Thread', those memory activities are recorded here. - // ThreadCritical is required to guard this global recorder. - static MemRecorder* volatile _global_recorder; - - // main thread id - debug_only(static intx _main_thread_tid;) - - // pending recorders to be merged - static MemRecorder* volatile _merge_pending_queue; - - NOT_PRODUCT(static volatile jint _pending_recorder_count;) - - // pooled memory recorders - static MemRecorder* volatile _pooled_recorders; - - // memory recorder pool management, uses following - // counter to determine if a released memory recorder - // should be pooled - - // latest thread count - static int _thread_count; - // pooled recorder count - static volatile jint _pooled_recorder_count; - - - // worker thread to merge pending recorders into snapshot - static MemTrackWorker* _worker_thread; - - // how many safepoints we skipped without entering sync point - static int _sync_point_skip_count; - - // if the tracker is properly intialized - static bool _is_tracker_ready; - // tracking level (off, summary and detail) - static enum NMTLevel _tracking_level; - - // current nmt state - static volatile enum NMTStates _state; - // the reason for shutting down nmt - static enum ShutdownReason _reason; - // the generation that NMT is processing - static volatile unsigned long _processing_generation; - // although NMT is still procesing current generation, but - // there is not more recorder to process, set idle state - static volatile bool _worker_thread_idle; - - // if NMT should slow down calling thread to allow - // worker thread to catch up - static volatile bool _slowdown_calling_thread; - - // pending memory op count. - // Certain memory ops need to pre-reserve sequence number - // before memory operation can happen to avoid race condition. - // See MemTracker::Tracker for detail - static volatile jint _pending_op_count; }; -#endif // !INCLUDE_NMT +#endif // INCLUDE_NMT #endif // SHARE_VM_SERVICES_MEM_TRACKER_HPP + diff --git a/hotspot/src/share/vm/services/nmtCommon.cpp b/hotspot/src/share/vm/services/nmtCommon.cpp new file mode 100644 index 00000000000..0934e96dd7f --- /dev/null +++ b/hotspot/src/share/vm/services/nmtCommon.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#include "precompiled.hpp" +#include "services/nmtCommon.hpp" + +const char* NMTUtil::_memory_type_names[] = { + "Java Heap", + "Class", + "Thread", + "Thread Stack", + "Code", + "GC", + "Compiler", + "Internal", + "Other", + "Symbol", + "Native Memory Tracking", + "Shared class space", + "Arena Chunk", + "Test", + "Tracing", + "Unknown" +}; + + +const char* NMTUtil::scale_name(size_t scale) { + switch(scale) { + case K: return "KB"; + case M: return "MB"; + case G: return "GB"; + } + ShouldNotReachHere(); + return NULL; +} + +size_t NMTUtil::scale_from_name(const char* scale) { + assert(scale != NULL, "Null pointer check"); + if (strncmp(scale, "KB", 2) == 0 || + strncmp(scale, "kb", 2) == 0) { + return K; + } else if (strncmp(scale, "MB", 2) == 0 || + strncmp(scale, "mb", 2) == 0) { + return M; + } else if (strncmp(scale, "GB", 2) == 0 || + strncmp(scale, "gb", 2) == 0) { + return G; + } else { + return 0; // Invalid value + } + return K; +} + diff --git a/hotspot/src/share/vm/services/nmtCommon.hpp b/hotspot/src/share/vm/services/nmtCommon.hpp new file mode 100644 index 00000000000..f057484074f --- /dev/null +++ b/hotspot/src/share/vm/services/nmtCommon.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_SERVICES_NMT_COMMON_HPP +#define SHARE_VM_SERVICES_NMT_COMMON_HPP + +#include "memory/allocation.hpp" +#include "utilities/globalDefinitions.hpp" + +#define CALC_OBJ_SIZE_IN_TYPE(obj, type) (align_size_up_(sizeof(obj), sizeof(type))/sizeof(type)) + +// Data type for memory counters +#ifdef _LP64 + typedef jlong MemoryCounterType; +#else + typedef jint MemoryCounterType; +#endif + +// Native memory tracking level +enum NMT_TrackingLevel { + NMT_unknown = 0xFF, + NMT_off = 0x00, + NMT_minimal = 0x01, + NMT_summary = 0x02, + NMT_detail = 0x03 +}; + +// Number of stack frames to capture. This is a +// build time decision. +const int NMT_TrackingStackDepth = 4; + +// A few common utilities for native memory tracking +class NMTUtil : AllStatic { + public: + // Map memory type to index + static inline int flag_to_index(MEMFLAGS flag) { + return (flag & 0xff); + } + + // Map memory type to human readable name + static const char* flag_to_name(MEMFLAGS flag) { + return _memory_type_names[flag_to_index(flag)]; + } + + // Map an index to memory type + static MEMFLAGS index_to_flag(int index) { + return (MEMFLAGS)index; + } + + // Memory size scale + static const char* scale_name(size_t scale); + static size_t scale_from_name(const char* scale); + + // Translate memory size in specified scale + static size_t amount_in_scale(size_t amount, size_t scale) { + return (amount + scale / 2) / scale; + } + private: + static const char* _memory_type_names[mt_number_of_types]; +}; + + +#endif diff --git a/hotspot/src/share/vm/services/nmtDCmd.cpp b/hotspot/src/share/vm/services/nmtDCmd.cpp index 8ced28772f3..fcad784f586 100644 --- a/hotspot/src/share/vm/services/nmtDCmd.cpp +++ b/hotspot/src/share/vm/services/nmtDCmd.cpp @@ -22,6 +22,8 @@ * */ #include "precompiled.hpp" + +#include "runtime/mutexLocker.hpp" #include "services/nmtDCmd.hpp" #include "services/memReporter.hpp" #include "services/memTracker.hpp" @@ -49,13 +51,8 @@ NMTDCmd::NMTDCmd(outputStream* output, _shutdown("shutdown", "request runtime to shutdown itself and free the " \ "memory used by runtime.", "BOOLEAN", false, "false"), - _auto_shutdown("autoShutdown", "automatically shutdown itself under " \ - "stress situation", - "BOOLEAN", true, "true"), -#ifndef PRODUCT - _debug("debug", "print tracker statistics. Debug only, not thread safe", \ + _statistics("statistics", "print tracker statistics for tuning purpose.", \ "BOOLEAN", false, "false"), -#endif _scale("scale", "Memory usage in which scale, KB, MB or GB", "STRING", false, "KB") { _dcmdparser.add_dcmd_option(&_summary); @@ -64,25 +61,30 @@ NMTDCmd::NMTDCmd(outputStream* output, _dcmdparser.add_dcmd_option(&_summary_diff); _dcmdparser.add_dcmd_option(&_detail_diff); _dcmdparser.add_dcmd_option(&_shutdown); - _dcmdparser.add_dcmd_option(&_auto_shutdown); -#ifndef PRODUCT - _dcmdparser.add_dcmd_option(&_debug); -#endif + _dcmdparser.add_dcmd_option(&_statistics); _dcmdparser.add_dcmd_option(&_scale); } + +size_t NMTDCmd::get_scale(const char* scale) const { + if (scale == NULL) return 0; + return NMTUtil::scale_from_name(scale); +} + void NMTDCmd::execute(DCmdSource source, TRAPS) { + // Check NMT state + // native memory tracking has to be on + if (MemTracker::tracking_level() == NMT_off) { + output()->print_cr("Native memory tracking is not enabled"); + return; + } else if (MemTracker::tracking_level() == NMT_minimal) { + output()->print_cr("Native memory tracking has been shutdown"); + return; + } + const char* scale_value = _scale.value(); - size_t scale_unit; - if (strcmp(scale_value, "KB") == 0 || strcmp(scale_value, "kb") == 0) { - scale_unit = K; - } else if (strcmp(scale_value, "MB") == 0 || - strcmp(scale_value, "mb") == 0) { - scale_unit = M; - } else if (strcmp(scale_value, "GB") == 0 || - strcmp(scale_value, "gb") == 0) { - scale_unit = G; - } else { + size_t scale_unit = get_scale(scale_value); + if (scale_unit == 0) { output()->print_cr("Incorrect scale value: %s", scale_value); return; } @@ -94,19 +96,11 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) { if (_summary_diff.is_set() && _summary_diff.value()) { ++nopt; } if (_detail_diff.is_set() && _detail_diff.value()) { ++nopt; } if (_shutdown.is_set() && _shutdown.value()) { ++nopt; } - if (_auto_shutdown.is_set()) { ++nopt; } - -#ifndef PRODUCT - if (_debug.is_set() && _debug.value()) { ++nopt; } -#endif + if (_statistics.is_set() && _statistics.value()) { ++nopt; } if (nopt > 1) { output()->print_cr("At most one of the following option can be specified: " \ - "summary, detail, baseline, summary.diff, detail.diff, shutdown" -#ifndef PRODUCT - ", debug" -#endif - ); + "summary, detail, baseline, summary.diff, detail.diff, shutdown"); return; } else if (nopt == 0) { if (_summary.is_set()) { @@ -117,53 +111,47 @@ void NMTDCmd::execute(DCmdSource source, TRAPS) { } } -#ifndef PRODUCT - if (_debug.value()) { - output()->print_cr("debug command is NOT thread-safe, may cause crash"); - MemTracker::print_tracker_stats(output()); - return; - } -#endif - - // native memory tracking has to be on - if (!MemTracker::is_on() || MemTracker::shutdown_in_progress()) { - // if it is not on, what's the reason? - output()->print_cr("%s", MemTracker::reason()); - return; - } + // Serialize NMT query + MutexLocker locker(MemTracker::query_lock()); if (_summary.value()) { - BaselineTTYOutputer outputer(output()); - MemTracker::print_memory_usage(outputer, scale_unit, true); + report(true, scale_unit); } else if (_detail.value()) { - BaselineTTYOutputer outputer(output()); - MemTracker::print_memory_usage(outputer, scale_unit, false); + if (!check_detail_tracking_level(output())) { + return; + } + report(false, scale_unit); } else if (_baseline.value()) { - if (MemTracker::baseline()) { - output()->print_cr("Successfully baselined."); + MemBaseline& baseline = MemTracker::get_baseline(); + if (!baseline.baseline(MemTracker::tracking_level() != NMT_detail)) { + output()->print_cr("Baseline failed"); } else { - output()->print_cr("Baseline failed."); + output()->print_cr("Baseline succeeded"); } } else if (_summary_diff.value()) { - if (MemTracker::has_baseline()) { - BaselineTTYOutputer outputer(output()); - MemTracker::compare_memory_usage(outputer, scale_unit, true); + MemBaseline& baseline = MemTracker::get_baseline(); + if (baseline.baseline_type() >= MemBaseline::Summary_baselined) { + report_diff(true, scale_unit); } else { - output()->print_cr("No baseline to compare, run 'baseline' command first"); + output()->print_cr("No baseline for comparison"); } } else if (_detail_diff.value()) { - if (MemTracker::has_baseline()) { - BaselineTTYOutputer outputer(output()); - MemTracker::compare_memory_usage(outputer, scale_unit, false); + if (!check_detail_tracking_level(output())) { + return; + } + MemBaseline& baseline = MemTracker::get_baseline(); + if (baseline.baseline_type() == MemBaseline::Detail_baselined) { + report_diff(false, scale_unit); } else { - output()->print_cr("No baseline to compare to, run 'baseline' command first"); + output()->print_cr("No detail baseline for comparison"); } } else if (_shutdown.value()) { - MemTracker::shutdown(MemTracker::NMT_shutdown_user); - output()->print_cr("Shutdown is in progress, it will take a few moments to " \ - "completely shutdown"); - } else if (_auto_shutdown.is_set()) { - MemTracker::set_autoShutdown(_auto_shutdown.value()); + MemTracker::shutdown(); + output()->print_cr("Native memory tracking has been turned off"); + } else if (_statistics.value()) { + if (check_detail_tracking_level(output())) { + MemTracker::tuning_statistics(output()); + } } else { ShouldNotReachHere(); output()->print_cr("Unknown command"); @@ -181,3 +169,46 @@ int NMTDCmd::num_arguments() { } } +void NMTDCmd::report(bool summaryOnly, size_t scale_unit) { + MemBaseline baseline; + if (baseline.baseline(summaryOnly)) { + if (summaryOnly) { + MemSummaryReporter rpt(baseline, output(), scale_unit); + rpt.report(); + } else { + MemDetailReporter rpt(baseline, output(), scale_unit); + rpt.report(); + } + } +} + +void NMTDCmd::report_diff(bool summaryOnly, size_t scale_unit) { + MemBaseline& early_baseline = MemTracker::get_baseline(); + assert(early_baseline.baseline_type() != MemBaseline::Not_baselined, + "Not yet baselined"); + assert(summaryOnly || early_baseline.baseline_type() == MemBaseline::Detail_baselined, + "Not a detail baseline"); + + MemBaseline baseline; + if (baseline.baseline(summaryOnly)) { + if (summaryOnly) { + MemSummaryDiffReporter rpt(early_baseline, baseline, output(), scale_unit); + rpt.report_diff(); + } else { + MemDetailDiffReporter rpt(early_baseline, baseline, output(), scale_unit); + rpt.report_diff(); + } + } +} + +bool NMTDCmd::check_detail_tracking_level(outputStream* out) { + if (MemTracker::tracking_level() == NMT_detail) { + return true; + } else if (MemTracker::cmdline_tracking_level() == NMT_detail) { + out->print_cr("Tracking level has been downgraded due to lack of resources"); + return false; + } else { + out->print_cr("Detail tracking is not enabled"); + return false; + } +} diff --git a/hotspot/src/share/vm/services/nmtDCmd.hpp b/hotspot/src/share/vm/services/nmtDCmd.hpp index 62872c63a76..df1ab367fd1 100644 --- a/hotspot/src/share/vm/services/nmtDCmd.hpp +++ b/hotspot/src/share/vm/services/nmtDCmd.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,12 @@ #ifndef SHARE_VM_SERVICES_NMT_DCMD_HPP #define SHARE_VM_SERVICES_NMT_DCMD_HPP +#if INCLUDE_NMT + #include "services/diagnosticArgument.hpp" #include "services/diagnosticFramework.hpp" +#include "services/memBaseline.hpp" +#include "services/mallocTracker.hpp" /** * Native memory tracking DCmd implementation @@ -39,10 +43,7 @@ class NMTDCmd: public DCmdWithParser { DCmdArgument _summary_diff; DCmdArgument _detail_diff; DCmdArgument _shutdown; - DCmdArgument _auto_shutdown; -#ifndef PRODUCT - DCmdArgument _debug; -#endif + DCmdArgument _statistics; DCmdArgument _scale; public: @@ -61,6 +62,17 @@ class NMTDCmd: public DCmdWithParser { } static int num_arguments(); virtual void execute(DCmdSource source, TRAPS); + + private: + void report(bool summaryOnly, size_t scale); + void report_diff(bool summaryOnly, size_t scale); + + size_t get_scale(const char* scale) const; + + // check if NMT running at detail tracking level + bool check_detail_tracking_level(outputStream* out); }; +#endif // INCLUDE_NMT + #endif // SHARE_VM_SERVICES_NMT_DCMD_HPP diff --git a/hotspot/src/share/vm/services/virtualMemoryTracker.cpp b/hotspot/src/share/vm/services/virtualMemoryTracker.cpp new file mode 100644 index 00000000000..20c45363f6a --- /dev/null +++ b/hotspot/src/share/vm/services/virtualMemoryTracker.cpp @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#include "precompiled.hpp" + +#include "runtime/threadCritical.hpp" +#include "services/virtualMemoryTracker.hpp" + +size_t VirtualMemorySummary::_snapshot[CALC_OBJ_SIZE_IN_TYPE(VirtualMemorySnapshot, size_t)]; + +void VirtualMemorySummary::initialize() { + assert(sizeof(_snapshot) >= sizeof(VirtualMemorySnapshot), "Sanity Check"); + // Use placement operator new to initialize static data area. + ::new ((void*)_snapshot) VirtualMemorySnapshot(); +} + +SortedLinkedList* VirtualMemoryTracker::_reserved_regions; + +int compare_committed_region(const CommittedMemoryRegion& r1, const CommittedMemoryRegion& r2) { + return r1.compare(r2); +} + +int compare_reserved_region_base(const ReservedMemoryRegion& r1, const ReservedMemoryRegion& r2) { + return r1.compare(r2); +} + +bool ReservedMemoryRegion::add_committed_region(address addr, size_t size, const NativeCallStack& stack) { + assert(addr != NULL, "Invalid address"); + assert(size > 0, "Invalid size"); + assert(contain_region(addr, size), "Not contain this region"); + + if (all_committed()) return true; + + CommittedMemoryRegion committed_rgn(addr, size, stack); + LinkedListNode* node = _committed_regions.find_node(committed_rgn); + if (node != NULL) { + CommittedMemoryRegion* rgn = node->data(); + if (rgn->same_region(addr, size)) { + return true; + } + + if (rgn->adjacent_to(addr, size)) { + // check if the next region covers this committed region, + // the regions may not be merged due to different call stacks + LinkedListNode* next = + node->next(); + if (next != NULL && next->data()->contain_region(addr, size)) { + if (next->data()->same_region(addr, size)) { + next->data()->set_call_stack(stack); + } + return true; + } + if (rgn->call_stack()->equals(stack)) { + VirtualMemorySummary::record_uncommitted_memory(rgn->size(), flag()); + // the two adjacent regions have the same call stack, merge them + rgn->expand_region(addr, size); + VirtualMemorySummary::record_committed_memory(rgn->size(), flag()); + return true; + } + VirtualMemorySummary::record_committed_memory(size, flag()); + if (rgn->base() > addr) { + return _committed_regions.insert_before(committed_rgn, node) != NULL; + } else { + return _committed_regions.insert_after(committed_rgn, node) != NULL; + } + } + assert(rgn->contain_region(addr, size), "Must cover this region"); + return true; + } else { + // New committed region + VirtualMemorySummary::record_committed_memory(size, flag()); + return add_committed_region(committed_rgn); + } +} + +void ReservedMemoryRegion::set_all_committed(bool b) { + if (all_committed() != b) { + _all_committed = b; + if (b) { + VirtualMemorySummary::record_committed_memory(size(), flag()); + } + } +} + +bool ReservedMemoryRegion::remove_uncommitted_region(LinkedListNode* node, + address addr, size_t size) { + assert(addr != NULL, "Invalid address"); + assert(size > 0, "Invalid size"); + + CommittedMemoryRegion* rgn = node->data(); + assert(rgn->contain_region(addr, size), "Has to be contained"); + assert(!rgn->same_region(addr, size), "Can not be the same region"); + + if (rgn->base() == addr || + rgn->end() == addr + size) { + rgn->exclude_region(addr, size); + return true; + } else { + // split this region + address top =rgn->end(); + // use this region for lower part + size_t exclude_size = rgn->end() - addr; + rgn->exclude_region(addr, exclude_size); + + // higher part + address high_base = addr + size; + size_t high_size = top - high_base; + + CommittedMemoryRegion high_rgn(high_base, high_size, *rgn->call_stack()); + LinkedListNode* high_node = _committed_regions.add(high_rgn); + assert(high_node == NULL || node->next() == high_node, "Should be right after"); + return (high_node != NULL); + } + + return false; +} + +bool ReservedMemoryRegion::remove_uncommitted_region(address addr, size_t sz) { + // uncommit stack guard pages + if (flag() == mtThreadStack && !same_region(addr, sz)) { + return true; + } + + assert(addr != NULL, "Invalid address"); + assert(sz > 0, "Invalid size"); + + if (all_committed()) { + assert(_committed_regions.is_empty(), "Sanity check"); + assert(contain_region(addr, sz), "Reserved region does not contain this region"); + set_all_committed(false); + VirtualMemorySummary::record_uncommitted_memory(sz, flag()); + if (same_region(addr, sz)) { + return true; + } else { + CommittedMemoryRegion rgn(base(), size(), *call_stack()); + if (rgn.base() == addr || rgn.end() == (addr + sz)) { + rgn.exclude_region(addr, sz); + return add_committed_region(rgn); + } else { + // split this region + // top of the whole region + address top =rgn.end(); + // use this region for lower part + size_t exclude_size = rgn.end() - addr; + rgn.exclude_region(addr, exclude_size); + if (add_committed_region(rgn)) { + // higher part + address high_base = addr + sz; + size_t high_size = top - high_base; + CommittedMemoryRegion high_rgn(high_base, high_size, NativeCallStack::EMPTY_STACK); + return add_committed_region(high_rgn); + } else { + return false; + } + } + } + } else { + // we have to walk whole list to remove the committed regions in + // specified range + LinkedListNode* head = + _committed_regions.head(); + LinkedListNode* prev = NULL; + VirtualMemoryRegion uncommitted_rgn(addr, sz); + + while (head != NULL && !uncommitted_rgn.is_empty()) { + CommittedMemoryRegion* crgn = head->data(); + // this committed region overlaps to region to uncommit + if (crgn->overlap_region(uncommitted_rgn.base(), uncommitted_rgn.size())) { + if (crgn->same_region(uncommitted_rgn.base(), uncommitted_rgn.size())) { + // find matched region, remove the node will do + VirtualMemorySummary::record_uncommitted_memory(uncommitted_rgn.size(), flag()); + _committed_regions.remove_after(prev); + return true; + } else if (crgn->contain_region(uncommitted_rgn.base(), uncommitted_rgn.size())) { + // this committed region contains whole uncommitted region + VirtualMemorySummary::record_uncommitted_memory(uncommitted_rgn.size(), flag()); + return remove_uncommitted_region(head, uncommitted_rgn.base(), uncommitted_rgn.size()); + } else if (uncommitted_rgn.contain_region(crgn->base(), crgn->size())) { + // this committed region has been uncommitted + size_t exclude_size = crgn->end() - uncommitted_rgn.base(); + uncommitted_rgn.exclude_region(uncommitted_rgn.base(), exclude_size); + VirtualMemorySummary::record_uncommitted_memory(crgn->size(), flag()); + LinkedListNode* tmp = head; + head = head->next(); + _committed_regions.remove_after(prev); + continue; + } else if (crgn->contain_address(uncommitted_rgn.base())) { + size_t toUncommitted = crgn->end() - uncommitted_rgn.base(); + crgn->exclude_region(uncommitted_rgn.base(), toUncommitted); + uncommitted_rgn.exclude_region(uncommitted_rgn.base(), toUncommitted); + VirtualMemorySummary::record_uncommitted_memory(toUncommitted, flag()); + } else if (uncommitted_rgn.contain_address(crgn->base())) { + size_t toUncommitted = uncommitted_rgn.end() - crgn->base(); + crgn->exclude_region(crgn->base(), toUncommitted); + uncommitted_rgn.exclude_region(uncommitted_rgn.end() - toUncommitted, + toUncommitted); + VirtualMemorySummary::record_uncommitted_memory(toUncommitted, flag()); + } + } + prev = head; + head = head->next(); + } + } + + return true; +} + +void ReservedMemoryRegion::move_committed_regions(address addr, ReservedMemoryRegion& rgn) { + assert(addr != NULL, "Invalid address"); + + // split committed regions + LinkedListNode* head = + _committed_regions.head(); + LinkedListNode* prev = NULL; + + while (head != NULL) { + if (head->data()->base() >= addr) { + break; + } + prev = head; + head = head->next(); + } + + if (head != NULL) { + if (prev != NULL) { + prev->set_next(head->next()); + } else { + _committed_regions.set_head(NULL); + } + } + + rgn._committed_regions.set_head(head); +} + +size_t ReservedMemoryRegion::committed_size() const { + if (all_committed()) { + return size(); + } else { + size_t committed = 0; + LinkedListNode* head = + _committed_regions.head(); + while (head != NULL) { + committed += head->data()->size(); + head = head->next(); + } + return committed; + } +} + +void ReservedMemoryRegion::set_flag(MEMFLAGS f) { + assert((flag() == mtNone || flag() == f), "Overwrite memory type"); + if (flag() != f) { + VirtualMemorySummary::move_reserved_memory(flag(), f, size()); + VirtualMemorySummary::move_committed_memory(flag(), f, committed_size()); + _flag = f; + } +} + +bool VirtualMemoryTracker::initialize(NMT_TrackingLevel level) { + if (level >= NMT_summary) { + VirtualMemorySummary::initialize(); + } + return true; +} + +bool VirtualMemoryTracker::late_initialize(NMT_TrackingLevel level) { + if (level >= NMT_summary) { + _reserved_regions = new (std::nothrow, ResourceObj::C_HEAP, mtNMT) + SortedLinkedList(); + return (_reserved_regions != NULL); + } + return true; +} + +bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, + const NativeCallStack& stack, MEMFLAGS flag, bool all_committed) { + assert(base_addr != NULL, "Invalid address"); + assert(size > 0, "Invalid size"); + assert(_reserved_regions != NULL, "Sanity check"); + ReservedMemoryRegion rgn(base_addr, size, stack, flag); + ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); + LinkedListNode* node; + if (reserved_rgn == NULL) { + VirtualMemorySummary::record_reserved_memory(size, flag); + node = _reserved_regions->add(rgn); + if (node != NULL) { + node->data()->set_all_committed(all_committed); + return true; + } else { + return false; + } + } else { + if (reserved_rgn->same_region(base_addr, size)) { + reserved_rgn->set_call_stack(stack); + reserved_rgn->set_flag(flag); + return true; + } else if (reserved_rgn->adjacent_to(base_addr, size)) { + VirtualMemorySummary::record_reserved_memory(size, flag); + reserved_rgn->expand_region(base_addr, size); + reserved_rgn->set_call_stack(stack); + return true; + } else { + // Overlapped reservation. + // It can happen when the regions are thread stacks, as JNI + // thread does not detach from VM before exits, and leads to + // leak JavaThread object + if (reserved_rgn->flag() == mtThreadStack) { + guarantee(!CheckJNICalls, "Attached JNI thread exited without being detached"); + // Overwrite with new region + + // Release old region + VirtualMemorySummary::record_uncommitted_memory(reserved_rgn->committed_size(), reserved_rgn->flag()); + VirtualMemorySummary::record_released_memory(reserved_rgn->size(), reserved_rgn->flag()); + + // Add new region + VirtualMemorySummary::record_reserved_memory(rgn.size(), flag); + + *reserved_rgn = rgn; + return true; + } + + // CDS mapping region. + // CDS reserves the whole region for mapping CDS archive, then maps each section into the region. + // NMT reports CDS as a whole. + if (reserved_rgn->flag() == mtClassShared) { + assert(reserved_rgn->contain_region(base_addr, size), "Reserved CDS region should contain this mapping region"); + return true; + } + + ShouldNotReachHere(); + return false; + } + } +} + +void VirtualMemoryTracker::set_reserved_region_type(address addr, MEMFLAGS flag) { + assert(addr != NULL, "Invalid address"); + assert(_reserved_regions != NULL, "Sanity check"); + + ReservedMemoryRegion rgn(addr, 1); + ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); + if (reserved_rgn != NULL) { + assert(reserved_rgn->contain_address(addr), "Containment"); + if (reserved_rgn->flag() != flag) { + assert(reserved_rgn->flag() == mtNone, "Overwrite memory type"); + reserved_rgn->set_flag(flag); + } + } +} + +bool VirtualMemoryTracker::add_committed_region(address addr, size_t size, + const NativeCallStack& stack) { + assert(addr != NULL, "Invalid address"); + assert(size > 0, "Invalid size"); + assert(_reserved_regions != NULL, "Sanity check"); + + ReservedMemoryRegion rgn(addr, size); + ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); + + assert(reserved_rgn != NULL, "No reserved region"); + assert(reserved_rgn->contain_region(addr, size), "Not completely contained"); + return reserved_rgn->add_committed_region(addr, size, stack); +} + +bool VirtualMemoryTracker::remove_uncommitted_region(address addr, size_t size) { + assert(addr != NULL, "Invalid address"); + assert(size > 0, "Invalid size"); + assert(_reserved_regions != NULL, "Sanity check"); + + ReservedMemoryRegion rgn(addr, size); + ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); + assert(reserved_rgn != NULL, "No reserved region"); + assert(reserved_rgn->contain_region(addr, size), "Not completely contained"); + return reserved_rgn->remove_uncommitted_region(addr, size); +} + +bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) { + assert(addr != NULL, "Invalid address"); + assert(size > 0, "Invalid size"); + assert(_reserved_regions != NULL, "Sanity check"); + + ReservedMemoryRegion rgn(addr, size); + ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); + + assert(reserved_rgn != NULL, "No reserved region"); + + // uncommit regions within the released region + if (!reserved_rgn->remove_uncommitted_region(addr, size)) { + return false; + } + + + VirtualMemorySummary::record_released_memory(size, reserved_rgn->flag()); + + if (reserved_rgn->same_region(addr, size)) { + return _reserved_regions->remove(rgn); + } else { + assert(reserved_rgn->contain_region(addr, size), "Not completely contained"); + if (reserved_rgn->base() == addr || + reserved_rgn->end() == addr + size) { + reserved_rgn->exclude_region(addr, size); + return true; + } else { + address top = reserved_rgn->end(); + address high_base = addr + size; + ReservedMemoryRegion high_rgn(high_base, top - high_base, + *reserved_rgn->call_stack(), reserved_rgn->flag()); + + // use original region for lower region + reserved_rgn->exclude_region(addr, top - addr); + LinkedListNode* new_rgn = _reserved_regions->add(high_rgn); + if (new_rgn == NULL) { + return false; + } else { + reserved_rgn->move_committed_regions(addr, *new_rgn->data()); + return true; + } + } + } +} + + +bool VirtualMemoryTracker::walk_virtual_memory(VirtualMemoryWalker* walker) { + assert(_reserved_regions != NULL, "Sanity check"); + ThreadCritical tc; + LinkedListNode* head = _reserved_regions->head(); + while (head != NULL) { + const ReservedMemoryRegion* rgn = head->peek(); + if (!walker->do_allocation_site(rgn)) { + return false; + } + head = head->next(); + } + return true; +} + +// Transition virtual memory tracking level. +bool VirtualMemoryTracker::transition(NMT_TrackingLevel from, NMT_TrackingLevel to) { + if (from == NMT_minimal) { + assert(to == NMT_summary || to == NMT_detail, "Just check"); + VirtualMemorySummary::reset(); + } else if (to == NMT_minimal) { + assert(from == NMT_summary || from == NMT_detail, "Just check"); + // Clean up virtual memory tracking data structures. + ThreadCritical tc; + if (_reserved_regions != NULL) { + delete _reserved_regions; + _reserved_regions = NULL; + } + } + + return true; +} + + diff --git a/hotspot/src/share/vm/services/virtualMemoryTracker.hpp b/hotspot/src/share/vm/services/virtualMemoryTracker.hpp new file mode 100644 index 00000000000..bdb686c819f --- /dev/null +++ b/hotspot/src/share/vm/services/virtualMemoryTracker.hpp @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_SERVICES_VIRTUAL_MEMORY_TRACKER_HPP +#define SHARE_VM_SERVICES_VIRTUAL_MEMORY_TRACKER_HPP + +#if INCLUDE_NMT + +#include "memory/allocation.hpp" +#include "services/allocationSite.hpp" +#include "services/nmtCommon.hpp" +#include "utilities/linkedlist.hpp" +#include "utilities/nativeCallStack.hpp" +#include "utilities/ostream.hpp" + + +/* + * Virtual memory counter + */ +class VirtualMemory VALUE_OBJ_CLASS_SPEC { + private: + size_t _reserved; + size_t _committed; + + public: + VirtualMemory() : _reserved(0), _committed(0) { } + + inline void reserve_memory(size_t sz) { _reserved += sz; } + inline void commit_memory (size_t sz) { + _committed += sz; + assert(_committed <= _reserved, "Sanity check"); + } + + inline void release_memory (size_t sz) { + assert(_reserved >= sz, "Negative amount"); + _reserved -= sz; + } + + inline void uncommit_memory(size_t sz) { + assert(_committed >= sz, "Negative amount"); + _committed -= sz; + } + + void reset() { + _reserved = 0; + _committed = 0; + } + + inline size_t reserved() const { return _reserved; } + inline size_t committed() const { return _committed; } +}; + +// Virtual memory allocation site, keeps track where the virtual memory is reserved. +class VirtualMemoryAllocationSite : public AllocationSite { + public: + VirtualMemoryAllocationSite(const NativeCallStack& stack) : + AllocationSite(stack) { } + + inline void reserve_memory(size_t sz) { data()->reserve_memory(sz); } + inline void commit_memory (size_t sz) { data()->commit_memory(sz); } + inline void uncommit_memory(size_t sz) { data()->uncommit_memory(sz); } + inline void release_memory(size_t sz) { data()->release_memory(sz); } + inline size_t reserved() const { return peek()->reserved(); } + inline size_t committed() const { return peek()->committed(); } +}; + +class VirtualMemorySummary; + +// This class represents a snapshot of virtual memory at a given time. +// The latest snapshot is saved in a static area. +class VirtualMemorySnapshot : public ResourceObj { + friend class VirtualMemorySummary; + + private: + VirtualMemory _virtual_memory[mt_number_of_types]; + + public: + inline VirtualMemory* by_type(MEMFLAGS flag) { + int index = NMTUtil::flag_to_index(flag); + return &_virtual_memory[index]; + } + + inline VirtualMemory* by_index(int index) { + assert(index >= 0, "Index out of bound"); + assert(index < mt_number_of_types, "Index out of bound"); + return &_virtual_memory[index]; + } + + inline size_t total_reserved() const { + size_t amount = 0; + for (int index = 0; index < mt_number_of_types; index ++) { + amount += _virtual_memory[index].reserved(); + } + return amount; + } + + inline size_t total_committed() const { + size_t amount = 0; + for (int index = 0; index < mt_number_of_types; index ++) { + amount += _virtual_memory[index].committed(); + } + return amount; + } + + inline void reset() { + for (int index = 0; index < mt_number_of_types; index ++) { + _virtual_memory[index].reset(); + } + } + + void copy_to(VirtualMemorySnapshot* s) { + for (int index = 0; index < mt_number_of_types; index ++) { + s->_virtual_memory[index] = _virtual_memory[index]; + } + } +}; + +class VirtualMemorySummary : AllStatic { + public: + static void initialize(); + + static inline void record_reserved_memory(size_t size, MEMFLAGS flag) { + as_snapshot()->by_type(flag)->reserve_memory(size); + } + + static inline void record_committed_memory(size_t size, MEMFLAGS flag) { + as_snapshot()->by_type(flag)->commit_memory(size); + } + + static inline void record_uncommitted_memory(size_t size, MEMFLAGS flag) { + as_snapshot()->by_type(flag)->uncommit_memory(size); + } + + static inline void record_released_memory(size_t size, MEMFLAGS flag) { + as_snapshot()->by_type(flag)->release_memory(size); + } + + // Move virtual memory from one memory type to another. + // Virtual memory can be reserved before it is associated with a memory type, and tagged + // as 'unknown'. Once the memory is tagged, the virtual memory will be moved from 'unknown' + // type to specified memory type. + static inline void move_reserved_memory(MEMFLAGS from, MEMFLAGS to, size_t size) { + as_snapshot()->by_type(from)->release_memory(size); + as_snapshot()->by_type(to)->reserve_memory(size); + } + + static inline void move_committed_memory(MEMFLAGS from, MEMFLAGS to, size_t size) { + as_snapshot()->by_type(from)->uncommit_memory(size); + as_snapshot()->by_type(to)->commit_memory(size); + } + + static inline void snapshot(VirtualMemorySnapshot* s) { + as_snapshot()->copy_to(s); + } + + static inline void reset() { + as_snapshot()->reset(); + } + + static VirtualMemorySnapshot* as_snapshot() { + return (VirtualMemorySnapshot*)_snapshot; + } + + private: + static size_t _snapshot[CALC_OBJ_SIZE_IN_TYPE(VirtualMemorySnapshot, size_t)]; +}; + + + +/* + * A virtual memory region + */ +class VirtualMemoryRegion VALUE_OBJ_CLASS_SPEC { + private: + address _base_address; + size_t _size; + + public: + VirtualMemoryRegion(address addr, size_t size) : + _base_address(addr), _size(size) { + assert(addr != NULL, "Invalid address"); + assert(size > 0, "Invalid size"); + } + + inline address base() const { return _base_address; } + inline address end() const { return base() + size(); } + inline size_t size() const { return _size; } + + inline bool is_empty() const { return size() == 0; } + + inline bool contain_address(address addr) const { + return (addr >= base() && addr < end()); + } + + + inline bool contain_region(address addr, size_t size) const { + return contain_address(addr) && contain_address(addr + size - 1); + } + + inline bool same_region(address addr, size_t sz) const { + return (addr == base() && sz == size()); + } + + + inline bool overlap_region(address addr, size_t sz) const { + VirtualMemoryRegion rgn(addr, sz); + return contain_address(addr) || + contain_address(addr + sz - 1) || + rgn.contain_address(base()) || + rgn.contain_address(end() - 1); + } + + inline bool adjacent_to(address addr, size_t sz) const { + return (addr == end() || (addr + sz) == base()); + } + + void exclude_region(address addr, size_t sz) { + assert(contain_region(addr, sz), "Not containment"); + assert(addr == base() || addr + sz == end(), "Can not exclude from middle"); + size_t new_size = size() - sz; + + if (addr == base()) { + set_base(addr + sz); + } + set_size(new_size); + } + + void expand_region(address addr, size_t sz) { + assert(adjacent_to(addr, sz), "Not adjacent regions"); + if (base() == addr + sz) { + set_base(addr); + } + set_size(size() + sz); + } + + protected: + void set_base(address base) { + assert(base != NULL, "Sanity check"); + _base_address = base; + } + + void set_size(size_t size) { + assert(size > 0, "Sanity check"); + _size = size; + } +}; + + +class CommittedMemoryRegion : public VirtualMemoryRegion { + private: + NativeCallStack _stack; + + public: + CommittedMemoryRegion(address addr, size_t size, const NativeCallStack& stack) : + VirtualMemoryRegion(addr, size), _stack(stack) { } + + inline int compare(const CommittedMemoryRegion& rgn) const { + if (overlap_region(rgn.base(), rgn.size()) || + adjacent_to (rgn.base(), rgn.size())) { + return 0; + } else { + if (base() == rgn.base()) { + return 0; + } else if (base() > rgn.base()) { + return 1; + } else { + return -1; + } + } + } + + inline bool equals(const CommittedMemoryRegion& rgn) const { + return compare(rgn) == 0; + } + + inline void set_call_stack(const NativeCallStack& stack) { _stack = stack; } + inline const NativeCallStack* call_stack() const { return &_stack; } +}; + + +typedef LinkedListIterator CommittedRegionIterator; + +int compare_committed_region(const CommittedMemoryRegion&, const CommittedMemoryRegion&); +class ReservedMemoryRegion : public VirtualMemoryRegion { + private: + SortedLinkedList + _committed_regions; + + NativeCallStack _stack; + MEMFLAGS _flag; + + bool _all_committed; + + public: + ReservedMemoryRegion(address base, size_t size, const NativeCallStack& stack, + MEMFLAGS flag = mtNone) : + VirtualMemoryRegion(base, size), _stack(stack), _flag(flag), + _all_committed(false) { } + + + ReservedMemoryRegion(address base, size_t size) : + VirtualMemoryRegion(base, size), _stack(NativeCallStack::EMPTY_STACK), _flag(mtNone), + _all_committed(false) { } + + // Copy constructor + ReservedMemoryRegion(const ReservedMemoryRegion& rr) : + VirtualMemoryRegion(rr.base(), rr.size()) { + *this = rr; + } + + inline void set_call_stack(const NativeCallStack& stack) { _stack = stack; } + inline const NativeCallStack* call_stack() const { return &_stack; } + + void set_flag(MEMFLAGS flag); + inline MEMFLAGS flag() const { return _flag; } + + inline int compare(const ReservedMemoryRegion& rgn) const { + if (overlap_region(rgn.base(), rgn.size())) { + return 0; + } else { + if (base() == rgn.base()) { + return 0; + } else if (base() > rgn.base()) { + return 1; + } else { + return -1; + } + } + } + + inline bool equals(const ReservedMemoryRegion& rgn) const { + return compare(rgn) == 0; + } + + bool add_committed_region(address addr, size_t size, const NativeCallStack& stack); + bool remove_uncommitted_region(address addr, size_t size); + + size_t committed_size() const; + + // move committed regions that higher than specified address to + // the new region + void move_committed_regions(address addr, ReservedMemoryRegion& rgn); + + inline bool all_committed() const { return _all_committed; } + void set_all_committed(bool b); + + CommittedRegionIterator iterate_committed_regions() const { + return CommittedRegionIterator(_committed_regions.head()); + } + + ReservedMemoryRegion& operator= (const ReservedMemoryRegion& other) { + set_base(other.base()); + set_size(other.size()); + + _stack = *other.call_stack(); + _flag = other.flag(); + _all_committed = other.all_committed(); + if (other.all_committed()) { + set_all_committed(true); + } else { + CommittedRegionIterator itr = other.iterate_committed_regions(); + const CommittedMemoryRegion* rgn = itr.next(); + while (rgn != NULL) { + _committed_regions.add(*rgn); + rgn = itr.next(); + } + } + return *this; + } + + private: + // The committed region contains the uncommitted region, subtract the uncommitted + // region from this committed region + bool remove_uncommitted_region(LinkedListNode* node, + address addr, size_t sz); + + bool add_committed_region(const CommittedMemoryRegion& rgn) { + assert(rgn.base() != NULL, "Invalid base address"); + assert(size() > 0, "Invalid size"); + return _committed_regions.add(rgn) != NULL; + } +}; + +int compare_reserved_region_base(const ReservedMemoryRegion& r1, const ReservedMemoryRegion& r2); + +class VirtualMemoryWalker : public StackObj { + public: + virtual bool do_allocation_site(const ReservedMemoryRegion* rgn) { return false; } +}; + +// Main class called from MemTracker to track virtual memory allocations, commits and releases. +class VirtualMemoryTracker : AllStatic { + public: + static bool initialize(NMT_TrackingLevel level); + + // Late phase initialization + static bool late_initialize(NMT_TrackingLevel level); + + static bool add_reserved_region (address base_addr, size_t size, const NativeCallStack& stack, + MEMFLAGS flag = mtNone, bool all_committed = false); + + static bool add_committed_region (address base_addr, size_t size, const NativeCallStack& stack); + static bool remove_uncommitted_region (address base_addr, size_t size); + static bool remove_released_region (address base_addr, size_t size); + static void set_reserved_region_type (address addr, MEMFLAGS flag); + + // Walk virtual memory data structure for creating baseline, etc. + static bool walk_virtual_memory(VirtualMemoryWalker* walker); + + static bool transition(NMT_TrackingLevel from, NMT_TrackingLevel to); + + private: + static SortedLinkedList* _reserved_regions; +}; + + +#endif // INCLUDE_NMT + +#endif // SHARE_VM_SERVICES_VIRTUAL_MEMORY_TRACKER_HPP diff --git a/hotspot/src/share/vm/shark/sharkBuilder.cpp b/hotspot/src/share/vm/shark/sharkBuilder.cpp index 34de2737d15..329a94aa83c 100644 --- a/hotspot/src/share/vm/shark/sharkBuilder.cpp +++ b/hotspot/src/share/vm/shark/sharkBuilder.cpp @@ -413,7 +413,7 @@ CallInst* SharkBuilder::CreateDump(Value* value) { const char *name; if (value->hasName()) // XXX this leaks, but it's only debug code - name = strdup(value->getName().str().c_str()); + name = os::strdup(value->getName().str().c_str()); else name = "unnamed_value"; diff --git a/hotspot/src/share/vm/utilities/bitMap.cpp b/hotspot/src/share/vm/utilities/bitMap.cpp index acb5d6d892a..865ae79e775 100644 --- a/hotspot/src/share/vm/utilities/bitMap.cpp +++ b/hotspot/src/share/vm/utilities/bitMap.cpp @@ -567,7 +567,7 @@ class TestBitMap : public AllStatic { } static void testResizeNonResource() { - const uintx bitmap_bytes = BITMAP_SIZE / BitsPerByte; + const size_t bitmap_bytes = BITMAP_SIZE / BitsPerByte; // Test the default behavior testResize(false); @@ -575,13 +575,13 @@ class TestBitMap : public AllStatic { { // Make sure that AllocatorMallocLimit is larger than our allocation request // forcing it to call standard malloc() - UIntFlagSetting fs(ArrayAllocatorMallocLimit, bitmap_bytes * 4); + SizeTFlagSetting fs(ArrayAllocatorMallocLimit, bitmap_bytes * 4); testResize(false); } { // Make sure that AllocatorMallocLimit is smaller than our allocation request // forcing it to call mmap() (or equivalent) - UIntFlagSetting fs(ArrayAllocatorMallocLimit, bitmap_bytes / 4); + SizeTFlagSetting fs(ArrayAllocatorMallocLimit, bitmap_bytes / 4); testResize(false); } } diff --git a/hotspot/src/share/vm/utilities/exceptions.cpp b/hotspot/src/share/vm/utilities/exceptions.cpp index 0759eda3789..5295c9b3608 100644 --- a/hotspot/src/share/vm/utilities/exceptions.cpp +++ b/hotspot/src/share/vm/utilities/exceptions.cpp @@ -85,9 +85,13 @@ bool Exceptions::special_exception(Thread* thread, const char* file, int line, H #endif // ASSERT if (thread->is_VM_thread() - || thread->is_Compiler_thread() ) { + || thread->is_Compiler_thread() + || DumpSharedSpaces ) { // We do not care what kind of exception we get for the vm-thread or a thread which // is compiling. We just install a dummy exception object + // + // We also cannot throw a proper exception when dumping, because we cannot run + // Java bytecodes now. A dummy exception will suffice. thread->set_pending_exception(Universe::vm_exception(), file, line); return true; } @@ -108,9 +112,13 @@ bool Exceptions::special_exception(Thread* thread, const char* file, int line, S } if (thread->is_VM_thread() - || thread->is_Compiler_thread() ) { + || thread->is_Compiler_thread() + || DumpSharedSpaces ) { // We do not care what kind of exception we get for the vm-thread or a thread which // is compiling. We just install a dummy exception object + // + // We also cannot throw a proper exception when dumping, because we cannot run + // Java bytecodes now. A dummy exception will suffice. thread->set_pending_exception(Universe::vm_exception(), file, line); return true; } diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.hpp b/hotspot/src/share/vm/utilities/globalDefinitions.hpp index 2bbd31afb76..269853ee674 100644 --- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp +++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp @@ -558,6 +558,27 @@ inline double fabsd(double value) { return fabs(value); } +//---------------------------------------------------------------------------------------------------- +// Special casts +// Cast floats into same-size integers and vice-versa w/o changing bit-pattern +typedef union { + jfloat f; + jint i; +} FloatIntConv; + +typedef union { + jdouble d; + jlong l; + julong ul; +} DoubleLongConv; + +inline jint jint_cast (jfloat x) { return ((FloatIntConv*)&x)->i; } +inline jfloat jfloat_cast (jint x) { return ((FloatIntConv*)&x)->f; } + +inline jlong jlong_cast (jdouble x) { return ((DoubleLongConv*)&x)->l; } +inline julong julong_cast (jdouble x) { return ((DoubleLongConv*)&x)->ul; } +inline jdouble jdouble_cast (jlong x) { return ((DoubleLongConv*)&x)->d; } + inline jint low (jlong value) { return jint(value); } inline jint high(jlong value) { return jint(value >> 32); } diff --git a/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp b/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp index 22e5d277c81..576b0235af4 100644 --- a/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp +++ b/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp @@ -167,17 +167,6 @@ typedef uint16_t jushort; typedef uint32_t juint; typedef uint64_t julong; -//---------------------------------------------------------------------------------------------------- -// Special (possibly not-portable) casts -// Cast floats into same-size integers and vice-versa w/o changing bit-pattern -// %%%%%% These seem like standard C++ to me--how about factoring them out? - Ungar - -inline jint jint_cast (jfloat x) { return *(jint* )&x; } -inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } -inline julong julong_cast (jdouble x) { return *(julong* )&x; } - -inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } -inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } //---------------------------------------------------------------------------------------------------- // Constant for jlong (specifying an long long canstant is C++ compiler specific) diff --git a/hotspot/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp b/hotspot/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp index e91e6078098..b64bbf8da0f 100644 --- a/hotspot/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp +++ b/hotspot/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp @@ -183,15 +183,6 @@ typedef unsigned short jushort; typedef unsigned int juint; typedef unsigned long long julong; -//---------------------------------------------------------------------------------------------------- -// Special (possibly not-portable) casts -// Cast floats into same-size integers and vice-versa w/o changing bit-pattern - -inline jint jint_cast (jfloat x) { return *(jint* )&x; } -inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } - -inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } -inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } //---------------------------------------------------------------------------------------------------- // Constant for jlong (specifying an long long constant is C++ compiler specific) diff --git a/hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp b/hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp index db22cc8ada6..46e8f9e8e93 100644 --- a/hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp +++ b/hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp @@ -116,16 +116,6 @@ typedef unsigned short jushort; typedef unsigned int juint; typedef unsigned __int64 julong; -//---------------------------------------------------------------------------------------------------- -// Special (possibly not-portable) casts -// Cast floats into same-size integers and vice-versa w/o changing bit-pattern - -inline jint jint_cast (jfloat x) { return *(jint* )&x; } -inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } - -inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } -inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } - //---------------------------------------------------------------------------------------------------- // Non-standard stdlib-like stuff: diff --git a/hotspot/src/share/vm/utilities/globalDefinitions_xlc.hpp b/hotspot/src/share/vm/utilities/globalDefinitions_xlc.hpp index 8a340062129..ad71883f81d 100644 --- a/hotspot/src/share/vm/utilities/globalDefinitions_xlc.hpp +++ b/hotspot/src/share/vm/utilities/globalDefinitions_xlc.hpp @@ -114,16 +114,6 @@ typedef uint16_t jushort; typedef uint32_t juint; typedef uint64_t julong; -//---------------------------------------------------------------------------------------------------- -// Special (possibly not-portable) casts -// Cast floats into same-size integers and vice-versa w/o changing bit-pattern -// %%%%%% These seem like standard C++ to me--how about factoring them out? - Ungar - -inline jint jint_cast (jfloat x) { return *(jint* )&x; } -inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } - -inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } -inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } //---------------------------------------------------------------------------------------------------- // Constant for jlong (specifying an long long canstant is C++ compiler specific) diff --git a/hotspot/src/share/vm/utilities/hashtable.cpp b/hotspot/src/share/vm/utilities/hashtable.cpp index c0c73c425fc..85c8d5f26bb 100644 --- a/hotspot/src/share/vm/utilities/hashtable.cpp +++ b/hotspot/src/share/vm/utilities/hashtable.cpp @@ -37,21 +37,22 @@ #include "utilities/numberSeq.hpp" -// This is a generic hashtable, designed to be used for the symbol -// and string tables. -// -// It is implemented as an open hash table with a fixed number of buckets. -// -// %note: -// - HashtableEntrys are allocated in blocks to reduce the space overhead. +// This hashtable is implemented as an open hash table with a fixed number of buckets. -template BasicHashtableEntry* BasicHashtable::new_entry(unsigned int hashValue) { - BasicHashtableEntry* entry; - - if (_free_list) { +template BasicHashtableEntry* BasicHashtable::new_entry_free_list() { + BasicHashtableEntry* entry = NULL; + if (_free_list != NULL) { entry = _free_list; _free_list = _free_list->next(); - } else { + } + return entry; +} + +// HashtableEntrys are allocated in blocks to reduce the space overhead. +template BasicHashtableEntry* BasicHashtable::new_entry(unsigned int hashValue) { + BasicHashtableEntry* entry = new_entry_free_list(); + + if (entry == NULL) { if (_first_free_entry + _entry_size >= _end_block) { int block_size = MIN2(512, MAX2((int)_table_size / 2, (int)_number_of_entries)); int len = _entry_size * block_size; @@ -84,9 +85,9 @@ template HashtableEntry* Hashtable::new_entry( // This is somewhat an arbitrary heuristic but if one bucket gets to // rehash_count which is currently 100, there's probably something wrong. -template bool BasicHashtable::check_rehash_table(int count) { - assert(table_size() != 0, "underflow"); - if (count > (((double)number_of_entries()/(double)table_size())*rehash_multiple)) { +template bool RehashableHashtable::check_rehash_table(int count) { + assert(this->table_size() != 0, "underflow"); + if (count > (((double)this->number_of_entries()/(double)this->table_size())*rehash_multiple)) { // Set a flag for the next safepoint, which should be at some guaranteed // safepoint interval. return true; @@ -94,13 +95,13 @@ template bool BasicHashtable::check_rehash_table(int count) { return false; } -template juint Hashtable::_seed = 0; +template juint RehashableHashtable::_seed = 0; // Create a new table and using alternate hash code, populate the new table // with the existing elements. This can be used to change the hash code // and could in the future change the size of the table. -template void Hashtable::move_to(Hashtable* new_table) { +template void RehashableHashtable::move_to(RehashableHashtable* new_table) { // Initialize the global seed for hashing. _seed = AltHashing::compute_seed(); @@ -110,7 +111,7 @@ template void Hashtable::move_to(Hashtable* ne // Iterate through the table and create a new entry for the new table for (int i = 0; i < new_table->table_size(); ++i) { - for (HashtableEntry* p = bucket(i); p != NULL; ) { + for (HashtableEntry* p = this->bucket(i); p != NULL; ) { HashtableEntry* next = p->next(); T string = p->literal(); // Use alternate hashing algorithm on the symbol in the first table @@ -239,11 +240,11 @@ template void Hashtable::reverse(void* boundary) { } } -template int Hashtable::literal_size(Symbol *symbol) { +template int RehashableHashtable::literal_size(Symbol *symbol) { return symbol->size() * HeapWordSize; } -template int Hashtable::literal_size(oop oop) { +template int RehashableHashtable::literal_size(oop oop) { // NOTE: this would over-count if (pre-JDK8) java_lang_Class::has_offset_field() is true, // and the String.value array is shared by several Strings. However, starting from JDK8, // the String.value array is not shared anymore. @@ -256,12 +257,12 @@ template int Hashtable::literal_size(oop oop) { // Note: if you create a new subclass of Hashtable, you will need to // add a new function Hashtable::literal_size(MyNewType lit) -template void Hashtable::dump_table(outputStream* st, const char *table_name) { +template void RehashableHashtable::dump_table(outputStream* st, const char *table_name) { NumberSeq summary; int literal_bytes = 0; for (int i = 0; i < this->table_size(); ++i) { int count = 0; - for (HashtableEntry* e = bucket(i); + for (HashtableEntry* e = this->bucket(i); e != NULL; e = e->next()) { count++; literal_bytes += literal_size(e->literal()); @@ -271,7 +272,7 @@ template void Hashtable::dump_table(outputStream* st double num_buckets = summary.num(); double num_entries = summary.sum(); - int bucket_bytes = (int)num_buckets * sizeof(bucket(0)); + int bucket_bytes = (int)num_buckets * sizeof(HashtableBucket); int entry_bytes = (int)num_entries * sizeof(HashtableEntry); int total_bytes = literal_bytes + bucket_bytes + entry_bytes; @@ -354,12 +355,20 @@ template void BasicHashtable::verify_lookup_length(double load) // Explicitly instantiate these types +#if INCLUDE_ALL_GCS +template class Hashtable; +template class HashtableEntry; +template class BasicHashtable; +#endif template class Hashtable; +template class RehashableHashtable; +template class RehashableHashtable; template class Hashtable; template class Hashtable; template class Hashtable; #if defined(SOLARIS) || defined(CHECK_UNHANDLED_OOPS) template class Hashtable; +template class RehashableHashtable; #endif // SOLARIS || CHECK_UNHANDLED_OOPS template class Hashtable; template class Hashtable; diff --git a/hotspot/src/share/vm/utilities/hashtable.hpp b/hotspot/src/share/vm/utilities/hashtable.hpp index fcebf72ea95..9546323b496 100644 --- a/hotspot/src/share/vm/utilities/hashtable.hpp +++ b/hotspot/src/share/vm/utilities/hashtable.hpp @@ -178,11 +178,6 @@ protected: void verify_lookup_length(double load); #endif - enum { - rehash_count = 100, - rehash_multiple = 60 - }; - void initialize(int table_size, int entry_size, int number_of_entries); // Accessor @@ -194,12 +189,12 @@ protected: // The following method is not MT-safe and must be done under lock. BasicHashtableEntry** bucket_addr(int i) { return _buckets[i].entry_addr(); } + // Attempt to get an entry from the free list + BasicHashtableEntry* new_entry_free_list(); + // Table entry management BasicHashtableEntry* new_entry(unsigned int hashValue); - // Check that the table is unbalanced - bool check_rehash_table(int count); - // Used when moving the entry to another table // Clean up links, but do not add to free_list void unlink_entry(BasicHashtableEntry* entry) { @@ -277,8 +272,30 @@ protected: return (HashtableEntry**)BasicHashtable::bucket_addr(i); } +}; + +template class RehashableHashtable : public Hashtable { + protected: + + enum { + rehash_count = 100, + rehash_multiple = 60 + }; + + // Check that the table is unbalanced + bool check_rehash_table(int count); + + public: + RehashableHashtable(int table_size, int entry_size) + : Hashtable(table_size, entry_size) { } + + RehashableHashtable(int table_size, int entry_size, + HashtableBucket* buckets, int number_of_entries) + : Hashtable(table_size, entry_size, buckets, number_of_entries) { } + + // Function to move these elements into the new table. - void move_to(Hashtable* new_table); + void move_to(RehashableHashtable* new_table); static bool use_alternate_hashcode() { return _seed != 0; } static juint seed() { return _seed; } @@ -292,7 +309,6 @@ protected: static int literal_size(ConstantPool *cp) {Unimplemented(); return 0;} static int literal_size(Klass *k) {Unimplemented(); return 0;} -public: void dump_table(outputStream* st, const char *table_name); private: diff --git a/hotspot/src/share/vm/utilities/linkedlist.cpp b/hotspot/src/share/vm/utilities/linkedlist.cpp new file mode 100644 index 00000000000..ba96b2e247f --- /dev/null +++ b/hotspot/src/share/vm/utilities/linkedlist.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" + +/////////////// Unit tests /////////////// + +#ifndef PRODUCT + +#include "runtime/os.hpp" +#include "utilities/linkedlist.hpp" +#include "memory/allocation.hpp" +#include "memory/allocation.inline.hpp" + +class Integer : public StackObj { + private: + int _value; + public: + Integer(int i) : _value(i) { } + + int value() const { return _value; } + bool equals(const Integer& i) const { + return _value == i.value(); + } +}; + +int compare_Integer(const Integer& i1, const Integer& i2) { + return i1.value() - i2.value(); +} + +void check_list_values(const int* expected, const LinkedList* list) { + LinkedListNode* head = list->head(); + int index = 0; + while (head != NULL) { + assert(head->peek()->value() == expected[index], "Unexpected value"); + head = head->next(); + index ++; + } +} + +void Test_linked_list() { + LinkedListImpl ll; + + + // Test regular linked list + assert(ll.is_empty(), "Start with empty list"); + Integer one(1), two(2), three(3), four(4), five(5), six(6); + + ll.add(six); + assert(!ll.is_empty(), "Should not be empty"); + + Integer* i = ll.find(six); + assert(i != NULL, "Should find it"); + + i = ll.find(three); + assert(i == NULL, "Not in the list"); + + LinkedListNode* node = ll.find_node(six); + assert(node != NULL, "6 is in the list"); + + ll.insert_after(three, node); + ll.insert_before(one, node); + int expected[3] = {1, 6, 3}; + check_list_values(expected, &ll); + + ll.add(two); + ll.add(four); + ll.add(five); + + // Test sorted linked list + SortedLinkedList sl; + assert(sl.is_empty(), "Start with empty list"); + + size_t ll_size = ll.size(); + sl.move(&ll); + size_t sl_size = sl.size(); + + assert(ll_size == sl_size, "Should be the same size"); + assert(ll.is_empty(), "No more entires"); + + // sorted result + int sorted_result[] = {1, 2, 3, 4, 5, 6}; + check_list_values(sorted_result, &sl); + + node = sl.find_node(four); + assert(node != NULL, "4 is in the list"); + sl.remove_before(node); + sl.remove_after(node); + int remains[] = {1, 2, 4, 6}; + check_list_values(remains, &sl); +} +#endif // PRODUCT + diff --git a/hotspot/src/share/vm/utilities/linkedlist.hpp b/hotspot/src/share/vm/utilities/linkedlist.hpp new file mode 100644 index 00000000000..a76c15cca0e --- /dev/null +++ b/hotspot/src/share/vm/utilities/linkedlist.hpp @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_UTILITIES_LINKED_LIST_HPP +#define SHARE_VM_UTILITIES_LINKED_LIST_HPP + +#include "memory/allocation.hpp" + +/* + * The implementation of a generic linked list, which uses various + * backing storages, such as C heap, arena and resource, etc. + */ + + +// An entry in a linked list. It should use the same backing storage +// as the linked list that contains this entry. +template class LinkedListNode : public ResourceObj { + private: + E _data; // embedded content + LinkedListNode* _next; // next entry + + protected: + LinkedListNode() : _next(NULL) { } + + public: + LinkedListNode(const E& e): _data(e), _next(NULL) { } + + inline void set_next(LinkedListNode* node) { _next = node; } + inline LinkedListNode * next() const { return _next; } + + E* data() { return &_data; } + const E* peek() const { return &_data; } +}; + +// A linked list interface. It does not specify +// any storage type it uses, so all methods involving +// memory allocation or deallocation are pure virtual +template class LinkedList : public ResourceObj { + protected: + LinkedListNode* _head; + + public: + LinkedList() : _head(NULL) { } + + inline void set_head(LinkedListNode* h) { _head = h; } + inline LinkedListNode* head() const { return _head; } + inline bool is_empty() const { return head() == NULL; } + + inline size_t size() const { + LinkedListNode* p; + size_t count = 0; + for (p = head(); p != NULL; count++, p = p->next()); + return count; + } + + // Move all entries from specified linked list to this one + virtual void move(LinkedList* list) = 0; + + // Add an entry to this linked list + virtual LinkedListNode* add(const E& e) = 0; + // Add all entries from specified linked list to this one, + virtual void add(LinkedListNode* node) = 0; + + // Add a linked list to this linked list + virtual bool add(const LinkedList* list) = 0; + + // Search entry in the linked list + virtual LinkedListNode* find_node(const E& e) = 0; + virtual E* find(const E& e) = 0; + + // Insert entry to the linked list + virtual LinkedListNode* insert_before(const E& e, LinkedListNode* ref) = 0; + virtual LinkedListNode* insert_after (const E& e, LinkedListNode* ref) = 0; + + // Remove entry from the linked list + virtual bool remove(const E& e) = 0; + virtual bool remove(LinkedListNode* node) = 0; + virtual bool remove_before(LinkedListNode* ref) = 0; + virtual bool remove_after(LinkedListNode* ref) = 0; + + LinkedListNode* unlink_head() { + LinkedListNode* h = this->head(); + if (h != NULL) { + this->set_head(h->next()); + } + return h; + } + + DEBUG_ONLY(virtual ResourceObj::allocation_type storage_type() = 0;) +}; + +// A linked list implementation. +// The linked list can be allocated in various type of memory: C heap, arena and resource area, etc. +template + class LinkedListImpl : public LinkedList { + protected: + Arena* _arena; + public: + LinkedListImpl() : _arena(NULL) { } + LinkedListImpl(Arena* a) : _arena(a) { } + + virtual ~LinkedListImpl() { + clear(); + } + + virtual void clear() { + LinkedListNode* p = this->head(); + this->set_head(NULL); + while (p != NULL) { + LinkedListNode* to_delete = p; + p = p->next(); + delete_node(to_delete); + } + } + + // Add an entry to the linked list + virtual LinkedListNode* add(const E& e) { + LinkedListNode* node = this->new_node(e); + if (node != NULL) { + this->add(node); + } + + return node; + } + + virtual void add(LinkedListNode* node) { + assert(node != NULL, "NULL pointer"); + node->set_next(this->head()); + this->set_head(node); + } + + // Move a linked list to this linked list, both have to be allocated on the same + // storage type. + virtual void move(LinkedList* list) { + assert(list->storage_type() == this->storage_type(), "Different storage type"); + LinkedListNode* node = this->head(); + while (node != NULL && node->next() != NULL) { + node = node->next(); + } + if (node == NULL) { + this->set_head(list->head()); + } else { + node->set_next(list->head()); + } + // All entries are moved + list->set_head(NULL); + } + + virtual bool add(const LinkedList* list) { + LinkedListNode* node = list->head(); + while (node != NULL) { + if (this->add(*node->peek()) == NULL) { + return false; + } + node = node->next(); + } + return true; + } + + + virtual LinkedListNode* find_node(const E& e) { + LinkedListNode* p = this->head(); + while (p != NULL && !p->peek()->equals(e)) { + p = p->next(); + } + return p; + } + + E* find(const E& e) { + LinkedListNode* node = find_node(e); + return (node == NULL) ? NULL : node->data(); + } + + + // Add an entry in front of the reference entry + LinkedListNode* insert_before(const E& e, LinkedListNode* ref_node) { + LinkedListNode* node = this->new_node(e); + if (node == NULL) return NULL; + if (ref_node == this->head()) { + node->set_next(ref_node); + this->set_head(node); + } else { + LinkedListNode* p = this->head(); + while (p != NULL && p->next() != ref_node) { + p = p->next(); + } + assert(p != NULL, "ref_node not in the list"); + node->set_next(ref_node); + p->set_next(node); + } + return node; + } + + // Add an entry behind the reference entry + LinkedListNode* insert_after(const E& e, LinkedListNode* ref_node) { + LinkedListNode* node = this->new_node(e); + if (node == NULL) return NULL; + node->set_next(ref_node->next()); + ref_node->set_next(node); + return node; + } + + // Remove an entry from the linked list. + // Return true if the entry is successfully removed + virtual bool remove(const E& e) { + LinkedListNode* tmp = this->head(); + LinkedListNode* prev = NULL; + + while (tmp != NULL) { + if (tmp->peek()->equals(e)) { + return remove_after(prev); + } + prev = tmp; + tmp = tmp->next(); + } + return false; + } + + // Remove the node after the reference entry + virtual bool remove_after(LinkedListNode* prev) { + LinkedListNode* to_delete; + if (prev == NULL) { + to_delete = this->unlink_head(); + } else { + to_delete = prev->next(); + if (to_delete != NULL) { + prev->set_next(to_delete->next()); + } + } + + if (to_delete != NULL) { + delete_node(to_delete); + return true; + } + return false; + } + + virtual bool remove(LinkedListNode* node) { + LinkedListNode* p = this->head(); + while (p != NULL && p->next() != node) { + p = p->next(); + } + if (p != NULL) { + p->set_next(node->next()); + delete_node(node); + return true; + } else { + return false; + } + } + + virtual bool remove_before(LinkedListNode* ref) { + assert(ref != NULL, "NULL pointer"); + LinkedListNode* p = this->head(); + LinkedListNode* to_delete = NULL; // to be deleted + LinkedListNode* prev = NULL; // node before the node to be deleted + while (p != NULL && p != ref) { + prev = to_delete; + to_delete = p; + p = p->next(); + } + if (p == NULL || to_delete == NULL) return false; + assert(to_delete->next() == ref, "Wrong node to delete"); + assert(prev == NULL || prev->next() == to_delete, + "Sanity check"); + if (prev == NULL) { + assert(to_delete == this->head(), "Must be head"); + this->set_head(to_delete->next()); + } else { + prev->set_next(to_delete->next()); + } + delete_node(to_delete); + return true; + } + + DEBUG_ONLY(ResourceObj::allocation_type storage_type() { return T; }) + protected: + // Create new linked list node object in specified storage + LinkedListNode* new_node(const E& e) const { + switch(T) { + case ResourceObj::ARENA: { + assert(_arena != NULL, "Arena not set"); + return new(_arena) LinkedListNode(e); + } + case ResourceObj::RESOURCE_AREA: + case ResourceObj::C_HEAP: { + if (alloc_failmode == AllocFailStrategy::RETURN_NULL) { + return new(std::nothrow, T, F) LinkedListNode(e); + } else { + return new(T, F) LinkedListNode(e); + } + } + default: + ShouldNotReachHere(); + } + return NULL; + } + + // Delete linked list node object + void delete_node(LinkedListNode* node) { + if (T == ResourceObj::C_HEAP) { + delete node; + } + } +}; + +// Sorted linked list. The linked list maintains sorting order specified by the comparison +// function +template + class SortedLinkedList : public LinkedListImpl { + public: + SortedLinkedList() { } + SortedLinkedList(Arena* a) : LinkedListImpl(a) { } + + virtual LinkedListNode* add(const E& e) { + return LinkedListImpl::add(e); + } + + virtual void move(LinkedList* list) { + assert(list->storage_type() == this->storage_type(), "Different storage type"); + LinkedListNode* node; + while ((node = list->unlink_head()) != NULL) { + this->add(node); + } + assert(list->is_empty(), "All entries are moved"); + } + + virtual void add(LinkedListNode* node) { + assert(node != NULL, "NULL pointer"); + LinkedListNode* tmp = this->head(); + LinkedListNode* prev = NULL; + + int cmp_val; + while (tmp != NULL) { + cmp_val = FUNC(*tmp->peek(), *node->peek()); + if (cmp_val >= 0) { + break; + } + prev = tmp; + tmp = tmp->next(); + } + + if (prev != NULL) { + node->set_next(prev->next()); + prev->set_next(node); + } else { + node->set_next(this->head()); + this->set_head(node); + } + } + + virtual bool add(const LinkedList* list) { + return LinkedListImpl::add(list); + } + + virtual LinkedListNode* find_node(const E& e) { + LinkedListNode* p = this->head(); + + while (p != NULL) { + int comp_val = FUNC(*p->peek(), e); + if (comp_val == 0) { + return p; + } else if (comp_val > 0) { + return NULL; + } + p = p->next(); + } + return NULL; + } +}; + +// Iterates all entries in the list +template class LinkedListIterator : public StackObj { + private: + LinkedListNode* _p; + bool _is_empty; + public: + LinkedListIterator(LinkedListNode* head) : _p(head) { + _is_empty = (head == NULL); + } + + bool is_empty() const { return _is_empty; } + + const E* next() { + if (_p == NULL) return NULL; + const E* e = _p->peek(); + _p = _p->next(); + return e; + } +}; + +#endif diff --git a/hotspot/src/share/vm/utilities/nativeCallStack.cpp b/hotspot/src/share/vm/utilities/nativeCallStack.cpp new file mode 100644 index 00000000000..aa3a1e67a5d --- /dev/null +++ b/hotspot/src/share/vm/utilities/nativeCallStack.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "runtime/os.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/nativeCallStack.hpp" + +const NativeCallStack NativeCallStack::EMPTY_STACK(0, false); + +NativeCallStack::NativeCallStack(int toSkip, bool fillStack) : + _hash_value(0) { + +#if !PLATFORM_NATIVE_STACK_WALKING_SUPPORTED + fillStack = false; +#endif + + if (fillStack) { + os::get_native_stack(_stack, NMT_TrackingStackDepth, toSkip); + } else { + for (int index = 0; index < NMT_TrackingStackDepth; index ++) { + _stack[index] = NULL; + } + } +} + +NativeCallStack::NativeCallStack(address* pc, int frameCount) { + int frameToCopy = (frameCount < NMT_TrackingStackDepth) ? + frameCount : NMT_TrackingStackDepth; + int index; + for (index = 0; index < frameToCopy; index ++) { + _stack[index] = pc[index]; + } + for (; index < NMT_TrackingStackDepth; index ++) { + _stack[index] = NULL; + } +} + +// number of stack frames captured +int NativeCallStack::frames() const { + int index; + for (index = 0; index < NMT_TrackingStackDepth; index ++) { + if (_stack[index] == NULL) { + break; + } + } + return index; +} + +// Hash code. Any better algorithm? +int NativeCallStack::hash() const { + long hash_val = _hash_value; + if (hash_val == 0) { + long pc; + int index; + for (index = 0; index < NMT_TrackingStackDepth; index ++) { + pc = (long)_stack[index]; + if (pc == 0) break; + hash_val += pc; + } + + NativeCallStack* p = const_cast(this); + p->_hash_value = (int)(hash_val & 0xFFFFFFFF); + } + return _hash_value; +} + +void NativeCallStack::print_on(outputStream* out) const { + print_on(out, 0); +} + +// Decode and print this call path +void NativeCallStack::print_on(outputStream* out, int indent) const { + address pc; + char buf[1024]; + int offset; + if (is_empty()) { + for (int index = 0; index < indent; index ++) out->print(" "); +#if PLATFORM_NATIVE_STACK_WALKING_SUPPORTED + out->print("[BOOTSTRAP]"); +#else + out->print("[No stack]"); +#endif + } else { + for (int frame = 0; frame < NMT_TrackingStackDepth; frame ++) { + pc = get_frame(frame); + if (pc == NULL) break; + // Print indent + for (int index = 0; index < indent; index ++) out->print(" "); + if (os::dll_address_to_function_name(pc, buf, sizeof(buf), &offset)) { + out->print_cr("[" PTR_FORMAT "] %s+0x%x", p2i(pc), buf, offset); + } else { + out->print_cr("[" PTR_FORMAT "]", p2i(pc)); + } + } + } +} + diff --git a/hotspot/src/share/vm/utilities/nativeCallStack.hpp b/hotspot/src/share/vm/utilities/nativeCallStack.hpp new file mode 100644 index 00000000000..b6d6a35303e --- /dev/null +++ b/hotspot/src/share/vm/utilities/nativeCallStack.hpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_UTILITIES_NATIVE_CALL_STACK_HPP +#define SHARE_VM_UTILITIES_NATIVE_CALL_STACK_HPP + +#include "memory/allocation.hpp" +#include "services/nmtCommon.hpp" +#include "utilities/ostream.hpp" + +/* + * This class represents a native call path (does not include Java frame) + * + * This class is developed in the context of native memory tracking, it can + * be an useful tool for debugging purpose. + * + * For example, following code should print out native call path: + * + * .... + * NativeCallStack here; + * here.print_on(tty); + * .... + * + * However, there are a couple of restrictions on this class. If the restrictions are + * not strictly followed, it may break native memory tracking badly. + * + * 1. Number of stack frames to capture, is defined by native memory tracking. + * This number has impacts on how much memory to be used by native + * memory tracking. + * 2. The class is strict stack object, no heap or virtual memory can be allocated + * from it. + */ +class NativeCallStack : public StackObj { + public: + static const NativeCallStack EMPTY_STACK; + + private: + address _stack[NMT_TrackingStackDepth]; + int _hash_value; + + public: + NativeCallStack(int toSkip = 0, bool fillStack = false); + NativeCallStack(address* pc, int frameCount); + + + // if it is an empty stack + inline bool is_empty() const { + return _stack[0] == NULL; + } + + // number of stack frames captured + int frames() const; + + inline int compare(const NativeCallStack& other) const { + return memcmp(_stack, other._stack, sizeof(_stack)); + } + + inline bool equals(const NativeCallStack& other) const { + // compare hash values + if (hash() != other.hash()) return false; + // compare each frame + return compare(other) == 0; + } + + inline address get_frame(int index) const { + assert(index >= 0 && index < NMT_TrackingStackDepth, "Index out of bound"); + return _stack[index]; + } + + // Hash code. Any better algorithm? + int hash() const; + + void print_on(outputStream* out) const; + void print_on(outputStream* out, int indent) const; +}; + +#endif diff --git a/hotspot/src/share/vm/utilities/ostream.cpp b/hotspot/src/share/vm/utilities/ostream.cpp index 59943c4b161..ceb5774fe81 100644 --- a/hotspot/src/share/vm/utilities/ostream.cpp +++ b/hotspot/src/share/vm/utilities/ostream.cpp @@ -352,6 +352,7 @@ stringStream::~stringStream() {} xmlStream* xtty; outputStream* tty; outputStream* gclog_or_tty; +CDS_ONLY(fileStream* classlist_file;) // Only dump the classes that can be stored into the CDS archive extern Mutex* tty_lock; #define EXTRACHARLEN 32 @@ -463,7 +464,8 @@ static const char* make_log_name_internal(const char* log_name, const char* forc return buf; } -// log_name comes from -XX:LogFile=log_name or -Xloggc:log_name +// log_name comes from -XX:LogFile=log_name, -Xloggc:log_name or +// -XX:DumpLoadedClassList= // in log_name, %p => pid1234 and // %t => YYYY-MM-DD_HH-MM-SS static const char* make_log_name(const char* log_name, const char* force_directory) { @@ -1103,6 +1105,16 @@ void ostream_init_log() { gclog_or_tty = gclog; } +#if INCLUDE_CDS + // For -XX:DumpLoadedClassList= option + if (DumpLoadedClassList != NULL) { + const char* list_name = make_log_name(DumpLoadedClassList, NULL); + classlist_file = new(ResourceObj::C_HEAP, mtInternal) + fileStream(list_name); + FREE_C_HEAP_ARRAY(char, list_name, mtInternal); + } +#endif + // If we haven't lazily initialized the logfile yet, do it now, // to avoid the possibility of lazy initialization during a VM // crash, which can affect the stability of the fatal error handler. @@ -1115,6 +1127,11 @@ void ostream_exit() { static bool ostream_exit_called = false; if (ostream_exit_called) return; ostream_exit_called = true; +#if INCLUDE_CDS + if (classlist_file != NULL) { + delete classlist_file; + } +#endif if (gclog_or_tty != tty) { delete gclog_or_tty; } diff --git a/hotspot/src/share/vm/utilities/ostream.hpp b/hotspot/src/share/vm/utilities/ostream.hpp index 86d8099caa9..5bf1ce43828 100644 --- a/hotspot/src/share/vm/utilities/ostream.hpp +++ b/hotspot/src/share/vm/utilities/ostream.hpp @@ -214,6 +214,8 @@ class fileStream : public outputStream { void flush(); }; +CDS_ONLY(extern fileStream* classlist_file;) + // unlike fileStream, fdStream does unbuffered I/O by calling // open() and write() directly. It is async-safe, but output // from multiple thread may be mixed together. Used by fatal diff --git a/hotspot/src/share/vm/utilities/stringUtils.cpp b/hotspot/src/share/vm/utilities/stringUtils.cpp new file mode 100644 index 00000000000..6d906249cec --- /dev/null +++ b/hotspot/src/share/vm/utilities/stringUtils.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "utilities/stringUtils.hpp" + +int StringUtils::replace_no_expand(char* string, const char* from, const char* to) { + int replace_count = 0; + size_t from_len = strlen(from); + size_t to_len = strlen(to); + assert(from_len >= to_len, "must not expand input"); + + for (char* dst = string; *dst && (dst = strstr(dst, from)) != NULL;) { + char* left_over = dst + from_len; + memmove(dst, to, to_len); // does not copy trailing 0 of + dst += to_len; // skip over the replacement. + memmove(dst, left_over, strlen(left_over) + 1); // copies the trailing 0 of + ++ replace_count; + } + + return replace_count; +} diff --git a/hotspot/src/share/vm/utilities/stringUtils.hpp b/hotspot/src/share/vm/utilities/stringUtils.hpp new file mode 100644 index 00000000000..dfae74df8cd --- /dev/null +++ b/hotspot/src/share/vm/utilities/stringUtils.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_UTILITIES_STRINGUTILS_HPP +#define SHARE_VM_UTILITIES_STRINGUTILS_HPP + +#include "memory/allocation.hpp" + +class StringUtils : AllStatic { +public: + // Replace the substring with another string . must be + // no longer than . The input string is modified in-place. + // + // Replacement is done in a single pass left-to-right. So replace_no_expand("aaa", "aa", "a") + // will result in "aa", not "a". + // + // Returns the count of substrings that have been replaced. + static int replace_no_expand(char* string, const char* from, const char* to); +}; + +#endif // SHARE_VM_UTILITIES_STRINGUTILS_HPP diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp index 2bff5f5c824..3678068bed9 100644 --- a/hotspot/src/share/vm/utilities/vmError.cpp +++ b/hotspot/src/share/vm/utilities/vmError.cpp @@ -774,6 +774,11 @@ void VMError::report(outputStream* st) { st->cr(); } + STEP(228, "(Native Memory Tracking)" ) + if (_verbose) { + MemTracker::final_report(st); + } + STEP(230, "" ) if (_verbose) { @@ -897,9 +902,6 @@ void VMError::report_and_die() { static bool log_done = false; // done saving error log static bool transmit_report_done = false; // done error reporting - // disble NMT to avoid further exception - MemTracker::shutdown(MemTracker::NMT_error_reporting); - if (SuppressFatalErrorMessage) { os::abort(); } diff --git a/hotspot/test/TEST.ROOT b/hotspot/test/TEST.ROOT index e646c1aa08d..6bf9e9a9009 100644 --- a/hotspot/test/TEST.ROOT +++ b/hotspot/test/TEST.ROOT @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,6 @@ # It also contains test-suite configuration information. # The list of keywords supported in this test suite -keys=cte_test jcmd nmt regression gc +keys=cte_test jcmd nmt regression gc stress groups=TEST.groups [closed/TEST.groups] diff --git a/hotspot/test/TEST.groups b/hotspot/test/TEST.groups index b88243644c0..4f394f62aad 100644 --- a/hotspot/test/TEST.groups +++ b/hotspot/test/TEST.groups @@ -70,21 +70,29 @@ needs_jdk = \ runtime/7194254/Test7194254.java \ runtime/Metaspace/FragmentMetaspace.java \ runtime/NMT/BaselineWithParameter.java \ + runtime/NMT/JcmdBaselineDetail.java \ + runtime/NMT/JcmdDetailDiff.java \ + runtime/NMT/JcmdScaleDetail.java \ runtime/NMT/JcmdScale.java \ + runtime/NMT/JcmdSummaryDiff.java \ runtime/NMT/JcmdWithNMTDisabled.java \ + runtime/NMT/MallocRoundingReportTest.java \ + runtime/NMT/MallocSiteHashOverflow.java \ + runtime/NMT/MallocStressTest.java \ runtime/NMT/MallocTestType.java \ runtime/NMT/ReleaseCommittedMemory.java \ + runtime/NMT/ReleaseNoCommit.java \ runtime/NMT/ShutdownTwice.java \ runtime/NMT/SummaryAfterShutdown.java \ runtime/NMT/SummarySanityCheck.java \ runtime/NMT/ThreadedMallocTestType.java \ runtime/NMT/ThreadedVirtualAllocTestType.java \ + runtime/NMT/VirtualAllocCommitUncommitRecommit.java \ runtime/NMT/VirtualAllocTestType.java \ runtime/RedefineObject/TestRedefineObject.java \ runtime/Thread/TestThreadDumpMonitorContention.java \ runtime/XCheckJniJsig/XCheckJSig.java \ serviceability/attach/AttachWithStalePidFile.java \ - serviceability/jvmti/8036666/GetObjectLockCount.java \ serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java \ serviceability/dcmd/DynLibDcmdTest.java @@ -120,16 +128,12 @@ needs_compact3 = \ gc/6581734/Test6581734.java \ gc/7072527/TestFullGCCount.java \ gc/g1/TestHumongousAllocInitialMark.java \ - gc/g1/TestHumongousShrinkHeap.java \ gc/arguments/TestG1HeapRegionSize.java \ gc/metaspace/TestMetaspaceMemoryPool.java \ gc/arguments/TestDynMinHeapFreeRatio.java \ gc/arguments/TestDynMaxHeapFreeRatio.java \ - gc/parallelScavenge/TestDynShrinkHeap.java \ runtime/InternalApi/ThreadCpuTimesDeadlock.java \ serviceability/threads/TestFalseDeadLock.java \ - serviceability/jvmti/GetObjectSizeOverflow.java \ - serviceability/jvmti/TestRedefineWithUnresolvedClass.java # Compact 2 adds full VM tests compact2 = \ @@ -217,6 +221,7 @@ needs_g1gc = \ gc/arguments/TestMaxHeapSizeTools.java \ gc/arguments/TestMaxNewSize.java \ gc/arguments/TestUseCompressedOopsErgo.java \ + gc/class_unloading/TestG1ClassUnloadingHWM.java \ gc/g1/ \ gc/metaspace/G1AddMetaspaceDependency.java \ gc/metaspace/TestMetaspacePerfCounters.java \ @@ -257,7 +262,7 @@ needs_cmsgc = \ gc/arguments/TestCMSHeapSizeFlags.java \ gc/arguments/TestMaxNewSize.java \ gc/arguments/TestUseCompressedOopsErgo.java \ - gc/class_unloading/TestCMSClassUnloadingDisabledHWM.java \ + gc/class_unloading/TestCMSClassUnloadingEnabledHWM.java \ gc/concurrentMarkSweep/ \ gc/startup_warnings/TestCMS.java \ gc/startup_warnings/TestCMSIncrementalMode.java \ @@ -323,6 +328,21 @@ hotspot_gc = \ sanity/ExecuteInternalVMTests.java hotspot_runtime = \ + runtime/ \ + -runtime/6888954/vmerrors.sh \ + -runtime/RedefineObject/TestRedefineObject.java \ + -runtime/8003720/Test8003720.java \ + -runtime/Metaspace/FragmentMetaspace.java \ + -runtime/Metaspace/FragmentMetaspaceSimple.java \ + -runtime/Thread/TestThreadDumpMonitorContention.java \ + -runtime/SharedArchiveFile/SharedBaseAddress.java \ + -runtime/memory/ReserveMemory.java \ + -runtime/Unsafe/RangeCheck.java \ + -runtime/SharedArchiveFile/CdsSameObjectAlignment.java \ + -runtime/SharedArchiveFile/DefaultUseWithClient.java \ + -runtime/Thread/CancellableThreadTest.java + +hotspot_runtime_closed = \ sanity/ExecuteInternalVMTests.java hotspot_serviceability = \ diff --git a/hotspot/test/compiler/5091921/Test7005594.sh b/hotspot/test/compiler/5091921/Test7005594.sh index 2ca434ae876..4fa458ca723 100644 --- a/hotspot/test/compiler/5091921/Test7005594.sh +++ b/hotspot/test/compiler/5091921/Test7005594.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2014, 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 @@ -78,7 +78,7 @@ cp ${TESTSRC}/Test7005594.sh . ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} -d . Test7005594.java -${TESTJAVA}/bin/java ${TESTVMOPTS} -Xmx1600m -Xms1600m -XX:+IgnoreUnrecognizedVMOptions -XX:-ZapUnusedHeapArea -Xcomp -XX:CompileOnly=Test7005594.test Test7005594 > test.out 2>&1 +${TESTJAVA}/bin/java ${TESTOPTS} -Xmx1600m -Xms1600m -XX:+IgnoreUnrecognizedVMOptions -XX:-ZapUnusedHeapArea -Xcomp -XX:CompileOnly=Test7005594.test Test7005594 > test.out 2>&1 result=$? diff --git a/hotspot/test/compiler/6857159/Test6857159.sh b/hotspot/test/compiler/6857159/Test6857159.sh index e0430c43abc..cb790127c9c 100644 --- a/hotspot/test/compiler/6857159/Test6857159.sh +++ b/hotspot/test/compiler/6857159/Test6857159.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ cp ${TESTSRC}/Test6857159.sh . ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} -d . Test6857159.java -${TESTJAVA}/bin/java ${TESTVMOPTS} -Xbatch -XX:+PrintCompilation -XX:CompileOnly=Test6857159\$ct.run Test6857159 > test.out 2>&1 +${TESTJAVA}/bin/java ${TESTOPTS} -Xbatch -XX:+PrintCompilation -XX:CompileOnly=Test6857159\$ct.run Test6857159 > test.out 2>&1 grep "COMPILE SKIPPED" test.out diff --git a/hotspot/test/compiler/6894807/IsInstanceTest.java b/hotspot/test/compiler/6894807/IsInstanceTest.java index ddc3fed365a..d838c6ec662 100644 --- a/hotspot/test/compiler/6894807/IsInstanceTest.java +++ b/hotspot/test/compiler/6894807/IsInstanceTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2012, 2014, 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. + */ + /* * @test * @bug 6894807 diff --git a/hotspot/test/compiler/6894807/Test6894807.sh b/hotspot/test/compiler/6894807/Test6894807.sh index 229fb545108..609af074eda 100644 --- a/hotspot/test/compiler/6894807/Test6894807.sh +++ b/hotspot/test/compiler/6894807/Test6894807.sh @@ -1,55 +1,38 @@ #!/bin/sh +# +# Copyright (c) 2012, 2014, 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. +# if [ "${TESTSRC}" = "" ] -then TESTSRC=. -fi - -if [ "${TESTJAVA}" = "" ] then - PARENT=`dirname \`which java\`` - TESTJAVA=`dirname ${PARENT}` - echo "TESTJAVA not set, selecting " ${TESTJAVA} - echo "If this is incorrect, try setting the variable manually." + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" fi +echo "TESTSRC=${TESTSRC}" -if [ "${TESTCLASSES}" = "" ] -then - echo "TESTCLASSES not set. Test cannot execute. Failed." - exit 1 -fi +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../../test_env.sh -# set platform-dependent variables -OS=`uname -s` -case "$OS" in - SunOS | Linux | Darwin ) - NULL=/dev/null - PS=":" - FS="/" - ;; - Windows_* ) - NULL=NUL - PS=";" - FS="\\" - ;; - CYGWIN_* ) - NULL=/dev/null - PS=";" - FS="/" - ;; - * ) - echo "Unrecognized system!" - exit 1; - ;; -esac - -JEMMYPATH=${CPAPPEND} -CLASSPATH=.${PS}${TESTCLASSES}${PS}${JEMMYPATH} ; export CLASSPATH - -THIS_DIR=`pwd` - -${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -version - -${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} IsInstanceTest > test.out 2>&1 +${TESTJAVA}${FS}bin${FS}java ${TESTOPTS} IsInstanceTest > test.out 2>&1 cat test.out diff --git a/hotspot/test/compiler/6932496/Test6932496.java b/hotspot/test/compiler/6932496/Test6932496.java index 721b4e00b72..7f3c1697355 100644 --- a/hotspot/test/compiler/6932496/Test6932496.java +++ b/hotspot/test/compiler/6932496/Test6932496.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2014, 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 @@ -26,26 +26,162 @@ * @test * @bug 6932496 * @summary incorrect deopt of jsr subroutine on 64 bit c1 - * - * @compile -source 1.5 -target 1.5 -XDjsrlimit=0 Test6932496.java - * @run main/othervm -Xcomp -XX:CompileOnly=Test6932496.m Test6932496 + * @run main/othervm -Xcomp -XX:CompileOnly=Test.test Test6932496 */ +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.io.IOException; -public class Test6932496 { - static class A { - volatile boolean flag = false; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.FieldVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.Type; +import jdk.internal.org.objectweb.asm.Label; + +public class Test6932496 extends ClassLoader { + private static final int CLASS_FILE_VERSION = 49; + private static final String CLASS_TEST = "Test"; + private static final String CLASS_OBJECT = "java/lang/Object"; + private static final String METHOD_INIT = ""; + private static final String METHOD_TEST = "test"; + private static final String DESC_VOID_METHOD = "()V"; + private static final String FIELD_FLAG = "flag"; + + public static void main(String[] args) { + Test6932496 test = new Test6932496(); + test.execute(); } - static void m() { + private void execute() { + byte[] bytecode = Test6932496.generateTestClass(); + try { - } finally { - A a = new A(); - a.flag = true; + Files.write(Paths.get("Test.class.dump"), bytecode); + } catch (IOException e) { + System.err.println("classfile dump failed : " + e.getMessage()); + e.printStackTrace(); + } + try { + Class aClass = defineClass(CLASS_TEST, bytecode, 0, bytecode.length); + Method test = aClass.getDeclaredMethod(METHOD_TEST); + test.invoke(null); + } catch (ClassFormatError | IllegalArgumentException + | ReflectiveOperationException e) { + throw new RuntimeException("TESTBUG : generated class is invalid", e); } } + /* + public class Test { + volatile boolean flag = false; + public static void m() { + try { + } finally { + Test test = new Test(); + test.flag = true; + } + } + } + */ + private static byte[] generateTestClass() { + ClassWriter cw = new ClassWriter(0); + cw.visit(CLASS_FILE_VERSION, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, + CLASS_TEST, null, CLASS_OBJECT, null); + // volatile boolean flag; + { + FieldVisitor fv = cw.visitField(Opcodes.ACC_VOLATILE, FIELD_FLAG, + Type.BOOLEAN_TYPE.getDescriptor(), + /* signature = */ null, /* value = */ null); + } - static public void main(String[] args) { - m(); + /* + public Test() { + flag = false; + } + */ + { + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, + METHOD_INIT, DESC_VOID_METHOD, + /* signature = */ null, /* exceptions = */ null); + + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, CLASS_OBJECT, METHOD_INIT, + DESC_VOID_METHOD, false); + + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitInsn(Opcodes.ICONST_0); + mv.visitFieldInsn(Opcodes.PUTFIELD, CLASS_TEST, FIELD_FLAG, + Type.BOOLEAN_TYPE.getDescriptor()); + + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(/* stack = */ 2, /* locals = */ 1); + mv.visitEnd(); + } + + /* + public static void m() { + try { + } finally { + Test test = new Test(); + test.flag = true; + } + } + */ + { + MethodVisitor mv = cw.visitMethod( + Opcodes.ACC_STATIC + Opcodes.ACC_PUBLIC, + METHOD_TEST, DESC_VOID_METHOD, + /* signature = */ null, /* exceptions = */ null); + Label beginLabel = new Label(); + Label block1EndLabel = new Label(); + Label handlerLabel = new Label(); + Label block2EndLabel = new Label(); + Label label = new Label(); + Label endLabel = new Label(); + + mv.visitCode(); + mv.visitTryCatchBlock(beginLabel, block1EndLabel, handlerLabel, + /* type = */ null); + mv.visitTryCatchBlock(handlerLabel, block2EndLabel, handlerLabel, + /* type = */ null); + + mv.visitLabel(beginLabel); + mv.visitJumpInsn(Opcodes.JSR, label); + mv.visitLabel(block1EndLabel); + mv.visitJumpInsn(Opcodes.GOTO, endLabel); + + mv.visitLabel(handlerLabel); + mv.visitVarInsn(Opcodes.ASTORE, 0); + mv.visitJumpInsn(Opcodes.JSR, label); + mv.visitLabel(block2EndLabel); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitInsn(Opcodes.ATHROW); + + mv.visitLabel(label); + mv.visitVarInsn(Opcodes.ASTORE, 1); + mv.visitTypeInsn(Opcodes.NEW, CLASS_TEST); + mv.visitInsn(Opcodes.DUP); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, CLASS_TEST, METHOD_INIT, + DESC_VOID_METHOD); + mv.visitVarInsn(Opcodes.ASTORE, 2); + + mv.visitVarInsn(Opcodes.ALOAD, 2); + mv.visitInsn(Opcodes.ICONST_1); + mv.visitFieldInsn(Opcodes.PUTFIELD, CLASS_TEST, FIELD_FLAG, + Type.BOOLEAN_TYPE.getDescriptor()); + + mv.visitVarInsn(Opcodes.RET, 1); + + mv.visitLabel(endLabel); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(/* stack = */ 2, /* locals = */ 3); + mv.visitEnd(); + } + + cw.visitEnd(); + return cw.toByteArray(); } } diff --git a/hotspot/test/compiler/7068051/Test7068051.sh b/hotspot/test/compiler/7068051/Test7068051.sh index e287c243163..35bc0f72efd 100644 --- a/hotspot/test/compiler/7068051/Test7068051.sh +++ b/hotspot/test/compiler/7068051/Test7068051.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -41,5 +41,5 @@ ${COMPILEJAVA}/bin/jar -uf0 foo.jar Test7068051.java ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} -d . Test7068051.java -${TESTJAVA}/bin/java ${TESTVMOPTS} -showversion -Xbatch Test7068051 foo.jar +${TESTJAVA}/bin/java ${TESTOPTS} -showversion -Xbatch Test7068051 foo.jar diff --git a/hotspot/test/compiler/7070134/Test7070134.sh b/hotspot/test/compiler/7070134/Test7070134.sh index 2ef12d04b67..87618616c97 100644 --- a/hotspot/test/compiler/7070134/Test7070134.sh +++ b/hotspot/test/compiler/7070134/Test7070134.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ cp ${TESTSRC}/words . ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} -d . Stemmer.java -${TESTJAVA}/bin/java ${TESTVMOPTS} -Xbatch Stemmer words > test.out 2>&1 +${TESTJAVA}/bin/java ${TESTOPTS} -Xbatch Stemmer words > test.out 2>&1 exit $? diff --git a/hotspot/test/compiler/7200264/Test7200264.sh b/hotspot/test/compiler/7200264/Test7200264.sh index da1a59a29d7..5e7e508a5f2 100644 --- a/hotspot/test/compiler/7200264/Test7200264.sh +++ b/hotspot/test/compiler/7200264/Test7200264.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2014, 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 @@ -33,7 +33,7 @@ echo "TESTSRC=${TESTSRC}" ## Adding common setup Variables for running shell tests. . ${TESTSRC}/../../test_env.sh -${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -Xinternalversion | sed 's/amd64/x86/' | grep "x86" | grep "Server VM" | grep "debug" +${TESTJAVA}${FS}bin${FS}java ${TESTOPTS} -Xinternalversion | sed 's/amd64/x86/' | grep "x86" | grep "Server VM" | grep "debug" # Only test fastdebug Server VM on x86 if [ $? != 0 ] @@ -43,7 +43,7 @@ then fi # grep for support integer multiply vectors (cpu with SSE4.1) -${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -XX:+PrintMiscellaneous -XX:+Verbose -version | grep "cores per cpu" | grep "sse4.1" +${TESTJAVA}${FS}bin${FS}java ${TESTOPTS} -XX:+PrintMiscellaneous -XX:+Verbose -version | grep "cores per cpu" | grep "sse4.1" if [ $? != 0 ] then @@ -55,7 +55,7 @@ fi cp ${TESTSRC}${FS}TestIntVect.java . ${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} -d . TestIntVect.java -${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -Xbatch -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:+PrintCompilation -XX:+TraceNewVectors TestIntVect > test.out 2>&1 +${TESTJAVA}${FS}bin${FS}java ${TESTOPTS} -Xbatch -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:+PrintCompilation -XX:+TraceNewVectors TestIntVect > test.out 2>&1 COUNT=`grep AddVI test.out | wc -l | awk '{print $1}'` if [ $COUNT -lt 4 ] diff --git a/hotspot/test/compiler/8009761/Test8009761.java b/hotspot/test/compiler/8009761/Test8009761.java index ed0f94665e4..46aad286840 100644 --- a/hotspot/test/compiler/8009761/Test8009761.java +++ b/hotspot/test/compiler/8009761/Test8009761.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ import java.lang.reflect.Method; * @summary Deoptimization on sparc doesn't set Llast_SP correctly in the interpreter frames it creates * @build Test8009761 * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=exclude,Test8009761::m2 -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -Xss256K Test8009761 */ public class Test8009761 { diff --git a/hotspot/test/compiler/8010927/Test8010927.java b/hotspot/test/compiler/8010927/Test8010927.java index 6c9d9ab3d69..3c0f0915d36 100644 --- a/hotspot/test/compiler/8010927/Test8010927.java +++ b/hotspot/test/compiler/8010927/Test8010927.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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,6 +28,7 @@ * @library /testlibrary/whitebox /testlibrary * @build Test8010927 * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Xmx64m -XX:NewSize=20971520 -XX:MaxNewSize=32m -XX:-UseTLAB -XX:-UseParNewGC -XX:-UseAdaptiveSizePolicy Test8010927 */ diff --git a/hotspot/test/compiler/IntegerArithmetic/TestIntegerComparison.java b/hotspot/test/compiler/IntegerArithmetic/TestIntegerComparison.java new file mode 100644 index 00000000000..8d3c383a7ee --- /dev/null +++ b/hotspot/test/compiler/IntegerArithmetic/TestIntegerComparison.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test TestIntegerComparison + * @bug 8043284 8042786 + * @summary "Tests optimizations of signed and unsigned integer comparison." + * @run main/othervm -server -Xcomp -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation -XX:CompileOnly=TestIntegerComparison::testSigned,TestIntegerComparison::testUnsigned TestIntegerComparison + */ +public class TestIntegerComparison { + /** + * Tests optimization of signed integer comparison (see BoolNode::Ideal). + * The body of the if statement is unreachable and should not be compiled. + * @param c Character (value in the integer range [0, 65535]) + */ + public static void testSigned(char c) { + // The following addition may overflow. The result is in one + // of the two ranges [IntMax] and [IntMin, IntMin + CharMax - 1]. + int result = c + Integer.MAX_VALUE; + // CmpINode has to consider both result ranges instead of only + // the general [IntMin, IntMax] range to be able to prove that + // result is always unequal to CharMax. + if (result == Character.MAX_VALUE) { + // Unreachable + throw new RuntimeException("Should not reach here!"); + } + } + + /** + * Tests optimization of unsigned integer comparison (see CmpUNode::Value). + * The body of the if statement is unreachable and should not be compiled. + * @param c Character (value in the integer range [0, 65535]) + */ + public static void testUnsigned(char c) { + /* + * The following if statement consisting of two CmpIs is replaced + * by a CmpU during optimization (see 'IfNode::fold_compares'). + * + * The signed (lo < i) and (i < hi) are replaced by the unsigned + * (i - (lo+1) < hi - (lo+1)). In this case the unsigned comparison + * equals (result - 2) < 98 leading to the following CmpUNode: + * + * CmpU (AddI result, -2) 98 + * + * With the value of result this is simplified to: + * + * CmpU (AddI c, -(CharMax - IntMin)) 98 + * + * The subtraction may underflow. The result is in one of the two + * ranges [IntMin], [IntMax - CharMax + 1]. Both ranges have to be + * considered instead of only the general [IntMin, IntMax] to prove + * that due to the overflow the signed comparison result < 98 is + * always false. + */ + int result = c - (Character.MAX_VALUE - Integer.MIN_VALUE) + 2; + if (1 < result && result < 100) { + // Unreachable + throw new RuntimeException("Should not reach here!"); + } + } + + /** + * Tests optimizations of signed and unsigned integer comparison. + */ + public static void main(String[] args) { + // We use characters to get a limited integer range for free + for (int i = Character.MIN_VALUE; i <= Character.MAX_VALUE; ++i) { + testSigned((char) i); + testUnsigned((char) i); + } + } +} diff --git a/hotspot/test/compiler/arguments/TestUseBMI1InstructionsOnSupportedCPU.java b/hotspot/test/compiler/arguments/TestUseBMI1InstructionsOnSupportedCPU.java index 559c3a6a643..bf797b1773f 100644 --- a/hotspot/test/compiler/arguments/TestUseBMI1InstructionsOnSupportedCPU.java +++ b/hotspot/test/compiler/arguments/TestUseBMI1InstructionsOnSupportedCPU.java @@ -30,6 +30,7 @@ * @build TestUseBMI1InstructionsOnSupportedCPU * BMISupportedCPUTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseBMI1InstructionsOnSupportedCPU */ diff --git a/hotspot/test/compiler/arguments/TestUseBMI1InstructionsOnUnsupportedCPU.java b/hotspot/test/compiler/arguments/TestUseBMI1InstructionsOnUnsupportedCPU.java index 3df8d659cf0..2347181043c 100644 --- a/hotspot/test/compiler/arguments/TestUseBMI1InstructionsOnUnsupportedCPU.java +++ b/hotspot/test/compiler/arguments/TestUseBMI1InstructionsOnUnsupportedCPU.java @@ -30,6 +30,7 @@ * @build TestUseBMI1InstructionsOnUnsupportedCPU * BMIUnsupportedCPUTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseBMI1InstructionsOnUnsupportedCPU */ diff --git a/hotspot/test/compiler/arguments/TestUseCountLeadingZerosInstructionOnSupportedCPU.java b/hotspot/test/compiler/arguments/TestUseCountLeadingZerosInstructionOnSupportedCPU.java index c8437748341..68f2267a27c 100644 --- a/hotspot/test/compiler/arguments/TestUseCountLeadingZerosInstructionOnSupportedCPU.java +++ b/hotspot/test/compiler/arguments/TestUseCountLeadingZerosInstructionOnSupportedCPU.java @@ -30,6 +30,7 @@ * @build TestUseCountLeadingZerosInstructionOnSupportedCPU * BMISupportedCPUTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * TestUseCountLeadingZerosInstructionOnSupportedCPU diff --git a/hotspot/test/compiler/arguments/TestUseCountLeadingZerosInstructionOnUnsupportedCPU.java b/hotspot/test/compiler/arguments/TestUseCountLeadingZerosInstructionOnUnsupportedCPU.java index e2ffba28a43..68c855a78ef 100644 --- a/hotspot/test/compiler/arguments/TestUseCountLeadingZerosInstructionOnUnsupportedCPU.java +++ b/hotspot/test/compiler/arguments/TestUseCountLeadingZerosInstructionOnUnsupportedCPU.java @@ -30,6 +30,7 @@ * @build TestUseCountLeadingZerosInstructionOnUnsupportedCPU * BMIUnsupportedCPUTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * TestUseCountLeadingZerosInstructionOnUnsupportedCPU diff --git a/hotspot/test/compiler/arguments/TestUseCountTrailingZerosInstructionOnSupportedCPU.java b/hotspot/test/compiler/arguments/TestUseCountTrailingZerosInstructionOnSupportedCPU.java index d6628b1ed0c..2e4400b5fe7 100644 --- a/hotspot/test/compiler/arguments/TestUseCountTrailingZerosInstructionOnSupportedCPU.java +++ b/hotspot/test/compiler/arguments/TestUseCountTrailingZerosInstructionOnSupportedCPU.java @@ -30,6 +30,7 @@ * @build TestUseCountTrailingZerosInstructionOnSupportedCPU * BMISupportedCPUTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * TestUseCountTrailingZerosInstructionOnSupportedCPU diff --git a/hotspot/test/compiler/arguments/TestUseCountTrailingZerosInstructionOnUnsupportedCPU.java b/hotspot/test/compiler/arguments/TestUseCountTrailingZerosInstructionOnUnsupportedCPU.java index 67b6ea318ca..648c112282c 100644 --- a/hotspot/test/compiler/arguments/TestUseCountTrailingZerosInstructionOnUnsupportedCPU.java +++ b/hotspot/test/compiler/arguments/TestUseCountTrailingZerosInstructionOnUnsupportedCPU.java @@ -30,6 +30,7 @@ * @build TestUseCountTrailingZerosInstructionOnUnsupportedCPU * BMIUnsupportedCPUTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * TestUseCountTrailingZerosInstructionOnUnsupportedCPU diff --git a/hotspot/test/compiler/arraycopy/TestArrayOfNoTypeCheck.java b/hotspot/test/compiler/arraycopy/TestArrayOfNoTypeCheck.java new file mode 100644 index 00000000000..30e525e687e --- /dev/null +++ b/hotspot/test/compiler/arraycopy/TestArrayOfNoTypeCheck.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @bug 8055910 + * @summary Arrays.copyOf doesn't perform subtype check + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement TestArrayOfNoTypeCheck + * + */ + +import java.util.Arrays; + +public class TestArrayOfNoTypeCheck { + + static class A { + } + + static class B extends A { + } + + static B[] test(A[] arr) { + return Arrays.copyOf(arr, 10, B[].class); + } + + static public void main(String[] args) { + A[] arr = new A[20]; + for (int i = 0; i < 20000; i++) { + test(arr); + } + A[] arr2 = new A[20]; + arr2[0] = new A(); + boolean exception = false; + try { + test(arr2); + } catch (ArrayStoreException ase) { + exception = true; + } + if (!exception) { + throw new RuntimeException("TEST FAILED: ArrayStoreException not thrown"); + } + } +} diff --git a/hotspot/test/compiler/arraycopy/TestMissingControl.java b/hotspot/test/compiler/arraycopy/TestMissingControl.java new file mode 100644 index 00000000000..503e24be3b4 --- /dev/null +++ b/hotspot/test/compiler/arraycopy/TestMissingControl.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @bug 8055153 + * @summary missing control on LoadRange and LoadKlass when array copy macro node is expanded + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:-TieredCompilation TestMissingControl + * + */ +public class TestMissingControl { + + static int[] m1(int[] a2) { + int[] a1 = new int[10]; + System.arraycopy(a1, 0, a2, 0, 10); + return a1; + } + + static class A { + } + + static Object m2(Object[] a2) { + A[] a1 = new A[10]; + System.arraycopy(a1, 0, a2, 0, 10); + return a1; + } + + static void test1() { + int[] a2 = new int[10]; + int[] a3 = new int[5]; + + // compile m1 with arraycopy intrinsified + for (int i = 0; i < 20000; i++) { + m1(a2); + } + + // make m1 trap + for (int i = 0; i < 10; i++) { + try { + m1(a3); + } catch(IndexOutOfBoundsException ioobe) { + } + } + + // recompile m1 + for (int i = 0; i < 20000; i++) { + m1(a2); + } + + try { + m1(null); + } catch(NullPointerException npe) {} + } + + static void test2() { + A[] a2 = new A[10]; + A[] a3 = new A[5]; + + // compile m2 with arraycopy intrinsified + for (int i = 0; i < 20000; i++) { + m2(a2); + } + + // make m2 trap + for (int i = 0; i < 10; i++) { + try { + m2(a3); + } catch(IndexOutOfBoundsException ioobe) { + } + } + + // recompile m2 + for (int i = 0; i < 20000; i++) { + m2(a2); + } + + try { + m2(null); + } catch(NullPointerException npe) {} + } + + static public void main(String[] args) { + test1(); + test2(); + } +} diff --git a/hotspot/test/compiler/ciReplay/TestSA.sh b/hotspot/test/compiler/ciReplay/TestSA.sh index 56736c7807e..cc5f5037e7a 100644 --- a/hotspot/test/compiler/ciReplay/TestSA.sh +++ b/hotspot/test/compiler/ciReplay/TestSA.sh @@ -68,7 +68,7 @@ then fi echo "dumpreplaydata -a > ${replay_data}" | \ - ${JAVA} ${TESTVMOPTS} \ + ${JAVA} ${TESTOPTS} \ -cp ${TESTJAVA}${FS}lib${FS}sa-jdi.jar \ sun.jvm.hotspot.CLHSDB ${JAVA} ${core_file} diff --git a/hotspot/test/compiler/ciReplay/common.sh b/hotspot/test/compiler/ciReplay/common.sh index b7c5b7b4292..c7b656d26aa 100644 --- a/hotspot/test/compiler/ciReplay/common.sh +++ b/hotspot/test/compiler/ciReplay/common.sh @@ -22,6 +22,7 @@ # questions. # # +set -x # $1 - error code # $2 - test name @@ -56,7 +57,7 @@ positive_test() { shift name=$1 shift - VMOPTS="${TESTVMOPTS} $@" + VMOPTS="${TESTOPTS} $@" echo "POSITIVE TEST [$name]" start_test ${VMOPTS} exit_code=$? @@ -75,7 +76,7 @@ negative_test() { shift name=$1 shift - VMOPTS="${TESTVMOPTS} $@" + VMOPTS="${TESTOPTS} $@" echo "NEGATIVE TEST [$name]" start_test ${VMOPTS} exit_code=$? @@ -149,7 +150,7 @@ JAVA=${TESTJAVA}${FS}bin${FS}java replay_data=test_replay.txt -${JAVA} ${TESTVMOPTS} -Xinternalversion 2>&1 | grep debug +${JAVA} ${TESTOPTS} -Xinternalversion 2>&1 | grep debug # Only test fastdebug if [ $? -ne 0 ] @@ -158,7 +159,7 @@ then exit 0 fi -is_int=`${JAVA} ${TESTVMOPTS} -version 2>&1 | grep -c "interpreted mode"` +is_int=`${JAVA} ${TESTOPTS} -version 2>&1 | grep -c "interpreted mode"` # Not applicable for Xint if [ $is_int -ne 0 ] then @@ -168,14 +169,14 @@ fi cleanup -client_available=`${JAVA} ${TESTVMOPTS} -client -Xinternalversion 2>&1 | \ +client_available=`${JAVA} ${TESTOPTS} -client -Xinternalversion 2>&1 | \ grep -c Client` -server_available=`${JAVA} ${TESTVMOPTS} -server -Xinternalversion 2>&1 | \ +server_available=`${JAVA} ${TESTOPTS} -server -Xinternalversion 2>&1 | \ grep -c Server` -tiered_available=`${JAVA} ${TESTVMOPTS} -XX:+TieredCompilation -XX:+PrintFlagsFinal -version | \ +tiered_available=`${JAVA} ${TESTOPTS} -XX:+TieredCompilation -XX:+PrintFlagsFinal -version | \ grep TieredCompilation | \ grep -c true` -is_tiered=`${JAVA} ${TESTVMOPTS} -XX:+PrintFlagsFinal -version | \ +is_tiered=`${JAVA} ${TESTOPTS} -XX:+PrintFlagsFinal -version | \ grep TieredCompilation | \ grep -c true` # CompLevel_simple -- C1 @@ -207,7 +208,7 @@ generate_replay() { fi fi - cmd="${JAVA} ${TESTVMOPTS} $@ \ + cmd="${JAVA} ${TESTOPTS} $@ \ -Xms8m \ -Xmx32m \ -XX:MetaspaceSize=4m \ diff --git a/hotspot/test/compiler/classUnloading/anonymousClass/TestAnonymousClassUnloading.java b/hotspot/test/compiler/classUnloading/anonymousClass/TestAnonymousClassUnloading.java new file mode 100644 index 00000000000..6fbaf9cef72 --- /dev/null +++ b/hotspot/test/compiler/classUnloading/anonymousClass/TestAnonymousClassUnloading.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2014, 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. + */ + +import sun.hotspot.WhiteBox; +import sun.misc.Unsafe; +import sun.misc.IOUtils; + +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLConnection; + +/* + * @test TestAnonymousClassUnloading + * @bug 8054402 + * @summary "Tests unloading of anonymous classes." + * @library /testlibrary /testlibrary/whitebox + * @compile TestAnonymousClassUnloading.java + * @run main ClassFileInstaller TestAnonymousClassUnloading + * sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-BackgroundCompilation TestAnonymousClassUnloading + */ +public class TestAnonymousClassUnloading { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + private static int COMP_LEVEL_SIMPLE = 1; + private static int COMP_LEVEL_FULL_OPTIMIZATION = 4; + + /** + * We override hashCode here to be able to access this implementation + * via an Object reference (we cannot cast to TestAnonymousClassUnloading). + */ + @Override + public int hashCode() { + return 42; + } + + /** + * Does some work by using the anonymousClass. + * @param anonymousClass Class performing some work (will be unloaded) + */ + static private void doWork(Class anonymousClass) throws InstantiationException, IllegalAccessException { + // Create a new instance + Object anon = anonymousClass.newInstance(); + // We would like to call a method of anonymousClass here but we cannot cast because the class + // was loaded by a different class loader. One solution would be to use reflection but since + // we want C2 to implement the call as an IC we call Object::hashCode() here which actually + // calls anonymousClass::hashCode(). C2 will then implement this call as an IC. + if (anon.hashCode() != 42) { + new RuntimeException("Work not done"); + } + } + + /** + * Makes sure that method is compiled by forcing compilation if not yet compiled. + * @param m Method to be checked + */ + static private void makeSureIsCompiled(Method m) { + // Make sure background compilation is disabled + if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { + throw new RuntimeException("Background compilation enabled"); + } + + // Check if already compiled + if (!WHITE_BOX.isMethodCompiled(m)) { + // If not, try to compile it with C2 + if(!WHITE_BOX.enqueueMethodForCompilation(m, COMP_LEVEL_FULL_OPTIMIZATION)) { + // C2 compiler not available, try to compile with C1 + WHITE_BOX.enqueueMethodForCompilation(m, COMP_LEVEL_SIMPLE); + } + // Because background compilation is disabled, method should now be compiled + if(!WHITE_BOX.isMethodCompiled(m)) { + throw new RuntimeException(m + " not compiled"); + } + } + } + + /** + * This test creates stale Klass* metadata referenced by a compiled IC. + * + * The following steps are performed: + * (1) An anonymous version of TestAnonymousClassUnloading is loaded by a custom class loader + * (2) The method doWork that calls a method of the anonymous class is compiled. The call + * is implemented as an IC referencing Klass* metadata of the anonymous class. + * (3) Unloading of the anonymous class is enforced. The IC now references dead metadata. + */ + static public void main(String[] args) throws Exception { + // (1) Load an anonymous version of this class using the corresponding Unsafe method + URL classUrl = TestAnonymousClassUnloading.class.getResource("TestAnonymousClassUnloading.class"); + URLConnection connection = classUrl.openConnection(); + byte[] classBytes = IOUtils.readFully(connection.getInputStream(), connection.getContentLength(), true); + Class anonymousClass = UNSAFE.defineAnonymousClass(TestAnonymousClassUnloading.class, classBytes, null); + + // (2) Make sure all paths of doWork are profiled and compiled + for (int i = 0; i < 100000; ++i) { + doWork(anonymousClass); + } + + // Make sure doWork is compiled now + Method doWork = TestAnonymousClassUnloading.class.getDeclaredMethod("doWork", Class.class); + makeSureIsCompiled(doWork); + + // (3) Throw away reference to anonymousClass to allow unloading + anonymousClass = null; + + // Force garbage collection to trigger unloading of anonymousClass + // Dead metadata reference to anonymousClass triggers JDK-8054402 + WHITE_BOX.fullGC(); + } +} diff --git a/hotspot/test/compiler/classUnloading/methodUnloading/TestMethodUnloading.java b/hotspot/test/compiler/classUnloading/methodUnloading/TestMethodUnloading.java new file mode 100644 index 00000000000..9e7a19b69aa --- /dev/null +++ b/hotspot/test/compiler/classUnloading/methodUnloading/TestMethodUnloading.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2014, 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. + */ + +import sun.hotspot.WhiteBox; + +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; + +/* + * @test MethodUnloadingTest + * @bug 8029443 + * @summary "Tests the unloading of methods to to class unloading" + * @library /testlibrary /testlibrary/whitebox + * @build TestMethodUnloading + * @build WorkerClass + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-BackgroundCompilation -XX:-UseCompressedOops -XX:CompileOnly=TestMethodUnloading::doWork TestMethodUnloading + */ +public class TestMethodUnloading { + private static final String workerClassName = "WorkerClass"; + private static int work = -1; + + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static int COMP_LEVEL_SIMPLE = 1; + private static int COMP_LEVEL_FULL_OPTIMIZATION = 4; + + /** + * Does some work by either using the workerClass or locally producing values. + * @param workerClass Class performing some work (will be unloaded) + * @param useWorker If true the workerClass is used + */ + static private void doWork(Class workerClass, boolean useWorker) throws InstantiationException, IllegalAccessException { + if (useWorker) { + // Create a new instance + Object worker = workerClass.newInstance(); + // We would like to call a method of WorkerClass here but we cannot cast to WorkerClass + // because the class was loaded by a different class loader. One solution would be to use + // reflection but since we want C2 to implement the call as an optimized IC we call + // Object::hashCode() here which actually calls WorkerClass::hashCode(). + // C2 will then implement this call as an optimized IC that points to a to-interpreter stub + // referencing the Method* for WorkerClass::hashCode(). + work = worker.hashCode(); + if (work != 42) { + new RuntimeException("Work not done"); + } + } else { + // Do some important work here + work = 1; + } + } + + /** + * Makes sure that method is compiled by forcing compilation if not yet compiled. + * @param m Method to be checked + */ + static private void makeSureIsCompiled(Method m) { + // Make sure background compilation is disabled + if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { + throw new RuntimeException("Background compilation enabled"); + } + + // Check if already compiled + if (!WHITE_BOX.isMethodCompiled(m)) { + // If not, try to compile it with C2 + if(!WHITE_BOX.enqueueMethodForCompilation(m, COMP_LEVEL_FULL_OPTIMIZATION)) { + // C2 compiler not available, try to compile with C1 + WHITE_BOX.enqueueMethodForCompilation(m, COMP_LEVEL_SIMPLE); + } + // Because background compilation is disabled, method should now be compiled + if(!WHITE_BOX.isMethodCompiled(m)) { + throw new RuntimeException(m + " not compiled"); + } + } + } + + /** + * This test creates stale Method* metadata in a to-interpreter stub of an optimized IC. + * + * The following steps are performed: + * (1) A workerClass is loaded by a custom class loader + * (2) The method doWork that calls a method of the workerClass is compiled. The call + * is implemented as an optimized IC calling a to-interpreted stub. The to-interpreter + * stub contains a Method* to a workerClass method. + * (3) Unloading of the workerClass is enforced. The to-interpreter stub now contains a dead Method*. + * (4) Depending on the implementation of the IC, the compiled version of doWork should still be + * valid. We call it again without using the workerClass. + */ + static public void main(String[] args) throws Exception { + // (1) Create a custom class loader with no parent class loader + URL url = TestMethodUnloading.class.getProtectionDomain().getCodeSource().getLocation(); + URLClassLoader loader = new URLClassLoader(new URL[] {url}, null); + + // Load worker class with custom class loader + Class workerClass = Class.forName(workerClassName, true, loader); + + // (2) Make sure all paths of doWork are profiled and compiled + for (int i = 0; i < 100000; ++i) { + doWork(workerClass, true); + doWork(workerClass, false); + } + + // Make sure doWork is compiled now + Method doWork = TestMethodUnloading.class.getDeclaredMethod("doWork", Class.class, boolean.class); + makeSureIsCompiled(doWork); + + // (3) Throw away class loader and reference to workerClass to allow unloading + loader.close(); + loader = null; + workerClass = null; + + // Force garbage collection to trigger unloading of workerClass + // Dead reference to WorkerClass::hashCode triggers JDK-8029443 + WHITE_BOX.fullGC(); + + // (4) Depending on the implementation of the IC, the compiled version of doWork + // may still be valid here. Execute it without a workerClass. + doWork(null, false); + if (work != 1) { + throw new RuntimeException("Work not done"); + } + + doWork(Object.class, false); + } +} diff --git a/hotspot/test/compiler/classUnloading/methodUnloading/WorkerClass.java b/hotspot/test/compiler/classUnloading/methodUnloading/WorkerClass.java new file mode 100644 index 00000000000..c67154f2c80 --- /dev/null +++ b/hotspot/test/compiler/classUnloading/methodUnloading/WorkerClass.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014, 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. + */ + +/** + * Worker class that is dynamically loaded/unloaded by TestMethodUnloading. + */ +public class WorkerClass { + /** + * We override hashCode here to be able to access this implementation + * via an Object reference (we cannot cast to WorkerClass). + */ + @Override + public int hashCode() { + return 42; + } +} + diff --git a/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java b/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java new file mode 100644 index 00000000000..996d82a0feb --- /dev/null +++ b/hotspot/test/compiler/exceptions/TestRecursiveReplacedException.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @bug 8054224 + * @summary Recursive method compiled by C1 is unable to catch StackOverflowError + * @run main/othervm -Xcomp -XX:CompileOnly=Test.run -XX:+TieredCompilation -XX:TieredStopAtLevel=2 -Xss256K TestRecursiveReplacedException + * + */ + +public class TestRecursiveReplacedException { + + public static void main(String args[]) { + new TestRecursiveReplacedException().run(); + } + + public void run() { + try { + run(); + } catch (Throwable t) { + } + } +} diff --git a/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java b/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java index e8cfaa23f0f..45a79b0df37 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestAndnI BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestAndnI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java b/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java index 0dca7aa399a..8746fefa483 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestAndnL BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestAndnL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsiI.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsiI.java index 9f998bc05d5..7ced5242692 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsiI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsiI.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestBlsiI BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestBlsiI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsiL.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsiL.java index b7a36c65a26..d1fe59c204b 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsiL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsiL.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestBlsiL BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestBlsiL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsmskI.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsmskI.java index a06b429ec03..7cba5d88e36 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsmskI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsmskI.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestBlsmskI BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestBlsmskI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsmskL.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsmskL.java index 794c4c8f9a9..d2f3a3f6aea 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsmskL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsmskL.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestBlsmskL BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestBlsmskL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsrI.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsrI.java index 73ea886023f..53e7251143b 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsrI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsrI.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestBlsrI BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestBlsrI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsrL.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsrL.java index 861f160806b..5e3c885542b 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsrL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsrL.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestBlsrL BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestBlsrL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestLzcntI.java b/hotspot/test/compiler/intrinsics/bmi/TestLzcntI.java index a3720dc51e3..d13b89c0246 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestLzcntI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestLzcntI.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestLzcntI BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestLzcntI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestLzcntL.java b/hotspot/test/compiler/intrinsics/bmi/TestLzcntL.java index a3d788725a7..b75830df18a 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestLzcntL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestLzcntL.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestLzcntL BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestLzcntL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestTzcntI.java b/hotspot/test/compiler/intrinsics/bmi/TestTzcntI.java index d74c82a9a9e..285519537aa 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestTzcntI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestTzcntI.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestTzcntI BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestTzcntI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/TestTzcntL.java b/hotspot/test/compiler/intrinsics/bmi/TestTzcntL.java index 0c1841991d9..36683fe7283 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestTzcntL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestTzcntL.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestTzcntL BMITestRunner Expr * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestTzcntL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestI.java index 2a77347a749..3b6f2f3ee20 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestI.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build AddnTestI * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions AddnTestI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestL.java index 72da9a04485..c03469361b7 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/AddnTestL.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build AddnTestL * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions AddnTestL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestI.java index ce234a9b6fd..92a87875a5c 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestI.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build BlsiTestI * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsiTestI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestL.java index f8544f30155..2894a53b406 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsiTestL.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build BlsiTestL * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsiTestL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java index 1a7dd8f6e56..2f0be6af7bf 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestI.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build BlsmskTestI * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsmskTestI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java index e061206bc44..f2b7345b582 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsmskTestL.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build BlsmskTestL * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsmskTestL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestI.java index e086f9bfea7..67d1b90dcee 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestI.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build BlsrTestI * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsrTestI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestL.java index 0185aeb8d88..158f3440663 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/BlsrTestL.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build BlsrTestL * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseBMI1Instructions BlsrTestL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestI.java index 62c8642833b..d74c19ac107 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestI.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build LZcntTestI * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCountLeadingZerosInstruction LZcntTestI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestL.java index 16caae2b0d9..5c164ff8ac9 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/LZcntTestL.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build LZcntTestL * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCountLeadingZerosInstruction LZcntTestL */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestI.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestI.java index 2502641e8da..7fa47ba03e2 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestI.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestI.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build TZcntTestI * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCountTrailingZerosInstruction TZcntTestI */ diff --git a/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestL.java b/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestL.java index c08dbcc1fc9..e06f9ddb0b1 100644 --- a/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestL.java +++ b/hotspot/test/compiler/intrinsics/bmi/verifycode/TZcntTestL.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/whitebox .. * @build TZcntTestL * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCountTrailingZerosInstruction TZcntTestL */ diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactIntTest.java index a2770dcddf3..e0010c2a8f1 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactIntTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build AddExactIntTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics AddExactIntTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class AddExactIntTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactLongTest.java index c5756440769..38efa5d3867 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/AddExactLongTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build AddExactLongTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics AddExactLongTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class AddExactLongTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactIntTest.java index 19641a1f41a..be55af8c02f 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactIntTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build DecrementExactIntTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics DecrementExactIntTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class DecrementExactIntTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactLongTest.java index a5821698f9d..8a48401c712 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/DecrementExactLongTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build DecrementExactLongTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics DecrementExactLongTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class DecrementExactLongTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactIntTest.java index bca840ad8f1..657a3f7723d 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactIntTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build IncrementExactIntTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics IncrementExactIntTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class IncrementExactIntTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactLongTest.java index 5348b774f9b..7915f9fd028 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/IncrementExactLongTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build IncrementExactLongTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics IncrementExactLongTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class IncrementExactLongTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java index cecd6ce2d02..ba7506526da 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/IntrinsicBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ */ import com.oracle.java.testlibrary.Platform; +import intrinsics.Verifier; import java.io.FileOutputStream; import java.lang.reflect.Executable; @@ -79,10 +80,10 @@ public abstract class IntrinsicBase extends CompilerWhiteBoxTest { System.out.println("Expected intrinsic count is " + expectedIntrinsicCount + " name " + getIntrinsicId()); - final FileOutputStream out = new FileOutputStream(getVMOption("LogFile") + ".verify.properties"); + final FileOutputStream out = new FileOutputStream(getVMOption("LogFile") + Verifier.PROPERTY_FILE_SUFFIX); Properties expectedProps = new Properties(); - expectedProps.setProperty("intrinsic.name", getIntrinsicId()); - expectedProps.setProperty("intrinsic.expectedCount", String.valueOf(expectedIntrinsicCount)); + expectedProps.setProperty(Verifier.INTRINSIC_NAME_PROPERTY, getIntrinsicId()); + expectedProps.setProperty(Verifier.INTRINSIC_EXPECTED_COUNT_PROPERTY, String.valueOf(expectedIntrinsicCount)); expectedProps.store(out, null); out.close(); diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactIntTest.java index a09c1244905..d192f66469a 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactIntTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build MultiplyExactIntTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics MultiplyExactIntTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class MultiplyExactIntTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactLongTest.java index 23154e9ecef..bbca388fc01 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/MultiplyExactLongTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build MultiplyExactLongTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics MultiplyExactLongTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class MultiplyExactLongTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactIntTest.java index f41bb7995a9..a8a1def13b5 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactIntTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build NegateExactIntTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics NegateExactIntTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class NegateExactIntTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactLongTest.java index 27af6553036..ebdbdcca07b 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/NegateExactLongTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build NegateExactLongTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics NegateExactLongTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class NegateExactLongTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactIntTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactIntTest.java index c2cbbc4ef95..e3f7c460de9 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactIntTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactIntTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build SubtractExactIntTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics SubtractExactIntTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactLongTest.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactLongTest.java index bee6d3bafc4..ddb28fc0ee4 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactLongTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/sanity/SubtractExactLongTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,8 +24,10 @@ /* * @test * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * /compiler/testlibrary * @build SubtractExactLongTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod @@ -34,7 +36,7 @@ * -XX:+IgnoreUnrecognizedVMOptions -XX:+WhiteBoxAPI -XX:+LogCompilation * -XX:CompileCommand=compileonly,MathIntrinsic*::execMathMethod * -XX:LogFile=hs.log -XX:+UseMathExactIntrinsics SubtractExactLongTest - * @run main Verifier hs_neg.log hs.log + * @run main intrinsics.Verifier hs_neg.log hs.log */ public class SubtractExactLongTest { diff --git a/hotspot/test/compiler/intrinsics/mathexact/sanity/Verifier.java b/hotspot/test/compiler/intrinsics/mathexact/sanity/Verifier.java deleted file mode 100644 index 7b26d64c648..00000000000 --- a/hotspot/test/compiler/intrinsics/mathexact/sanity/Verifier.java +++ /dev/null @@ -1,71 +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. - */ - -import java.io.BufferedReader; -import java.io.FileReader; -import java.util.Properties; - -public class Verifier { - - public static void main(String[] args) throws Exception { - if (args.length == 0) - throw new RuntimeException("Test bug, nothing to verify"); - for (String hsLogFile : args) { - verify(hsLogFile); - } - } - - private static void verify(String hsLogFile) throws Exception { - System.out.println("Verifying " + hsLogFile); - - final Properties expectedProperties = new Properties(); - final FileReader reader = new FileReader(hsLogFile + ".verify.properties"); - expectedProperties.load(reader); - reader.close(); - - int fullMatchCnt = 0; - int suspectCnt = 0; - final String intrinsicId = expectedProperties.getProperty("intrinsic.name"); - final String prefix = " is compiled + for (int i = 0; i < 100000; ++i) { + TestMemBarAcquire p = new TestMemBarAcquire(new Object()); + } + } +} + diff --git a/hotspot/test/compiler/osr/TestOSRWithNonEmptyStack.java b/hotspot/test/compiler/osr/TestOSRWithNonEmptyStack.java new file mode 100644 index 00000000000..82bbfc2ae4c --- /dev/null +++ b/hotspot/test/compiler/osr/TestOSRWithNonEmptyStack.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2014, 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. + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Label; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +/** + * @test + * @bug 8051344 + * @summary Force OSR compilation with non-empty stack at the OSR entry point. + * @compile -XDignore.symbol.file TestOSRWithNonEmptyStack.java + * @run main/othervm -XX:CompileOnly=TestCase.test TestOSRWithNonEmptyStack + */ +public class TestOSRWithNonEmptyStack extends ClassLoader { + private static final int CLASS_FILE_VERSION = 52; + private static final String CLASS_NAME = "TestCase"; + private static final String METHOD_NAME = "test"; + private static final int ITERATIONS = 1_000_000; + + private static byte[] generateTestClass() { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + + cw.visit(TestOSRWithNonEmptyStack.CLASS_FILE_VERSION, ACC_PUBLIC, + TestOSRWithNonEmptyStack.CLASS_NAME, null, "java/lang/Object", + null); + + TestOSRWithNonEmptyStack.generateConstructor(cw); + TestOSRWithNonEmptyStack.generateTestMethod(cw); + + cw.visitEnd(); + return cw.toByteArray(); + } + + private static void generateConstructor(ClassWriter classWriter) { + MethodVisitor mv = classWriter.visitMethod(ACC_PUBLIC, "", "()V", + null, null); + + mv.visitCode(); + + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", + false); + mv.visitInsn(RETURN); + + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + private static void generateTestMethod(ClassWriter classWriter) { + MethodVisitor mv = classWriter.visitMethod(ACC_PUBLIC, + TestOSRWithNonEmptyStack.METHOD_NAME, "()V", null, null); + Label osrEntryPoint = new Label(); + + mv.visitCode(); + // Push 'this' into stack before OSR entry point to bail out compilation + mv.visitVarInsn(ALOAD, 0); + // Setup loop counter + mv.visitInsn(ICONST_0); + mv.visitVarInsn(ISTORE, 1); + // Begin loop + mv.visitLabel(osrEntryPoint); + // Increment loop counter + mv.visitVarInsn(ILOAD, 1); + mv.visitInsn(ICONST_1); + mv.visitInsn(IADD); + // Duplicate it for loop condition check + mv.visitInsn(DUP); + mv.visitVarInsn(ISTORE, 1); + // Check loop condition + mv.visitLdcInsn(TestOSRWithNonEmptyStack.ITERATIONS); + mv.visitJumpInsn(IF_ICMPLT, osrEntryPoint); + // Pop 'this'. + mv.visitInsn(POP); + mv.visitInsn(RETURN); + + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + private void run() { + byte[] bytecode = TestOSRWithNonEmptyStack.generateTestClass(); + + try { + Class klass = defineClass(TestOSRWithNonEmptyStack.CLASS_NAME, + bytecode, 0, bytecode.length); + + Constructor ctor = klass.getConstructor(); + Method method = klass.getDeclaredMethod( + TestOSRWithNonEmptyStack.METHOD_NAME); + + Object testCase = ctor.newInstance(); + method.invoke(testCase); + } catch (Exception e) { + throw new RuntimeException( + "Test bug: generated class should be valid.", e); + } + } + + public static void main(String args[]) { + new TestOSRWithNonEmptyStack().run(); + } +} diff --git a/hotspot/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig.java index 0b336c8bc30..cb42f62141d 100644 --- a/hotspot/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig @@ -53,16 +54,19 @@ public class TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig CommandLineOptionTest.verifyOptionValueForSameVM(optionName, TestPrintPreciseRTMLockingStatisticsBase.DEFAULT_VALUE, CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:+UseRTMLocking"); CommandLineOptionTest.verifyOptionValueForSameVM(optionName, TestPrintPreciseRTMLockingStatisticsBase.DEFAULT_VALUE, CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:-UseRTMLocking", prepareOptionValue("true")); // verify that option could be turned on CommandLineOptionTest.verifyOptionValueForSameVM(optionName, "true", CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:+UseRTMLocking", prepareOptionValue("true")); } diff --git a/hotspot/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig.java index b22c6ab4538..5190bb8ec55 100644 --- a/hotspot/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * TestPrintPreciseRTMLockingStatisticsOptionOnUnsupportedConfig diff --git a/hotspot/test/compiler/rtm/cli/TestRTMAbortRatioOptionOnSupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestRTMAbortRatioOptionOnSupportedConfig.java index ea3d2d37bff..ffa4bb45a0f 100644 --- a/hotspot/test/compiler/rtm/cli/TestRTMAbortRatioOptionOnSupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestRTMAbortRatioOptionOnSupportedConfig.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMAbortRatioOptionOnSupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMAbortRatioOptionOnSupportedConfig */ diff --git a/hotspot/test/compiler/rtm/cli/TestRTMAbortRatioOptionOnUnsupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestRTMAbortRatioOptionOnUnsupportedConfig.java index d4807742814..eb952eebdd5 100644 --- a/hotspot/test/compiler/rtm/cli/TestRTMAbortRatioOptionOnUnsupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestRTMAbortRatioOptionOnUnsupportedConfig.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMAbortRatioOptionOnUnsupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMAbortRatioOptionOnUnsupportedConfig */ diff --git a/hotspot/test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnSupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnSupportedConfig.java index 86ddcdcdaee..979615d6c9e 100644 --- a/hotspot/test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnSupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnSupportedConfig.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMTotalCountIncrRateOptionOnSupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * TestRTMTotalCountIncrRateOptionOnSupportedConfig diff --git a/hotspot/test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnUnsupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnUnsupportedConfig.java index 32cd417ba67..8dcffdee53e 100644 --- a/hotspot/test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnUnsupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestRTMTotalCountIncrRateOptionOnUnsupportedConfig.java @@ -35,6 +35,7 @@ import rtm.predicate.SupportedVM; * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMTotalCountIncrRateOptionOnUnsupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * TestRTMTotalCountIncrRateOptionOnUnsupportedConfig diff --git a/hotspot/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnSupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnSupportedConfig.java index bd9d78266e3..c921b9b3f11 100644 --- a/hotspot/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnSupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnSupportedConfig.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMDeoptOptionOnSupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMDeoptOptionOnSupportedConfig */ @@ -62,13 +63,16 @@ public class TestUseRTMDeoptOptionOnSupportedConfig // verify default value CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", TestUseRTMDeoptOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:+UseRTMLocking"); // verify that option is off when UseRTMLocking is off - CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", - "false", "-XX:-UseRTMLocking", "-XX:+UseRTMDeopt"); + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", "false", + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking", "-XX:+UseRTMDeopt"); // verify that option could be turned on - CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", - "true", "-XX:+UseRTMLocking", "-XX:+UseRTMDeopt"); + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", "true", + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking", "-XX:+UseRTMDeopt"); } public static void main(String args[]) throws Throwable { diff --git a/hotspot/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnUnsupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnUnsupportedConfig.java index 2f3b8367519..6679df1297b 100644 --- a/hotspot/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnUnsupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnUnsupportedConfig.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMDeoptOptionOnUnsupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMDeoptOptionOnUnsupportedConfig */ diff --git a/hotspot/test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnSupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnSupportedConfig.java index b01a6a70974..f04c154c8da 100644 --- a/hotspot/test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnSupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnSupportedConfig.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMForStackLocksOptionOnSupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * TestUseRTMForStackLocksOptionOnSupportedConfig diff --git a/hotspot/test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnUnsupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnUnsupportedConfig.java index 557f17a4743..bf84bde179b 100644 --- a/hotspot/test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnUnsupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMForStackLocksOptionOnUnsupportedConfig.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMForStackLocksOptionOnUnsupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI * TestUseRTMForStackLocksOptionOnUnsupportedConfig diff --git a/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnSupportedConfig.java b/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnSupportedConfig.java index 72af53530d3..cb3a777f9f7 100644 --- a/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnSupportedConfig.java +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnSupportedConfig.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMLockingOptionOnSupportedConfig * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMLockingOptionOnSupportedConfig */ @@ -58,24 +59,31 @@ public class TestUseRTMLockingOptionOnSupportedConfig new String[]{ RTMGenericCommandLineOptionTest.RTM_INSTR_ERROR, unrecongnizedOption - }, ExitCode.OK, "-XX:+UseRTMLocking" + }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking" ); CommandLineOptionTest.verifySameJVMStartup(null, new String[]{ RTMGenericCommandLineOptionTest.RTM_INSTR_ERROR, unrecongnizedOption - }, ExitCode.OK, "-XX:-UseRTMLocking" + }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking" ); // verify that UseRTMLocking is of by default CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", - TestUseRTMLockingOptionOnSupportedConfig.DEFAULT_VALUE); + TestUseRTMLockingOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS); // verify that we can change UseRTMLocking value CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", TestUseRTMLockingOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:-UseRTMLocking"); CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", - "true", "-XX:+UseRTMLocking"); + "true", CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); } public static void main(String args[]) throws Throwable { diff --git a/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedCPU.java b/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedCPU.java index 6e100240964..a4fe7d07930 100644 --- a/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedCPU.java +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedCPU.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMLockingOptionOnUnsupportedCPU * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMLockingOptionOnUnsupportedCPU */ diff --git a/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedVM.java b/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedVM.java index 0121856bccf..8119019ae56 100644 --- a/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedVM.java +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnUnsupportedVM.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMLockingOptionOnUnsupportedVM * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMLockingOptionOnUnsupportedVM */ diff --git a/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionWithBiasedLocking.java b/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionWithBiasedLocking.java index 89b30bfb21f..0786a608550 100644 --- a/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionWithBiasedLocking.java +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionWithBiasedLocking.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMLockingOptionWithBiasedLocking * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMLockingOptionWithBiasedLocking */ @@ -53,18 +54,22 @@ public class TestUseRTMLockingOptionWithBiasedLocking // verify that we will not get a warning CommandLineOptionTest.verifySameJVMStartup(null, new String[] { warningMessage }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:+UseRTMLocking", "-XX:-UseBiasedLocking"); // verify that we will get a warning CommandLineOptionTest.verifySameJVMStartup( new String[] { warningMessage }, null, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:+UseRTMLocking", "-XX:+UseBiasedLocking"); // verify that UseBiasedLocking is false when we use rtm locking CommandLineOptionTest.verifyOptionValueForSameVM("UseBiasedLocking", - "false", "-XX:+UseRTMLocking"); + "false", CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); // verify that we can't turn on biased locking when // using rtm locking CommandLineOptionTest.verifyOptionValueForSameVM("UseBiasedLocking", - "false", "-XX:+UseRTMLocking", "-XX:+UseBiasedLocking"); + "false", CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking", "-XX:+UseBiasedLocking"); } public static void main(String args[]) throws Throwable { diff --git a/hotspot/test/compiler/rtm/locking/TestRTMAbortRatio.java b/hotspot/test/compiler/rtm/locking/TestRTMAbortRatio.java index 2c179d8eebe..f4eb6a9c458 100644 --- a/hotspot/test/compiler/rtm/locking/TestRTMAbortRatio.java +++ b/hotspot/test/compiler/rtm/locking/TestRTMAbortRatio.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMAbortRatio * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMAbortRatio */ diff --git a/hotspot/test/compiler/rtm/locking/TestRTMAbortThreshold.java b/hotspot/test/compiler/rtm/locking/TestRTMAbortThreshold.java index dbed5f0fbc7..9447b88c41d 100644 --- a/hotspot/test/compiler/rtm/locking/TestRTMAbortThreshold.java +++ b/hotspot/test/compiler/rtm/locking/TestRTMAbortThreshold.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMAbortThreshold * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMAbortThreshold */ diff --git a/hotspot/test/compiler/rtm/locking/TestRTMAfterNonRTMDeopt.java b/hotspot/test/compiler/rtm/locking/TestRTMAfterNonRTMDeopt.java index 2cf81f7f659..d46433da725 100644 --- a/hotspot/test/compiler/rtm/locking/TestRTMAfterNonRTMDeopt.java +++ b/hotspot/test/compiler/rtm/locking/TestRTMAfterNonRTMDeopt.java @@ -32,6 +32,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMAfterNonRTMDeopt * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMAfterNonRTMDeopt */ diff --git a/hotspot/test/compiler/rtm/locking/TestRTMDeoptOnHighAbortRatio.java b/hotspot/test/compiler/rtm/locking/TestRTMDeoptOnHighAbortRatio.java index 911cc21a940..4c662e088ec 100644 --- a/hotspot/test/compiler/rtm/locking/TestRTMDeoptOnHighAbortRatio.java +++ b/hotspot/test/compiler/rtm/locking/TestRTMDeoptOnHighAbortRatio.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMDeoptOnHighAbortRatio * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMDeoptOnHighAbortRatio */ diff --git a/hotspot/test/compiler/rtm/locking/TestRTMDeoptOnLowAbortRatio.java b/hotspot/test/compiler/rtm/locking/TestRTMDeoptOnLowAbortRatio.java index bcadaab6c52..4101cbf4a52 100644 --- a/hotspot/test/compiler/rtm/locking/TestRTMDeoptOnLowAbortRatio.java +++ b/hotspot/test/compiler/rtm/locking/TestRTMDeoptOnLowAbortRatio.java @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMDeoptOnLowAbortRatio * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMDeoptOnLowAbortRatio */ diff --git a/hotspot/test/compiler/rtm/locking/TestRTMLockingCalculationDelay.java b/hotspot/test/compiler/rtm/locking/TestRTMLockingCalculationDelay.java index 61f84e430c4..6355d97e487 100644 --- a/hotspot/test/compiler/rtm/locking/TestRTMLockingCalculationDelay.java +++ b/hotspot/test/compiler/rtm/locking/TestRTMLockingCalculationDelay.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMLockingCalculationDelay * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMLockingCalculationDelay */ diff --git a/hotspot/test/compiler/rtm/locking/TestRTMLockingThreshold.java b/hotspot/test/compiler/rtm/locking/TestRTMLockingThreshold.java index 548a09c6e85..a30ee9a061f 100644 --- a/hotspot/test/compiler/rtm/locking/TestRTMLockingThreshold.java +++ b/hotspot/test/compiler/rtm/locking/TestRTMLockingThreshold.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMLockingThreshold * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMLockingThreshold */ diff --git a/hotspot/test/compiler/rtm/locking/TestRTMRetryCount.java b/hotspot/test/compiler/rtm/locking/TestRTMRetryCount.java index 1a0c20cd77b..4fceef23919 100644 --- a/hotspot/test/compiler/rtm/locking/TestRTMRetryCount.java +++ b/hotspot/test/compiler/rtm/locking/TestRTMRetryCount.java @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMRetryCount * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMRetryCount */ diff --git a/hotspot/test/compiler/rtm/locking/TestRTMSpinLoopCount.java b/hotspot/test/compiler/rtm/locking/TestRTMSpinLoopCount.java index 8cb4bbbe9a3..1d78747bb97 100644 --- a/hotspot/test/compiler/rtm/locking/TestRTMSpinLoopCount.java +++ b/hotspot/test/compiler/rtm/locking/TestRTMSpinLoopCount.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMSpinLoopCount * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMSpinLoopCount */ diff --git a/hotspot/test/compiler/rtm/locking/TestRTMTotalCountIncrRate.java b/hotspot/test/compiler/rtm/locking/TestRTMTotalCountIncrRate.java index e1f94d31d93..4af5a59c5b7 100644 --- a/hotspot/test/compiler/rtm/locking/TestRTMTotalCountIncrRate.java +++ b/hotspot/test/compiler/rtm/locking/TestRTMTotalCountIncrRate.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestRTMTotalCountIncrRate * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestRTMTotalCountIncrRate */ diff --git a/hotspot/test/compiler/rtm/locking/TestUseRTMAfterLockInflation.java b/hotspot/test/compiler/rtm/locking/TestUseRTMAfterLockInflation.java index f1fa1393fb6..d6ee5b6e3ba 100644 --- a/hotspot/test/compiler/rtm/locking/TestUseRTMAfterLockInflation.java +++ b/hotspot/test/compiler/rtm/locking/TestUseRTMAfterLockInflation.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMAfterLockInflation * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMAfterLockInflation */ diff --git a/hotspot/test/compiler/rtm/locking/TestUseRTMDeopt.java b/hotspot/test/compiler/rtm/locking/TestUseRTMDeopt.java index 2e0dcbc7111..9b93ad75f09 100644 --- a/hotspot/test/compiler/rtm/locking/TestUseRTMDeopt.java +++ b/hotspot/test/compiler/rtm/locking/TestUseRTMDeopt.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMDeopt * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMDeopt */ diff --git a/hotspot/test/compiler/rtm/locking/TestUseRTMForInflatedLocks.java b/hotspot/test/compiler/rtm/locking/TestUseRTMForInflatedLocks.java index 34fa34c559e..936abf8005c 100644 --- a/hotspot/test/compiler/rtm/locking/TestUseRTMForInflatedLocks.java +++ b/hotspot/test/compiler/rtm/locking/TestUseRTMForInflatedLocks.java @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMForInflatedLocks * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMForInflatedLocks */ diff --git a/hotspot/test/compiler/rtm/locking/TestUseRTMForStackLocks.java b/hotspot/test/compiler/rtm/locking/TestUseRTMForStackLocks.java index 0a54511a73b..b3b44984df0 100644 --- a/hotspot/test/compiler/rtm/locking/TestUseRTMForStackLocks.java +++ b/hotspot/test/compiler/rtm/locking/TestUseRTMForStackLocks.java @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMForStackLocks * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMForStackLocks */ diff --git a/hotspot/test/compiler/rtm/locking/TestUseRTMXendForLockBusy.java b/hotspot/test/compiler/rtm/locking/TestUseRTMXendForLockBusy.java index 4fc5d999b71..899f96d39cf 100644 --- a/hotspot/test/compiler/rtm/locking/TestUseRTMXendForLockBusy.java +++ b/hotspot/test/compiler/rtm/locking/TestUseRTMXendForLockBusy.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMXendForLockBusy * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMXendForLockBusy */ diff --git a/hotspot/test/compiler/rtm/method_options/TestNoRTMLockElidingOption.java b/hotspot/test/compiler/rtm/method_options/TestNoRTMLockElidingOption.java index 46c9482dc13..265f9e86c1d 100644 --- a/hotspot/test/compiler/rtm/method_options/TestNoRTMLockElidingOption.java +++ b/hotspot/test/compiler/rtm/method_options/TestNoRTMLockElidingOption.java @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestNoRTMLockElidingOption * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestNoRTMLockElidingOption */ diff --git a/hotspot/test/compiler/rtm/method_options/TestUseRTMLockElidingOption.java b/hotspot/test/compiler/rtm/method_options/TestUseRTMLockElidingOption.java index 701cf8d0606..e2ded9c51d7 100644 --- a/hotspot/test/compiler/rtm/method_options/TestUseRTMLockElidingOption.java +++ b/hotspot/test/compiler/rtm/method_options/TestUseRTMLockElidingOption.java @@ -31,6 +31,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestUseRTMLockElidingOption * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestUseRTMLockElidingOption */ diff --git a/hotspot/test/compiler/rtm/print/TestPrintPreciseRTMLockingStatistics.java b/hotspot/test/compiler/rtm/print/TestPrintPreciseRTMLockingStatistics.java index eebaac23b32..39dbd2522cf 100644 --- a/hotspot/test/compiler/rtm/print/TestPrintPreciseRTMLockingStatistics.java +++ b/hotspot/test/compiler/rtm/print/TestPrintPreciseRTMLockingStatistics.java @@ -32,6 +32,7 @@ * @library /testlibrary /testlibrary/whitebox /compiler/testlibrary * @build TestPrintPreciseRTMLockingStatistics * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions * -XX:+WhiteBoxAPI TestPrintPreciseRTMLockingStatistics */ diff --git a/hotspot/test/compiler/testlibrary/intrinsics/Verifier.java b/hotspot/test/compiler/testlibrary/intrinsics/Verifier.java new file mode 100644 index 00000000000..595d3819f36 --- /dev/null +++ b/hotspot/test/compiler/testlibrary/intrinsics/Verifier.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2013, 2014, 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 intrinsics; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.Properties; + +public class Verifier { + enum VerificationStrategy { + VERIFY_STRONG_EQUALITY { + @Override + void verify(Properties expectedProperties, int fullMatchCnt, + int suspectCnt) { + int expectedCount = Integer.parseInt( + expectedProperties.getProperty( + Verifier.INTRINSIC_EXPECTED_COUNT_PROPERTY)); + String intrinsicID = expectedProperties.getProperty( + Verifier.INTRINSIC_NAME_PROPERTY); + + System.out.println("Intrinsic " + intrinsicID + + " verification, expected: " + expectedCount + + ", matched: " + fullMatchCnt + + ", suspected: " + suspectCnt); + if (expectedCount != fullMatchCnt) { + throw new RuntimeException( + "Unexpected count of intrinsic " + + intrinsicID + + " expected:" + expectedCount + + ", matched: " + fullMatchCnt + + ", suspected: " + suspectCnt); + } + } + }, + + VERIFY_INTRINSIC_USAGE { + @Override + void verify(Properties expectedProperties, int fullMatchCnt, + int suspectCnt) { + boolean isExpected = Boolean.parseBoolean( + expectedProperties.getProperty( + Verifier.INTRINSIC_IS_EXPECTED_PROPERTY)); + String intrinsicID = expectedProperties.getProperty( + Verifier.INTRINSIC_NAME_PROPERTY); + + System.out.println("Intrinsic " + intrinsicID + + " verification, is expected: " + isExpected + + ", matched: " + fullMatchCnt + + ", suspected: " + suspectCnt); + if ((fullMatchCnt == 0 && isExpected) + || (fullMatchCnt > 0 && !isExpected)) { + throw new RuntimeException( + "Unexpected count of intrinsic " + + intrinsicID + + " is expected:" + isExpected + + ", matched: " + fullMatchCnt + + ", suspected: " + suspectCnt); + } + } + }; + + void verify(Properties expectedProperties, int fullMathCnt, + int suspectCnt) { + throw new RuntimeException("Default strategy is not implemented."); + } + } + + public static final String PROPERTY_FILE_SUFFIX = ".verify.properties"; + public static final String INTRINSIC_NAME_PROPERTY = "intrinsic.name"; + public static final String INTRINSIC_IS_EXPECTED_PROPERTY + = "intrinsic.expected"; + public static final String INTRINSIC_EXPECTED_COUNT_PROPERTY + = "intrinsic.expectedCount"; + private static final String DEFAULT_STRATEGY + = VerificationStrategy.VERIFY_STRONG_EQUALITY.name(); + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + throw new RuntimeException("Test bug, nothing to verify"); + } + for (String hsLogFile : args) { + verify(hsLogFile); + } + } + + private static void verify(String hsLogFile) throws Exception { + System.out.println("Verifying " + hsLogFile); + + Properties expectedProperties = new Properties(); + FileReader reader = new FileReader(hsLogFile + + Verifier.PROPERTY_FILE_SUFFIX); + expectedProperties.load(reader); + reader.close(); + + int fullMatchCnt = 0; + int suspectCnt = 0; + String intrinsicId = expectedProperties.getProperty( + Verifier.INTRINSIC_NAME_PROPERTY); + String prefix = " { + boolean isTiered = IntrinsicPredicates.WHITE_BOX.getBooleanVMFlag( + "TieredCompilation"); + long tieredMaxLevel = IntrinsicPredicates.WHITE_BOX.getIntxVMFlag( + "TieredStopAtLevel"); + boolean maxLevelIsReachable = (tieredMaxLevel + == IntrinsicPredicates.TIERED_MAX_LEVEL); + return Platform.isServer() && (!isTiered || maxLevelIsReachable); + }; + + public static final BooleanSupplier SHA1_INSTRUCTION_AVAILABLE + = new CPUSpecificPredicate("sparc.*", new String[] { "sha1" }, + null); + + public static final BooleanSupplier SHA256_INSTRUCTION_AVAILABLE + = new CPUSpecificPredicate("sparc.*", new String[] { "sha256" }, + null); + + public static final BooleanSupplier SHA512_INSTRUCTION_AVAILABLE + = new CPUSpecificPredicate("sparc.*", new String[] { "sha512" }, + null); + + public static final BooleanSupplier ANY_SHA_INSTRUCTION_AVAILABLE + = new OrPredicate(IntrinsicPredicates.SHA1_INSTRUCTION_AVAILABLE, + new OrPredicate( + IntrinsicPredicates.SHA256_INSTRUCTION_AVAILABLE, + IntrinsicPredicates.SHA512_INSTRUCTION_AVAILABLE)); + + public static final BooleanSupplier SHA1_INTRINSICS_AVAILABLE + = new AndPredicate(new AndPredicate( + IntrinsicPredicates.SHA1_INSTRUCTION_AVAILABLE, + IntrinsicPredicates.COMPILABLE_BY_C2), + IntrinsicPredicates.booleanOptionValue("UseSHA1Intrinsics")); + + public static final BooleanSupplier SHA256_INTRINSICS_AVAILABLE + = new AndPredicate(new AndPredicate( + IntrinsicPredicates.SHA256_INSTRUCTION_AVAILABLE, + IntrinsicPredicates.COMPILABLE_BY_C2), + IntrinsicPredicates.booleanOptionValue("UseSHA256Intrinsics")); + + public static final BooleanSupplier SHA512_INTRINSICS_AVAILABLE + = new AndPredicate(new AndPredicate( + IntrinsicPredicates.SHA512_INSTRUCTION_AVAILABLE, + IntrinsicPredicates.COMPILABLE_BY_C2), + IntrinsicPredicates.booleanOptionValue("UseSHA512Intrinsics")); + + private static BooleanSupplier booleanOptionValue(String option) { + return () -> IntrinsicPredicates.WHITE_BOX.getBooleanVMFlag(option); + } + + private IntrinsicPredicates() { + } +} diff --git a/hotspot/test/compiler/tiered/NonTieredLevelsTest.java b/hotspot/test/compiler/tiered/NonTieredLevelsTest.java index ab975e4bdb7..f84903c3502 100644 --- a/hotspot/test/compiler/tiered/NonTieredLevelsTest.java +++ b/hotspot/test/compiler/tiered/NonTieredLevelsTest.java @@ -29,6 +29,7 @@ import java.util.function.IntPredicate; * @ignore 8046268 * @build NonTieredLevelsTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:-TieredCompilation * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* diff --git a/hotspot/test/compiler/tiered/TieredLevelsTest.java b/hotspot/test/compiler/tiered/TieredLevelsTest.java index d862f50e904..882762b8e45 100644 --- a/hotspot/test/compiler/tiered/TieredLevelsTest.java +++ b/hotspot/test/compiler/tiered/TieredLevelsTest.java @@ -27,6 +27,7 @@ * @ignore 8046268 * @build TieredLevelsTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+TieredCompilation * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* diff --git a/hotspot/test/compiler/types/correctness/CorrectnessTest.java b/hotspot/test/compiler/types/correctness/CorrectnessTest.java index 9c84167aa9f..3d0bca775dd 100644 --- a/hotspot/test/compiler/types/correctness/CorrectnessTest.java +++ b/hotspot/test/compiler/types/correctness/CorrectnessTest.java @@ -29,6 +29,7 @@ * execution/MethodHandleDelegate.java * @build CorrectnessTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockExperimentalVMOptions * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:TypeProfileLevel=222 -XX:+UseTypeSpeculation diff --git a/hotspot/test/compiler/types/correctness/OffTest.java b/hotspot/test/compiler/types/correctness/OffTest.java index 52bd8943b85..04be21cd2ee 100644 --- a/hotspot/test/compiler/types/correctness/OffTest.java +++ b/hotspot/test/compiler/types/correctness/OffTest.java @@ -30,6 +30,7 @@ * @build CorrectnessTest * @build OffTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/timeout=1200 OffTest */ diff --git a/hotspot/test/compiler/whitebox/ClearMethodStateTest.java b/hotspot/test/compiler/whitebox/ClearMethodStateTest.java index 8adbf2075a9..94d822ac646 100644 --- a/hotspot/test/compiler/whitebox/ClearMethodStateTest.java +++ b/hotspot/test/compiler/whitebox/ClearMethodStateTest.java @@ -30,6 +30,7 @@ import java.util.function.Function; * @ignore 8046268 * @build ClearMethodStateTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* ClearMethodStateTest * @summary testing of WB::clearMethodState() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/compiler/whitebox/DeoptimizeAllTest.java b/hotspot/test/compiler/whitebox/DeoptimizeAllTest.java index 1a297ae350e..7c55fe39265 100644 --- a/hotspot/test/compiler/whitebox/DeoptimizeAllTest.java +++ b/hotspot/test/compiler/whitebox/DeoptimizeAllTest.java @@ -28,6 +28,7 @@ * @ignore 8046268 * @build DeoptimizeAllTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* DeoptimizeAllTest * @summary testing of WB::deoptimizeAll() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/compiler/whitebox/DeoptimizeMethodTest.java b/hotspot/test/compiler/whitebox/DeoptimizeMethodTest.java index bee232d2cb0..ce9cfe82da1 100644 --- a/hotspot/test/compiler/whitebox/DeoptimizeMethodTest.java +++ b/hotspot/test/compiler/whitebox/DeoptimizeMethodTest.java @@ -28,6 +28,7 @@ * @ignore 8046268 * @build DeoptimizeMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* DeoptimizeMethodTest * @summary testing of WB::deoptimizeMethod() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/compiler/whitebox/EnqueueMethodForCompilationTest.java b/hotspot/test/compiler/whitebox/EnqueueMethodForCompilationTest.java index 5c6c4f39690..f0895bad817 100644 --- a/hotspot/test/compiler/whitebox/EnqueueMethodForCompilationTest.java +++ b/hotspot/test/compiler/whitebox/EnqueueMethodForCompilationTest.java @@ -28,6 +28,7 @@ * @ignore 8046268 * @build EnqueueMethodForCompilationTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm/timeout=600 -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* EnqueueMethodForCompilationTest * @summary testing of WB::enqueueMethodForCompilation() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/compiler/whitebox/GetNMethodTest.java b/hotspot/test/compiler/whitebox/GetNMethodTest.java index 14bc76278dd..8a24fce9e9b 100644 --- a/hotspot/test/compiler/whitebox/GetNMethodTest.java +++ b/hotspot/test/compiler/whitebox/GetNMethodTest.java @@ -31,6 +31,7 @@ import sun.hotspot.code.NMethod; * @ignore 8046268 * @build GetNMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* GetNMethodTest * @summary testing of WB::getNMethod() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/compiler/whitebox/IsMethodCompilableTest.java b/hotspot/test/compiler/whitebox/IsMethodCompilableTest.java index acede605a4f..0e340f9b238 100644 --- a/hotspot/test/compiler/whitebox/IsMethodCompilableTest.java +++ b/hotspot/test/compiler/whitebox/IsMethodCompilableTest.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /testlibrary/com/oracle/java/testlibrary * @build IsMethodCompilableTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main ClassFileInstaller com.oracle.java.testlibrary.Platform * @run main/othervm/timeout=2400 -Xbootclasspath/a:. -Xmixed -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:PerMethodRecompilationCutoff=3 -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* IsMethodCompilableTest * @summary testing of WB::isMethodCompilable() diff --git a/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java b/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java index 651002d078e..5891ef7ac05 100644 --- a/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java +++ b/hotspot/test/compiler/whitebox/MakeMethodNotCompilableTest.java @@ -28,6 +28,7 @@ * @ignore 8046268 * @build MakeMethodNotCompilableTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm/timeout=2400 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* MakeMethodNotCompilableTest * @summary testing of WB::makeMethodNotCompilable() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/compiler/whitebox/SetDontInlineMethodTest.java b/hotspot/test/compiler/whitebox/SetDontInlineMethodTest.java index ae42d531ab6..67e323ebce5 100644 --- a/hotspot/test/compiler/whitebox/SetDontInlineMethodTest.java +++ b/hotspot/test/compiler/whitebox/SetDontInlineMethodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build SetDontInlineMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* SetDontInlineMethodTest * @summary testing of WB::testSetDontInlineMethod() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/compiler/whitebox/SetForceInlineMethodTest.java b/hotspot/test/compiler/whitebox/SetForceInlineMethodTest.java index 086d7c22ef5..560ad029604 100644 --- a/hotspot/test/compiler/whitebox/SetForceInlineMethodTest.java +++ b/hotspot/test/compiler/whitebox/SetForceInlineMethodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build SetForceInlineMethodTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* SetForceInlineMethodTest * @summary testing of WB::testSetForceInlineMethod() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/gc/arguments/TestArrayAllocatorMallocLimit.java b/hotspot/test/gc/arguments/TestArrayAllocatorMallocLimit.java new file mode 100644 index 00000000000..3bbe15075a4 --- /dev/null +++ b/hotspot/test/gc/arguments/TestArrayAllocatorMallocLimit.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test TestArrayAllocatorMallocLimit + * @summary Sanity check that the ArrayAllocatorMallocLimit flag can be set. + * The test helps verifying that size_t flags can be set/read. + * @bug 8054823 + * @key gc + * @library /testlibrary + * @run driver TestArrayAllocatorMallocLimit + */ + +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; +import java.math.BigInteger; + +public class TestArrayAllocatorMallocLimit { + public static void main(String [] args) throws Exception { + testDefaultValue(); + testSetValue(); + } + + private static final String flagName = "ArrayAllocatorMallocLimit"; + + // size_t ArrayAllocatorMallocLimit = 18446744073709551615{experimental} + private static final String printFlagsFinalPattern = " *size_t *" + flagName + " *:?= *(\\d+) *\\{experimental\\} *"; + + public static void testDefaultValue() throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", "-XX:+PrintFlagsFinal", "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + String value = output.firstMatch(printFlagsFinalPattern, 1); + + try { + Asserts.assertNotNull(value, "Couldn't find size_t flag " + flagName); + + // A size_t is not always parseable with Long.parseValue, + // use BigInteger instead. + BigInteger biValue = new BigInteger(value); + + // Sanity check that we got a non-zero value. + Asserts.assertNotEquals(biValue, "0"); + + output.shouldHaveExitValue(0); + } catch (Exception e) { + System.err.println(output.getOutput()); + throw e; + } + } + + public static void testSetValue() throws Exception { + long flagValue = 2048; + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", "-XX:" + flagName + "=" + flagValue, "-XX:+PrintFlagsFinal", "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + String value = output.firstMatch(printFlagsFinalPattern, 1); + + try { + Asserts.assertNotNull("Couldn't find size_t flag " + flagName); + + long longValue = Long.parseLong(value); + + Asserts.assertEquals(longValue, flagValue); + + output.shouldHaveExitValue(0); + } catch (Exception e) { + System.err.println(output.getOutput()); + throw e; + } + } + +} diff --git a/hotspot/test/gc/arguments/TestCMSHeapSizeFlags.java b/hotspot/test/gc/arguments/TestCMSHeapSizeFlags.java index 3dc688ce8ab..f142964e380 100644 --- a/hotspot/test/gc/arguments/TestCMSHeapSizeFlags.java +++ b/hotspot/test/gc/arguments/TestCMSHeapSizeFlags.java @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestCMSHeapSizeFlags TestMaxHeapSizeTools * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm TestCMSHeapSizeFlags * @author thomas.schatzl@oracle.com */ diff --git a/hotspot/test/gc/arguments/TestG1HeapSizeFlags.java b/hotspot/test/gc/arguments/TestG1HeapSizeFlags.java index 31ab5e333c0..fabc4f02192 100644 --- a/hotspot/test/gc/arguments/TestG1HeapSizeFlags.java +++ b/hotspot/test/gc/arguments/TestG1HeapSizeFlags.java @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestG1HeapSizeFlags TestMaxHeapSizeTools * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm TestG1HeapSizeFlags * @author thomas.schatzl@oracle.com */ diff --git a/hotspot/test/gc/arguments/TestMinInitialErgonomics.java b/hotspot/test/gc/arguments/TestMinInitialErgonomics.java index 8352d92c8b0..1f90bec1e01 100644 --- a/hotspot/test/gc/arguments/TestMinInitialErgonomics.java +++ b/hotspot/test/gc/arguments/TestMinInitialErgonomics.java @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestMinInitialErgonomics TestMaxHeapSizeTools * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm TestMinInitialErgonomics * @author thomas.schatzl@oracle.com */ diff --git a/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java b/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java index 947fd0f9c88..5d7b60395cd 100644 --- a/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java +++ b/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestParallelHeapSizeFlags TestMaxHeapSizeTools * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm TestParallelHeapSizeFlags * @author thomas.schatzl@oracle.com */ diff --git a/hotspot/test/gc/arguments/TestSerialHeapSizeFlags.java b/hotspot/test/gc/arguments/TestSerialHeapSizeFlags.java index 967adf64147..0313da53aac 100644 --- a/hotspot/test/gc/arguments/TestSerialHeapSizeFlags.java +++ b/hotspot/test/gc/arguments/TestSerialHeapSizeFlags.java @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestSerialHeapSizeFlags TestMaxHeapSizeTools * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm TestSerialHeapSizeFlags * @author thomas.schatzl@oracle.com */ diff --git a/hotspot/test/gc/arguments/TestUseCompressedOopsErgo.java b/hotspot/test/gc/arguments/TestUseCompressedOopsErgo.java index 14ca6ed8d3b..eb947c13488 100644 --- a/hotspot/test/gc/arguments/TestUseCompressedOopsErgo.java +++ b/hotspot/test/gc/arguments/TestUseCompressedOopsErgo.java @@ -1,5 +1,5 @@ /* -* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +* Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestUseCompressedOopsErgo TestUseCompressedOopsErgoTools * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm TestUseCompressedOopsErgo -XX:+UseG1GC * @run main/othervm TestUseCompressedOopsErgo -XX:+UseParallelGC * @run main/othervm TestUseCompressedOopsErgo -XX:+UseParallelGC -XX:-UseParallelOldGC diff --git a/hotspot/test/gc/class_unloading/AllocateBeyondMetaspaceSize.java b/hotspot/test/gc/class_unloading/AllocateBeyondMetaspaceSize.java new file mode 100644 index 00000000000..4998fa8e562 --- /dev/null +++ b/hotspot/test/gc/class_unloading/AllocateBeyondMetaspaceSize.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, 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. + */ + +import sun.hotspot.WhiteBox; + +class AllocateBeyondMetaspaceSize { + public static Object dummy; + + public static void main(String [] args) { + if (args.length != 2) { + throw new IllegalArgumentException("Usage: "); + } + + long metaspaceSize = Long.parseLong(args[0]); + long youngGenSize = Long.parseLong(args[1]); + + run(metaspaceSize, youngGenSize); + } + + private static void run(long metaspaceSize, long youngGenSize) { + WhiteBox wb = WhiteBox.getWhiteBox(); + + long allocationBeyondMetaspaceSize = metaspaceSize * 2; + long metaspace = wb.allocateMetaspace(null, allocationBeyondMetaspaceSize); + + triggerYoungGC(youngGenSize); + + wb.freeMetaspace(null, metaspace, metaspace); + } + + private static void triggerYoungGC(long youngGenSize) { + long approxAllocSize = 32 * 1024; + long numAllocations = 2 * youngGenSize / approxAllocSize; + + for (long i = 0; i < numAllocations; i++) { + dummy = new byte[(int)approxAllocSize]; + } + } +} diff --git a/hotspot/test/gc/class_unloading/TestCMSClassUnloadingEnabledHWM.java b/hotspot/test/gc/class_unloading/TestCMSClassUnloadingEnabledHWM.java new file mode 100644 index 00000000000..38c1e16cea1 --- /dev/null +++ b/hotspot/test/gc/class_unloading/TestCMSClassUnloadingEnabledHWM.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @key gc + * @bug 8049831 + * @library /testlibrary /testlibrary/whitebox + * @build TestCMSClassUnloadingEnabledHWM AllocateBeyondMetaspaceSize + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run driver TestCMSClassUnloadingEnabledHWM + * @summary Test that -XX:-CMSClassUnloadingEnabled will trigger a Full GC when more than MetaspaceSize metadata is allocated. + */ + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; + +import java.util.ArrayList; +import java.util.Arrays; + +public class TestCMSClassUnloadingEnabledHWM { + private static long MetaspaceSize = 32 * 1024 * 1024; + private static long YoungGenSize = 32 * 1024 * 1024; + + private static OutputAnalyzer run(boolean enableUnloading) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:MetaspaceSize=" + MetaspaceSize, + "-Xmn" + YoungGenSize, + "-XX:+UseConcMarkSweepGC", + "-XX:" + (enableUnloading ? "+" : "-") + "CMSClassUnloadingEnabled", + "-XX:+PrintHeapAtGC", + "-XX:+PrintGCDetails", + "AllocateBeyondMetaspaceSize", + "" + MetaspaceSize, + "" + YoungGenSize); + return new OutputAnalyzer(pb.start()); + } + + public static OutputAnalyzer runWithCMSClassUnloading() throws Exception { + return run(true); + } + + public static OutputAnalyzer runWithoutCMSClassUnloading() throws Exception { + return run(false); + } + + public static void testWithoutCMSClassUnloading() throws Exception { + // -XX:-CMSClassUnloadingEnabled is used, so we expect a full GC instead of a concurrent cycle. + OutputAnalyzer out = runWithoutCMSClassUnloading(); + + out.shouldMatch(".*Full GC.*"); + out.shouldNotMatch(".*CMS Initial Mark.*"); + } + + public static void testWithCMSClassUnloading() throws Exception { + // -XX:+CMSClassUnloadingEnabled is used, so we expect a concurrent cycle instead of a full GC. + OutputAnalyzer out = runWithCMSClassUnloading(); + + out.shouldMatch(".*CMS Initial Mark.*"); + out.shouldNotMatch(".*Full GC.*"); + } + + public static void main(String args[]) throws Exception { + testWithCMSClassUnloading(); + testWithoutCMSClassUnloading(); + } +} + diff --git a/hotspot/test/gc/class_unloading/TestG1ClassUnloadingHWM.java b/hotspot/test/gc/class_unloading/TestG1ClassUnloadingHWM.java new file mode 100644 index 00000000000..c2c17145762 --- /dev/null +++ b/hotspot/test/gc/class_unloading/TestG1ClassUnloadingHWM.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @key gc + * @bug 8049831 + * @library /testlibrary /testlibrary/whitebox + * @build TestG1ClassUnloadingHWM AllocateBeyondMetaspaceSize + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run driver TestG1ClassUnloadingHWM + * @summary Test that -XX:-ClassUnloadingWithConcurrentMark will trigger a Full GC when more than MetaspaceSize metadata is allocated. + */ + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; + +import java.util.ArrayList; +import java.util.Arrays; + +public class TestG1ClassUnloadingHWM { + private static long MetaspaceSize = 32 * 1024 * 1024; + private static long YoungGenSize = 32 * 1024 * 1024; + + private static OutputAnalyzer run(boolean enableUnloading) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:MetaspaceSize=" + MetaspaceSize, + "-Xmn" + YoungGenSize, + "-XX:+UseG1GC", + "-XX:" + (enableUnloading ? "+" : "-") + "ClassUnloadingWithConcurrentMark", + "-XX:+PrintHeapAtGC", + "-XX:+PrintGCDetails", + "AllocateBeyondMetaspaceSize", + "" + MetaspaceSize, + "" + YoungGenSize); + return new OutputAnalyzer(pb.start()); + } + + public static OutputAnalyzer runWithG1ClassUnloading() throws Exception { + return run(true); + } + + public static OutputAnalyzer runWithoutG1ClassUnloading() throws Exception { + return run(false); + } + + public static void testWithoutG1ClassUnloading() throws Exception { + // -XX:-ClassUnloadingWithConcurrentMark is used, so we expect a full GC instead of a concurrent cycle. + OutputAnalyzer out = runWithoutG1ClassUnloading(); + + out.shouldMatch(".*Full GC.*"); + out.shouldNotMatch(".*initial-mark.*"); + } + + public static void testWithG1ClassUnloading() throws Exception { + // -XX:+ClassUnloadingWithConcurrentMark is used, so we expect a concurrent cycle instead of a full GC. + OutputAnalyzer out = runWithG1ClassUnloading(); + + out.shouldMatch(".*initial-mark.*"); + out.shouldNotMatch(".*Full GC.*"); + } + + public static void main(String args[]) throws Exception { + testWithG1ClassUnloading(); + testWithoutG1ClassUnloading(); + } +} + diff --git a/hotspot/test/gc/g1/TestDeferredRSUpdate.java b/hotspot/test/gc/g1/TestDeferredRSUpdate.java index 7d2ebdc4eb3..f00967d1df4 100644 --- a/hotspot/test/gc/g1/TestDeferredRSUpdate.java +++ b/hotspot/test/gc/g1/TestDeferredRSUpdate.java @@ -23,7 +23,7 @@ /* * @test TestDeferredRSUpdate - * @bug 8040977 + * @bug 8040977 8052170 * @summary Ensure that running with -XX:-G1DeferredRSUpdate does not crash the VM * @key gc * @library /testlibrary @@ -38,6 +38,7 @@ public class TestDeferredRSUpdate { ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", "-Xmx10M", + "-XX:+PrintGCDetails", // G1DeferredRSUpdate is a develop option, but we cannot limit execution of this test to only debug VMs. "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-G1DeferredRSUpdate", diff --git a/hotspot/test/gc/g1/TestEagerReclaimHumongousRegions.java b/hotspot/test/gc/g1/TestEagerReclaimHumongousRegions.java new file mode 100644 index 00000000000..b920e7ab832 --- /dev/null +++ b/hotspot/test/gc/g1/TestEagerReclaimHumongousRegions.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test TestEagerReclaimHumongousRegions + * @bug 8027959 + * @summary Test to make sure that eager reclaim of humongous objects work. We simply try to fill + * up the heap with humongous objects that should be eagerly reclaimable to avoid Full GC. + * @key gc + * @library /testlibrary + */ + +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.LinkedList; + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; +import com.oracle.java.testlibrary.Asserts; + +class ReclaimRegionFast { + public static final int M = 1024*1024; + + public static LinkedList garbageList = new LinkedList(); + + public static void genGarbage() { + for (int i = 0; i < 32*1024; i++) { + garbageList.add(new int[100]); + } + garbageList.clear(); + } + + // A large object referenced by a static. + static int[] filler = new int[10 * M]; + + public static void main(String[] args) { + + int[] large = new int[M]; + + Object ref_from_stack = large; + + for (int i = 0; i < 100; i++) { + // A large object that will be reclaimed eagerly. + large = new int[6*M]; + genGarbage(); + // Make sure that the compiler cannot completely remove + // the allocation of the large object until here. + System.out.println(large); + } + + // Keep the reference to the first object alive. + System.out.println(ref_from_stack); + } +} + +public class TestEagerReclaimHumongousRegions { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UseG1GC", + "-Xms128M", + "-Xmx128M", + "-Xmn16M", + "-XX:+PrintGC", + ReclaimRegionFast.class.getName()); + + Pattern p = Pattern.compile("Full GC"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + int found = 0; + Matcher m = p.matcher(output.getStdout()); + while (m.find()) { found++; } + System.out.println("Issued " + found + " Full GCs"); + Asserts.assertLT(found, 10, "Found that " + found + " Full GCs were issued. This is larger than the bound. Eager reclaim seems to not work at all"); + + output.shouldHaveExitValue(0); + } +} diff --git a/hotspot/test/gc/g1/TestEagerReclaimHumongousRegions2.java b/hotspot/test/gc/g1/TestEagerReclaimHumongousRegions2.java new file mode 100644 index 00000000000..dafbde0ed6f --- /dev/null +++ b/hotspot/test/gc/g1/TestEagerReclaimHumongousRegions2.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test TestEagerReclaimHumongousRegions2 + * @bug 8051973 + * @summary Test to make sure that eager reclaim of humongous objects correctly clears + * mark bitmaps at reclaim. + * @key gc + * @library /testlibrary + */ + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Random; + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; + +// An object that has a few references to other instances to slow down marking. +class ObjectWithSomeRefs { + public ObjectWithSomeRefs other1; + public ObjectWithSomeRefs other2; + public ObjectWithSomeRefs other3; + public ObjectWithSomeRefs other4; +} + +class ReclaimRegionFast { + public static final long MAX_MILLIS_FOR_RUN = 50 * 1000; // The maximum runtime for the actual test. + + public static final int M = 1024*1024; + + public static LinkedList garbageList = new LinkedList(); + + public static void genGarbage(Object large) { + for (int i = 0; i < 64*1024; i++) { + Object[] garbage = new Object[50]; + garbage[0] = large; + garbageList.add(garbage); + } + garbageList.clear(); + } + + public static ArrayList longList = new ArrayList(); + + public static void main(String[] args) { + + for (int i = 0; i < 16*1024; i++) { + longList.add(new ObjectWithSomeRefs()); + } + + Random rnd = new Random(); + for (int i = 0; i < longList.size(); i++) { + int len = longList.size(); + longList.get(i).other1 = longList.get(rnd.nextInt(len)); + longList.get(i).other2 = longList.get(rnd.nextInt(len)); + longList.get(i).other3 = longList.get(rnd.nextInt(len)); + longList.get(i).other4 = longList.get(rnd.nextInt(len)); + } + + int[] large1 = new int[M]; + int[] large2 = null; + int[] large3 = null; + int[] large4 = null; + + Object ref_from_stack = large1; + + long start_millis = System.currentTimeMillis(); + + for (int i = 0; i < 20; i++) { + long current_millis = System.currentTimeMillis(); + if ((current_millis - start_millis) > MAX_MILLIS_FOR_RUN) { + System.out.println("Finishing test because maximum runtime exceeded"); + break; + } + // A set of large objects that will be reclaimed eagerly - and hopefully marked. + large1 = new int[M - 20]; + large2 = new int[M - 20]; + large3 = new int[M - 20]; + large4 = new int[M - 20]; + genGarbage(large1); + // Make sure that the compiler cannot completely remove + // the allocation of the large object until here. + System.out.println(large1 + " " + large2 + " " + large3 + " " + large4); + } + + // Keep the reference to the first object alive. + System.out.println(ref_from_stack); + } +} + +public class TestEagerReclaimHumongousRegions2 { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UseG1GC", + "-Xms128M", + "-Xmx128M", + "-Xmn2M", + "-XX:G1HeapRegionSize=1M", + "-XX:InitiatingHeapOccupancyPercent=0", // Want to have as much as possible initial marks. + "-XX:+PrintGC", + "-XX:+VerifyAfterGC", + "-XX:ConcGCThreads=1", // Want to make marking as slow as possible. + "-XX:+IgnoreUnrecognizedVMOptions", // G1VerifyBitmaps is develop only. + "-XX:+G1VerifyBitmaps", + ReclaimRegionFast.class.getName()); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + } +} + diff --git a/hotspot/test/gc/g1/TestGCLogMessages.java b/hotspot/test/gc/g1/TestGCLogMessages.java index dbb617001ba..ee338013f4b 100644 --- a/hotspot/test/gc/g1/TestGCLogMessages.java +++ b/hotspot/test/gc/g1/TestGCLogMessages.java @@ -23,7 +23,7 @@ /* * @test TestGCLogMessages - * @bug 8035406 8027295 8035398 8019342 + * @bug 8035406 8027295 8035398 8019342 8027959 * @summary Ensure that the PrintGCDetails output for a minor GC with G1 * includes the expected necessary messages. * @key gc @@ -54,6 +54,7 @@ public class TestGCLogMessages { output.shouldNotContain("[String Dedup Fixup"); output.shouldNotContain("[Young Free CSet"); output.shouldNotContain("[Non-Young Free CSet"); + output.shouldNotContain("[Humongous Reclaim"); output.shouldHaveExitValue(0); pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", @@ -71,6 +72,10 @@ public class TestGCLogMessages { output.shouldContain("[String Dedup Fixup"); output.shouldNotContain("[Young Free CSet"); output.shouldNotContain("[Non-Young Free CSet"); + output.shouldContain("[Humongous Reclaim"); + output.shouldNotContain("[Humongous Total"); + output.shouldNotContain("[Humongous Candidate"); + output.shouldNotContain("[Humongous Reclaimed"); output.shouldHaveExitValue(0); pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC", @@ -90,6 +95,10 @@ public class TestGCLogMessages { output.shouldContain("[String Dedup Fixup"); output.shouldContain("[Young Free CSet"); output.shouldContain("[Non-Young Free CSet"); + output.shouldContain("[Humongous Reclaim"); + output.shouldContain("[Humongous Total"); + output.shouldContain("[Humongous Candidate"); + output.shouldContain("[Humongous Reclaimed"); output.shouldHaveExitValue(0); } diff --git a/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java b/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java index 0d3ad500a87..122597aea64 100644 --- a/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java +++ b/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox * @build TestHumongousCodeCacheRoots * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @summary Humongous objects may have references from the code cache * @run main TestHumongousCodeCacheRoots */ diff --git a/hotspot/test/gc/g1/TestHumongousShrinkHeap.java b/hotspot/test/gc/g1/TestHumongousShrinkHeap.java index 4cf6c4187c4..5a15f9f11d8 100644 --- a/hotspot/test/gc/g1/TestHumongousShrinkHeap.java +++ b/hotspot/test/gc/g1/TestHumongousShrinkHeap.java @@ -22,9 +22,8 @@ */ /** - * @ignore 8041506, 8041946, 8042051 * @test TestHumongousShrinkHeap - * @bug 8036025 + * @bug 8036025 8056043 * @summary Verify that heap shrinks after GC in the presence of fragmentation due to humongous objects * @library /testlibrary * @run main/othervm -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=50 -XX:+UseG1GC -XX:G1HeapRegionSize=1M -verbose:gc TestHumongousShrinkHeap diff --git a/hotspot/test/gc/g1/TestStringDeduplicationTools.java b/hotspot/test/gc/g1/TestStringDeduplicationTools.java index 1f8347bf4a1..916d632a45d 100644 --- a/hotspot/test/gc/g1/TestStringDeduplicationTools.java +++ b/hotspot/test/gc/g1/TestStringDeduplicationTools.java @@ -129,8 +129,19 @@ class TestStringDeduplicationTools { return list; } + /** + * Verifies that the given list contains expected number of unique strings. + * It's possible that deduplication hasn't completed yet, so the method + * will perform several attempts to check with a little pause between. + * The method throws RuntimeException to signal that verification failed. + * + * @param list strings to check + * @param uniqueExpected expected number of unique strings + * @throws RuntimeException if check fails + */ private static void verifyStrings(ArrayList list, int uniqueExpected) { - for (;;) { + boolean passed = false; + for (int attempts = 0; attempts < 10; attempts++) { // Check number of deduplicated strings ArrayList unique = new ArrayList(uniqueExpected); for (String string: list) { @@ -153,11 +164,11 @@ class TestStringDeduplicationTools { ", uniqueExpected=" + uniqueExpected); if (unique.size() == uniqueExpected) { - System.out.println("Deduplication completed"); + System.out.println("Deduplication completed (as fast as " + attempts + " iterations)"); + passed = true; break; } else { System.out.println("Deduplication not completed, waiting..."); - // Give the deduplication thread time to complete try { Thread.sleep(1000); @@ -166,6 +177,9 @@ class TestStringDeduplicationTools { } } } + if (!passed) { + throw new RuntimeException("String verification failed"); + } } private static OutputAnalyzer runTest(String... extraArgs) throws Exception { @@ -247,14 +261,20 @@ class TestStringDeduplicationTools { forceDeduplication(ageThreshold, FullGC); // Wait for deduplication to occur - while (getValue(dupString1) != getValue(baseString)) { + for (int attempts = 0; attempts < 10; attempts++) { + if (getValue(dupString1) == getValue(baseString)) { + break; + } System.out.println("Waiting..."); try { - Thread.sleep(100); + Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } + if (getValue(dupString1) != getValue(baseString)) { + throw new RuntimeException("Deduplication has not occurred"); + } // Create a new duplicate of baseString StringBuilder sb2 = new StringBuilder(baseString); diff --git a/hotspot/test/gc/whitebox/TestWBGC.java b/hotspot/test/gc/whitebox/TestWBGC.java new file mode 100644 index 00000000000..8cdb74b326b --- /dev/null +++ b/hotspot/test/gc/whitebox/TestWBGC.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test TestWBGC + * @bug 8055098 + * @summary Test verify that WB methods isObjectInOldGen and youngGC works correctly. + * @library /testlibrary /testlibrary/whitebox + * @build TestWBGC + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run driver TestWBGC + */ +import com.oracle.java.testlibrary.*; +import sun.hotspot.WhiteBox; + +public class TestWBGC { + + public static void main(String args[]) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + true, + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:MaxTenuringThreshold=1", + "-XX:+PrintGC", + GCYoungTest.class.getName()); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + System.out.println(output.getStdout()); + output.shouldHaveExitValue(0); + output.shouldContain("WhiteBox Initiated Young GC"); + output.shouldNotContain("Full"); + // To be sure that we don't provoke Full GC additionaly to young + } + + public static class GCYoungTest { + static WhiteBox wb = WhiteBox.getWhiteBox(); + public static Object obj; + + public static void main(String args[]) { + obj = new Object(); + Asserts.assertFalse(wb.isObjectInOldGen(obj)); + wb.youngGC(); + wb.youngGC(); + // 2 young GC is needed to promote object into OldGen + Asserts.assertTrue(wb.isObjectInOldGen(obj)); + } + } +} diff --git a/hotspot/test/runtime/6626217/Test6626217.sh b/hotspot/test/runtime/6626217/Test6626217.sh index 99ee83a06ec..a697191293a 100644 --- a/hotspot/test/runtime/6626217/Test6626217.sh +++ b/hotspot/test/runtime/6626217/Test6626217.sh @@ -69,7 +69,7 @@ ${MV} many_loader.class many_loader.impl2 ${MV} many_loader.impl1 many_loader.class ${RM} many_loader.java -${JAVA} ${TESTVMOPTS} -Xverify -Xint -cp . bug_21227 >test.out 2>&1 +${JAVA} ${TESTOPTS} -Xverify -Xint -cp . bug_21227 >test.out 2>&1 grep "loader constraint" test.out exit $? diff --git a/hotspot/test/runtime/6888954/vmerrors.sh b/hotspot/test/runtime/6888954/vmerrors.sh index 0fdd4ca0be4..28f6eea0fec 100644 --- a/hotspot/test/runtime/6888954/vmerrors.sh +++ b/hotspot/test/runtime/6888954/vmerrors.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2014, 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 @@ -44,6 +44,16 @@ # export TESTJAVA TESTVMOPTS # sh test/runtime/6888954/vmerrors.sh +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" + +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../../test_env.sh + ulimit -c 0 # no core files i=1 @@ -84,7 +94,7 @@ do i2=$i [ $i -lt 10 ] && i2=0$i - "$TESTJAVA/bin/java" $TESTVMOPTS -XX:+IgnoreUnrecognizedVMOptions \ + "$TESTJAVA/bin/java" $TESTOPTS -XX:+IgnoreUnrecognizedVMOptions \ -XX:-TransmitErrorReport -XX:-CreateMinidumpOnCrash \ -XX:ErrorHandlerTest=${i} -version > ${i2}.out 2>&1 diff --git a/hotspot/test/runtime/7107135/Test7107135.sh b/hotspot/test/runtime/7107135/Test7107135.sh index 0aa0e864e1e..30604ff6313 100644 --- a/hotspot/test/runtime/7107135/Test7107135.sh +++ b/hotspot/test/runtime/7107135/Test7107135.sh @@ -25,7 +25,6 @@ # ## -## @ignore 8025519 ## @test Test7107135.sh ## @bug 7107135 ## @bug 8021296 diff --git a/hotspot/test/runtime/7116786/Test7116786.java b/hotspot/test/runtime/7116786/Test7116786.java index b914019258e..48eaf440b86 100644 --- a/hotspot/test/runtime/7116786/Test7116786.java +++ b/hotspot/test/runtime/7116786/Test7116786.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, 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 @@ -147,7 +147,8 @@ class VerifyErrorCases { "no stackmap frame at jump location or bad jump", "Inconsistent stackmap frames at branch target "), - new Case("case15", "stackMapTable.cpp", true, "check_new_object", + /* Backward jump with uninit is allowed starting with JDK 8 */ + new Case("case15", "stackMapTable.cpp", false, "check_new_object", "backward jump with uninit", "Uninitialized object exists on backward branch "), diff --git a/hotspot/test/runtime/7158988/FieldMonitor.java b/hotspot/test/runtime/7158988/FieldMonitor.java index 9b16fc3d798..07c40053d34 100644 --- a/hotspot/test/runtime/7158988/FieldMonitor.java +++ b/hotspot/test/runtime/7158988/FieldMonitor.java @@ -26,6 +26,7 @@ * @bug 7158988 * @key regression * @summary verify jvm does not crash while debugging + * @ignore 8055145 * @run compile TestPostFieldModification.java * @run main/othervm FieldMonitor * @author axel.siebenborn@sap.com diff --git a/hotspot/test/runtime/7162488/Test7162488.sh b/hotspot/test/runtime/7162488/Test7162488.sh index fa47ad94746..b87d92b8d0c 100644 --- a/hotspot/test/runtime/7162488/Test7162488.sh +++ b/hotspot/test/runtime/7162488/Test7162488.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -45,14 +45,14 @@ JAVA=${TESTJAVA}${FS}bin${FS}java # OPTION=this_is_not_an_option -${JAVA} ${TESTVMOPTS} -showversion -XX:${OPTION} 2>&1 | grep "Unrecognized VM option" +${JAVA} -showversion -XX:${OPTION} 2>&1 | grep "Unrecognized VM option" if [ "$?" != "0" ] then printf "FAILED: option not flagged as unrecognized.\n" exit 1 fi -${JAVA} ${TESTVMOPTS} -showversion -XX:${OPTION} 2>&1 | grep ${OPTION} +${JAVA} -showversion -XX:${OPTION} 2>&1 | grep ${OPTION} if [ "$?" != "0" ] then printf "FAILED: bad option not named as being bad.\n" diff --git a/hotspot/test/runtime/ClassUnload/KeepAliveClass.java b/hotspot/test/runtime/ClassUnload/KeepAliveClass.java index 57590022f86..4b9d9e41e02 100644 --- a/hotspot/test/runtime/ClassUnload/KeepAliveClass.java +++ b/hotspot/test/runtime/ClassUnload/KeepAliveClass.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @build KeepAliveClass test.Empty * @build ClassUnloadCommon * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI KeepAliveClass */ diff --git a/hotspot/test/runtime/ClassUnload/KeepAliveClassLoader.java b/hotspot/test/runtime/ClassUnload/KeepAliveClassLoader.java index a14cf0f0443..3b1f814f7f8 100644 --- a/hotspot/test/runtime/ClassUnload/KeepAliveClassLoader.java +++ b/hotspot/test/runtime/ClassUnload/KeepAliveClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @build KeepAliveClassLoader test.Empty * @build ClassUnloadCommon * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI KeepAliveClassLoader */ diff --git a/hotspot/test/runtime/ClassUnload/KeepAliveObject.java b/hotspot/test/runtime/ClassUnload/KeepAliveObject.java index dd460d4da73..8c46a0be4cf 100644 --- a/hotspot/test/runtime/ClassUnload/KeepAliveObject.java +++ b/hotspot/test/runtime/ClassUnload/KeepAliveObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @build KeepAliveObject test.Empty * @build ClassUnloadCommon * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI KeepAliveObject */ diff --git a/hotspot/test/runtime/ClassUnload/KeepAliveSoftReference.java b/hotspot/test/runtime/ClassUnload/KeepAliveSoftReference.java index a7613b886dd..80258dc4ed1 100644 --- a/hotspot/test/runtime/ClassUnload/KeepAliveSoftReference.java +++ b/hotspot/test/runtime/ClassUnload/KeepAliveSoftReference.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @build KeepAliveSoftReference test.Empty * @build ClassUnloadCommon * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI KeepAliveSoftReference */ diff --git a/hotspot/test/runtime/ClassUnload/UnloadTest.java b/hotspot/test/runtime/ClassUnload/UnloadTest.java index 1dca14b8a28..6398a077a17 100644 --- a/hotspot/test/runtime/ClassUnload/UnloadTest.java +++ b/hotspot/test/runtime/ClassUnload/UnloadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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,6 +28,7 @@ * @build ClassUnloadCommon test.Empty * @build UnloadTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI UnloadTest */ import sun.hotspot.WhiteBox; diff --git a/hotspot/test/runtime/CompressedOops/CompressedClassPointers.java b/hotspot/test/runtime/CompressedOops/CompressedClassPointers.java index bb68a6ff074..1ec42fd622d 100644 --- a/hotspot/test/runtime/CompressedOops/CompressedClassPointers.java +++ b/hotspot/test/runtime/CompressedOops/CompressedClassPointers.java @@ -88,7 +88,6 @@ public class CompressedClassPointers { "-version"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldContain("HeapBaseMinAddress must be at least"); - output.shouldContain("HotSpot"); output.shouldHaveExitValue(0); } diff --git a/hotspot/test/runtime/NMT/AutoshutdownNMT.java b/hotspot/test/runtime/NMT/AutoshutdownNMT.java new file mode 100644 index 00000000000..8739dc8d99f --- /dev/null +++ b/hotspot/test/runtime/NMT/AutoshutdownNMT.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @key nmt + * @summary Test for deprecated message if -XX:-AutoShutdownNMT is specified + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.*; + +public class AutoshutdownNMT { + + public static void main(String args[]) throws Exception { + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:NativeMemoryTracking=detail", + "-XX:-AutoShutdownNMT", + "-version"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("ignoring option AutoShutdownNMT"); + } +} diff --git a/hotspot/test/runtime/NMT/JcmdBaselineDetail.java b/hotspot/test/runtime/NMT/JcmdBaselineDetail.java new file mode 100644 index 00000000000..501b860f8bc --- /dev/null +++ b/hotspot/test/runtime/NMT/JcmdBaselineDetail.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @key nmt jcmd + * @summary Verify that jcmd correctly reports that baseline succeeds with NMT enabled with detailed tracking. + * @library /testlibrary + * @run main/othervm -XX:NativeMemoryTracking=detail JcmdBaselineDetail + */ + +import com.oracle.java.testlibrary.*; + +public class JcmdBaselineDetail { + + public static void main(String args[]) throws Exception { + // Grab my own PID + String pid = Integer.toString(ProcessTools.getProcessId()); + OutputAnalyzer output; + + ProcessBuilder pb = new ProcessBuilder(); + + // Run 'jcmd VM.native_memory baseline=true' + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "baseline=true"}); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Baseline succeeded"); + } +} diff --git a/hotspot/test/runtime/NMT/JcmdDetailDiff.java b/hotspot/test/runtime/NMT/JcmdDetailDiff.java new file mode 100644 index 00000000000..8f151c29b12 --- /dev/null +++ b/hotspot/test/runtime/NMT/JcmdDetailDiff.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @summary run NMT baseline, allocate memory and verify output from detail.diff + * @key nmt jcmd + * @library /testlibrary /testlibrary/whitebox + * @ignore + * @build JcmdDetailDiff + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail JcmdDetailDiff + */ + +import com.oracle.java.testlibrary.*; + +import sun.hotspot.WhiteBox; + +public class JcmdDetailDiff { + + public static WhiteBox wb = WhiteBox.getWhiteBox(); + + public static void main(String args[]) throws Exception { + ProcessBuilder pb = new ProcessBuilder(); + OutputAnalyzer output; + // Grab my own PID + String pid = Integer.toString(ProcessTools.getProcessId()); + + long commitSize = 128 * 1024; + long reserveSize = 256 * 1024; + long addr; + + // Run 'jcmd VM.native_memory baseline=true' + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "baseline=true"}); + pb.start().waitFor(); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Baseline succeeded"); + + addr = wb.NMTReserveMemory(reserveSize); + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail.diff", "scale=KB"}); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=256KB +256KB, committed=0KB)"); + + wb.NMTCommitMemory(addr, commitSize); + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail.diff", "scale=KB"}); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=256KB +256KB, committed=128KB +128KB)"); + + wb.NMTUncommitMemory(addr, commitSize); + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail.diff", "scale=KB"}); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=256KB +256KB, committed=0KB)"); + + wb.NMTReleaseMemory(addr, reserveSize); + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail.diff", "scale=KB"}); + + output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("Test (reserved="); + } +} diff --git a/hotspot/test/runtime/NMT/JcmdScale.java b/hotspot/test/runtime/NMT/JcmdScale.java index e57d27b59a3..3d8a9518760 100644 --- a/hotspot/test/runtime/NMT/JcmdScale.java +++ b/hotspot/test/runtime/NMT/JcmdScale.java @@ -41,15 +41,15 @@ public class JcmdScale { pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "scale=KB"}); output = new OutputAnalyzer(pb.start()); - output.shouldContain("KB, committed="); + output.shouldContain("KB, committed="); pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "scale=MB"}); output = new OutputAnalyzer(pb.start()); - output.shouldContain("MB, committed="); + output.shouldContain("MB, committed="); pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "scale=GB"}); output = new OutputAnalyzer(pb.start()); - output.shouldContain("GB, committed="); + output.shouldContain("GB, committed="); pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "scale=apa"}); output = new OutputAnalyzer(pb.start()); @@ -57,7 +57,7 @@ public class JcmdScale { pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary", "scale=GB"}); output = new OutputAnalyzer(pb.start()); - output.shouldContain("GB, committed="); + output.shouldContain("GB, committed="); pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary", "scale=apa"}); output = new OutputAnalyzer(pb.start()); diff --git a/hotspot/test/runtime/NMT/JcmdScaleDetail.java b/hotspot/test/runtime/NMT/JcmdScaleDetail.java new file mode 100644 index 00000000000..97c809dc0a0 --- /dev/null +++ b/hotspot/test/runtime/NMT/JcmdScaleDetail.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @key nmt jcmd + * @summary Test the NMT scale parameter with detail tracking level + * @library /testlibrary + * @run main/othervm -XX:NativeMemoryTracking=detail JcmdScaleDetail + */ + +import com.oracle.java.testlibrary.*; + +public class JcmdScaleDetail { + + public static void main(String args[]) throws Exception { + ProcessBuilder pb = new ProcessBuilder(); + OutputAnalyzer output; + // Grab my own PID + String pid = Integer.toString(ProcessTools.getProcessId()); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "scale=KB"}); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("KB, committed="); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "scale=MB"}); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("MB, committed="); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "scale=GB"}); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("GB, committed="); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "scale=apa"}); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Incorrect scale value: apa"); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary", "scale=GB"}); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("GB, committed="); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary", "scale=apa"}); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Incorrect scale value: apa"); + } +} diff --git a/hotspot/test/runtime/NMT/JcmdSummaryDiff.java b/hotspot/test/runtime/NMT/JcmdSummaryDiff.java new file mode 100644 index 00000000000..93c13d7aa3c --- /dev/null +++ b/hotspot/test/runtime/NMT/JcmdSummaryDiff.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @summary run NMT baseline, allocate memory and verify output from summary.diff + * @key nmt jcmd + * @library /testlibrary /testlibrary/whitebox + * @build JcmdSummaryDiff + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary JcmdSummaryDiff + */ + +import com.oracle.java.testlibrary.*; + +import sun.hotspot.WhiteBox; + +public class JcmdSummaryDiff { + + public static WhiteBox wb = WhiteBox.getWhiteBox(); + + public static void main(String args[]) throws Exception { + ProcessBuilder pb = new ProcessBuilder(); + OutputAnalyzer output; + // Grab my own PID + String pid = Integer.toString(ProcessTools.getProcessId()); + + long commitSize = 128 * 1024; + long reserveSize = 256 * 1024; + long addr; + + // Run 'jcmd VM.native_memory baseline=true' + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "baseline=true"}); + pb.start().waitFor(); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Baseline succeeded"); + + addr = wb.NMTReserveMemory(reserveSize); + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary.diff", "scale=KB"}); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=256KB +256KB, committed=0KB)"); + + wb.NMTCommitMemory(addr, commitSize); + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary.diff", "scale=KB"}); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=256KB +256KB, committed=128KB +128KB)"); + + wb.NMTUncommitMemory(addr, commitSize); + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary.diff", "scale=KB"}); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=256KB +256KB, committed=0KB)"); + + wb.NMTReleaseMemory(addr, reserveSize); + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary.diff", "scale=KB"}); + + output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("Test (reserved="); + } +} diff --git a/hotspot/test/runtime/NMT/MallocRoundingReportTest.java b/hotspot/test/runtime/NMT/MallocRoundingReportTest.java new file mode 100644 index 00000000000..f0a838a3200 --- /dev/null +++ b/hotspot/test/runtime/NMT/MallocRoundingReportTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @summary Test consistency of NMT by creating allocations of the Test type with various sizes and verifying visibility with jcmd + * @key nmt jcmd + * @library /testlibrary /testlibrary/whitebox + * @build MallocRoundingReportTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail MallocRoundingReportTest + * + */ + +import com.oracle.java.testlibrary.*; + +import sun.hotspot.WhiteBox; + +public class MallocRoundingReportTest { + private static long K = 1024; + + public static void main(String args[]) throws Exception { + OutputAnalyzer output; + WhiteBox wb = WhiteBox.getWhiteBox(); + + // Grab my own PID + String pid = Integer.toString(ProcessTools.getProcessId()); + ProcessBuilder pb = new ProcessBuilder(); + + long[] additionalBytes = {0, 1, 512, 650}; + long[] kByteSize = {1024, 2048}; + long mallocd_total = 0; + for ( int i = 0; i < kByteSize.length; i++) + { + for (int j = 0; j < (additionalBytes.length); j++) { + long curKB = kByteSize[i] + additionalBytes[j]; + // round up/down to the nearest KB to match NMT reporting + long numKB = (curKB % kByteSize[i] >= 512) ? ((curKB / K) + 1) : curKB / K; + // Use WB API to alloc and free with the mtTest type + mallocd_total = wb.NMTMalloc(curKB); + // Run 'jcmd VM.native_memory summary', check for expected output + // NMT does not track memory allocations less than 1KB, and rounds to the nearest KB + String expectedOut = ("Test (reserved=" + numKB + "KB, committed=" + numKB + "KB)"); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary" }); + output = new OutputAnalyzer(pb.start()); + output.shouldContain(expectedOut); + + wb.NMTFree(mallocd_total); + // Run 'jcmd VM.native_memory summary', check for expected output + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary" }); + output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("Test (reserved="); + } + } + } +} diff --git a/hotspot/test/runtime/NMT/MallocSiteHashOverflow.java b/hotspot/test/runtime/NMT/MallocSiteHashOverflow.java new file mode 100644 index 00000000000..4868731526a --- /dev/null +++ b/hotspot/test/runtime/NMT/MallocSiteHashOverflow.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @summary Test corner case that overflows malloc site hashtable bucket + * @key nmt jcmd stress + * @library /testlibrary /testlibrary/whitebox + * @ignore - This test is disabled since it will stress NMT and timeout during normal testing + * @build MallocSiteHashOverflow + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm/timeout=480 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail MallocSiteHashOverflow + */ + +import com.oracle.java.testlibrary.*; +import sun.hotspot.WhiteBox; + +public class MallocSiteHashOverflow { + private static long K = 1024; + public static void main(String args[]) throws Exception { + String vm_name = System.getProperty("java.vm.name"); + + // For 32-bit systems, create 257 malloc sites with the same hash bucket to overflow a hash bucket + // For 64-bit systems, create 64K + 1 malloc sites with the same hash bucket to overflow a hash bucket + long entries = 257; + if (Platform.is64bit()) { + entries = 64 * K + 1; + } + + OutputAnalyzer output; + WhiteBox wb = WhiteBox.getWhiteBox(); + + // Grab my own PID + String pid = Integer.toString(ProcessTools.getProcessId()); + ProcessBuilder pb = new ProcessBuilder(); + + wb.NMTOverflowHashBucket(entries); + + // Run 'jcmd VM.native_memory summary' + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "statistics"}); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Tracking level has been downgraded due to lack of resources"); + } +} diff --git a/hotspot/test/runtime/NMT/MallocStressTest.java b/hotspot/test/runtime/NMT/MallocStressTest.java new file mode 100644 index 00000000000..41f1ff870ea --- /dev/null +++ b/hotspot/test/runtime/NMT/MallocStressTest.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @summary Stress test for malloc tracking + * @key nmt jcmd stress + * @library /testlibrary /testlibrary/whitebox + * @build MallocStressTest + * @ignore - This test is disabled since it will stress NMT and timeout during normal testing + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail MallocStressTest + */ + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import com.oracle.java.testlibrary.*; +import sun.hotspot.WhiteBox; + +public class MallocStressTest { + private static int K = 1024; + + // The stress test runs in three phases: + // 1. alloc: A lot of malloc with fewer free, which simulates a burst memory allocation + // that is usually seen during startup or class loading. + // 2. pause: Pause the test to check accuracy of native memory tracking + // 3. release: Release all malloc'd memory and check native memory tracking result. + public enum TestPhase { + alloc, + pause, + release + }; + + static TestPhase phase = TestPhase.alloc; + + // malloc'd memory + static ArrayList mallocd_memory = new ArrayList(); + static long mallocd_total = 0; + static WhiteBox whiteBox; + static AtomicInteger pause_count = new AtomicInteger(); + + static boolean is_64_bit_system; + + private static boolean is_64_bit_system() { return is_64_bit_system; } + + public static void main(String args[]) throws Exception { + is_64_bit_system = (Platform.is64bit()); + + OutputAnalyzer output; + whiteBox = WhiteBox.getWhiteBox(); + + // Grab my own PID + String pid = Integer.toString(ProcessTools.getProcessId()); + ProcessBuilder pb = new ProcessBuilder(); + + AllocThread[] alloc_threads = new AllocThread[256]; + ReleaseThread[] release_threads = new ReleaseThread[64]; + + int index; + // Create many allocation threads + for (index = 0; index < alloc_threads.length; index ++) { + alloc_threads[index] = new AllocThread(); + } + + // Fewer release threads + for (index = 0; index < release_threads.length; index ++) { + release_threads[index] = new ReleaseThread(); + } + + if (is_64_bit_system()) { + sleep_wait(2*60*1000); + } else { + sleep_wait(60*1000); + } + // pause the stress test + phase = TestPhase.pause; + while (pause_count.intValue() < alloc_threads.length + release_threads.length) { + sleep_wait(10); + } + + long mallocd_total_in_KB = (mallocd_total + K / 2) / K; + + // Now check if the result from NMT matches the total memory allocated. + String expected_test_summary = "Test (reserved=" + mallocd_total_in_KB +"KB, committed=" + mallocd_total_in_KB + "KB)"; + // Run 'jcmd VM.native_memory summary' + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); + output = new OutputAnalyzer(pb.start()); + output.shouldContain(expected_test_summary); + + // Release all allocated memory + phase = TestPhase.release; + synchronized(mallocd_memory) { + mallocd_memory.notifyAll(); + } + + // Join all threads + for (index = 0; index < alloc_threads.length; index ++) { + try { + alloc_threads[index].join(); + } catch (InterruptedException e) { + } + } + + for (index = 0; index < release_threads.length; index ++) { + try { + release_threads[index].join(); + } catch (InterruptedException e) { + } + } + + // All test memory allocated should be released + output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("Test (reserved="); + + // Verify that tracking level has not been downgraded + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "statistics"}); + output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("Tracking level has been downgraded due to lack of resources"); + } + + private static void sleep_wait(int n) { + try { + Thread.sleep(n); + } catch (InterruptedException e) { + } + } + + + static class MallocMemory { + private long addr; + private int size; + + MallocMemory(long addr, int size) { + this.addr = addr; + this.size = size; + } + + long addr() { return this.addr; } + int size() { return this.size; } + } + + static class AllocThread extends Thread { + AllocThread() { + this.setName("MallocThread"); + this.start(); + } + + // AllocThread only runs "Alloc" phase + public void run() { + Random random = new Random(); + while (MallocStressTest.phase == TestPhase.alloc) { + int r = Math.abs(random.nextInt()); + // Only malloc small amount to avoid OOM + int size = r % 32; + if (is_64_bit_system()) { + r = r % 32 * K; + } else { + r = r % 64; + } + if (size == 0) size = 1; + long addr = MallocStressTest.whiteBox.NMTMallocWithPseudoStack(size, r); + if (addr != 0) { + MallocMemory mem = new MallocMemory(addr, size); + synchronized(MallocStressTest.mallocd_memory) { + MallocStressTest.mallocd_memory.add(mem); + MallocStressTest.mallocd_total += size; + } + } else { + System.out.println("Out of malloc memory"); + break; + } + } + MallocStressTest.pause_count.incrementAndGet(); + } + } + + static class ReleaseThread extends Thread { + private Random random = new Random(); + ReleaseThread() { + this.setName("ReleaseThread"); + this.start(); + } + + public void run() { + while(true) { + switch(MallocStressTest.phase) { + case alloc: + slow_release(); + break; + case pause: + enter_pause(); + break; + case release: + quick_release(); + return; + } + } + } + + private void enter_pause() { + MallocStressTest.pause_count.incrementAndGet(); + while (MallocStressTest.phase != MallocStressTest.TestPhase.release) { + try { + synchronized(MallocStressTest.mallocd_memory) { + MallocStressTest.mallocd_memory.wait(10); + } + } catch (InterruptedException e) { + } + } + } + + private void quick_release() { + List free_list; + while (true) { + synchronized(MallocStressTest.mallocd_memory) { + if (MallocStressTest.mallocd_memory.isEmpty()) return; + int size = Math.min(MallocStressTest.mallocd_memory.size(), 5000); + List subList = MallocStressTest.mallocd_memory.subList(0, size); + free_list = new ArrayList(subList); + subList.clear(); + } + for (int index = 0; index < free_list.size(); index ++) { + MallocMemory mem = free_list.get(index); + MallocStressTest.whiteBox.NMTFree(mem.addr()); + } + } + } + + private void slow_release() { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + } + synchronized(MallocStressTest.mallocd_memory) { + if (MallocStressTest.mallocd_memory.isEmpty()) return; + int n = Math.abs(random.nextInt()) % MallocStressTest.mallocd_memory.size(); + MallocMemory mem = mallocd_memory.remove(n); + MallocStressTest.whiteBox.NMTFree(mem.addr()); + MallocStressTest.mallocd_total -= mem.size(); + } + } + } +} diff --git a/hotspot/test/runtime/NMT/MallocTestType.java b/hotspot/test/runtime/NMT/MallocTestType.java index 8a39af790e4..d570c2fa6a5 100644 --- a/hotspot/test/runtime/NMT/MallocTestType.java +++ b/hotspot/test/runtime/NMT/MallocTestType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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,6 +28,7 @@ * @library /testlibrary /testlibrary/whitebox * @build MallocTestType * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail MallocTestType */ @@ -51,11 +52,6 @@ public class MallocTestType { long memAlloc1 = wb.NMTMalloc(512 * 1024); wb.NMTFree(memAlloc2); - // Use WB API to ensure that all data has been merged before we continue - if (!wb.NMTWaitForDataMerge()) { - throw new Exception("Call to WB API NMTWaitForDataMerge() failed"); - } - // Run 'jcmd VM.native_memory summary' pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); output = new OutputAnalyzer(pb.start()); @@ -64,10 +60,6 @@ public class MallocTestType { // Free the memory allocated by NMTAllocTest wb.NMTFree(memAlloc1); - // Use WB API to ensure that all data has been merged before we continue - if (!wb.NMTWaitForDataMerge()) { - throw new Exception("Call to WB API NMTWaitForDataMerge() failed"); - } output = new OutputAnalyzer(pb.start()); output.shouldNotContain("Test (reserved="); } diff --git a/hotspot/test/runtime/NMT/NMTWithCDS.java b/hotspot/test/runtime/NMT/NMTWithCDS.java new file mode 100644 index 00000000000..1d865618f67 --- /dev/null +++ b/hotspot/test/runtime/NMT/NMTWithCDS.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @bug 8055061 + * @key nmt + * @library /testlibrary + * @run main NMTWithCDS + */ +import com.oracle.java.testlibrary.*; + +public class NMTWithCDS { + + public static void main(String[] args) throws Exception { + ProcessBuilder pb; + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:dump"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + try { + output.shouldContain("Loading classes to share"); + output.shouldHaveExitValue(0); + + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", "-XX:NativeMemoryTracking=detail", "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:on", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("sharing"); + output.shouldHaveExitValue(0); + + } catch (RuntimeException e) { + // Report 'passed' if CDS was turned off. + output.shouldContain("Unable to use shared archive"); + output.shouldHaveExitValue(1); + } + } +} diff --git a/hotspot/test/runtime/NMT/PrintNMTStatistics.java b/hotspot/test/runtime/NMT/PrintNMTStatistics.java index 534fe7ae421..5a4b6b08f5d 100644 --- a/hotspot/test/runtime/NMT/PrintNMTStatistics.java +++ b/hotspot/test/runtime/NMT/PrintNMTStatistics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox * @build PrintNMTStatistics * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main PrintNMTStatistics */ @@ -45,10 +46,6 @@ public class PrintNMTStatistics { // We start a new java process running with an argument and use WB API to ensure // we have data for NMT on VM exit if (args.length > 0) { - // Use WB API to ensure that all data has been merged before we continue - if (!WhiteBox.getWhiteBox().NMTWaitForDataMerge()) { - throw new Exception("Call to WB API NMTWaitForDataMerge() failed"); - } return; } diff --git a/hotspot/test/runtime/NMT/ReleaseCommittedMemory.java b/hotspot/test/runtime/NMT/ReleaseCommittedMemory.java index 66bc3b18969..64ef463e5c0 100644 --- a/hotspot/test/runtime/NMT/ReleaseCommittedMemory.java +++ b/hotspot/test/runtime/NMT/ReleaseCommittedMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox * @build ReleaseCommittedMemory * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail ReleaseCommittedMemory */ @@ -44,7 +45,6 @@ public class ReleaseCommittedMemory { addr = wb.NMTReserveMemory(reserveSize); wb.NMTCommitMemory(addr, 128*1024); wb.NMTReleaseMemory(addr, reserveSize); - wb.NMTWaitForDataMerge(); } } diff --git a/hotspot/test/runtime/NMT/ReleaseNoCommit.java b/hotspot/test/runtime/NMT/ReleaseNoCommit.java new file mode 100644 index 00000000000..94fba371df6 --- /dev/null +++ b/hotspot/test/runtime/NMT/ReleaseNoCommit.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @summary Release uncommitted memory and make sure NMT handles it correctly + * @key nmt regression + * @library /testlibrary /testlibrary/whitebox + * @build ReleaseNoCommit + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary ReleaseNoCommit + */ + +import com.oracle.java.testlibrary.JDKToolFinder; +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; + +import sun.hotspot.WhiteBox; + +public class ReleaseNoCommit { + + public static void main(String args[]) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + long reserveSize = 256 * 1024; + long addr; + + ProcessBuilder pb = new ProcessBuilder(); + OutputAnalyzer output; + // Grab my own PID + String pid = Integer.toString(ProcessTools.getProcessId()); + + addr = wb.NMTReserveMemory(reserveSize); + // Check for reserved + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "scale=KB"}); + output = new OutputAnalyzer(pb.start()); + output.shouldContain(" Test (reserved=256KB, committed=0KB)"); + + wb.NMTReleaseMemory(addr, reserveSize); + + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "scale=KB"}); + output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("Test (reserved="); + } +} diff --git a/hotspot/test/runtime/NMT/ShutdownTwice.java b/hotspot/test/runtime/NMT/ShutdownTwice.java index 1bb327aedb1..436e0c2c643 100644 --- a/hotspot/test/runtime/NMT/ShutdownTwice.java +++ b/hotspot/test/runtime/NMT/ShutdownTwice.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,12 +45,12 @@ public class ShutdownTwice { output = new OutputAnalyzer(pb.start()); // Verify that jcmd reports that NMT is shutting down - output.shouldContain("Shutdown is in progress, it will take a few moments to completely shutdown"); + output.shouldContain("Native memory tracking has been turned off"); // Run shutdown again output = new OutputAnalyzer(pb.start()); // Verify that jcmd reports that NMT has been shutdown already - output.shouldContain("Native memory tracking has been shutdown by user"); + output.shouldContain("Native memory tracking has been shutdown"); } } diff --git a/hotspot/test/runtime/NMT/SummaryAfterShutdown.java b/hotspot/test/runtime/NMT/SummaryAfterShutdown.java index 96061f3f004..ea1f3a60737 100644 --- a/hotspot/test/runtime/NMT/SummaryAfterShutdown.java +++ b/hotspot/test/runtime/NMT/SummaryAfterShutdown.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -44,13 +44,13 @@ public class SummaryAfterShutdown { output = new OutputAnalyzer(pb.start()); // Verify that jcmd reports that NMT is shutting down - output.shouldContain("Shutdown is in progress, it will take a few moments to completely shutdown"); + output.shouldContain("Native memory tracking has been turned off"); // Run 'jcmd VM.native_memory summary' pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); output = new OutputAnalyzer(pb.start()); // Verify that jcmd reports that NMT has been shutdown - output.shouldContain("Native memory tracking has been shutdown by user"); + output.shouldContain("Native memory tracking has been shutdown"); } } diff --git a/hotspot/test/runtime/NMT/SummarySanityCheck.java b/hotspot/test/runtime/NMT/SummarySanityCheck.java index 221ad9365e5..04185bb492d 100644 --- a/hotspot/test/runtime/NMT/SummarySanityCheck.java +++ b/hotspot/test/runtime/NMT/SummarySanityCheck.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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,6 +28,7 @@ * @library /testlibrary /testlibrary/whitebox * @build SummarySanityCheck * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+WhiteBoxAPI SummarySanityCheck */ @@ -44,11 +45,6 @@ public class SummarySanityCheck { // Grab my own PID String pid = Integer.toString(ProcessTools.getProcessId()); - // Use WB API to ensure that all data has been merged before we continue - if (!WhiteBox.getWhiteBox().NMTWaitForDataMerge()) { - throw new Exception("Call to WB API NMTWaitForDataMerge() failed"); - } - ProcessBuilder pb = new ProcessBuilder(); // Run 'jcmd VM.native_memory summary scale=KB' @@ -69,13 +65,13 @@ public class SummarySanityCheck { // Match '- (reserved=KB, committed=KB) Pattern mtTypePattern = Pattern.compile("-\\s+(?[\\w\\s]+)\\(reserved=(?\\d+)KB,\\scommitted=(?\\d+)KB\\)"); // Match 'Total: reserved=KB, committed=KB' - Pattern totalMemoryPattern = Pattern.compile("Total\\:\\s\\sreserved=(?\\d+)KB,\\s\\scommitted=(?\\d+)KB"); + Pattern totalMemoryPattern = Pattern.compile("Total\\:\\sreserved=(?\\d+)KB,\\scommitted=(?\\d+)KB"); for (int i = 0; i < lines.length; i++) { if (lines[i].startsWith("Total")) { Matcher totalMemoryMatcher = totalMemoryPattern.matcher(lines[i]); - if (totalMemoryMatcher.matches() && totalMemoryMatcher.groupCount() == 2) { + if (totalMemoryMatcher.matches()) { totalCommitted = Integer.parseInt(totalMemoryMatcher.group("committed")); totalReserved = Integer.parseInt(totalMemoryMatcher.group("reserved")); } else { diff --git a/hotspot/test/runtime/NMT/ThreadedMallocTestType.java b/hotspot/test/runtime/NMT/ThreadedMallocTestType.java index eadb719d50d..036db1ea3c0 100644 --- a/hotspot/test/runtime/NMT/ThreadedMallocTestType.java +++ b/hotspot/test/runtime/NMT/ThreadedMallocTestType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build ThreadedMallocTestType * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail ThreadedMallocTestType */ @@ -58,11 +59,6 @@ public class ThreadedMallocTestType { allocThread.start(); allocThread.join(); - // Use WB API to ensure that all data has been merged before we continue - if (!wb.NMTWaitForDataMerge()) { - throw new Exception("Call to WB API NMTWaitForDataMerge() failed"); - } - // Run 'jcmd VM.native_memory summary' pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); output = new OutputAnalyzer(pb.start()); @@ -80,11 +76,6 @@ public class ThreadedMallocTestType { freeThread.start(); freeThread.join(); - // Use WB API to ensure that all data has been merged before we continue - if (!wb.NMTWaitForDataMerge()) { - throw new Exception("Call to WB API NMTWaitForDataMerge() failed"); - } - output = new OutputAnalyzer(pb.start()); output.shouldNotContain("Test (reserved="); } diff --git a/hotspot/test/runtime/NMT/ThreadedVirtualAllocTestType.java b/hotspot/test/runtime/NMT/ThreadedVirtualAllocTestType.java index 1a7b929f0d6..c0bb7616cbd 100644 --- a/hotspot/test/runtime/NMT/ThreadedVirtualAllocTestType.java +++ b/hotspot/test/runtime/NMT/ThreadedVirtualAllocTestType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build ThreadedVirtualAllocTestType * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail ThreadedVirtualAllocTestType */ @@ -60,8 +61,6 @@ public class ThreadedVirtualAllocTestType { reserveThread.start(); reserveThread.join(); - mergeData(); - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail"}); output = new OutputAnalyzer(pb.start()); output.shouldContain("Test (reserved=512KB, committed=0KB)"); @@ -77,8 +76,6 @@ public class ThreadedVirtualAllocTestType { commitThread.start(); commitThread.join(); - mergeData(); - output = new OutputAnalyzer(pb.start()); output.shouldContain("Test (reserved=512KB, committed=128KB)"); if (has_nmt_detail) { @@ -93,8 +90,6 @@ public class ThreadedVirtualAllocTestType { uncommitThread.start(); uncommitThread.join(); - mergeData(); - output = new OutputAnalyzer(pb.start()); output.shouldContain("Test (reserved=512KB, committed=0KB)"); output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + commitSize) + "\\] committed"); @@ -107,17 +102,9 @@ public class ThreadedVirtualAllocTestType { releaseThread.start(); releaseThread.join(); - mergeData(); - output = new OutputAnalyzer(pb.start()); output.shouldNotContain("Test (reserved="); output.shouldNotContain("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved"); } - public static void mergeData() throws Exception { - // Use WB API to ensure that all data has been merged before we continue - if (!wb.NMTWaitForDataMerge()) { - throw new Exception("Call to WB API NMTWaitForDataMerge() failed"); } - } -} diff --git a/hotspot/test/runtime/NMT/VirtualAllocCommitUncommitRecommit.java b/hotspot/test/runtime/NMT/VirtualAllocCommitUncommitRecommit.java new file mode 100644 index 00000000000..2524e233ca3 --- /dev/null +++ b/hotspot/test/runtime/NMT/VirtualAllocCommitUncommitRecommit.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @summary Test reserve/commit/uncommit/release of virtual memory and that we track it correctly + * @key nmt jcmd + * @library /testlibrary /testlibrary/whitebox + * @build VirtualAllocCommitUncommitRecommit + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail VirtualAllocCommitUncommitRecommit + * + */ + +import com.oracle.java.testlibrary.*; + +import sun.hotspot.WhiteBox; + +public class VirtualAllocCommitUncommitRecommit { + + public static WhiteBox wb = WhiteBox.getWhiteBox(); + + public static void main(String args[]) throws Exception { + OutputAnalyzer output; + long commitSize = 128 * 1024; // 128KB + long reserveSize = 4 * 1024 * 1024; // 4096KB + long addr; + + String pid = Integer.toString(ProcessTools.getProcessId()); + ProcessBuilder pb = new ProcessBuilder(); + + boolean has_nmt_detail = wb.NMTIsDetailSupported(); + if (has_nmt_detail) { + System.out.println("NMT detail support detected."); + } else { + System.out.println("NMT detail support not detected."); + } + + // reserve + addr = wb.NMTReserveMemory(reserveSize); + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, + "VM.native_memory", "detail" }); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=4096KB, committed=0KB)"); + if (has_nmt_detail) { + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + + Long.toHexString(addr + reserveSize) + + "\\] reserved 4096KB for Test"); + } + + long addrA = addr; + long addrB = addr + commitSize; + long addrC = addr + (2 * commitSize); + long addrD = addr + (3 * commitSize); + long addrE = addr + (4 * commitSize); + long addrF = addr + (5 * commitSize); + + // commit ABCD + wb.NMTCommitMemory(addrA, commitSize); + wb.NMTCommitMemory(addrB, commitSize); + wb.NMTCommitMemory(addrC, commitSize); + wb.NMTCommitMemory(addrD, commitSize); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=4096KB, committed=512KB)"); + + if (has_nmt_detail) { + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + + Long.toHexString(addr + reserveSize) + + "\\] reserved 4096KB for Test"); + } + // uncommit BC + wb.NMTUncommitMemory(addrB, commitSize); + wb.NMTUncommitMemory(addrC, commitSize); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=4096KB, committed=256KB)"); + + if (has_nmt_detail) { + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + + Long.toHexString(addr + reserveSize) + + "\\] reserved 4096KB for Test"); + } + + // commit EF + wb.NMTCommitMemory(addrE, commitSize); + wb.NMTCommitMemory(addrF, commitSize); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=4096KB, committed=512KB)"); + if (has_nmt_detail) { + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + + Long.toHexString(addr + reserveSize) + + "\\] reserved 4096KB for Test"); + } + + // uncommit A + wb.NMTUncommitMemory(addrA, commitSize); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=4096KB, committed=384KB)"); + if (has_nmt_detail) { + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + + Long.toHexString(addr + reserveSize) + + "\\] reserved 4096KB for Test"); + } + + // commit ABC + wb.NMTCommitMemory(addrA, commitSize); + wb.NMTCommitMemory(addrB, commitSize); + wb.NMTCommitMemory(addrC, commitSize); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=4096KB, committed=768KB)"); + if (has_nmt_detail) { + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + + Long.toHexString(addr + reserveSize) + + "\\] reserved 4096KB for Test"); + } + + // uncommit ABCDEF + wb.NMTUncommitMemory(addrA, commitSize); + wb.NMTUncommitMemory(addrB, commitSize); + wb.NMTUncommitMemory(addrC, commitSize); + wb.NMTUncommitMemory(addrD, commitSize); + wb.NMTUncommitMemory(addrE, commitSize); + wb.NMTUncommitMemory(addrF, commitSize); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Test (reserved=4096KB, committed=0KB)"); + if (has_nmt_detail) { + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + + Long.toHexString(addr + reserveSize) + + "\\] reserved 4096KB for Test"); + } + + // release + wb.NMTReleaseMemory(addr, reserveSize); + output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("Test (reserved="); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); + } +} diff --git a/hotspot/test/runtime/NMT/VirtualAllocTestType.java b/hotspot/test/runtime/NMT/VirtualAllocTestType.java index 86e751c6b31..579fba6b44f 100644 --- a/hotspot/test/runtime/NMT/VirtualAllocTestType.java +++ b/hotspot/test/runtime/NMT/VirtualAllocTestType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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,6 +28,7 @@ * @library /testlibrary /testlibrary/whitebox * @build VirtualAllocTestType * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail VirtualAllocTestType */ @@ -54,7 +55,6 @@ public class VirtualAllocTestType { } addr = wb.NMTReserveMemory(reserveSize); - mergeData(); pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail"}); output = new OutputAnalyzer(pb.start()); @@ -65,7 +65,6 @@ public class VirtualAllocTestType { wb.NMTCommitMemory(addr, commitSize); - mergeData(); output = new OutputAnalyzer(pb.start()); output.shouldContain("Test (reserved=256KB, committed=128KB)"); @@ -75,24 +74,15 @@ public class VirtualAllocTestType { wb.NMTUncommitMemory(addr, commitSize); - mergeData(); output = new OutputAnalyzer(pb.start()); output.shouldContain("Test (reserved=256KB, committed=0KB)"); output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + commitSize) + "\\] committed"); wb.NMTReleaseMemory(addr, reserveSize); - mergeData(); output = new OutputAnalyzer(pb.start()); output.shouldNotContain("Test (reserved="); output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved"); } - - public static void mergeData() throws Exception { - // Use WB API to ensure that all data has been merged before we continue - if (!wb.NMTWaitForDataMerge()) { - throw new Exception("Call to WB API NMTWaitForDataMerge() failed"); } - } -} diff --git a/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java b/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java index dff619248ec..6989a643f59 100644 --- a/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java +++ b/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java @@ -21,8 +21,7 @@ * questions. */ -/* @ignore JDK-8043896 - * @test LimitSharedSizes +/* @test LimitSharedSizes * @summary Test handling of limits on shared space size * @library /testlibrary * @run main LimitSharedSizes diff --git a/hotspot/test/runtime/interned/SanityTest.java b/hotspot/test/runtime/interned/SanityTest.java index 779d3fc78c6..84bc1fee7e5 100644 --- a/hotspot/test/runtime/interned/SanityTest.java +++ b/hotspot/test/runtime/interned/SanityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build SanityTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SanityTest */ diff --git a/hotspot/test/runtime/jsig/Test8017498.sh b/hotspot/test/runtime/jsig/Test8017498.sh index a28bbe4b478..64b019d5a84 100644 --- a/hotspot/test/runtime/jsig/Test8017498.sh +++ b/hotspot/test/runtime/jsig/Test8017498.sh @@ -24,7 +24,6 @@ # ## -## @ignore 8028806 ## @test Test8017498.sh ## @bug 8017498 ## @bug 8020791 @@ -35,11 +34,11 @@ ## @run shell/timeout=60 Test8017498.sh ## -if [ "${TESTSRC}" = "" ] -then - TESTSRC=${PWD} +if [ -z "${TESTSRC}" ]; then + TESTSRC="${PWD}" echo "TESTSRC not set. Using "${TESTSRC}" as default" fi + echo "TESTSRC=${TESTSRC}" ## Adding common setup Variables for running shell tests. . ${TESTSRC}/../../test_env.sh @@ -52,13 +51,13 @@ case "$OS" in Linux) echo "Testing on Linux" gcc_cmd=`which gcc` - if [ "x$gcc_cmd" == "x" ]; then + if [ -z "$gcc_cmd" ]; then echo "WARNING: gcc not found. Cannot execute test." 2>&1 exit 0; fi MY_LD_PRELOAD=${TESTJAVA}${FS}jre${FS}lib${FS}${VM_CPU}${FS}libjsig.so - if [ "$VM_BITS" == "32" ] && [ "$VM_CPU" != "arm" ] && [ "$VM_CPU" != "ppc" ]; then - EXTRA_CFLAG=-m32 + if [ "$VM_BITS" = "32" ] && [ "$VM_CPU" != "arm" ] && [ "$VM_CPU" != "ppc" ]; then + EXTRA_CFLAG=-m32 fi echo MY_LD_PRELOAD = ${MY_LD_PRELOAD} ;; @@ -70,26 +69,29 @@ esac THIS_DIR=. -cp ${TESTSRC}${FS}*.java ${THIS_DIR} +cp "${TESTSRC}${FS}"*.java "${THIS_DIR}" ${COMPILEJAVA}${FS}bin${FS}javac *.java $gcc_cmd -DLINUX -fPIC -shared \ ${EXTRA_CFLAG} -z noexecstack \ - -o ${TESTSRC}${FS}libTestJNI.so \ + -o libTestJNI.so \ -I${COMPILEJAVA}${FS}include \ -I${COMPILEJAVA}${FS}include${FS}linux \ ${TESTSRC}${FS}TestJNI.c +if [ $? -ne 0 ] ; then + echo "Compile failed, Ignoring failed compilation and forcing the test to pass" + exit 0 +fi + # run the java test in the background cmd="LD_PRELOAD=$MY_LD_PRELOAD \ ${TESTJAVA}${FS}bin${FS}java \ - -Djava.library.path=${TESTSRC}${FS} -server TestJNI 100" -echo "$cmd > test.out 2>&1" -eval $cmd > test.out 2>&1 + -Djava.library.path=. -server TestJNI 100" +echo "$cmd > test.out" +eval $cmd > test.out -grep "old handler" test.out > ${NULL} -if [ $? = 0 ] -then +if grep "old handler" test.out > ${NULL}; then echo "Test Passed" exit 0 fi diff --git a/hotspot/test/runtime/memory/ReadFromNoaccessArea.java b/hotspot/test/runtime/memory/ReadFromNoaccessArea.java index 484240287ce..7a6c71d4b4d 100644 --- a/hotspot/test/runtime/memory/ReadFromNoaccessArea.java +++ b/hotspot/test/runtime/memory/ReadFromNoaccessArea.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build ReadFromNoaccessArea * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main ReadFromNoaccessArea */ diff --git a/hotspot/test/runtime/memory/ReserveMemory.java b/hotspot/test/runtime/memory/ReserveMemory.java index 3a65ca605c2..1708657f34c 100644 --- a/hotspot/test/runtime/memory/ReserveMemory.java +++ b/hotspot/test/runtime/memory/ReserveMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ * @library /testlibrary /testlibrary/whitebox * @build ReserveMemory * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main ReserveMemory */ diff --git a/hotspot/test/runtime/memory/RunUnitTestsConcurrently.java b/hotspot/test/runtime/memory/RunUnitTestsConcurrently.java index 35b855e0af3..e979504c664 100644 --- a/hotspot/test/runtime/memory/RunUnitTestsConcurrently.java +++ b/hotspot/test/runtime/memory/RunUnitTestsConcurrently.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build RunUnitTestsConcurrently * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI RunUnitTestsConcurrently 30 15000 */ diff --git a/hotspot/test/runtime/memory/StressVirtualSpaceResize.java b/hotspot/test/runtime/memory/StressVirtualSpaceResize.java index df978012080..ebe5110a64f 100644 --- a/hotspot/test/runtime/memory/StressVirtualSpaceResize.java +++ b/hotspot/test/runtime/memory/StressVirtualSpaceResize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build StressVirtualSpaceResize * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI StressVirtualSpaceResize */ diff --git a/hotspot/test/runtime/whitebox/WBStackSize.java b/hotspot/test/runtime/whitebox/WBStackSize.java index ff1a4dcca25..b487bb472bc 100644 --- a/hotspot/test/runtime/whitebox/WBStackSize.java +++ b/hotspot/test/runtime/whitebox/WBStackSize.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build WBStackSize * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xss512k WBStackSize */ diff --git a/hotspot/test/sanity/WBApi.java b/hotspot/test/sanity/WBApi.java index 3d926777121..58c100f496e 100644 --- a/hotspot/test/sanity/WBApi.java +++ b/hotspot/test/sanity/WBApi.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build WBApi * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI WBApi */ diff --git a/hotspot/test/sanity/WhiteBox.java b/hotspot/test/sanity/WhiteBox.java index a366e4dd117..b47d339f55e 100644 --- a/hotspot/test/sanity/WhiteBox.java +++ b/hotspot/test/sanity/WhiteBox.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/hotspot/test/serviceability/ParserTest.java b/hotspot/test/serviceability/ParserTest.java index 8db151f4d3f..264b25c89bf 100644 --- a/hotspot/test/serviceability/ParserTest.java +++ b/hotspot/test/serviceability/ParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.parser.* * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI ParserTest */ diff --git a/hotspot/test/serviceability/attach/AttachSetGetFlag.java b/hotspot/test/serviceability/attach/AttachSetGetFlag.java new file mode 100644 index 00000000000..230ef3c8c27 --- /dev/null +++ b/hotspot/test/serviceability/attach/AttachSetGetFlag.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @bug 8054823 + * @summary Tests the setFlag and printFlag attach command + * @library /testlibrary + * @build com.oracle.java.testlibrary.* AttachSetGetFlag + * @run driver AttachSetGetFlag + */ + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import sun.tools.attach.HotSpotVirtualMachine; + +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.Platform; +import com.oracle.java.testlibrary.ProcessTools; +import com.sun.tools.attach.VirtualMachine; + +public class AttachSetGetFlag { + + public static void main(String... args) throws Exception { + // Test a manageable uintx flag. + testGetFlag("MaxHeapFreeRatio", "60"); + testSetFlag("MaxHeapFreeRatio", "50", "60"); + + // Test a non-manageable size_t flag. + // Since it is not manageable, we can't test the setFlag functionality. + testGetFlag("ArrayAllocatorMallocLimit", "128"); + // testSetFlag("ArrayAllocatorMallocLimit", "64", "128"); + } + + public static ProcessBuilder runTarget(String flagName, String flagValue) throws Exception { + return ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockExperimentalVMOptions", + "-XX:" + flagName + "=" + flagValue, + "AttachSetGetFlag$Target"); + } + + public static void testGetFlag(String flagName, String flagValue) throws Exception { + ProcessBuilder pb = runTarget(flagName, flagValue); + + Process target = pb.start(); + + try { + waitForReady(target); + + int pid = (int)target.getPid(); + + HotSpotVirtualMachine vm = (HotSpotVirtualMachine)VirtualMachine.attach(((Integer)pid).toString()); + + // Test Get + BufferedReader remoteDataReader = new BufferedReader(new InputStreamReader( + vm.printFlag(flagName))); + + boolean foundExpectedLine = false; + + String line = null; + while((line = remoteDataReader.readLine()) != null) { + System.out.println("printFlag: " + line); + if (line.equals("-XX:" + flagName + "=" + flagValue)) { + foundExpectedLine = true; + } + } + + Asserts.assertTrue(foundExpectedLine, "Didn't get the expected output: '-XX:" + flagName + "=" + flagValue + "'"); + + vm.detach(); + } + finally { + target.destroy(); + target.waitFor(); + } + } + + public static void testSetFlag(String flagName, String initialFlagValue, String flagValue) throws Exception { + ProcessBuilder pb = runTarget(flagName, initialFlagValue); + + Process target = pb.start(); + + try { + waitForReady(target); + + int pid = (int)target.getPid(); + + HotSpotVirtualMachine vm = (HotSpotVirtualMachine)VirtualMachine.attach(((Integer)pid).toString()); + + // First set the value. + BufferedReader remoteDataReader = new BufferedReader(new InputStreamReader( + vm.setFlag(flagName, flagValue))); + + String line; + while((line = remoteDataReader.readLine()) != null) { + System.out.println("setFlag: " + line); + // Just empty the stream. + } + remoteDataReader.close(); + + // Then read and make sure we get back the set value. + remoteDataReader = new BufferedReader(new InputStreamReader(vm.printFlag(flagName))); + + boolean foundExpectedLine = false; + line = null; + while((line = remoteDataReader.readLine()) != null) { + System.out.println("getFlag: " + line); + if (line.equals("-XX:" + flagName + "=" + flagValue)) { + foundExpectedLine = true; + } + } + + Asserts.assertTrue(foundExpectedLine, "Didn't get the expected output: '-XX:" + flagName + "=" + flagValue + "'"); + + vm.detach(); + + } finally { + target.destroy(); + target.waitFor(); + } + } + + private static void waitForReady(Process target) throws Exception { + InputStream os = target.getInputStream(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(os))) { + String line; + while ((line = reader.readLine()) != null) { + if ("Ready".equals(line)) { + return; + } + } + } + } + + + public static class Target { + public static void main(String [] args) throws Exception { + System.out.println("Ready"); + System.out.flush(); + while (true) { + Thread.sleep(1000); + } + } + } +} diff --git a/hotspot/test/test_env.sh b/hotspot/test/test_env.sh index fa912dde237..e9fc3abb0ff 100644 --- a/hotspot/test/test_env.sh +++ b/hotspot/test/test_env.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2013, 2014, 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,6 +50,9 @@ then fi echo "TESTCLASSES=${TESTCLASSES}" +TESTOPTS="${TESTVMOPTS} ${TESTJAVAOPTS}" +echo "TESTOPTS=${TESTOPTS}" + # set platform-dependent variables OS=`uname -s` case "$OS" in @@ -101,14 +104,14 @@ THIS_DIR=. echo "THIS_DIR=${THIS_DIR}" # Check to ensure the java defined actually works -${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -version +${TESTJAVA}${FS}bin${FS}java ${TESTOPTS} -version if [ $? != 0 ]; then - echo "Wrong TESTJAVA or TESTVMOPTS:" - echo $TESTJAVA TESTVMOPTS + echo "Wrong TESTJAVA or TESTJAVAOPTS or TESTVMOPTS:" + echo ''$TESTJAVA'' ''$TESTJAVAOPTS'' ''$TESTVMOPTS'' exit 1 fi -${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -Xinternalversion > vm_version.out 2>&1 +${TESTJAVA}${FS}bin${FS}java ${TESTOPTS} -Xinternalversion > vm_version.out 2>&1 VM_TYPE="unknown" grep "Server" vm_version.out > ${NULL} diff --git a/hotspot/test/testlibrary/com/oracle/java/testlibrary/BuildHelper.java b/hotspot/test/testlibrary/com/oracle/java/testlibrary/BuildHelper.java new file mode 100644 index 00000000000..0297b2ca6a0 --- /dev/null +++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/BuildHelper.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014, 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 com.oracle.java.testlibrary; + +import java.io.File; +import java.io.FileReader; +import java.util.Properties; + +public class BuildHelper { + + /** + * Commercial builds should have the BUILD_TYPE set to commercial + * within the release file, found at the root of the JDK. + */ + public static boolean isCommercialBuild() throws Exception { + String buildType = getReleaseProperty("BUILD_TYPE","notFound"); + return buildType.equals("commercial"); + } + + + /** + * Return the value for property key, or defaultValue if no property not found. + * If present, double quotes are trimmed. + */ + public static String getReleaseProperty(String key, String defaultValue) throws Exception { + Properties properties = getReleaseProperties(); + String value = properties.getProperty(key, defaultValue); + return trimDoubleQuotes(value); + } + + /** + * Return the value for property key, or null if no property not found. + * If present, double quotes are trimmed. + */ + public static String getReleaseProperty(String key) throws Exception { + return getReleaseProperty(key, null); + } + + /** + * Get properties from the release file + */ + public static Properties getReleaseProperties() throws Exception { + Properties properties = new Properties(); + properties.load(new FileReader(getReleaseFile())); + return properties; + } + + /** + * Every JDK has a release file in its root. + * @return A handler to the release file. + */ + public static File getReleaseFile() throws Exception { + String jdkPath = getJDKRoot(); + File releaseFile = new File(jdkPath,"release"); + if ( ! releaseFile.canRead() ) { + throw new Exception("Release file is not readable, or it is absent: " + + releaseFile.getCanonicalPath()); + } + return releaseFile; + } + + /** + * Returns path to the JDK under test. + * This path is obtained through the test.jdk property, usually set by JTREG. + */ + public static String getJDKRoot() { + String jdkPath = System.getProperty("test.jdk"); + if (jdkPath == null) { + throw new RuntimeException("System property 'test.jdk' not set. This property is normally set by jtreg. " + + "When running test separately, set this property using '-Dtest.jdk=/path/to/jdk'."); + } + return jdkPath; + } + + /** + * Trim double quotes from the beginning and the end of the given string. + * @param original string to trim. + * @return a new trimmed string. + */ + public static String trimDoubleQuotes(String original) { + if (original == null) { return null; } + String trimmed = original.replaceAll("^\"+|\"+$", ""); + return trimmed; + } +} diff --git a/hotspot/test/testlibrary/com/oracle/java/testlibrary/Platform.java b/hotspot/test/testlibrary/com/oracle/java/testlibrary/Platform.java index fa4b3636121..0211514e532 100644 --- a/hotspot/test/testlibrary/com/oracle/java/testlibrary/Platform.java +++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/Platform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ public class Platform { private static final String osName = System.getProperty("os.name"); private static final String dataModel = System.getProperty("sun.arch.data.model"); private static final String vmVersion = System.getProperty("java.vm.version"); + private static final String javaVersion = System.getProperty("java.version"); private static final String osArch = System.getProperty("os.arch"); private static final String vmName = System.getProperty("java.vm.name"); @@ -83,7 +84,8 @@ public class Platform { } public static boolean isDebugBuild() { - return vmVersion.toLowerCase().contains("debug"); + return (vmVersion.toLowerCase().contains("debug") || + javaVersion.toLowerCase().contains("debug")); } public static String getVMVersion() { diff --git a/hotspot/test/testlibrary/ctw/test/ClassesDirTest.java b/hotspot/test/testlibrary/ctw/test/ClassesDirTest.java index cc28d403400..5cbaaf9a66a 100644 --- a/hotspot/test/testlibrary/ctw/test/ClassesDirTest.java +++ b/hotspot/test/testlibrary/ctw/test/ClassesDirTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src * @build ClassFileInstaller sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox Foo Bar * @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main ClassesDirTest prepare * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld classes * @run main ClassesDirTest check ctw.log diff --git a/hotspot/test/testlibrary/ctw/test/ClassesListTest.java b/hotspot/test/testlibrary/ctw/test/ClassesListTest.java index 64c5203cf10..ea8abe27106 100644 --- a/hotspot/test/testlibrary/ctw/test/ClassesListTest.java +++ b/hotspot/test/testlibrary/ctw/test/ClassesListTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src * @build ClassFileInstaller sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox Foo Bar * @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main ClassesListTest prepare * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld classes.lst * @run main ClassesListTest check ctw.log diff --git a/hotspot/test/testlibrary/ctw/test/JarDirTest.java b/hotspot/test/testlibrary/ctw/test/JarDirTest.java index abc5a0843d2..193aea4f647 100644 --- a/hotspot/test/testlibrary/ctw/test/JarDirTest.java +++ b/hotspot/test/testlibrary/ctw/test/JarDirTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src * @build ClassFileInstaller com.oracle.java.testlibrary.* sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox Foo Bar * @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main JarDirTest prepare * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld jars/* * @run main JarDirTest check ctw.log diff --git a/hotspot/test/testlibrary/ctw/test/JarsTest.java b/hotspot/test/testlibrary/ctw/test/JarsTest.java index c9bfb303132..87bafe85898 100644 --- a/hotspot/test/testlibrary/ctw/test/JarsTest.java +++ b/hotspot/test/testlibrary/ctw/test/JarsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src * @build ClassFileInstaller com.oracle.java.testlibrary.* sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox Foo Bar * @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main JarsTest prepare * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld foo.jar bar.jar * @run main JarsTest check ctw.log diff --git a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java index bb58361ffda..605e42c1a98 100644 --- a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java +++ b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java @@ -73,6 +73,8 @@ public class WhiteBox { // Memory public native long getObjectAddress(Object o); public native int getHeapOopSize(); + public native boolean isObjectInOldGen(Object o); + public native long getObjectSize(Object o); // Runtime // Make sure class name is in the correct format @@ -95,7 +97,8 @@ public class WhiteBox { public native void NMTCommitMemory(long addr, long size); public native void NMTUncommitMemory(long addr, long size); public native void NMTReleaseMemory(long addr, long size); - public native boolean NMTWaitForDataMerge(); + public native void NMTOverflowHashBucket(long num); + public native long NMTMallocWithPseudoStack(long size, int index); public native boolean NMTIsDetailSupported(); // Compiler @@ -149,6 +152,9 @@ public class WhiteBox { public native long allocateMetaspace(ClassLoader classLoader, long size); public native void freeMetaspace(ClassLoader classLoader, long addr, long size); + // force Young GC + public native void youngGC(); + // force Full GC public native void fullGC(); @@ -167,17 +173,20 @@ public class WhiteBox { public native void setIntxVMFlag(String name, long value); public native void setUintxVMFlag(String name, long value); public native void setUint64VMFlag(String name, long value); + public native void setSizeTVMFlag(String name, long value); public native void setStringVMFlag(String name, String value); public native void setDoubleVMFlag(String name, double value); public native Boolean getBooleanVMFlag(String name); public native Long getIntxVMFlag(String name); public native Long getUintxVMFlag(String name); public native Long getUint64VMFlag(String name); + public native Long getSizeTVMFlag(String name); public native String getStringVMFlag(String name); public native Double getDoubleVMFlag(String name); private final List> flagsGetters = Arrays.asList( this::getBooleanVMFlag, this::getIntxVMFlag, this::getUintxVMFlag, - this::getUint64VMFlag, this::getStringVMFlag, this::getDoubleVMFlag); + this::getUint64VMFlag, this::getSizeTVMFlag, this::getStringVMFlag, + this::getDoubleVMFlag); public Object getVMFlag(String name) { return flagsGetters.stream() diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/BooleanTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/BooleanTest.java index a76fd4d9a22..ae068f8867a 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/BooleanTest.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/BooleanTest.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build BooleanTest ClassFileInstaller sun.hotspot.WhiteBox com.oracle.java.testlibrary.* * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI BooleanTest * @summary testing of WB::set/getBooleanVMFlag() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/DoubleTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/DoubleTest.java index 2110fb9d7a7..0b320e7b21b 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/DoubleTest.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/DoubleTest.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build DoubleTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI DoubleTest * @summary testing of WB::set/getDoubleVMFlag() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/IntxTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/IntxTest.java index 6ffbd832611..3b957eff084 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/IntxTest.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/IntxTest.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build IntxTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI IntxTest * @summary testing of WB::set/getIntxVMFlag() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/SizeTTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/SizeTTest.java new file mode 100644 index 00000000000..51559cfbb53 --- /dev/null +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/SizeTTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test SizeTTest + * @bug 8054823 + * @library /testlibrary /testlibrary/whitebox + * @build SizeTTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions SizeTTest + * @summary testing of WB::set/getSizeTVMFlag() + */ +import com.oracle.java.testlibrary.Platform; + +public class SizeTTest { + private static final String FLAG_NAME = "ArrayAllocatorMallocLimit"; + private static final Long[] TESTS = {0L, 100L, (long) Integer.MAX_VALUE, + (1L << 32L) - 1L, 1L << 32L}; + private static final Long[] EXPECTED_64 = TESTS; + private static final Long[] EXPECTED_32 = {0L, 100L, + (long) Integer.MAX_VALUE, (1L << 32L) - 1L, 0L}; + + public static void main(String[] args) throws Exception { + VmFlagTest.runTest(FLAG_NAME, TESTS, + Platform.is64bit() ? EXPECTED_64 : EXPECTED_32, + VmFlagTest.WHITE_BOX::setSizeTVMFlag, + VmFlagTest.WHITE_BOX::getSizeTVMFlag); + } +} diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/StringTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/StringTest.java index 70aa0664c54..77a02d3d2d4 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/StringTest.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/StringTest.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build StringTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI StringTest * @summary testing of WB::set/getStringVMFlag() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/Uint64Test.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/Uint64Test.java index 2f616d45507..fd3a82c41e8 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/Uint64Test.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/Uint64Test.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build Uint64Test * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI Uint64Test * @summary testing of WB::set/getUint64VMFlag() * @author igor.ignatyev@oracle.com diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/UintxTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/UintxTest.java index 7141221f204..40eb85f868f 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/UintxTest.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/UintxTest.java @@ -27,6 +27,7 @@ * @library /testlibrary /testlibrary/whitebox * @build UintxTest * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI UintxTest * @summary testing of WB::set/getUintxVMFlag() * @author igor.ignatyev@oracle.com diff --git a/jaxp/.hgignore b/jaxp/.hgignore index 970fc82946b..195f7dbfed1 100644 --- a/jaxp/.hgignore +++ b/jaxp/.hgignore @@ -2,6 +2,6 @@ ^dist/ ^drop/ ^drop_included/ -^webrev/ +^webrev /nbproject/private/ ^.hgtip diff --git a/jaxp/.hgtags b/jaxp/.hgtags index 051c0879c7b..ea01e6ee220 100644 --- a/jaxp/.hgtags +++ b/jaxp/.hgtags @@ -269,3 +269,7 @@ f9c82769a6bc2b219a8f01c24afe5c91039267d7 jdk9-b19 345af113f57206711f75089c3ebf84a36a789122 jdk9-b24 73757f4b8aa353ca8937abc07e79e7ece4c011a1 jdk9-b25 a5aea8318ae4a9c2105228568688875142d70344 jdk9-b26 +2bfaf29cc90b19948938e3ef1a0983eee68806c7 jdk9-b27 +dc1e26434b3fd7e9b8eeab149103c1e30965f95c jdk9-b28 +30adcd13a313ea91e81164801a2f89282756d933 jdk9-b29 +d181d4002214e4914d5525bd5ee13369311c765c jdk9-b30 diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/Constants.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/Constants.java index 4e276fb6ad6..3a84797138b 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/Constants.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/Constants.java @@ -1,13 +1,10 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. - */ - -/* - * Copyright 2005 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -41,6 +38,9 @@ public final class Constants { public static final String NS_XMLSCHEMA = "http://www.w3.org/2001/XMLSchema".intern(); public static final String NS_DTD = "http://www.w3.org/TR/REC-xml".intern(); + // Schema Versions: + public static final String W3C_XML_SCHEMA10_NS_URI = "http://www.w3.org/XML/XMLSchema/v1.0".intern(); + // Schema features public static final String SUN_SCHEMA_FEATURE_PREFIX = "http://java.sun.com/xml/schema/features/"; public static final String SUN_REPORT_IGNORED_ELEMENT_CONTENT_WHITESPACE = "report-ignored-element-content-whitespace"; @@ -487,6 +487,40 @@ public final class Constants { /** XInclude fixup language feature ("xinclude/fixup-language"). */ public static final String XINCLUDE_FIXUP_LANGUAGE_FEATURE = "xinclude/fixup-language"; + /** + * Feature to ignore xsi:type attributes on elements during validation, + * until a global element declaration is found. ("validation/schema/ignore-xsi-type-until-elemdecl") + * If this feature is on when validating a document, then beginning at the validation root + * element, xsi:type attributes are ignored until a global element declaration is + * found for an element. Once a global element declaration has been found, xsi:type + * attributes will start being processed for the sub-tree beginning at the element for + * which the declaration was found. + * + * Suppose an element A has two element children, B and C. + * + * If a global element declaration is found for A, xsi:type attributes on A, B and C, + * and all of B and C's descendents, will be processed. + * + * If no global element declaration is found for A or B, but one is found for C, + * then xsi:type attributes will be ignored on A and B (and any descendents of B, + * until a global element declaration is found), but xsi:type attributes will be + * processed for C and all of C's descendents. + * + * Once xsi:type attributes stop being ignored for a subtree, they do not start + * being ignored again, even if more elements are encountered for which no global + * element declaration can be found. + */ + public static final String IGNORE_XSI_TYPE_FEATURE = "validation/schema/ignore-xsi-type-until-elemdecl"; + + /** Perform checking of ID/IDREFs ("validation/id-idref-checking") */ + public static final String ID_IDREF_CHECKING_FEATURE = "validation/id-idref-checking"; + + /** Feature to ignore errors caused by identity constraints ("validation/identity-constraint-checking") */ + public static final String IDC_CHECKING_FEATURE = "validation/identity-constraint-checking"; + + /** Feature to ignore errors caused by unparsed entities ("validation/unparsed-entity-checking") */ + public static final String UNPARSED_ENTITY_CHECKING_FEATURE = "validation/unparsed-entity-checking"; + /** * Internal feature. When set to true the schema validator will only use * schema components from the grammar pool provided. @@ -598,6 +632,8 @@ public final class Constants { /** Validation manager property ("internal/validation-manager"). */ public static final String VALIDATION_MANAGER_PROPERTY = "internal/validation-manager"; + /** Schema type of the root element in a document ("validation/schema/root-type-definition"). */ + public static final String ROOT_TYPE_DEFINITION_PROPERTY = "validation/schema/root-type-definition"; /** XPointer Schema property ("xpointer-schema"). */ public static final String XPOINTER_SCHEMA_PROPERTY = "xpointer-schema"; @@ -739,6 +775,10 @@ public final class Constants { XINCLUDE_FEATURE, XINCLUDE_FIXUP_BASE_URIS_FEATURE, XINCLUDE_FIXUP_LANGUAGE_FEATURE, + IGNORE_XSI_TYPE_FEATURE, + ID_IDREF_CHECKING_FEATURE, + IDC_CHECKING_FEATURE, + UNPARSED_ENTITY_CHECKING_FEATURE, NAMESPACE_GROWTH_FEATURE, TOLERATE_DUPLICATES_FEATURE, }; @@ -762,6 +802,7 @@ public final class Constants { VALIDATION_MANAGER_PROPERTY, BUFFER_SIZE_PROPERTY, SECURITY_MANAGER_PROPERTY, + ROOT_TYPE_DEFINITION_PROPERTY, LOCALE_PROPERTY, SCHEMA_DV_FACTORY_PROPERTY, }; diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLScanner.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLScanner.java index 28fbc69270e..58700e2934b 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLScanner.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLScanner.java @@ -973,6 +973,7 @@ public abstract class XMLScanner fStringBuffer2.append('\n'); } } else if (c != -1 && XMLChar.isHighSurrogate(c)) { + fStringBuffer3.clear(); if (scanSurrogates(fStringBuffer3)) { stringBuffer.append(fStringBuffer3); if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) { diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/ValidatedInfo.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/ValidatedInfo.java index e6b8f267a5d..cc84353bd64 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/ValidatedInfo.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/dv/ValidatedInfo.java @@ -1,13 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -/* - * Copyright 2001, 2002,2004 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -21,6 +18,7 @@ package com.sun.org.apache.xerces.internal.impl.dv; import com.sun.org.apache.xerces.internal.xs.ShortList; +import com.sun.org.apache.xerces.internal.xs.XSConstants; /** * Class to get the information back after content is validated. This info @@ -95,4 +93,60 @@ public class ValidatedInfo { else return actualValue.toString(); } + + /** + * Returns true if the two ValidatedInfo objects can be compared in the same + * value space. + */ + public static boolean isComparable(ValidatedInfo info1, ValidatedInfo info2) { + final short primitiveType1 = convertToPrimitiveKind(info1.actualValueType); + final short primitiveType2 = convertToPrimitiveKind(info2.actualValueType); + if (primitiveType1 != primitiveType2) { + return (primitiveType1 == XSConstants.ANYSIMPLETYPE_DT && primitiveType2 == XSConstants.STRING_DT || + primitiveType1 == XSConstants.STRING_DT && primitiveType2 == XSConstants.ANYSIMPLETYPE_DT); + } + else if (primitiveType1 == XSConstants.LIST_DT || primitiveType1 == XSConstants.LISTOFUNION_DT) { + final ShortList typeList1 = info1.itemValueTypes; + final ShortList typeList2 = info2.itemValueTypes; + final int typeList1Length = typeList1 != null ? typeList1.getLength() : 0; + final int typeList2Length = typeList2 != null ? typeList2.getLength() : 0; + if (typeList1Length != typeList2Length) { + return false; + } + for (int i = 0; i < typeList1Length; ++i) { + final short primitiveItem1 = convertToPrimitiveKind(typeList1.item(i)); + final short primitiveItem2 = convertToPrimitiveKind(typeList2.item(i)); + if (primitiveItem1 != primitiveItem2) { + if (primitiveItem1 == XSConstants.ANYSIMPLETYPE_DT && primitiveItem2 == XSConstants.STRING_DT || + primitiveItem1 == XSConstants.STRING_DT && primitiveItem2 == XSConstants.ANYSIMPLETYPE_DT) { + continue; + } + return false; + } + } + } + return true; + } + + /** + * Returns the primitive type of the given type. + * @param valueType A value type as defined in XSConstants. + * @return The primitive type from which valueType was derived. + */ + private static short convertToPrimitiveKind(short valueType) { + /** Primitive datatypes. */ + if (valueType <= XSConstants.NOTATION_DT) { + return valueType; + } + /** Types derived from string. */ + if (valueType <= XSConstants.ENTITY_DT) { + return XSConstants.STRING_DT; + } + /** Types derived from decimal. */ + if (valueType <= XSConstants.POSITIVEINTEGER_DT) { + return XSConstants.DECIMAL_DT; + } + /** Other types. */ + return valueType; + } } diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties index d986140a86e..5b73376eb50 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLSchemaMessages.properties @@ -14,20 +14,18 @@ # Identity constraints - AbsentKeyValue = Identity Constraint error (cvc-identity-constraint.4.2.1): element \"{0}\" has a key with no value. + AbsentKeyValue = cvc-identity-constraint.4.2.1.a: Element \"{0}\" has no value for the key \"{1}\". DuplicateField = Duplicate match in scope for field \"{0}\". - DuplicateKey = Duplicate key value [{0}] declared for identity constraint of element \"{1}\". - DuplicateUnique = Duplicate unique value [{0}] declared for identity constraint of element \"{1}\". - FieldMultipleMatch = Identity constraint error: field \"{0}\" matches more than one value within the scope of its selector; fields must match unique values. + DuplicateKey = cvc-identity-constraint.4.2.2: Duplicate key value [{0}] declared for identity constraint \"{2}\" of element \"{1}\". + DuplicateUnique = cvc-identity-constraint.4.1: Duplicate unique value [{0}] declared for identity constraint \"{2}\" of element \"{1}\". + FieldMultipleMatch = cvc-identity-constraint.3: Field \"{0}\" of identity constraint \"{1}\" matches more than one value within the scope of its selector; fields must match unique values. FixedDiffersFromActual = The content of this element is not equivalent to the value of the \"fixed\" attribute in the element's declaration in the schema. - KeyMatchesNillable = Identity Constraint error (cvc-identity-constraint.4.2.3): element \"{0}\" has a key which matches an element which has nillable set to true. - KeyNotEnoughValues = Not enough values specified for identity constraint specified for element \"{0}\". - KeyNotFound = Key ''{0}'' with value ''{1}'' not found for identity constraint of element ''{2}''. - KeyRefNotEnoughValues = Not enough values specified for identity constraint specified for element \"{0}\". + KeyMatchesNillable = cvc-identity-constraint.4.2.3: Element \"{0}\" has the key \"{1}\" which matches an element which has nillable set to true. + KeyNotEnoughValues = cvc-identity-constraint.4.2.1.b: Not enough values specified for identity constraint specified for element \"{0}\". + KeyNotFound = cvc-identity-constraint.4.3: Key ''{0}'' with value ''{1}'' not found for identity constraint of element ''{2}''. KeyRefOutOfScope = Identity Constraint error: identity constraint \"{0}\" has a keyref which refers to a key or unique that is out of scope. KeyRefReferNotFound = Key reference declaration \"{0}\" refers to unknown key with name \"{1}\". - UniqueNotEnoughValues = Not enough values specified for identity constraint specified for element \"{0}\". - UnknownField = Internal identity constraint error; unknown field \"{0}\". + UnknownField = Internal identity constraint error; unknown field \"{0}\" for identity constraint \"{2}\" specified for element \"{1}\". # Ideally, we should only use the following error keys, not the ones under # "Identity constraints". And we should cover all of the following errors. @@ -79,6 +77,7 @@ cvc-minLength-valid = cvc-minLength-valid: Value ''{0}'' with length = ''{1}'' is not facet-valid with respect to minLength ''{2}'' for type ''{3}''. cvc-pattern-valid = cvc-pattern-valid: Value ''{0}'' is not facet-valid with respect to pattern ''{1}'' for type ''{2}''. cvc-totalDigits-valid = cvc-totalDigits-valid: Value ''{0}'' has {1} total digits, but the number of total digits has been limited to {2}. + cvc-type.1 = cvc-type.1: The type definition ''{0}'' was not found. cvc-type.2 = cvc-type.2: The type definition cannot be abstract for element {0}. cvc-type.3.1.1 = cvc-type.3.1.1: Element ''{0}'' is a simple type, so it cannot have attributes, excepting those whose namespace name is identical to ''http://www.w3.org/2001/XMLSchema-instance'' and whose [local name] is one of ''type'', ''nil'', ''schemaLocation'' or ''noNamespaceSchemaLocation''. However, the attribute, ''{1}'' was found. cvc-type.3.1.2 = cvc-type.3.1.2: Element ''{0}'' is a simple type, so it must have no element information item [children]. diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/validation/ConfigurableValidationState.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/validation/ConfigurableValidationState.java new file mode 100644 index 00000000000..ac8406dd325 --- /dev/null +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/validation/ConfigurableValidationState.java @@ -0,0 +1,123 @@ +/* + * Copyright 2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sun.org.apache.xerces.internal.impl.validation; + +/** + *

An extension of ValidationState which can be configured to turn + * off checking for ID/IDREF errors and unparsed entity errors.

+ * + * @xerces.internal + * + * @author Peter McCracken, IBM + */ +public final class ConfigurableValidationState extends ValidationState { + + /** + * Whether to check for ID/IDREF errors + */ + private boolean fIdIdrefChecking; + + /** + * Whether to check for unparsed entity errors + */ + private boolean fUnparsedEntityChecking; + + /** + * Creates a new ConfigurableValidationState. + * By default, error checking for both ID/IDREFs + * and unparsed entities are turned on. + */ + public ConfigurableValidationState() { + super(); + fIdIdrefChecking = true; + fUnparsedEntityChecking = true; + } + + /** + * Turns checking for ID/IDREF errors on and off. + * @param setting: true to turn on error checking + * false to turn off error checking + */ + public void setIdIdrefChecking(boolean setting) { + fIdIdrefChecking = setting; + } + + /** + * Turns checking for unparsed entity errors on and off. + * @param setting: true to turn on error checking + * false to turn off error checking + */ + public void setUnparsedEntityChecking(boolean setting) { + fUnparsedEntityChecking = setting; + } + + /** + * Checks if all IDREFs have a corresponding ID. + * @return null, if ID/IDREF checking is turned off + * otherwise, returns the value of the super implementation + */ + public String checkIDRefID() { + return (fIdIdrefChecking) ? super.checkIDRefID() : null; + } + + /** + * Checks if an ID has already been declared. + * @return false, if ID/IDREF checking is turned off + * otherwise, returns the value of the super implementation + */ + public boolean isIdDeclared(String name) { + return (fIdIdrefChecking) ? super.isIdDeclared(name) : false; + } + + /** + * Checks if an entity is declared. + * @return true, if unparsed entity checking is turned off + * otherwise, returns the value of the super implementation + */ + public boolean isEntityDeclared(String name) { + return (fUnparsedEntityChecking) ? super.isEntityDeclared(name) : true; + } + + /** + * Checks if an entity is unparsed. + * @return true, if unparsed entity checking is turned off + * otherwise, returns the value of the super implementation + */ + public boolean isEntityUnparsed(String name) { + return (fUnparsedEntityChecking) ? super.isEntityUnparsed(name) : true; + } + + /** + * Adds the ID, if ID/IDREF checking is enabled. + * @param the ID to add + */ + public void addId(String name) { + if (fIdIdrefChecking) { + super.addId(name); + } + } + + /** + * Adds the IDREF, if ID/IDREF checking is enabled. + * @param the IDREF to add + */ + public void addIdRef(String name) { + if (fIdIdrefChecking) { + super.addIdRef(name); + } + } +} diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaValidator.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaValidator.java index fd03b7a8f75..dbe788ac599 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaValidator.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/xs/XMLSchemaValidator.java @@ -1,13 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -/* - * Copyright 1999-2005 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -38,6 +35,7 @@ import com.sun.org.apache.xerces.internal.impl.dv.DatatypeException; import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException; import com.sun.org.apache.xerces.internal.impl.dv.ValidatedInfo; import com.sun.org.apache.xerces.internal.impl.dv.XSSimpleType; +import com.sun.org.apache.xerces.internal.impl.validation.ConfigurableValidationState; import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager; import com.sun.org.apache.xerces.internal.impl.validation.ValidationState; import com.sun.org.apache.xerces.internal.impl.xs.identity.Field; @@ -185,6 +183,22 @@ public class XMLSchemaValidator protected static final String TOLERATE_DUPLICATES = Constants.XERCES_FEATURE_PREFIX + Constants.TOLERATE_DUPLICATES_FEATURE; + /** Feature identifier: whether to ignore xsi:type attributes until a global element declaration is encountered */ + protected static final String IGNORE_XSI_TYPE = + Constants.XERCES_FEATURE_PREFIX + Constants.IGNORE_XSI_TYPE_FEATURE; + + /** Feature identifier: whether to ignore ID/IDREF errors */ + protected static final String ID_IDREF_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.ID_IDREF_CHECKING_FEATURE; + + /** Feature identifier: whether to ignore unparsed entity errors */ + protected static final String UNPARSED_ENTITY_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.UNPARSED_ENTITY_CHECKING_FEATURE; + + /** Feature identifier: whether to ignore identity constraint errors */ + protected static final String IDENTITY_CONSTRAINT_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.IDC_CHECKING_FEATURE; + protected static final String REPORT_WHITESPACE = Constants.SUN_SCHEMA_FEATURE_PREFIX + Constants.SUN_REPORT_IGNORED_ELEMENT_CONTENT_WHITESPACE; @@ -228,6 +242,10 @@ public class XMLSchemaValidator protected static final String JAXP_SCHEMA_LANGUAGE = Constants.JAXP_PROPERTY_PREFIX + Constants.SCHEMA_LANGUAGE; + /** Property identifier: root type definition. */ + protected static final String ROOT_TYPE_DEF = + Constants.XERCES_PROPERTY_PREFIX + Constants.ROOT_TYPE_DEFINITION_PROPERTY; + /** Property identifier: Schema DV Factory */ protected static final String SCHEMA_DV_FACTORY = Constants.XERCES_PROPERTY_PREFIX + Constants.SCHEMA_DV_FACTORY_PROPERTY; @@ -254,6 +272,10 @@ public class XMLSchemaValidator VALIDATE_ANNOTATIONS, HONOUR_ALL_SCHEMALOCATIONS, USE_GRAMMAR_POOL_ONLY, + IGNORE_XSI_TYPE, + ID_IDREF_CHECKING, + IDENTITY_CONSTRAINT_CHECKING, + UNPARSED_ENTITY_CHECKING, NAMESPACE_GROWTH, TOLERATE_DUPLICATES, USE_SERVICE_MECHANISM @@ -267,6 +289,10 @@ public class XMLSchemaValidator // those set by the application. This goes against the // whole purpose of XMLComponent#getFeatureDefault but // it can't be helped in this case. -Ac + // NOTE: Instead of adding default values here, add them (and + // the corresponding recognized features) to the objects + // that have an XMLSchemaValidator instance as a member, + // such as the parser configurations. -PM null, //Boolean.FALSE, null, //Boolean.FALSE, null, //Boolean.FALSE, @@ -279,6 +305,10 @@ public class XMLSchemaValidator null, null, null, + null, + null, + null, + null, Boolean.TRUE }; @@ -293,13 +323,14 @@ public class XMLSchemaValidator SCHEMA_NONS_LOCATION, JAXP_SCHEMA_SOURCE, JAXP_SCHEMA_LANGUAGE, + ROOT_TYPE_DEF, SCHEMA_DV_FACTORY, XML_SECURITY_PROPERTY_MANAGER }; /** Property defaults. */ private static final Object[] PROPERTY_DEFAULTS = - { null, null, null, null, null, null, null, null, null, null, null, null, null}; + { null, null, null, null, null, null, null, null, null, null, null}; // this is the number of valuestores of each kind // we expect an element to have. It's almost @@ -482,7 +513,7 @@ public class XMLSchemaValidator // updated during reset protected ValidationManager fValidationManager = null; - protected ValidationState fValidationState = new ValidationState(); + protected ConfigurableValidationState fValidationState = new ConfigurableValidationState(); protected XMLGrammarPool fGrammarPool; // schema location property values @@ -562,6 +593,9 @@ public class XMLSchemaValidator * this exception. */ public void setProperty(String propertyId, Object value) throws XMLConfigurationException { + if (propertyId.equals(ROOT_TYPE_DEF)) { + fRootTypeQName = (javax.xml.namespace.QName)value; + } } // setProperty(String,Object) /** @@ -1234,6 +1268,12 @@ public class XMLSchemaValidator /** temporary qname */ private final QName fTempQName = new QName(); + private javax.xml.namespace.QName fRootTypeQName = null; + + private int fIgnoreXSITypeDepth; + + private boolean fIDCChecking; + /** temporary validated info */ private ValidatedInfo fValidatedInfo = new ValidatedInfo(); @@ -1384,6 +1424,38 @@ public class XMLSchemaValidator fValidationManager.addValidationState(fValidationState); fValidationState.setSymbolTable(fSymbolTable); + boolean ignoreXSIType; + try { + ignoreXSIType = componentManager.getFeature(IGNORE_XSI_TYPE); + } + catch (XMLConfigurationException e) { + ignoreXSIType = false; + } + // An initial value of -1 means that the root element considers itself + // below the depth where xsi:type stopped being ignored (which means that + // xsi:type attributes will not be ignored for the entire document) + fIgnoreXSITypeDepth = ignoreXSIType ? 0 : -1; + + try { + fIDCChecking = componentManager.getFeature(IDENTITY_CONSTRAINT_CHECKING); + } + catch (XMLConfigurationException e) { + fIDCChecking = true; + } + + try { + fValidationState.setIdIdrefChecking(componentManager.getFeature(ID_IDREF_CHECKING)); + } + catch (XMLConfigurationException e) { + fValidationState.setIdIdrefChecking(true); + } + + try { + fValidationState.setUnparsedEntityChecking(componentManager.getFeature(UNPARSED_ENTITY_CHECKING)); + } + catch (XMLConfigurationException e) { + fValidationState.setUnparsedEntityChecking(true); + } // get schema location properties try { @@ -1550,7 +1622,9 @@ public class XMLSchemaValidator // handle start document void handleStartDocument(XMLLocator locator, String encoding) { - fValueStoreCache.startDocument(); + if (fIDCChecking) { + fValueStoreCache.startDocument(); + } if (fAugPSVI) { fCurrentPSVI.fGrammars = null; fCurrentPSVI.fSchemaInformation = null; @@ -1558,7 +1632,9 @@ public class XMLSchemaValidator } // handleStartDocument(XMLLocator,String) void handleEndDocument() { - fValueStoreCache.endDocument(); + if (fIDCChecking) { + fValueStoreCache.endDocument(); + } } // handleEndDocument() // handle character contents @@ -1850,22 +1926,54 @@ public class XMLSchemaValidator return augs; } - // try again to get the element decl: - // case 1: find declaration for root element - // case 2: find declaration for element from another namespace - if (fCurrentElemDecl == null) { - if (sGrammar != null) { - fCurrentElemDecl = sGrammar.getGlobalElementDecl(element.localpart); + // 1.2.1.1 A type definition was stipulated by the processor + if (fElementDepth == 0 && fRootTypeQName != null) { + String rootTypeNamespace = fRootTypeQName.getNamespaceURI(); + if (rootTypeNamespace != null && rootTypeNamespace.equals(XMLConstants.NULL_NS_URI)) { + rootTypeNamespace = null; + } + SchemaGrammar grammarForRootType = + findSchemaGrammar( + XSDDescription.CONTEXT_ELEMENT, rootTypeNamespace, null, null, null); + if (grammarForRootType != null) { + fCurrentType = grammarForRootType.getGlobalTypeDecl(fRootTypeQName.getLocalPart()); + } + if (fCurrentType == null) { + String typeName = (fRootTypeQName.getPrefix().equals(XMLConstants.DEFAULT_NS_PREFIX)) ? + fRootTypeQName.getLocalPart() : + fRootTypeQName.getPrefix()+":"+fRootTypeQName.getLocalPart(); + reportSchemaError("cvc-type.1", new Object[] {typeName}); } } - if (fCurrentElemDecl != null) { - // then get the type - fCurrentType = fCurrentElemDecl.fType; + // if there was no processor stipulated type + if (fCurrentType == null) { + // try again to get the element decl: + // case 1: find declaration for root element + // case 2: find declaration for element from another namespace + if (fCurrentElemDecl == null) { + if (sGrammar != null) { + fCurrentElemDecl = sGrammar.getGlobalElementDecl(element.localpart); + } + } + + if (fCurrentElemDecl != null) { + // then get the type + fCurrentType = fCurrentElemDecl.fType; + } } - // get type from xsi:type - String xsiType = attributes.getValue(SchemaSymbols.URI_XSI, SchemaSymbols.XSI_TYPE); + + // check if we should be ignoring xsi:type on this element + if (fElementDepth == fIgnoreXSITypeDepth && fCurrentElemDecl == null) { + fIgnoreXSITypeDepth++; + } + + // process xsi:type attribute information + String xsiType = null; + if (fElementDepth >= fIgnoreXSITypeDepth) { + xsiType = attributes.getValue(SchemaSymbols.URI_XSI, SchemaSymbols.XSI_TYPE); + } // if no decl/type found for the current element if (fCurrentType == null && xsiType == null) { @@ -2044,13 +2152,17 @@ public class XMLSchemaValidator XSComplexTypeDecl ctype = (XSComplexTypeDecl) fCurrentType; attrGrp = ctype.getAttrGrp(); } - // activate identity constraints - fValueStoreCache.startElement(); - fMatcherStack.pushContext(); - if (fCurrentElemDecl != null && fCurrentElemDecl.fIDCPos > 0) { - fIdConstraint = true; - // initialize when identity constrains are defined for the elem - fValueStoreCache.initValueStoresFor(fCurrentElemDecl, this); + + if (fIDCChecking) { + // activate identity constraints + fValueStoreCache.startElement(); + fMatcherStack.pushContext(); + //if (fCurrentElemDecl != null && fCurrentElemDecl.fIDCPos > 0 && !fIgnoreIDC) { + if (fCurrentElemDecl != null && fCurrentElemDecl.fIDCPos > 0) { + fIdConstraint = true; + // initialize when identity constrains are defined for the elem + fValueStoreCache.initValueStoresFor(fCurrentElemDecl, this); + } } processAttributes(element, attributes, attrGrp); @@ -2138,66 +2250,75 @@ public class XMLSchemaValidator // now validate the content of the element processElementContent(element); - // Element Locally Valid (Element) - // 6 The element information item must be valid with respect to each of the {identity-constraint definitions} as per Identity-constraint Satisfied (3.11.4). + if (fIDCChecking) { + // Element Locally Valid (Element) + // 6 The element information item must be valid with respect to each of the {identity-constraint definitions} as per Identity-constraint Satisfied (3.11.4). - // call matchers and de-activate context - int oldCount = fMatcherStack.getMatcherCount(); - for (int i = oldCount - 1; i >= 0; i--) { - XPathMatcher matcher = fMatcherStack.getMatcherAt(i); - if (fCurrentElemDecl == null) - matcher.endElement(element, null, false, fValidatedInfo.actualValue, fValidatedInfo.actualValueType, fValidatedInfo.itemValueTypes); + // call matchers and de-activate context + int oldCount = fMatcherStack.getMatcherCount(); + for (int i = oldCount - 1; i >= 0; i--) { + XPathMatcher matcher = fMatcherStack.getMatcherAt(i); + if (fCurrentElemDecl == null) + matcher.endElement(element, null, false, fValidatedInfo.actualValue, fValidatedInfo.actualValueType, fValidatedInfo.itemValueTypes); - else - matcher.endElement( - element, - fCurrentType, - fCurrentElemDecl.getNillable(), - fDefaultValue == null - ? fValidatedInfo.actualValue - : fCurrentElemDecl.fDefault.actualValue, - fDefaultValue == null - ? fValidatedInfo.actualValueType - : fCurrentElemDecl.fDefault.actualValueType, - fDefaultValue == null - ? fValidatedInfo.itemValueTypes - : fCurrentElemDecl.fDefault.itemValueTypes); - } + else + matcher.endElement( + element, + fCurrentType, + fCurrentElemDecl.getNillable(), + fDefaultValue == null + ? fValidatedInfo.actualValue + : fCurrentElemDecl.fDefault.actualValue, + fDefaultValue == null + ? fValidatedInfo.actualValueType + : fCurrentElemDecl.fDefault.actualValueType, + fDefaultValue == null + ? fValidatedInfo.itemValueTypes + : fCurrentElemDecl.fDefault.itemValueTypes); + } - if (fMatcherStack.size() > 0) { - fMatcherStack.popContext(); - } + if (fMatcherStack.size() > 0) { + fMatcherStack.popContext(); + } - int newCount = fMatcherStack.getMatcherCount(); - // handle everything *but* keyref's. - for (int i = oldCount - 1; i >= newCount; i--) { - XPathMatcher matcher = fMatcherStack.getMatcherAt(i); - if (matcher instanceof Selector.Matcher) { - Selector.Matcher selMatcher = (Selector.Matcher) matcher; - IdentityConstraint id; - if ((id = selMatcher.getIdentityConstraint()) != null - && id.getCategory() != IdentityConstraint.IC_KEYREF) { - fValueStoreCache.transplant(id, selMatcher.getInitialDepth()); + int newCount = fMatcherStack.getMatcherCount(); + // handle everything *but* keyref's. + for (int i = oldCount - 1; i >= newCount; i--) { + XPathMatcher matcher = fMatcherStack.getMatcherAt(i); + if (matcher instanceof Selector.Matcher) { + Selector.Matcher selMatcher = (Selector.Matcher) matcher; + IdentityConstraint id; + if ((id = selMatcher.getIdentityConstraint()) != null + && id.getCategory() != IdentityConstraint.IC_KEYREF) { + fValueStoreCache.transplant(id, selMatcher.getInitialDepth()); + } } } - } - // now handle keyref's/... - for (int i = oldCount - 1; i >= newCount; i--) { - XPathMatcher matcher = fMatcherStack.getMatcherAt(i); - if (matcher instanceof Selector.Matcher) { - Selector.Matcher selMatcher = (Selector.Matcher) matcher; - IdentityConstraint id; - if ((id = selMatcher.getIdentityConstraint()) != null - && id.getCategory() == IdentityConstraint.IC_KEYREF) { - ValueStoreBase values = - fValueStoreCache.getValueStoreFor(id, selMatcher.getInitialDepth()); - if (values != null) // nothing to do if nothing matched! - values.endDocumentFragment(); + // now handle keyref's/... + for (int i = oldCount - 1; i >= newCount; i--) { + XPathMatcher matcher = fMatcherStack.getMatcherAt(i); + if (matcher instanceof Selector.Matcher) { + Selector.Matcher selMatcher = (Selector.Matcher) matcher; + IdentityConstraint id; + if ((id = selMatcher.getIdentityConstraint()) != null + && id.getCategory() == IdentityConstraint.IC_KEYREF) { + ValueStoreBase values = + fValueStoreCache.getValueStoreFor(id, selMatcher.getInitialDepth()); + if (values != null) // nothing to do if nothing matched! + values.endDocumentFragment(); + } } } + fValueStoreCache.endElement(); + } + + // Check if we should modify the xsi:type ignore depth + // This check is independent of whether this is the validation root, + // and should be done before the element depth is decremented. + if (fElementDepth < fIgnoreXSITypeDepth) { + fIgnoreXSITypeDepth--; } - fValueStoreCache.endElement(); SchemaGrammar[] grammars = null; // have we reached the end tag of the validation root? @@ -2290,7 +2411,14 @@ public class XMLSchemaValidator // otherwise partial, and anything above this level will be partial else { fCurrentPSVI.fValidationAttempted = ElementPSVI.VALIDATION_PARTIAL; - fNFullValidationDepth = fNNoneValidationDepth = fElementDepth - 1; + } + + // this guarantees that depth settings do not cross-over between sibling nodes + if (fNFullValidationDepth == fElementDepth) { + fNFullValidationDepth = fElementDepth - 1; + } + if (fNNoneValidationDepth == fElementDepth) { + fNNoneValidationDepth = fElementDepth - 1; } if (fDefaultValue != null) @@ -2554,14 +2682,22 @@ public class XMLSchemaValidator // if there is no current type, set this one as current. // and we don't need to do extra checking if (fCurrentType != null) { + short block = XSConstants.DERIVATION_NONE; // 4.3 The local type definition must be validly derived from the {type definition} given the union of the {disallowed substitutions} and the {type definition}'s {prohibited substitutions}, as defined in Type Derivation OK (Complex) (3.4.6) (if it is a complex type definition), or given {disallowed substitutions} as defined in Type Derivation OK (Simple) (3.14.6) (if it is a simple type definition). - short block = fCurrentElemDecl.fBlock; - if (fCurrentType.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) + // Note: It's possible to have fCurrentType be non-null and fCurrentElemDecl + // be null, if the current type is set using the property "root-type-definition". + // In that case, we don't disallow any substitutions. -PM + if (fCurrentElemDecl != null) { + block = fCurrentElemDecl.fBlock; + } + if (fCurrentType.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { block |= ((XSComplexTypeDecl) fCurrentType).fBlock; - if (!XSConstraints.checkTypeDerivationOk(type, fCurrentType, block)) + } + if (!XSConstraints.checkTypeDerivationOk(type, fCurrentType, block)) { reportSchemaError( - "cvc-elt.4.3", - new Object[] { element.rawname, xsiType, fCurrentType.getName()}); + "cvc-elt.4.3", + new Object[] { element.rawname, xsiType, fCurrentType.getName()}); + } } return type; @@ -2835,9 +2971,10 @@ public class XMLSchemaValidator } // get the value constraint from use or decl - // 4 The item's actual value must match the value of the {value constraint}, if it is present and fixed. // now check the value against the simpleType + // 4 The item's actual value must match the value of the {value constraint}, if it is present and fixed. + // now check the value against the simpleType if (actualValue != null && currDecl.getConstraintType() == XSConstants.VC_FIXED) { - if (!isComparable(fValidatedInfo, currDecl.fDefault) || !actualValue.equals(currDecl.fDefault.actualValue)) { + if (!ValidatedInfo.isComparable(fValidatedInfo, currDecl.fDefault) || !actualValue.equals(currDecl.fDefault.actualValue)) { reportSchemaError( "cvc-attribute.4", new Object[] { @@ -2852,7 +2989,7 @@ public class XMLSchemaValidator if (actualValue != null && currUse != null && currUse.fConstraintType == XSConstants.VC_FIXED) { - if (!isComparable(fValidatedInfo, currUse.fDefault) || !actualValue.equals(currUse.fDefault.actualValue)) { + if (!ValidatedInfo.isComparable(fValidatedInfo, currUse.fDefault) || !actualValue.equals(currUse.fDefault.actualValue)) { reportSchemaError( "cvc-complex-type.3.1", new Object[] { @@ -3083,7 +3220,7 @@ public class XMLSchemaValidator } // 5.2.2.2.2 If the {content type} of the actual type definition is a simple type definition, then the actual value of the item must match the canonical lexical representation of the {value constraint} value. else if (ctype.fContentType == XSComplexTypeDecl.CONTENTTYPE_SIMPLE) { - if (actualValue != null && (!isComparable(fValidatedInfo, fCurrentElemDecl.fDefault) + if (actualValue != null && (!ValidatedInfo.isComparable(fValidatedInfo, fCurrentElemDecl.fDefault) || !actualValue.equals(fCurrentElemDecl.fDefault.actualValue))) { reportSchemaError( "cvc-elt.5.2.2.2.2", @@ -3094,7 +3231,7 @@ public class XMLSchemaValidator } } } else if (fCurrentType.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { - if (actualValue != null && (!isComparable(fValidatedInfo, fCurrentElemDecl.fDefault) + if (actualValue != null && (!ValidatedInfo.isComparable(fValidatedInfo, fCurrentElemDecl.fDefault) || !actualValue.equals(fCurrentElemDecl.fDefault.actualValue))) { // REVISIT: the spec didn't mention this case: fixed // value with simple type @@ -3240,53 +3377,6 @@ public class XMLSchemaValidator XMLErrorReporter.SEVERITY_ERROR); } - /** Returns true if the two ValidatedInfo objects can be compared in the same value space. **/ - private boolean isComparable(ValidatedInfo info1, ValidatedInfo info2) { - final short primitiveType1 = convertToPrimitiveKind(info1.actualValueType); - final short primitiveType2 = convertToPrimitiveKind(info2.actualValueType); - if (primitiveType1 != primitiveType2) { - return (primitiveType1 == XSConstants.ANYSIMPLETYPE_DT && primitiveType2 == XSConstants.STRING_DT || - primitiveType1 == XSConstants.STRING_DT && primitiveType2 == XSConstants.ANYSIMPLETYPE_DT); - } - else if (primitiveType1 == XSConstants.LIST_DT || primitiveType1 == XSConstants.LISTOFUNION_DT) { - final ShortList typeList1 = info1.itemValueTypes; - final ShortList typeList2 = info2.itemValueTypes; - final int typeList1Length = typeList1 != null ? typeList1.getLength() : 0; - final int typeList2Length = typeList2 != null ? typeList2.getLength() : 0; - if (typeList1Length != typeList2Length) { - return false; - } - for (int i = 0; i < typeList1Length; ++i) { - final short primitiveItem1 = convertToPrimitiveKind(typeList1.item(i)); - final short primitiveItem2 = convertToPrimitiveKind(typeList2.item(i)); - if (primitiveItem1 != primitiveItem2) { - if (primitiveItem1 == XSConstants.ANYSIMPLETYPE_DT && primitiveItem2 == XSConstants.STRING_DT || - primitiveItem1 == XSConstants.STRING_DT && primitiveItem2 == XSConstants.ANYSIMPLETYPE_DT) { - continue; - } - return false; - } - } - } - return true; - } - - private short convertToPrimitiveKind(short valueType) { - /** Primitive datatypes. */ - if (valueType <= XSConstants.NOTATION_DT) { - return valueType; - } - /** Types derived from string. */ - if (valueType <= XSConstants.ENTITY_DT) { - return XSConstants.STRING_DT; - } - /** Types derived from decimal. */ - if (valueType <= XSConstants.POSITIVEINTEGER_DT) { - return XSConstants.DECIMAL_DT; - } - /** Other types. */ - return valueType; - } private String expectedStr(Vector expected) { StringBuffer ret = new StringBuffer("{"); diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchema.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchema.java index 070086aa98b..5b05a6d1c28 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchema.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchema.java @@ -1,13 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -/* - * Copyright 2005 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -32,11 +29,19 @@ final class XMLSchema extends AbstractXMLSchema { /** The grammar pool is immutable */ private final XMLGrammarPool fGrammarPool; + /** Whether to consider this schema to be fully composed */ + private final boolean fFullyComposed; + /** Constructor */ public XMLSchema(XMLGrammarPool grammarPool) { - fGrammarPool = grammarPool; + this(grammarPool, true); } + public XMLSchema(XMLGrammarPool grammarPool, boolean fullyComposed) { + fGrammarPool = grammarPool; + fFullyComposed = fullyComposed; + } + /* * XSGrammarPoolContainer methods */ @@ -60,7 +65,7 @@ final class XMLSchema extends AbstractXMLSchema { * can be considered to be a fully composed schema */ public boolean isFullyComposed() { - return true; + return fFullyComposed; } } // XMLSchema diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java index a453dbf4227..3542750efaf 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaFactory.java @@ -1,13 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -/* - * Copyright 2005 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -68,12 +65,21 @@ import org.xml.sax.SAXParseException; */ public final class XMLSchemaFactory extends SchemaFactory { - // property identifiers + // feature identifiers + + /** JAXP Source feature prefix. */ + private static final String JAXP_SOURCE_FEATURE_PREFIX = "http://javax.xml.transform"; /** Feature identifier: schema full checking. */ private static final String SCHEMA_FULL_CHECKING = Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_FULL_CHECKING; + /** Feature identifier: use grammar pool only. */ + private static final String USE_GRAMMAR_POOL_ONLY = + Constants.XERCES_FEATURE_PREFIX + Constants.USE_GRAMMAR_POOL_ONLY_FEATURE; + + // property identifiers + /** Property identifier: grammar pool. */ private static final String XMLGRAMMAR_POOL = Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY; @@ -104,7 +110,7 @@ public final class XMLSchemaFactory extends SchemaFactory { private final DOMEntityResolverWrapper fDOMEntityResolverWrapper; /** The ErrorHandlerWrapper */ - private ErrorHandlerWrapper fErrorHandlerWrapper; + private final ErrorHandlerWrapper fErrorHandlerWrapper; /** The SecurityManager. */ private XMLSecurityManager fSecurityManager; @@ -113,7 +119,10 @@ public final class XMLSchemaFactory extends SchemaFactory { private XMLSecurityPropertyManager fSecurityPropertyMgr; /** The container for the real grammar pool. */ - private XMLGrammarPoolWrapper fXMLGrammarPoolWrapper; + private final XMLGrammarPoolWrapper fXMLGrammarPoolWrapper; + + /** Whether or not to allow new schemas to be added to the grammar pool */ + private boolean fUseGrammarPoolOnly; /** * Indicates whether implementation parts should use @@ -138,6 +147,7 @@ public final class XMLSchemaFactory extends SchemaFactory { fXMLSchemaLoader.setProperty(XMLGRAMMAR_POOL, fXMLGrammarPoolWrapper); fXMLSchemaLoader.setEntityResolver(fDOMEntityResolverWrapper); fXMLSchemaLoader.setErrorHandler(fErrorHandlerWrapper); + fUseGrammarPoolOnly = true; // Enable secure processing feature by default fSecurityManager = new XMLSecurityManager(true); @@ -170,7 +180,8 @@ public final class XMLSchemaFactory extends SchemaFactory { "SchemaLanguageLengthZero", null)); } // only W3C XML Schema 1.0 is supported - return schemaLanguage.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI); + return schemaLanguage.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI) || + schemaLanguage.equals(Constants.W3C_XML_SCHEMA10_NS_URI); } public LSResourceResolver getResourceResolver() { @@ -202,7 +213,7 @@ public final class XMLSchemaFactory extends SchemaFactory { XMLInputSource[] xmlInputSources = new XMLInputSource[schemas.length]; InputStream inputStream; Reader reader; - for( int i=0; i 1) { - schema = new XMLSchema(new ReadOnlyGrammarPool(pool)); - } - else if (grammarCount == 1) { - Grammar[] grammars = pool.retrieveInitialGrammarSet(XMLGrammarDescription.XML_SCHEMA); - schema = new SimpleXMLSchema(grammars[0]); + if (fUseGrammarPoolOnly) { + if (grammarCount > 1) { + schema = new XMLSchema(new ReadOnlyGrammarPool(pool)); + } + else if (grammarCount == 1) { + Grammar[] grammars = pool.retrieveInitialGrammarSet(XMLGrammarDescription.XML_SCHEMA); + schema = new SimpleXMLSchema(grammars[0]); + } + else { + schema = new EmptyXMLSchema(); + } } else { - schema = new EmptyXMLSchema(); + schema = new XMLSchema(new ReadOnlyGrammarPool(pool), false); } propagateFeatures(schema); propagateProperties(schema); @@ -286,6 +305,14 @@ public final class XMLSchemaFactory extends SchemaFactory { } public Schema newSchema() throws SAXException { + /* + * It would make sense to return an EmptyXMLSchema object here, if + * fUseGrammarPoolOnly is set to true. However, because the default + * value of this feature is true, doing so would change the default + * behaviour of this method. Thus, we return a WeakReferenceXMLSchema + * regardless of the value of fUseGrammarPoolOnly. -PM + */ + // Use a Schema that uses the system id as the equality source. AbstractXMLSchema schema = new WeakReferenceXMLSchema(); propagateFeatures(schema); @@ -293,15 +320,38 @@ public final class XMLSchemaFactory extends SchemaFactory { return schema; } + public Schema newSchema(XMLGrammarPool pool) throws SAXException { + // If the "use-grammar-pool-only" feature is set to true + // prevent the application's grammar pool from being mutated + // by wrapping it in a ReadOnlyGrammarPool. + final AbstractXMLSchema schema = (fUseGrammarPoolOnly) ? + new XMLSchema(new ReadOnlyGrammarPool(pool)) : + new XMLSchema(pool, false); + propagateFeatures(schema); + return schema; + } + public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException { if (name == null) { throw new NullPointerException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(), "FeatureNameNull", null)); } + if (name.startsWith(JAXP_SOURCE_FEATURE_PREFIX)) { + // Indicates to the caller that this SchemaFactory supports a specific JAXP Source. + if (name.equals(StreamSource.FEATURE) || + name.equals(SAXSource.FEATURE) || + name.equals(DOMSource.FEATURE) || + name.equals(StAXSource.FEATURE)) { + return true; + } + } if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) { return (fSecurityManager != null && fSecurityManager.isSecureProcessing()); } + else if (name.equals(USE_GRAMMAR_POOL_ONLY)) { + return fUseGrammarPoolOnly; + } try { return fXMLSchemaLoader.getFeature(name); } @@ -358,6 +408,16 @@ public final class XMLSchemaFactory extends SchemaFactory { throw new NullPointerException(JAXPValidationMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(), "FeatureNameNull", null)); } + if (name.startsWith(JAXP_SOURCE_FEATURE_PREFIX)) { + if (name.equals(StreamSource.FEATURE) || + name.equals(SAXSource.FEATURE) || + name.equals(DOMSource.FEATURE) || + name.equals(StAXSource.FEATURE)) { + throw new SAXNotSupportedException( + SAXMessageFormatter.formatMessage(fXMLSchemaLoader.getLocale(), + "feature-read-only", new Object [] {name})); + } + } if (name.equals(XMLConstants.FEATURE_SECURE_PROCESSING)) { if (System.getSecurityManager() != null && (!value)) { throw new SAXNotSupportedException( @@ -377,7 +437,12 @@ public final class XMLSchemaFactory extends SchemaFactory { fXMLSchemaLoader.setProperty(SECURITY_MANAGER, fSecurityManager); return; - } else if (name.equals(Constants.ORACLE_FEATURE_SERVICE_MECHANISM)) { + } + else if (name.equals(USE_GRAMMAR_POOL_ONLY)) { + fUseGrammarPoolOnly = value; + return; + } + else if (name.equals(Constants.ORACLE_FEATURE_SERVICE_MECHANISM)) { //in secure mode, let _useServicesMechanism be determined by the constructor if (System.getSecurityManager() != null) return; diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java index 58a74191cf0..807dc917908 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/jaxp/validation/XMLSchemaValidatorComponentManager.java @@ -1,13 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -/* - * Copyright 2005 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -69,13 +66,41 @@ final class XMLSchemaValidatorComponentManager extends ParserConfigurationSettin private static final String VALIDATION = Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE; + /** Feature identifier: use grammar pool only. */ + private static final String USE_GRAMMAR_POOL_ONLY = + Constants.XERCES_FEATURE_PREFIX + Constants.USE_GRAMMAR_POOL_ONLY_FEATURE; + + /** Feature identifier: whether to ignore xsi:type attributes until a global element declaration is encountered */ + protected static final String IGNORE_XSI_TYPE = + Constants.XERCES_FEATURE_PREFIX + Constants.IGNORE_XSI_TYPE_FEATURE; + + /** Feature identifier: whether to ignore ID/IDREF errors */ + protected static final String ID_IDREF_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.ID_IDREF_CHECKING_FEATURE; + + /** Feature identifier: whether to ignore unparsed entity errors */ + protected static final String UNPARSED_ENTITY_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.UNPARSED_ENTITY_CHECKING_FEATURE; + + /** Feature identifier: whether to ignore identity constraint errors */ + protected static final String IDENTITY_CONSTRAINT_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.IDC_CHECKING_FEATURE; + + /** Feature identifier: disallow DOCTYPE declaration */ + private static final String DISALLOW_DOCTYPE_DECL_FEATURE = + Constants.XERCES_FEATURE_PREFIX + Constants.DISALLOW_DOCTYPE_DECL_FEATURE; + + /** Feature identifier: expose schema normalized value */ + private static final String NORMALIZE_DATA = + Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_NORMALIZED_VALUE; + /** Feature identifier: send element default value via characters() */ private static final String SCHEMA_ELEMENT_DEFAULT = Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_ELEMENT_DEFAULT; - /** Feature identifier: use grammar pool only. */ - private static final String USE_GRAMMAR_POOL_ONLY = - Constants.XERCES_FEATURE_PREFIX + Constants.USE_GRAMMAR_POOL_ONLY_FEATURE; + /** Feature identifier: augment PSVI */ + private static final String SCHEMA_AUGMENT_PSVI = + Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_AUGMENT_PSVI; // property identifiers @@ -230,10 +255,32 @@ final class XMLSchemaValidatorComponentManager extends ParserConfigurationSettin fErrorReporter.putMessageFormatter(XSMessageFormatter.SCHEMA_DOMAIN, new XSMessageFormatter()); // add all recognized features and properties and apply their defaults + final String [] recognizedFeatures = { + DISALLOW_DOCTYPE_DECL_FEATURE, + NORMALIZE_DATA, + SCHEMA_ELEMENT_DEFAULT, + SCHEMA_AUGMENT_PSVI + }; + addRecognizedFeatures(recognizedFeatures); + fFeatures.put(DISALLOW_DOCTYPE_DECL_FEATURE, Boolean.FALSE); + fFeatures.put(NORMALIZE_DATA, Boolean.FALSE); + fFeatures.put(SCHEMA_ELEMENT_DEFAULT, Boolean.FALSE); + fFeatures.put(SCHEMA_AUGMENT_PSVI, Boolean.TRUE); + addRecognizedParamsAndSetDefaults(fEntityManager, grammarContainer); addRecognizedParamsAndSetDefaults(fErrorReporter, grammarContainer); addRecognizedParamsAndSetDefaults(fSchemaValidator, grammarContainer); + /* TODO: are other XMLSchemaValidator default values never set? + * Initial investigation indicates that they aren't set, but + * that they all have default values of false, so it works out + * anyway -PM + */ + fFeatures.put(IGNORE_XSI_TYPE, Boolean.FALSE); + fFeatures.put(ID_IDREF_CHECKING, Boolean.TRUE); + fFeatures.put(IDENTITY_CONSTRAINT_CHECKING, Boolean.TRUE); + fFeatures.put(UNPARSED_ENTITY_CHECKING, Boolean.TRUE); + boolean secureProcessing = grammarContainer.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING); if (System.getSecurityManager() != null) { _isSecureMode = true; diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/StandardParserConfiguration.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/StandardParserConfiguration.java index 5ac09f8f866..464f93e8c92 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/StandardParserConfiguration.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/StandardParserConfiguration.java @@ -1,13 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -/* - * Copyright 2001-2004 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -109,6 +106,22 @@ public class StandardParserConfiguration protected static final String HONOUR_ALL_SCHEMALOCATIONS = Constants.XERCES_FEATURE_PREFIX + Constants.HONOUR_ALL_SCHEMALOCATIONS_FEATURE; + /** Feature identifier: whether to ignore xsi:type attributes until a global element declaration is encountered */ + protected static final String IGNORE_XSI_TYPE = + Constants.XERCES_FEATURE_PREFIX + Constants.IGNORE_XSI_TYPE_FEATURE; + + /** Feature identifier: whether to ignore ID/IDREF errors */ + protected static final String ID_IDREF_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.ID_IDREF_CHECKING_FEATURE; + + /** Feature identifier: whether to ignore unparsed entity errors */ + protected static final String UNPARSED_ENTITY_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.UNPARSED_ENTITY_CHECKING_FEATURE; + + /** Feature identifier: whether to ignore identity constraint errors */ + protected static final String IDENTITY_CONSTRAINT_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.IDC_CHECKING_FEATURE; + /** Feature identifier: namespace growth */ protected static final String NAMESPACE_GROWTH = Constants.XERCES_FEATURE_PREFIX + Constants.NAMESPACE_GROWTH_FEATURE; @@ -135,6 +148,10 @@ public class StandardParserConfiguration protected static final String SCHEMA_DV_FACTORY = Constants.XERCES_PROPERTY_PREFIX + Constants.SCHEMA_DV_FACTORY_PROPERTY; + /** Property identifier: root type definition. */ + protected static final String ROOT_TYPE_DEF = + Constants.XERCES_PROPERTY_PREFIX + Constants.ROOT_TYPE_DEFINITION_PROPERTY; + // // Data // @@ -211,6 +228,10 @@ public class StandardParserConfiguration // not-recognized exception to be thrown. -Ac XMLSCHEMA_VALIDATION, XMLSCHEMA_FULL_CHECKING, + IGNORE_XSI_TYPE, + ID_IDREF_CHECKING, + IDENTITY_CONSTRAINT_CHECKING, + UNPARSED_ENTITY_CHECKING, }; addRecognizedFeatures(recognizedFeatures); @@ -221,6 +242,10 @@ public class StandardParserConfiguration setFeature(GENERATE_SYNTHETIC_ANNOTATIONS, false); setFeature(VALIDATE_ANNOTATIONS, false); setFeature(HONOUR_ALL_SCHEMALOCATIONS, false); + setFeature(IGNORE_XSI_TYPE, false); + setFeature(ID_IDREF_CHECKING, true); + setFeature(IDENTITY_CONSTRAINT_CHECKING, true); + setFeature(UNPARSED_ENTITY_CHECKING, true); setFeature(NAMESPACE_GROWTH, false); setFeature(TOLERATE_DUPLICATES, false); @@ -233,11 +258,11 @@ public class StandardParserConfiguration // not-recognized exception to be thrown. -Ac SCHEMA_LOCATION, SCHEMA_NONS_LOCATION, + ROOT_TYPE_DEF, SCHEMA_DV_FACTORY, - }; - - addRecognizedProperties(recognizedProperties); + }; + addRecognizedProperties(recognizedProperties); } // (SymbolTable,XMLGrammarPool) // diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java index c0283f74e3c..975367eb2be 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/XML11Configuration.java @@ -1,13 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -/* - * Copyright 2001-2005 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -184,7 +181,21 @@ public class XML11Configuration extends ParserConfigurationSettings protected static final String EXTERNAL_PARAMETER_ENTITIES = Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE; + /** Feature identifier: whether to ignore xsi:type attributes until a global element declaration is encountered */ + protected static final String IGNORE_XSI_TYPE = + Constants.XERCES_FEATURE_PREFIX + Constants.IGNORE_XSI_TYPE_FEATURE; + /** Feature identifier: whether to ignore ID/IDREF errors */ + protected static final String ID_IDREF_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.ID_IDREF_CHECKING_FEATURE; + + /** Feature identifier: whether to ignore unparsed entity errors */ + protected static final String UNPARSED_ENTITY_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.UNPARSED_ENTITY_CHECKING_FEATURE; + + /** Feature identifier: whether to ignore identity constraint errors */ + protected static final String IDENTITY_CONSTRAINT_CHECKING = + Constants.XERCES_FEATURE_PREFIX + Constants.IDC_CHECKING_FEATURE; // property identifiers @@ -267,6 +278,10 @@ public class XML11Configuration extends ParserConfigurationSettings protected static final String JAXP_SCHEMA_SOURCE = Constants.JAXP_PROPERTY_PREFIX + Constants.SCHEMA_SOURCE; + /** Property identifier: root type definition. */ + protected static final String ROOT_TYPE_DEF = + Constants.XERCES_PROPERTY_PREFIX + Constants.ROOT_TYPE_DEFINITION_PROPERTY; + /** Property identifier: locale. */ protected static final String LOCALE = Constants.XERCES_PROPERTY_PREFIX + Constants.LOCALE_PROPERTY; @@ -467,25 +482,27 @@ public class XML11Configuration extends ParserConfigurationSettings // add default recognized features final String[] recognizedFeatures = - { - CONTINUE_AFTER_FATAL_ERROR, LOAD_EXTERNAL_DTD, // from XMLDTDScannerImpl - VALIDATION, - NAMESPACES, - NORMALIZE_DATA, SCHEMA_ELEMENT_DEFAULT, SCHEMA_AUGMENT_PSVI, - GENERATE_SYNTHETIC_ANNOTATIONS, VALIDATE_ANNOTATIONS, - HONOUR_ALL_SCHEMALOCATIONS, NAMESPACE_GROWTH, - TOLERATE_DUPLICATES, - USE_GRAMMAR_POOL_ONLY, - // NOTE: These shouldn't really be here but since the XML Schema - // validator is constructed dynamically, its recognized - // features might not have been set and it would cause a - // not-recognized exception to be thrown. -Ac - XMLSCHEMA_VALIDATION, XMLSCHEMA_FULL_CHECKING, - EXTERNAL_GENERAL_ENTITIES, - EXTERNAL_PARAMETER_ENTITIES, - PARSER_SETTINGS, - XMLConstants.FEATURE_SECURE_PROCESSING - }; + { + CONTINUE_AFTER_FATAL_ERROR, LOAD_EXTERNAL_DTD, // from XMLDTDScannerImpl + VALIDATION, + NAMESPACES, + NORMALIZE_DATA, SCHEMA_ELEMENT_DEFAULT, SCHEMA_AUGMENT_PSVI, + GENERATE_SYNTHETIC_ANNOTATIONS, VALIDATE_ANNOTATIONS, + HONOUR_ALL_SCHEMALOCATIONS, IGNORE_XSI_TYPE, + ID_IDREF_CHECKING, IDENTITY_CONSTRAINT_CHECKING, + UNPARSED_ENTITY_CHECKING, + NAMESPACE_GROWTH, TOLERATE_DUPLICATES, + USE_GRAMMAR_POOL_ONLY, + // NOTE: These shouldn't really be here but since the XML Schema + // validator is constructed dynamically, its recognized + // features might not have been set and it would cause a + // not-recognized exception to be thrown. -Ac + XMLSCHEMA_VALIDATION, XMLSCHEMA_FULL_CHECKING, + EXTERNAL_GENERAL_ENTITIES, + EXTERNAL_PARAMETER_ENTITIES, + PARSER_SETTINGS, + XMLConstants.FEATURE_SECURE_PROCESSING + }; addRecognizedFeatures(recognizedFeatures); // set state for default features fFeatures.put(VALIDATION, Boolean.FALSE); @@ -500,6 +517,10 @@ public class XML11Configuration extends ParserConfigurationSettings fFeatures.put(GENERATE_SYNTHETIC_ANNOTATIONS, Boolean.FALSE); fFeatures.put(VALIDATE_ANNOTATIONS, Boolean.FALSE); fFeatures.put(HONOUR_ALL_SCHEMALOCATIONS, Boolean.FALSE); + fFeatures.put(IGNORE_XSI_TYPE, Boolean.FALSE); + fFeatures.put(ID_IDREF_CHECKING, Boolean.TRUE); + fFeatures.put(IDENTITY_CONSTRAINT_CHECKING, Boolean.TRUE); + fFeatures.put(UNPARSED_ENTITY_CHECKING, Boolean.TRUE); fFeatures.put(NAMESPACE_GROWTH, Boolean.FALSE); fFeatures.put(TOLERATE_DUPLICATES, Boolean.FALSE); fFeatures.put(USE_GRAMMAR_POOL_ONLY, Boolean.FALSE); @@ -508,41 +529,42 @@ public class XML11Configuration extends ParserConfigurationSettings // add default recognized properties final String[] recognizedProperties = - { - SYMBOL_TABLE, - ERROR_HANDLER, - ENTITY_RESOLVER, - ERROR_REPORTER, - ENTITY_MANAGER, - DOCUMENT_SCANNER, - DTD_SCANNER, - DTD_PROCESSOR, - DTD_VALIDATOR, - DATATYPE_VALIDATOR_FACTORY, - VALIDATION_MANAGER, - SCHEMA_VALIDATOR, - XML_STRING, - XMLGRAMMAR_POOL, - JAXP_SCHEMA_SOURCE, - JAXP_SCHEMA_LANGUAGE, - // NOTE: These shouldn't really be here but since the XML Schema - // validator is constructed dynamically, its recognized - // properties might not have been set and it would cause a - // not-recognized exception to be thrown. -Ac - SCHEMA_LOCATION, - SCHEMA_NONS_LOCATION, - LOCALE, - SCHEMA_DV_FACTORY, - SECURITY_MANAGER, - XML_SECURITY_PROPERTY_MANAGER + { + SYMBOL_TABLE, + ERROR_HANDLER, + ENTITY_RESOLVER, + ERROR_REPORTER, + ENTITY_MANAGER, + DOCUMENT_SCANNER, + DTD_SCANNER, + DTD_PROCESSOR, + DTD_VALIDATOR, + DATATYPE_VALIDATOR_FACTORY, + VALIDATION_MANAGER, + SCHEMA_VALIDATOR, + XML_STRING, + XMLGRAMMAR_POOL, + JAXP_SCHEMA_SOURCE, + JAXP_SCHEMA_LANGUAGE, + // NOTE: These shouldn't really be here but since the XML Schema + // validator is constructed dynamically, its recognized + // properties might not have been set and it would cause a + // not-recognized exception to be thrown. -Ac + SCHEMA_LOCATION, + SCHEMA_NONS_LOCATION, + ROOT_TYPE_DEF, + LOCALE, + SCHEMA_DV_FACTORY, + SECURITY_MANAGER, + XML_SECURITY_PROPERTY_MANAGER }; addRecognizedProperties(recognizedProperties); - if (symbolTable == null) { - symbolTable = new SymbolTable(); - } - fSymbolTable = symbolTable; - fProperties.put(SYMBOL_TABLE, fSymbolTable); + if (symbolTable == null) { + symbolTable = new SymbolTable(); + } + fSymbolTable = symbolTable; + fProperties.put(SYMBOL_TABLE, fSymbolTable); fGrammarPool = grammarPool; if (fGrammarPool != null) { @@ -597,8 +619,7 @@ public class XML11Configuration extends ParserConfigurationSettings // REVISIT: What is the right thing to do? -Ac } - fConfigUpdated = false; - + fConfigUpdated = false; } // (SymbolTable,XMLGrammarPool) // diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/Catalog.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/Catalog.java index a5c0e683825..0e5cb5a3e3f 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/Catalog.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/Catalog.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// Catalog.java - Represents OASIS Open Catalog files. - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -25,28 +19,24 @@ package com.sun.org.apache.xml.internal.resolver; import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl; import com.sun.org.apache.xerces.internal.utils.SecuritySupport; -import java.io.IOException; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.io.DataInputStream; - -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Vector; - -import java.net.URL; -import java.net.MalformedURLException; - -import javax.xml.parsers.SAXParserFactory; - -import com.sun.org.apache.xml.internal.resolver.CatalogManager; +import com.sun.org.apache.xml.internal.resolver.helpers.FileURL; import com.sun.org.apache.xml.internal.resolver.helpers.PublicId; import com.sun.org.apache.xml.internal.resolver.readers.CatalogReader; +import com.sun.org.apache.xml.internal.resolver.readers.OASISXMLCatalogReader; import com.sun.org.apache.xml.internal.resolver.readers.SAXCatalogReader; import com.sun.org.apache.xml.internal.resolver.readers.TR9401CatalogReader; -import com.sun.org.apache.xml.internal.resolver.readers.OASISXMLCatalogReader; -import com.sun.org.apache.xml.internal.resolver.helpers.FileURL; +import java.io.DataInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Vector; +import javax.xml.parsers.SAXParserFactory; /** * Represents OASIS Open Catalog files. @@ -154,8 +144,6 @@ import com.sun.org.apache.xml.internal.resolver.helpers.FileURL; * *

Additional catalogs may also be loaded with the * {@link #parseCatalog} method.

- * - * * *

Change Log:

*
@@ -823,7 +811,7 @@ public class Catalog { catalogCwd = FileURL.makeURL("basename"); } catch (MalformedURLException e) { String userdir = SecuritySupport.getSystemProperty("user.dir"); - userdir.replace('\\', '/'); + userdir = userdir.replace('\\', '/'); catalogManager.debug.message(1, "Malformed URL on cwd", userdir); catalogCwd = null; } @@ -2113,66 +2101,113 @@ public class Catalog { } } - /** - * Perform character normalization on a URI reference. - * - * @param uriref The URI reference - * @return The normalized URI reference. - */ - protected String normalizeURI(String uriref) { - if (uriref == null) { - return null; + + /** + * Perform character normalization on a URI reference. + * + * @param uriref The URI reference + * @return The normalized URI reference. + */ + protected String normalizeURI(String uriref) { + if (uriref == null) { + return null; + } + final int length = uriref.length(); + for (int i = 0; i < length; ++i) { + char c = uriref.charAt(i); + if ((c <= 0x20) // ctrl + || (c > 0x7F) // high ascii + || (c == 0x22) // " + || (c == 0x3C) // < + || (c == 0x3E) // > + || (c == 0x5C) // \ + || (c == 0x5E) // ^ + || (c == 0x60) // ` + || (c == 0x7B) // { + || (c == 0x7C) // | + || (c == 0x7D) // } + || (c == 0x7F)) { + return normalizeURI(uriref, i); + } + } + return uriref; } - byte[] bytes; - try { - bytes = uriref.getBytes("UTF-8"); - } catch (UnsupportedEncodingException uee) { - // this can't happen - catalogManager.debug.message(1, "UTF-8 is an unsupported encoding!?"); - return uriref; + /** + * Perform character normalization on a URI reference. + * + * @param uriref The URI reference + * @param index The index of the first character which requires escaping. + * @return The normalized URI reference. + */ + private String normalizeURI(String uriref, int index) { + final StringBuilder buffer = new StringBuilder(); + for (int i = 0; i < index; ++i) { + buffer.append(uriref.charAt(i)); + } + final byte[] bytes; + try { + bytes = uriref.substring(index).getBytes("UTF-8"); + } + catch (UnsupportedEncodingException uee) { + // this can't happen + catalogManager.debug.message(1, "UTF-8 is an unsupported encoding!?"); + return uriref; + } + for (int count = 0; count < bytes.length; ++count) { + int ch = bytes[count] & 0xFF; + if ((ch <= 0x20) // ctrl + || (ch > 0x7F) // high ascii + || (ch == 0x22) // " + || (ch == 0x3C) // < + || (ch == 0x3E) // > + || (ch == 0x5C) // \ + || (ch == 0x5E) // ^ + || (ch == 0x60) // ` + || (ch == 0x7B) // { + || (ch == 0x7C) // | + || (ch == 0x7D) // } + || (ch == 0x7F)) { + writeEncodedByte(ch, buffer); + } + else { + buffer.append((char) bytes[count]); + } + } + return buffer.toString(); } - StringBuilder newRef = new StringBuilder(bytes.length); - for (int count = 0; count < bytes.length; count++) { - int ch = bytes[count] & 0xFF; - - if ((ch <= 0x20) // ctrl - || (ch > 0x7F) // high ascii - || (ch == 0x22) // " - || (ch == 0x3C) // < - || (ch == 0x3E) // > - || (ch == 0x5C) // \ - || (ch == 0x5E) // ^ - || (ch == 0x60) // ` - || (ch == 0x7B) // { - || (ch == 0x7C) // | - || (ch == 0x7D) // } - || (ch == 0x7F)) { - newRef.append(encodedByte(ch)); - } else { - newRef.append((char) bytes[count]); - } + /** + * Perform %-encoding on a single byte. + * + * @param b The 8-bit integer that represents the byte. (Bytes are signed + * but encoding needs to look at the bytes unsigned.) + * @return The %-encoded string for the byte in question. + */ + protected String encodedByte(int b) { + StringBuilder buffer = new StringBuilder(3); + writeEncodedByte(b, buffer); + return buffer.toString(); } - return newRef.toString(); - } - - /** - * Perform %-encoding on a single byte. - * - * @param b The 8-bit integer that represents th byte. (Bytes are signed - but encoding needs to look at the bytes unsigned.) - * @return The %-encoded string for the byte in question. - */ - protected String encodedByte (int b) { - String hex = Integer.toHexString(b).toUpperCase(); - if (hex.length() < 2) { - return "%0" + hex; - } else { - return "%" + hex; + /** + * Perform %-encoding on a single byte. + * + * @param b The 8-bit integer that represents the byte. (Bytes are signed + * but encoding needs to look at the bytes unsigned.) + * @param buffer The target for the %-encoded string for the byte in question. + */ + private void writeEncodedByte(int b, StringBuilder buffer) { + String hex = Integer.toHexString(b).toUpperCase(Locale.ENGLISH); + if (hex.length() < 2) { + buffer.append("%0"); + buffer.append(hex); + } + else { + buffer.append('%'); + buffer.append(hex); + } } - } // ----------------------------------------------------------------- diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogEntry.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogEntry.java index 076e2f52b98..e90d8faa51c 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogEntry.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogEntry.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// CatalogEntry.java - Represents Catalog entries - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -53,18 +47,18 @@ import java.util.Vector; */ public class CatalogEntry { /** The nextEntry is the ordinal number of the next entry type. */ - protected static int nextEntry = 0; + static int nextEntry = 0; /** * The entryTypes vector maps catalog entry names * (e.g., 'BASE' or 'SYSTEM') to their type (1, 2, etc.). * Names are case sensitive. */ - protected static Hashtable entryTypes = new Hashtable(); + static final Hashtable entryTypes = new Hashtable(); /** The entryTypes vector maps catalog entry types to the number of arguments they're required to have. */ - protected static Vector entryArgs = new Vector(); + static final Vector entryArgs = new Vector(); /** * Adds a new catalog entry type. diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogException.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogException.java index c0b5404376c..1fc92db135b 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogException.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogException.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// CatalogException.java - Catalog exception - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -36,6 +30,9 @@ package com.sun.org.apache.xml.internal.resolver; * */ public class CatalogException extends Exception { + + private static final long serialVersionUID = 4007157171817798450L; + /** A wrapper around another exception */ public static final int WRAPPER = 1; /** An invalid entry */ @@ -56,8 +53,8 @@ public class CatalogException extends Exception { /** * The embedded exception if tunnelling, or null. */ - private Exception exception = null; - private int exceptionType = 0; + private final Exception exception; + private final int exceptionType; /** * Create a new CatalogException. diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogManager.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogManager.java index ab8cd27c647..b5fccbf04aa 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogManager.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/CatalogManager.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// CatalogManager.java - Access CatalogManager.properties - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -124,78 +118,78 @@ import sun.reflect.misc.ReflectUtil; */ public class CatalogManager { - private static String pFiles = "xml.catalog.files"; - private static String pVerbosity = "xml.catalog.verbosity"; - private static String pPrefer = "xml.catalog.prefer"; - private static String pStatic = "xml.catalog.staticCatalog"; - private static String pAllowPI = "xml.catalog.allowPI"; - private static String pClassname = "xml.catalog.className"; - private static String pIgnoreMissing = "xml.catalog.ignoreMissing"; + private static final String pFiles = "xml.catalog.files"; + private static final String pVerbosity = "xml.catalog.verbosity"; + private static final String pPrefer = "xml.catalog.prefer"; + private static final String pStatic = "xml.catalog.staticCatalog"; + private static final String pAllowPI = "xml.catalog.allowPI"; + private static final String pClassname = "xml.catalog.className"; + private static final String pIgnoreMissing = "xml.catalog.ignoreMissing"; - /** A static CatalogManager instance for sharing */ - private static CatalogManager staticManager = new CatalogManager(); + /** A static CatalogManager instance for sharing */ + private static final CatalogManager staticManager = new CatalogManager(); - /** The bootstrap resolver to use when loading XML Catalogs. */ - private BootstrapResolver bResolver = new BootstrapResolver(); + /** The bootstrap resolver to use when loading XML Catalogs. */ + private BootstrapResolver bResolver = new BootstrapResolver(); - /** Flag to ignore missing property files and/or properties */ - private boolean ignoreMissingProperties + /** Flag to ignore missing property files and/or properties */ + private boolean ignoreMissingProperties = (SecuritySupport.getSystemProperty(pIgnoreMissing) != null - || SecuritySupport.getSystemProperty(pFiles) != null); + || SecuritySupport.getSystemProperty(pFiles) != null); - /** Holds the resources after they are loaded from the file. */ - private ResourceBundle resources; + /** Holds the resources after they are loaded from the file. */ + private ResourceBundle resources; - /** The name of the CatalogManager properties file. */ - private String propertyFile = "CatalogManager.properties"; + /** The name of the CatalogManager properties file. */ + private String propertyFile = "CatalogManager.properties"; - /** The location of the propertyFile */ - private URL propertyFileURI = null; + /** The location of the propertyFile */ + private URL propertyFileURI = null; - /** Default catalog files list. */ - private String defaultCatalogFiles = "./xcatalog"; + /** Default catalog files list. */ + private String defaultCatalogFiles = "./xcatalog"; - /** Current catalog files list. */ - private String catalogFiles = null; + /** Current catalog files list. */ + private String catalogFiles = null; - /** Did the catalogFiles come from the properties file? */ - private boolean fromPropertiesFile = false; + /** Did the catalogFiles come from the properties file? */ + private boolean fromPropertiesFile = false; - /** Default verbosity level if there is no property setting for it. */ - private int defaultVerbosity = 1; + /** Default verbosity level if there is no property setting for it. */ + private int defaultVerbosity = 1; - /** Current verbosity level. */ - private Integer verbosity = null; + /** Current verbosity level. */ + private Integer verbosity = null; - /** Default preference setting. */ - private boolean defaultPreferPublic = true; + /** Default preference setting. */ + private boolean defaultPreferPublic = true; - /** Current preference setting. */ - private Boolean preferPublic = null; + /** Current preference setting. */ + private Boolean preferPublic = null; - /** Default setting of the static catalog flag. */ - private boolean defaultUseStaticCatalog = true; + /** Default setting of the static catalog flag. */ + private boolean defaultUseStaticCatalog = true; - /** Current setting of the static catalog flag. */ - private Boolean useStaticCatalog = null; + /** Current setting of the static catalog flag. */ + private Boolean useStaticCatalog = null; - /** The static catalog used by this manager. */ - private static Catalog staticCatalog = null; + /** The static catalog used by this manager. */ + private static volatile Catalog staticCatalog = null; - /** Default setting of the oasisXMLCatalogPI flag. */ - private boolean defaultOasisXMLCatalogPI = true; + /** Default setting of the oasisXMLCatalogPI flag. */ + private boolean defaultOasisXMLCatalogPI = true; - /** Current setting of the oasisXMLCatalogPI flag. */ - private Boolean oasisXMLCatalogPI = null; + /** Current setting of the oasisXMLCatalogPI flag. */ + private Boolean oasisXMLCatalogPI = null; - /** Default setting of the relativeCatalogs flag. */ - private boolean defaultRelativeCatalogs = true; + /** Default setting of the relativeCatalogs flag. */ + private boolean defaultRelativeCatalogs = true; - /** Current setting of the relativeCatalogs flag. */ - private Boolean relativeCatalogs = null; + /** Current setting of the relativeCatalogs flag. */ + private Boolean relativeCatalogs = null; - /** Current catalog class name. */ - private String catalogClassName = null; + /** Current catalog class name. */ + private String catalogClassName = null; /** * Indicates whether implementation parts should use * service loader (or similar). @@ -203,26 +197,26 @@ public class CatalogManager { */ private boolean useServicesMechanism; - /** The manager's debug object. Used for printing debugging messages. - * - *

This field is public so that objects that have access to this - * CatalogManager can use this debug object.

- */ - public Debug debug = null; + /** The manager's debug object. Used for printing debugging messages. + * + *

This field is public so that objects that have access to this + * CatalogManager can use this debug object.

+ */ + public Debug debug = null; - /** Constructor. */ - public CatalogManager() { - init(); - } + /** Constructor. */ + public CatalogManager() { + init(); + } - /** Constructor that specifies an explicit property file. */ - public CatalogManager(String propertyFile) { - this.propertyFile = propertyFile; - init(); + /** Constructor that specifies an explicit property file. */ + public CatalogManager(String propertyFile) { + this.propertyFile = propertyFile; + init(); } private void init() { - debug = new Debug(); + debug = new Debug(); // Note that we don't setDebug() here; we do that lazily. Either the // user will set it explicitly, or we'll do it automagically if they // read from the propertyFile for some other reason. That way, there's @@ -231,616 +225,638 @@ public class CatalogManager { if (System.getSecurityManager() == null) { useServicesMechanism = true; } - } - /** Set the bootstrap resolver.*/ - public void setBootstrapResolver(BootstrapResolver resolver) { - bResolver = resolver; - } + // Make sure verbosity is set by xml.catalog.verbosity sysprop + // setting, if defined. + queryVerbosityFromSysProp(); + } - /** Get the bootstrap resolver.*/ - public BootstrapResolver getBootstrapResolver() { - return bResolver; - } + /** Set the bootstrap resolver + * @param resolver the bootstrap resolver + */ + public void setBootstrapResolver(BootstrapResolver resolver) { + bResolver = resolver; + } - /** - * Load the properties from the propertyFile and build the - * resources from it. - */ - private synchronized void readProperties() { - try { - propertyFileURI = CatalogManager.class.getResource("/"+propertyFile); - InputStream in = - CatalogManager.class.getResourceAsStream("/"+propertyFile); - if (in==null) { - if (!ignoreMissingProperties) { - System.err.println("Cannot find "+propertyFile); - // there's no reason to give this warning more than once - ignoreMissingProperties = true; + /** Get the bootstrap resolver + * @return the bootstrap resolver + */ + public BootstrapResolver getBootstrapResolver() { + return bResolver; + } + + /** Query system property for verbosity level. */ + private void queryVerbosityFromSysProp() { + String verbStr = SecuritySupport.getSystemProperty(pVerbosity); + if (verbStr != null) { + try { + int verb = Integer.parseInt(verbStr.trim()); + verbosity = new Integer(verb); + debug.setDebug(verb); + } catch (Exception e) { + System.err.println("Cannot parse verbosity: \"" + verbStr + "\""); + } } - return; - } - resources = new PropertyResourceBundle(in); - } catch (MissingResourceException mre) { - if (!ignoreMissingProperties) { - System.err.println("Cannot read "+propertyFile); - } - } catch (java.io.IOException e) { - if (!ignoreMissingProperties) { - System.err.println("Failure trying to read "+propertyFile); - } } - // This is a bit of a hack. After we've successfully read the properties, - // use them to set the default debug level, if the user hasn't already set - // the default debug level. - if (verbosity == null) { - try { - String verbStr = resources.getString("verbosity"); - int verb = Integer.parseInt(verbStr.trim()); - debug.setDebug(verb); - verbosity = new Integer(verb); - } catch (Exception e) { - // nop - } - } - } - - /** - * Allow access to the static CatalogManager - */ - public static CatalogManager getStaticManager() { - return staticManager; - } - - /** - * How are missing properties handled? - * - *

If true, missing or unreadable property files will - * not be reported. Otherwise, a message will be sent to System.err. - *

- */ - public boolean getIgnoreMissingProperties() { - return ignoreMissingProperties; - } - - /** - * How should missing properties be handled? - * - *

If ignore is true, missing or unreadable property files will - * not be reported. Otherwise, a message will be sent to System.err. - *

- */ - public void setIgnoreMissingProperties(boolean ignore) { - ignoreMissingProperties = ignore; - } - - /** - * How are missing properties handled? - * - *

If ignore is true, missing or unreadable property files will - * not be reported. Otherwise, a message will be sent to System.err. - *

- * - * @deprecated No longer static; use get/set methods. - */ - public void ignoreMissingProperties(boolean ignore) { - setIgnoreMissingProperties(ignore); - } - - /** - * Obtain the verbosity setting from the properties. - * - * @return The verbosity level from the propertyFile or the - * defaultVerbosity. - */ - private int queryVerbosity () { - String defaultVerbStr = Integer.toString(defaultVerbosity); - - String verbStr = SecuritySupport.getSystemProperty(pVerbosity); - - if (verbStr == null) { - if (resources==null) readProperties(); - if (resources != null) { + /** + * Load the properties from the propertyFile and build the + * resources from it. + */ + private synchronized void readProperties() { try { - verbStr = resources.getString("verbosity"); + propertyFileURI = CatalogManager.class.getResource("/"+propertyFile); + InputStream in = + CatalogManager.class.getResourceAsStream("/"+propertyFile); + if (in==null) { + if (!ignoreMissingProperties) { + System.err.println("Cannot find "+propertyFile); + // there's no reason to give this warning more than once + ignoreMissingProperties = true; + } + return; + } + resources = new PropertyResourceBundle(in); + } catch (MissingResourceException mre) { + if (!ignoreMissingProperties) { + System.err.println("Cannot read "+propertyFile); + } + } catch (java.io.IOException e) { + if (!ignoreMissingProperties) { + System.err.println("Failure trying to read "+propertyFile); + } + } + + // This is a bit of a hack. After we've successfully read the properties, + // use them to set the default debug level, if the user hasn't already set + // the default debug level. + if (verbosity == null) { + try { + String verbStr = resources.getString("verbosity"); + int verb = Integer.parseInt(verbStr.trim()); + debug.setDebug(verb); + verbosity = new Integer(verb); + } catch (Exception e) { + // nop + } + } + } + + /** + * Allow access to the static CatalogManager + */ + public static CatalogManager getStaticManager() { + return staticManager; + } + + /** + * How are missing properties handled? + * + *

If true, missing or unreadable property files will + * not be reported. Otherwise, a message will be sent to System.err. + *

+ */ + public boolean getIgnoreMissingProperties() { + return ignoreMissingProperties; + } + + /** + * How should missing properties be handled? + * + *

If ignore is true, missing or unreadable property files will + * not be reported. Otherwise, a message will be sent to System.err. + *

+ */ + public void setIgnoreMissingProperties(boolean ignore) { + ignoreMissingProperties = ignore; + } + + /** + * How are missing properties handled? + * + *

If ignore is true, missing or unreadable property files will + * not be reported. Otherwise, a message will be sent to System.err. + *

+ * + * @deprecated No longer static; use get/set methods. + */ + public void ignoreMissingProperties(boolean ignore) { + setIgnoreMissingProperties(ignore); + } + + /** + * Obtain the verbosity setting from the properties. + * + * @return The verbosity level from the propertyFile or the + * defaultVerbosity. + */ + private int queryVerbosity () { + String defaultVerbStr = Integer.toString(defaultVerbosity); + + String verbStr = SecuritySupport.getSystemProperty(pVerbosity); + + if (verbStr == null) { + if (resources==null) readProperties(); + if (resources != null) { + try { + verbStr = resources.getString("verbosity"); + } catch (MissingResourceException e) { + verbStr = defaultVerbStr; + } + } else { + verbStr = defaultVerbStr; + } + } + + int verb = defaultVerbosity; + + try { + verb = Integer.parseInt(verbStr.trim()); + } catch (Exception e) { + System.err.println("Cannot parse verbosity: \"" + verbStr + "\""); + } + + // This is a bit of a hack. After we've successfully got the verbosity, + // we have to use it to set the default debug level, + // if the user hasn't already set the default debug level. + if (verbosity == null) { + debug.setDebug(verb); + verbosity = new Integer(verb); + } + + return verb; + } + + /** + * What is the current verbosity? + */ + public int getVerbosity() { + if (verbosity == null) { + verbosity = new Integer(queryVerbosity()); + } + + return verbosity.intValue(); + } + + /** + * Set the current verbosity. + */ + public void setVerbosity (int verbosity) { + this.verbosity = new Integer(verbosity); + debug.setDebug(verbosity); + } + + /** + * What is the current verbosity? + * + * @deprecated No longer static; use get/set methods. + */ + public int verbosity () { + return getVerbosity(); + } + + /** + * Obtain the relativeCatalogs setting from the properties. + * + * @return The relativeCatalogs setting from the propertyFile or the + * defaultRelativeCatalogs. + */ + private boolean queryRelativeCatalogs () { + if (resources==null) readProperties(); + + if (resources==null) return defaultRelativeCatalogs; + + try { + String allow = resources.getString("relative-catalogs"); + return (allow.equalsIgnoreCase("true") + || allow.equalsIgnoreCase("yes") + || allow.equalsIgnoreCase("1")); } catch (MissingResourceException e) { - verbStr = defaultVerbStr; + return defaultRelativeCatalogs; } - } else { - verbStr = defaultVerbStr; - } } - int verb = defaultVerbosity; - - try { - verb = Integer.parseInt(verbStr.trim()); - } catch (Exception e) { - System.err.println("Cannot parse verbosity: \"" + verbStr + "\""); - } - - // This is a bit of a hack. After we've successfully got the verbosity, - // we have to use it to set the default debug level, - // if the user hasn't already set the default debug level. - if (verbosity == null) { - debug.setDebug(verb); - verbosity = new Integer(verb); - } - - return verb; - } - - /** - * What is the current verbosity? - */ - public int getVerbosity() { - if (verbosity == null) { - verbosity = new Integer(queryVerbosity()); - } - - return verbosity.intValue(); - } - - /** - * Set the current verbosity. - */ - public void setVerbosity (int verbosity) { - this.verbosity = new Integer(verbosity); - debug.setDebug(verbosity); - } - - /** - * What is the current verbosity? - * - * @deprecated No longer static; use get/set methods. - */ - public int verbosity () { - return getVerbosity(); - } - - /** - * Obtain the relativeCatalogs setting from the properties. - * - * @return The relativeCatalogs setting from the propertyFile or the - * defaultRelativeCatalogs. - */ - private boolean queryRelativeCatalogs () { - if (resources==null) readProperties(); - - if (resources==null) return defaultRelativeCatalogs; - - try { - String allow = resources.getString("relative-catalogs"); - return (allow.equalsIgnoreCase("true") - || allow.equalsIgnoreCase("yes") - || allow.equalsIgnoreCase("1")); - } catch (MissingResourceException e) { - return defaultRelativeCatalogs; - } - } - - /** - * Get the relativeCatalogs setting. - * - *

This property is used when the catalogFiles property is - * interrogated. If true, then relative catalog entry file names - * are returned. If false, relative catalog entry file names are - * made absolute with respect to the properties file before returning - * them.

- * - *

This property only applies when the catalog files - * come from a properties file. If they come from a system property or - * the default list, they are never considered relative. (What would - * they be relative to?)

- * - *

In the properties, a value of 'yes', 'true', or '1' is considered - * true, anything else is false.

- * - * @return The relativeCatalogs setting from the propertyFile or the - * defaultRelativeCatalogs. - */ - public boolean getRelativeCatalogs () { - if (relativeCatalogs == null) { - relativeCatalogs = new Boolean(queryRelativeCatalogs()); - } - - return relativeCatalogs.booleanValue(); - } - - /** - * Set the relativeCatalogs setting. - * - * @see #getRelativeCatalogs() - */ - public void setRelativeCatalogs (boolean relative) { - relativeCatalogs = new Boolean(relative); - } - - /** - * Get the relativeCatalogs setting. - * - * @deprecated No longer static; use get/set methods. - */ - public boolean relativeCatalogs () { - return getRelativeCatalogs(); - } - - /** - * Obtain the list of catalog files from the properties. - * - * @return A semicolon delimited list of catlog file URIs - */ - private String queryCatalogFiles () { - String catalogList = SecuritySupport.getSystemProperty(pFiles); - fromPropertiesFile = false; - - if (catalogList == null) { - if (resources == null) readProperties(); - if (resources != null) { - try { - catalogList = resources.getString("catalogs"); - fromPropertiesFile = true; - } catch (MissingResourceException e) { - System.err.println(propertyFile + ": catalogs not found."); - catalogList = null; + /** + * Get the relativeCatalogs setting. + * + *

This property is used when the catalogFiles property is + * interrogated. If true, then relative catalog entry file names + * are returned. If false, relative catalog entry file names are + * made absolute with respect to the properties file before returning + * them.

+ * + *

This property only applies when the catalog files + * come from a properties file. If they come from a system property or + * the default list, they are never considered relative. (What would + * they be relative to?)

+ * + *

In the properties, a value of 'yes', 'true', or '1' is considered + * true, anything else is false.

+ * + * @return The relativeCatalogs setting from the propertyFile or the + * defaultRelativeCatalogs. + */ + public boolean getRelativeCatalogs () { + if (relativeCatalogs == null) { + relativeCatalogs = queryRelativeCatalogs() ? Boolean.TRUE : Boolean.FALSE; } - } + + return relativeCatalogs.booleanValue(); } - if (catalogList == null) { - catalogList = defaultCatalogFiles; + /** + * Set the relativeCatalogs setting. + * + * @see #getRelativeCatalogs() + */ + public void setRelativeCatalogs (boolean relative) { + relativeCatalogs = relative ? Boolean.TRUE : Boolean.FALSE; } - return catalogList; - } - - /** - * Return the current list of catalog files. - * - * @return A vector of the catalog file names or null if no catalogs - * are available in the properties. - */ - public Vector getCatalogFiles() { - if (catalogFiles == null) { - catalogFiles = queryCatalogFiles(); + /** + * Get the relativeCatalogs setting. + * + * @deprecated No longer static; use get/set methods. + */ + public boolean relativeCatalogs () { + return getRelativeCatalogs(); } - StringTokenizer files = new StringTokenizer(catalogFiles, ";"); - Vector catalogs = new Vector(); - while (files.hasMoreTokens()) { - String catalogFile = files.nextToken(); - URL absURI = null; + /** + * Obtain the list of catalog files from the properties. + * + * @return A semicolon delimited list of catlog file URIs + */ + private String queryCatalogFiles () { + String catalogList = SecuritySupport.getSystemProperty(pFiles); + fromPropertiesFile = false; - if (fromPropertiesFile && !relativeCatalogs()) { - try { - absURI = new URL(propertyFileURI, catalogFile); - catalogFile = absURI.toString(); - } catch (MalformedURLException mue) { - absURI = null; + if (catalogList == null) { + if (resources == null) readProperties(); + if (resources != null) { + try { + catalogList = resources.getString("catalogs"); + fromPropertiesFile = true; + } catch (MissingResourceException e) { + System.err.println(propertyFile + ": catalogs not found."); + catalogList = null; + } + } } - } - catalogs.add(catalogFile); + if (catalogList == null) { + catalogList = defaultCatalogFiles; + } + + return catalogList; } - return catalogs; - } + /** + * Return the current list of catalog files. + * + * @return A vector of the catalog file names or null if no catalogs + * are available in the properties. + */ + public Vector getCatalogFiles() { + if (catalogFiles == null) { + catalogFiles = queryCatalogFiles(); + } - /** - * Set the list of catalog files. - */ - public void setCatalogFiles(String fileList) { - catalogFiles = fileList; - fromPropertiesFile = false; - } + StringTokenizer files = new StringTokenizer(catalogFiles, ";"); + Vector catalogs = new Vector(); + while (files.hasMoreTokens()) { + String catalogFile = files.nextToken(); + URL absURI = null; - /** - * Return the current list of catalog files. - * - * @return A vector of the catalog file names or null if no catalogs - * are available in the properties. - * - * @deprecated No longer static; use get/set methods. - */ - public Vector catalogFiles() { - return getCatalogFiles(); - } + if (fromPropertiesFile && !relativeCatalogs()) { + try { + absURI = new URL(propertyFileURI, catalogFile); + catalogFile = absURI.toString(); + } catch (MalformedURLException mue) { + absURI = null; + } + } - /** - * Obtain the preferPublic setting from the properties. - * - *

In the properties, a value of 'public' is true, - * anything else is false.

- * - * @return True if prefer is public or the - * defaultPreferSetting. - */ - private boolean queryPreferPublic () { - String prefer = SecuritySupport.getSystemProperty(pPrefer); + catalogs.add(catalogFile); + } - if (prefer == null) { - if (resources==null) readProperties(); - if (resources==null) return defaultPreferPublic; - try { - prefer = resources.getString("prefer"); - } catch (MissingResourceException e) { - return defaultPreferPublic; - } + return catalogs; } - if (prefer == null) { - return defaultPreferPublic; + /** + * Set the list of catalog files. + */ + public void setCatalogFiles(String fileList) { + catalogFiles = fileList; + fromPropertiesFile = false; } - return (prefer.equalsIgnoreCase("public")); - } - - /** - * Return the current prefer public setting. - * - * @return True if public identifiers are preferred. - */ - public boolean getPreferPublic () { - if (preferPublic == null) { - preferPublic = new Boolean(queryPreferPublic()); - } - return preferPublic.booleanValue(); - } - - /** - * Set the prefer public setting. - */ - public void setPreferPublic (boolean preferPublic) { - this.preferPublic = new Boolean(preferPublic); - } - - /** - * Return the current prefer public setting. - * - * @return True if public identifiers are preferred. - * - * @deprecated No longer static; use get/set methods. - */ - public boolean preferPublic () { - return getPreferPublic(); - } - - /** - * Obtain the static-catalog setting from the properties. - * - *

In the properties, a value of 'yes', 'true', or '1' is considered - * true, anything else is false.

- * - * @return The static-catalog setting from the propertyFile or the - * defaultUseStaticCatalog. - */ - private boolean queryUseStaticCatalog () { - String staticCatalog = SecuritySupport.getSystemProperty(pStatic); - - if (staticCatalog == null) { - if (resources==null) readProperties(); - if (resources==null) return defaultUseStaticCatalog; - try { - staticCatalog = resources.getString("static-catalog"); - } catch (MissingResourceException e) { - return defaultUseStaticCatalog; - } + /** + * Return the current list of catalog files. + * + * @return A vector of the catalog file names or null if no catalogs + * are available in the properties. + * + * @deprecated No longer static; use get/set methods. + */ + public Vector catalogFiles() { + return getCatalogFiles(); } - if (staticCatalog == null) { - return defaultUseStaticCatalog; + /** + * Obtain the preferPublic setting from the properties. + * + *

In the properties, a value of 'public' is true, + * anything else is false.

+ * + * @return True if prefer is public or the + * defaultPreferSetting. + */ + private boolean queryPreferPublic () { + String prefer = SecuritySupport.getSystemProperty(pPrefer); + + if (prefer == null) { + if (resources==null) readProperties(); + if (resources==null) return defaultPreferPublic; + try { + prefer = resources.getString("prefer"); + } catch (MissingResourceException e) { + return defaultPreferPublic; + } + } + + if (prefer == null) { + return defaultPreferPublic; + } + + return (prefer.equalsIgnoreCase("public")); } - return (staticCatalog.equalsIgnoreCase("true") - || staticCatalog.equalsIgnoreCase("yes") - || staticCatalog.equalsIgnoreCase("1")); - } - - /** - * Get the current use static catalog setting. - */ - public boolean getUseStaticCatalog() { - if (useStaticCatalog == null) { - useStaticCatalog = new Boolean(queryUseStaticCatalog()); + /** + * Return the current prefer public setting. + * + * @return True if public identifiers are preferred. + */ + public boolean getPreferPublic () { + if (preferPublic == null) { + preferPublic = queryPreferPublic() ? Boolean.TRUE : Boolean.FALSE; + } + return preferPublic.booleanValue(); } - return useStaticCatalog.booleanValue(); - } - - /** - * Set the use static catalog setting. - */ - public void setUseStaticCatalog(boolean useStatic) { - useStaticCatalog = new Boolean(useStatic); - } - - /** - * Get the current use static catalog setting. - * - * @deprecated No longer static; use get/set methods. - */ - public boolean staticCatalog() { - return getUseStaticCatalog(); - } - - /** - * Get a new catalog instance. - * - * This method always returns a new instance of the underlying catalog class. - */ - public Catalog getPrivateCatalog() { - Catalog catalog = staticCatalog; - - if (useStaticCatalog == null) { - useStaticCatalog = new Boolean(getUseStaticCatalog()); + /** + * Set the prefer public setting. + */ + public void setPreferPublic (boolean preferPublic) { + this.preferPublic = preferPublic ? Boolean.TRUE : Boolean.FALSE; } - if (catalog == null || !useStaticCatalog.booleanValue()) { + /** + * Return the current prefer public setting. + * + * @return True if public identifiers are preferred. + * + * @deprecated No longer static; use get/set methods. + */ + public boolean preferPublic () { + return getPreferPublic(); + } - try { - String catalogClassName = getCatalogClassName(); + /** + * Obtain the static-catalog setting from the properties. + * + *

In the properties, a value of 'yes', 'true', or '1' is considered + * true, anything else is false.

+ * + * @return The static-catalog setting from the propertyFile or the + * defaultUseStaticCatalog. + */ + private boolean queryUseStaticCatalog () { + String staticCatalog = SecuritySupport.getSystemProperty(pStatic); + if (staticCatalog == null) { + if (resources==null) readProperties(); + if (resources==null) return defaultUseStaticCatalog; + try { + staticCatalog = resources.getString("static-catalog"); + } catch (MissingResourceException e) { + return defaultUseStaticCatalog; + } + } + + if (staticCatalog == null) { + return defaultUseStaticCatalog; + } + + return (staticCatalog.equalsIgnoreCase("true") + || staticCatalog.equalsIgnoreCase("yes") + || staticCatalog.equalsIgnoreCase("1")); + } + + /** + * Get the current use static catalog setting. + */ + public boolean getUseStaticCatalog() { + if (useStaticCatalog == null) { + useStaticCatalog = queryUseStaticCatalog() ? Boolean.TRUE : Boolean.FALSE; + } + + return useStaticCatalog.booleanValue(); + } + + /** + * Set the use static catalog setting. + */ + public void setUseStaticCatalog(boolean useStatic) { + useStaticCatalog = useStatic ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Get the current use static catalog setting. + * + * @deprecated No longer static; use get/set methods. + */ + public boolean staticCatalog() { + return getUseStaticCatalog(); + } + + /** + * Get a new catalog instance. + * + * This method always returns a new instance of the underlying catalog class. + */ + public Catalog getPrivateCatalog() { + Catalog catalog = staticCatalog; + + if (useStaticCatalog == null) { + useStaticCatalog = getUseStaticCatalog() ? Boolean.TRUE : Boolean.FALSE; + } + + if (catalog == null || !useStaticCatalog.booleanValue()) { + + try { + String catalogClassName = getCatalogClassName(); + + if (catalogClassName == null) { + catalog = new Catalog(); + } else { + try { + catalog = (Catalog) ReflectUtil.forName(catalogClassName).newInstance(); + } catch (ClassNotFoundException cnfe) { + debug.message(1,"Catalog class named '" + + catalogClassName + + "' could not be found. Using default."); + catalog = new Catalog(); + } catch (ClassCastException cnfe) { + debug.message(1,"Class named '" + + catalogClassName + + "' is not a Catalog. Using default."); + catalog = new Catalog(); + } + } + + catalog.setCatalogManager(this); + catalog.setupReaders(); + catalog.loadSystemCatalogs(); + } catch (Exception ex) { + ex.printStackTrace(); + } + + if (useStaticCatalog.booleanValue()) { + staticCatalog = catalog; + } + } + + return catalog; + } + + /** + * Get a catalog instance. + * + * If this manager uses static catalogs, the same static catalog will + * always be returned. Otherwise a new catalog will be returned. + */ + public Catalog getCatalog() { + Catalog catalog = staticCatalog; + + if (useStaticCatalog == null) { + useStaticCatalog = getUseStaticCatalog() ? Boolean.TRUE : Boolean.FALSE; + } + + if (catalog == null || !useStaticCatalog.booleanValue()) { + catalog = getPrivateCatalog(); + if (useStaticCatalog.booleanValue()) { + staticCatalog = catalog; + } + } + + return catalog; + } + + /** + *

Obtain the oasisXMLCatalogPI setting from the properties.

+ * + *

In the properties, a value of 'yes', 'true', or '1' is considered + * true, anything else is false.

+ * + * @return The oasisXMLCatalogPI setting from the propertyFile or the + * defaultOasisXMLCatalogPI. + */ + public boolean queryAllowOasisXMLCatalogPI () { + String allow = SecuritySupport.getSystemProperty(pAllowPI); + + if (allow == null) { + if (resources==null) readProperties(); + if (resources==null) return defaultOasisXMLCatalogPI; + try { + allow = resources.getString("allow-oasis-xml-catalog-pi"); + } catch (MissingResourceException e) { + return defaultOasisXMLCatalogPI; + } + } + + if (allow == null) { + return defaultOasisXMLCatalogPI; + } + + return (allow.equalsIgnoreCase("true") + || allow.equalsIgnoreCase("yes") + || allow.equalsIgnoreCase("1")); + } + + /** + * Get the current XML Catalog PI setting. + */ + public boolean getAllowOasisXMLCatalogPI () { + if (oasisXMLCatalogPI == null) { + oasisXMLCatalogPI = queryAllowOasisXMLCatalogPI() ? Boolean.TRUE : Boolean.FALSE; + } + + return oasisXMLCatalogPI.booleanValue(); + } + + public boolean useServicesMechanism() { + return useServicesMechanism; + } + /** + * Set the XML Catalog PI setting + */ + public void setAllowOasisXMLCatalogPI(boolean allowPI) { + oasisXMLCatalogPI = allowPI ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Get the current XML Catalog PI setting. + * + * @deprecated No longer static; use get/set methods. + */ + public boolean allowOasisXMLCatalogPI() { + return getAllowOasisXMLCatalogPI(); + } + + /** + * Obtain the Catalog class name setting from the properties. + * + */ + public String queryCatalogClassName () { + String className = SecuritySupport.getSystemProperty(pClassname); + + if (className == null) { + if (resources==null) readProperties(); + if (resources==null) return null; + try { + return resources.getString("catalog-class-name"); + } catch (MissingResourceException e) { + return null; + } + } + + return className; + } + + /** + * Get the current Catalog class name. + */ + public String getCatalogClassName() { if (catalogClassName == null) { - catalog = new Catalog(); - } else { - try { - catalog = (Catalog) ReflectUtil.forName(catalogClassName).newInstance(); - } catch (ClassNotFoundException cnfe) { - debug.message(1,"Catalog class named '" - + catalogClassName - + "' could not be found. Using default."); - catalog = new Catalog(); - } catch (ClassCastException cnfe) { - debug.message(1,"Class named '" - + catalogClassName - + "' is not a Catalog. Using default."); - catalog = new Catalog(); - } + catalogClassName = queryCatalogClassName(); } - catalog.setCatalogManager(this); - catalog.setupReaders(); - catalog.loadSystemCatalogs(); - } catch (Exception ex) { - ex.printStackTrace(); - } - - if (useStaticCatalog.booleanValue()) { - staticCatalog = catalog; - } + return catalogClassName; } - return catalog; - } - - /** - * Get a catalog instance. - * - * If this manager uses static catalogs, the same static catalog will - * always be returned. Otherwise a new catalog will be returned. - */ - public Catalog getCatalog() { - Catalog catalog = staticCatalog; - - if (useStaticCatalog == null) { - useStaticCatalog = new Boolean(getUseStaticCatalog()); + /** + * Set the Catalog class name. + */ + public void setCatalogClassName(String className) { + catalogClassName = className; } - if (catalog == null || !useStaticCatalog.booleanValue()) { - catalog = getPrivateCatalog(); - if (useStaticCatalog.booleanValue()) { - staticCatalog = catalog; - } + /** + * Get the current Catalog class name. + * + * @deprecated No longer static; use get/set methods. + */ + public String catalogClassName() { + return getCatalogClassName(); } - - return catalog; - } - - /** - *

Obtain the oasisXMLCatalogPI setting from the properties.

- * - *

In the properties, a value of 'yes', 'true', or '1' is considered - * true, anything else is false.

- * - * @return The oasisXMLCatalogPI setting from the propertyFile or the - * defaultOasisXMLCatalogPI. - */ - public boolean queryAllowOasisXMLCatalogPI () { - String allow = SecuritySupport.getSystemProperty(pAllowPI); - - if (allow == null) { - if (resources==null) readProperties(); - if (resources==null) return defaultOasisXMLCatalogPI; - try { - allow = resources.getString("allow-oasis-xml-catalog-pi"); - } catch (MissingResourceException e) { - return defaultOasisXMLCatalogPI; - } - } - - if (allow == null) { - return defaultOasisXMLCatalogPI; - } - - return (allow.equalsIgnoreCase("true") - || allow.equalsIgnoreCase("yes") - || allow.equalsIgnoreCase("1")); - } - - /** - * Get the current XML Catalog PI setting. - */ - public boolean getAllowOasisXMLCatalogPI () { - if (oasisXMLCatalogPI == null) { - oasisXMLCatalogPI = new Boolean(queryAllowOasisXMLCatalogPI()); - } - - return oasisXMLCatalogPI.booleanValue(); - } - - public boolean useServicesMechanism() { - return useServicesMechanism; - } - /** - * Set the XML Catalog PI setting - */ - public void setAllowOasisXMLCatalogPI(boolean allowPI) { - oasisXMLCatalogPI = new Boolean(allowPI); - } - - /** - * Get the current XML Catalog PI setting. - * - * @deprecated No longer static; use get/set methods. - */ - public boolean allowOasisXMLCatalogPI() { - return getAllowOasisXMLCatalogPI(); - } - - /** - * Obtain the Catalog class name setting from the properties. - * - */ - public String queryCatalogClassName () { - String className = SecuritySupport.getSystemProperty(pClassname); - - if (className == null) { - if (resources==null) readProperties(); - if (resources==null) return null; - try { - return resources.getString("catalog-class-name"); - } catch (MissingResourceException e) { - return null; - } - } - - return className; - } - - /** - * Get the current Catalog class name. - */ - public String getCatalogClassName() { - if (catalogClassName == null) { - catalogClassName = queryCatalogClassName(); - } - - return catalogClassName; - } - - /** - * Set the Catalog class name. - */ - public void setCatalogClassName(String className) { - catalogClassName = className; - } - - /** - * Get the current Catalog class name. - * - * @deprecated No longer static; use get/set methods. - */ - public String catalogClassName() { - return getCatalogClassName(); - } } diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/Resolver.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/Resolver.java index 721d67c8d4e..d566895ccce 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/Resolver.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/Resolver.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// Resolver.java - Represents an extension of OASIS Open Catalog files. - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -94,7 +88,7 @@ public class Resolver extends Catalog { SAXCatalogReader saxReader = new SAXCatalogReader(spf); - saxReader.setCatalogParser(null, "XMLCatalog", + saxReader.setCatalogParser(null, "XCatalog", "com.sun.org.apache.xml.internal.resolver.readers.XCatalogReader"); saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName, diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/BootstrapResolver.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/BootstrapResolver.java index 9d5df419b6a..5bdf3d8e94a 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/BootstrapResolver.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/BootstrapResolver.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// BootstrapResolver.java - Resolve entities and URIs internally - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -65,14 +59,17 @@ public class BootstrapResolver implements EntityResolver, URIResolver { /** System identifier for OASIS XML Catalog files. */ public static final String xmlCatalogSysId = "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"; + /** Public identifier for legacy Apache XCatalog files. There is no official system identifier for XCatalog files. */ + public static final String xCatalogPubId = "-//DTD XCatalog//EN"; + /** Private hash used for public identifiers. */ - private Hashtable publicMap = new Hashtable(); + private final Hashtable publicMap = new Hashtable(); /** Private hash used for system identifiers. */ - private Hashtable systemMap = new Hashtable(); + private final Hashtable systemMap = new Hashtable(); /** Private hash used for URIs. */ - private Hashtable uriMap = new Hashtable(); + private final Hashtable uriMap = new Hashtable(); /** Constructor. */ public BootstrapResolver() { @@ -91,6 +88,11 @@ public class BootstrapResolver implements EntityResolver, URIResolver { if (url != null) { uriMap.put(xmlCatalogXSD, url.toString()); } + + url = this.getClass().getResource("/com/sun/org/apache/xml/internal/resolver/etc/xcatalog.dtd"); + if (url != null) { + publicMap.put(xCatalogPubId, url.toString()); + } } /** SAX resolveEntity API. */ diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/Debug.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/Debug.java index fbf2b8f2674..6a6c03a48e5 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/Debug.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/Debug.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// Debug.java - Print debug messages - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/FileURL.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/FileURL.java index e5a5605323e..6717739005a 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/FileURL.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/FileURL.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// FileURL.java - Construct a file: scheme URL - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/Namespaces.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/Namespaces.java index 02624a61615..6511cf26224 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/Namespaces.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/Namespaces.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// Namespaces.java - Analyze namespace nodes in a DOM tree - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -47,8 +41,9 @@ public class Namespaces { String name = element.getTagName(); String prefix = ""; - if (name.indexOf(':') > 0) { - prefix = name.substring(0, name.indexOf(':')); + final int indexOfColon = name.indexOf(':'); + if (indexOfColon > 0) { + prefix = name.substring(0, indexOfColon); } return prefix; @@ -64,8 +59,9 @@ public class Namespaces { public static String getLocalName(Element element) { String name = element.getTagName(); - if (name.indexOf(':') > 0) { - name = name.substring(name.indexOf(':')+1); + final int indexOfColon = name.indexOf(':'); + if (indexOfColon > 0) { + name = name.substring(indexOfColon + 1); } return name; @@ -85,7 +81,7 @@ public class Namespaces { return null; } - if (prefix.equals("")) { + if (prefix.length() == 0) { if (((Element) node).hasAttribute("xmlns")) { return ((Element) node).getAttribute("xmlns"); } diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/PublicId.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/PublicId.java index 8211a770db5..d70f4f034ac 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/PublicId.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/helpers/PublicId.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// PublicId.java - Information about public identifiers - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -34,7 +28,8 @@ package com.sun.org.apache.xml.internal.resolver.helpers; * */ public abstract class PublicId { - protected PublicId() { } + + protected PublicId() {} /** * Normalize a public identifier. @@ -58,18 +53,17 @@ public abstract class PublicId { * @return The normalized identifier. */ public static String normalize(String publicId) { - String normal = publicId.replace('\t', ' '); - normal = normal.replace('\r', ' '); - normal = normal.replace('\n', ' '); - normal = normal.trim(); + String normal = publicId.replace('\t', ' '); + normal = normal.replace('\r', ' '); + normal = normal.replace('\n', ' '); + normal = normal.trim(); - int pos; + int pos; - while ((pos = normal.indexOf(" ")) >= 0) { - normal = normal.substring(0, pos) + normal.substring(pos+1); - } - - return normal; + while ((pos = normal.indexOf(" ")) >= 0) { + normal = normal.substring(0, pos) + normal.substring(pos+1); + } + return normal; } /** @@ -83,21 +77,24 @@ public abstract class PublicId { * @return The normalized identifier. */ public static String encodeURN(String publicId) { - String urn = PublicId.normalize(publicId); + String urn = PublicId.normalize(publicId); - urn = PublicId.stringReplace(urn, "%", "%25"); - urn = PublicId.stringReplace(urn, ";", "%3B"); - urn = PublicId.stringReplace(urn, "'", "%27"); - urn = PublicId.stringReplace(urn, "?", "%3F"); - urn = PublicId.stringReplace(urn, "#", "%23"); - urn = PublicId.stringReplace(urn, "+", "%2B"); - urn = PublicId.stringReplace(urn, " ", "+"); - urn = PublicId.stringReplace(urn, "::", ";"); - urn = PublicId.stringReplace(urn, ":", "%3A"); - urn = PublicId.stringReplace(urn, "//", ":"); - urn = PublicId.stringReplace(urn, "/", "%2F"); + urn = PublicId.stringReplace(urn, "%", "%25"); + urn = PublicId.stringReplace(urn, ";", "%3B"); + urn = PublicId.stringReplace(urn, "'", "%27"); + urn = PublicId.stringReplace(urn, "?", "%3F"); + urn = PublicId.stringReplace(urn, "#", "%23"); + urn = PublicId.stringReplace(urn, "+", "%2B"); + urn = PublicId.stringReplace(urn, " ", "+"); + urn = PublicId.stringReplace(urn, "::", ";"); + urn = PublicId.stringReplace(urn, ":", "%3A"); + urn = PublicId.stringReplace(urn, "//", ":"); + urn = PublicId.stringReplace(urn, "/", "%2F"); - return "urn:publicid:" + urn; + StringBuilder buffer = new StringBuilder(13 + urn.length()); + buffer.append("urn:publicid:"); + buffer.append(urn); + return buffer.toString(); } /** @@ -111,51 +108,62 @@ public abstract class PublicId { * @return The normalized identifier. */ public static String decodeURN(String urn) { - String publicId = ""; + String publicId; + if (urn.startsWith("urn:publicid:")) { + publicId = urn.substring(13); + } + else { + return urn; + } - if (urn.startsWith("urn:publicid:")) { - publicId = urn.substring(13); - } else { - return urn; - } + final boolean hasEscape = (publicId.indexOf('%') >= 0); + if (hasEscape) { + publicId = PublicId.stringReplace(publicId, "%2F", "/"); + } + publicId = PublicId.stringReplace(publicId, ":", "//"); + if (hasEscape) { + publicId = PublicId.stringReplace(publicId, "%3A", ":"); + } + publicId = PublicId.stringReplace(publicId, ";", "::"); + publicId = PublicId.stringReplace(publicId, "+", " "); + if (hasEscape) { + publicId = PublicId.stringReplace(publicId, "%2B", "+"); + publicId = PublicId.stringReplace(publicId, "%23", "#"); + publicId = PublicId.stringReplace(publicId, "%3F", "?"); + publicId = PublicId.stringReplace(publicId, "%27", "'"); + publicId = PublicId.stringReplace(publicId, "%3B", ";"); + publicId = PublicId.stringReplace(publicId, "%25", "%"); + } - publicId = PublicId.stringReplace(publicId, "%2F", "/"); - publicId = PublicId.stringReplace(publicId, ":", "//"); - publicId = PublicId.stringReplace(publicId, "%3A", ":"); - publicId = PublicId.stringReplace(publicId, ";", "::"); - publicId = PublicId.stringReplace(publicId, "+", " "); - publicId = PublicId.stringReplace(publicId, "%2B", "+"); - publicId = PublicId.stringReplace(publicId, "%23", "#"); - publicId = PublicId.stringReplace(publicId, "%3F", "?"); - publicId = PublicId.stringReplace(publicId, "%27", "'"); - publicId = PublicId.stringReplace(publicId, "%3B", ";"); - publicId = PublicId.stringReplace(publicId, "%25", "%"); - - return publicId; - } + return publicId; + } /** * Replace one string with another. - * */ private static String stringReplace(String str, - String oldStr, - String newStr) { - - String result = ""; - int pos = str.indexOf(oldStr); - - // System.out.println(str + ": " + oldStr + " => " + newStr); - - while (pos >= 0) { - // System.out.println(str + " (" + pos + ")"); - result += str.substring(0, pos); - result += newStr; - str = str.substring(pos+1); - - pos = str.indexOf(oldStr); - } - - return result + str; + String oldStr, + String newStr) { + int pos = str.indexOf(oldStr); + if (pos >= 0) { + final StringBuilder buffer = new StringBuilder(); + final int oldStrLength = oldStr.length(); + int start = 0; + do { + for (int i = start; i < pos; ++i) { + buffer.append(str.charAt(i)); + } + buffer.append(newStr); + start = pos + oldStrLength; + pos = str.indexOf(oldStr, start); + } + while (pos >= 0); + final int strLength = str.length(); + for (int i = start; i < strLength; ++i) { + buffer.append(str.charAt(i)); + } + return buffer.toString(); + } + return str; } } diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/CatalogReader.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/CatalogReader.java index cc8bd09f2cb..444b38769dc 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/CatalogReader.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/CatalogReader.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// CatalogReader.java - An interface for reading catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/DOMCatalogParser.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/DOMCatalogParser.java index ff4af953946..c0e34011ab0 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/DOMCatalogParser.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/DOMCatalogParser.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// DOMCatalogParser.java - An interface for reading catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/DOMCatalogReader.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/DOMCatalogReader.java index bd14b28ae39..22d131fe0f7 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/DOMCatalogReader.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/DOMCatalogReader.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// DOMCatalogReader.java - Read XML Catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/ExtendedXMLCatalogReader.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/ExtendedXMLCatalogReader.java index a638a4757e4..2fd0dc6b1e2 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/ExtendedXMLCatalogReader.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/ExtendedXMLCatalogReader.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// ExtendedXMLCatalogReader.java - Read XML Catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -166,7 +160,7 @@ public class ExtendedXMLCatalogReader extends OASISXMLCatalogReader { String baseURI = (String) baseURIStack.peek(); if (!baseURI.equals(popURI)) { - entryType = catalog.BASE; + entryType = Catalog.BASE; entryArgs.add(baseURI); debug.message(4, "(reset) xml:base", baseURI); diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/OASISXMLCatalogReader.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/OASISXMLCatalogReader.java index f1d6b8983b5..04ddf623199 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/OASISXMLCatalogReader.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/OASISXMLCatalogReader.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// OASISXMLCatalogReader.java - Read XML Catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -23,16 +17,16 @@ package com.sun.org.apache.xml.internal.resolver.readers; -import java.util.Stack; -import java.util.Vector; -import java.util.Enumeration; import com.sun.org.apache.xml.internal.resolver.Catalog; import com.sun.org.apache.xml.internal.resolver.CatalogEntry; import com.sun.org.apache.xml.internal.resolver.CatalogException; import com.sun.org.apache.xml.internal.resolver.helpers.PublicId; - -import org.xml.sax.*; +import java.util.Enumeration; +import java.util.Stack; +import java.util.Vector; +import javax.xml.parsers.SAXParserFactory; import org.w3c.dom.*; +import org.xml.sax.*; /** * Parse OASIS Entity Resolution Technical Committee @@ -71,6 +65,17 @@ public class OASISXMLCatalogReader extends SAXCatalogReader implements SAXCatalo return catalog; } + /** Default constructor */ + public OASISXMLCatalogReader() { + super(); + } + + /** Constructor allowing for providing custom SAX parser factory */ + public OASISXMLCatalogReader(SAXParserFactory parserFactory, Catalog catalog) { + super(parserFactory); + setCatalog(catalog); + } + /** * Are we in an extension namespace? * @@ -102,7 +107,7 @@ public class OASISXMLCatalogReader extends SAXCatalogReader implements SAXCatalo return; } - /** The SAX startDocument method does nothing. */ + /** The SAX startDocument */ public void startDocument () throws SAXException { baseURIStack.push(catalog.getCurrentBase()); @@ -371,14 +376,14 @@ public class OASISXMLCatalogReader extends SAXCatalogReader implements SAXCatalo } if (localName.equals("doctype")) { - entryType = catalog.DOCTYPE; + entryType = Catalog.DOCTYPE; entryArgs.add(atts.getValue("name")); entryArgs.add(atts.getValue("uri")); } else if (localName.equals("document")) { - entryType = catalog.DOCUMENT; + entryType = Catalog.DOCUMENT; entryArgs.add(atts.getValue("uri")); } else if (localName.equals("dtddecl")) { - entryType = catalog.DTDDECL; + entryType = Catalog.DTDDECL; entryArgs.add(atts.getValue("publicId")); entryArgs.add(atts.getValue("uri")); } else if (localName.equals("entity")) { @@ -432,7 +437,7 @@ public class OASISXMLCatalogReader extends SAXCatalogReader implements SAXCatalo && checkAttributes(atts, attName2); } - /** The SAX endElement method does nothing. */ + /** The SAX endElement */ public void endElement (String namespaceURI, String localName, String qName) @@ -452,7 +457,7 @@ public class OASISXMLCatalogReader extends SAXCatalogReader implements SAXCatalo String baseURI = (String) baseURIStack.peek(); if (!baseURI.equals(popURI)) { - entryType = catalog.BASE; + entryType = Catalog.BASE; entryArgs.add(baseURI); debug.message(4, "(reset) xml:base", baseURI); @@ -477,7 +482,7 @@ public class OASISXMLCatalogReader extends SAXCatalogReader implements SAXCatalo String override = (String) overrideStack.peek(); if (!override.equals(popOverride)) { - entryType = catalog.OVERRIDE; + entryType = Catalog.OVERRIDE; entryArgs.add(override); overrideStack.push(override); diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXCatalogParser.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXCatalogParser.java index 782744bc5ee..b70f7dc64fe 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXCatalogParser.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXCatalogParser.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// SAXCatalogParser.java - An interface for reading catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXCatalogReader.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXCatalogReader.java index 105cb7671ae..d5d5d2aca27 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXCatalogReader.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXCatalogReader.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// SAXCatalogReader.java - Read XML Catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -77,427 +71,423 @@ import sun.reflect.misc.ReflectUtil; * */ public class SAXCatalogReader implements CatalogReader, ContentHandler, DocumentHandler { - /** The SAX Parser Factory */ - protected SAXParserFactory parserFactory = null; + /** The SAX Parser Factory */ + protected SAXParserFactory parserFactory = null; - /** The SAX Parser Class */ - protected String parserClass = null; + /** The SAX Parser Class */ + protected String parserClass = null; - /** + /** * Mapping table from QNames to CatalogParser classes. * *

Each key in this hash table has the form "elementname" * or "{namespaceuri}elementname". The former is used if the * namespace URI is null.

*/ - protected Hashtable namespaceMap = new Hashtable(); + protected Hashtable namespaceMap = new Hashtable(); - /** The parser in use for the current catalog. */ - private SAXCatalogParser saxParser = null; + /** The parser in use for the current catalog. */ + private SAXCatalogParser saxParser = null; - /** Set if something goes horribly wrong. It allows the class to + /** Set if something goes horribly wrong. It allows the class to * ignore the rest of the events that are received. */ - private boolean abandonHope = false; + private boolean abandonHope = false; - /** The Catalog that we're working for. */ - private Catalog catalog; + /** The Catalog that we're working for. */ + private Catalog catalog; - /** Set the XML SAX Parser Factory. - */ - public void setParserFactory(SAXParserFactory parserFactory) { - this.parserFactory = parserFactory; - } + /** Set the XML SAX Parser Factory. + */ + public void setParserFactory(SAXParserFactory parserFactory) { + this.parserFactory = parserFactory; + } - /** Set the XML SAX Parser Class - */ - public void setParserClass(String parserClass) { - this.parserClass = parserClass; - } + /** Set the XML SAX Parser Class + */ + public void setParserClass(String parserClass) { + this.parserClass = parserClass; + } - /** Get the parser factory currently in use. */ - public SAXParserFactory getParserFactory() { - return parserFactory; - } + /** Get the parser factory currently in use. */ + public SAXParserFactory getParserFactory() { + return parserFactory; + } - /** Get the parser class currently in use. */ - public String getParserClass() { - return parserClass; - } + /** Get the parser class currently in use. */ + public String getParserClass() { + return parserClass; + } - /** The debug class to use for this reader. - * - * This is a bit of a hack. Anyway, whenever we read for a catalog, - * we extract the debug object - * from the catalog's manager so that we can use it to print messages. - * - * In production, we don't really expect any messages so it doesn't - * really matter. But it's still a bit of a hack. - */ - protected Debug debug = CatalogManager.getStaticManager().debug; + /** The debug class to use for this reader. + * + * This is a bit of a hack. Anyway, whenever we read for a catalog, + * we extract the debug object + * from the catalog's manager so that we can use it to print messages. + * + * In production, we don't really expect any messages so it doesn't + * really matter. But it's still a bit of a hack. + */ + protected Debug debug = CatalogManager.getStaticManager().debug; - /** The constructor */ - public SAXCatalogReader() { - parserFactory = null; - parserClass = null; - } + /** The constructor */ + public SAXCatalogReader() { + parserFactory = null; + parserClass = null; + } - /** The constructor */ - public SAXCatalogReader(SAXParserFactory parserFactory) { - this.parserFactory = parserFactory; - } + /** The constructor */ + public SAXCatalogReader(SAXParserFactory parserFactory) { + this.parserFactory = parserFactory; + } - /** The constructor */ - public SAXCatalogReader(String parserClass) { - this.parserClass = parserClass; - } + /** The constructor */ + public SAXCatalogReader(String parserClass) { + this.parserClass = parserClass; + } - /** Set the SAXCatalogParser class for the given namespace/root + /** + * Set the SAXCatalogParser class for the given namespace/root * element type. */ - public void setCatalogParser(String namespaceURI, - String rootElement, - String parserClass) { - if (namespaceURI == null) { - namespaceMap.put(rootElement, parserClass); - } else { - namespaceMap.put("{"+namespaceURI+"}"+rootElement, parserClass); + public void setCatalogParser(String namespaceURI, + String rootElement, + String parserClass) { + namespaceURI = namespaceURI != null ? namespaceURI.trim() : ""; + namespaceMap.put("{"+namespaceURI+"}"+rootElement, parserClass); } - } - /** Get the SAXCatalogParser class for the given namespace/root + /** + * Get the SAXCatalogParser class for the given namespace/root * element type. */ - public String getCatalogParser(String namespaceURI, - String rootElement) { - if (namespaceURI == null) { - return (String) namespaceMap.get(rootElement); - } else { - return (String) namespaceMap.get("{"+namespaceURI+"}"+rootElement); - } - } - - /** - * Parse an XML Catalog file. - * - * @param catalog The catalog to which this catalog file belongs - * @param fileUrl The URL or filename of the catalog file to process - * - * @throws MalformedURLException Improper fileUrl - * @throws IOException Error reading catalog file - */ - public void readCatalog(Catalog catalog, String fileUrl) - throws MalformedURLException, IOException, - CatalogException { - - URL url = null; - - try { - url = new URL(fileUrl); - } catch (MalformedURLException e) { - url = new URL("file:///" + fileUrl); + public String getCatalogParser(String namespaceURI, + String rootElement) { + namespaceURI = namespaceURI != null ? namespaceURI.trim() : ""; + return (String) namespaceMap.get("{"+namespaceURI+"}"+rootElement); } - debug = catalog.getCatalogManager().debug; + /** + * Parse an XML Catalog file. + * + * @param catalog The catalog to which this catalog file belongs + * @param fileUrl The URL or filename of the catalog file to process + * + * @throws MalformedURLException Improper fileUrl + * @throws IOException Error reading catalog file + */ + public void readCatalog(Catalog catalog, String fileUrl) + throws MalformedURLException, IOException, + CatalogException { - try { - URLConnection urlCon = url.openConnection(); - readCatalog(catalog, urlCon.getInputStream()); - } catch (FileNotFoundException e) { - catalog.getCatalogManager().debug.message(1, "Failed to load catalog, file not found", + URL url = null; + + try { + url = new URL(fileUrl); + } catch (MalformedURLException e) { + url = new URL("file:///" + fileUrl); + } + + debug = catalog.getCatalogManager().debug; + + try { + URLConnection urlCon = url.openConnection(); + readCatalog(catalog, urlCon.getInputStream()); + } catch (FileNotFoundException e) { + catalog.getCatalogManager().debug.message(1, "Failed to load catalog, file not found", url.toString()); - } - } - - /** - * Parse an XML Catalog stream. - * - * @param catalog The catalog to which this catalog file belongs - * @param is The input stream from which the catalog will be read - * - * @throws MalformedURLException Improper fileUrl - * @throws IOException Error reading catalog file - * @throws CatalogException A Catalog exception - */ - public void readCatalog(Catalog catalog, InputStream is) - throws IOException, CatalogException { - - // Create an instance of the parser - if (parserFactory == null && parserClass == null) { - debug.message(1, "Cannot read SAX catalog without a parser"); - throw new CatalogException(CatalogException.UNPARSEABLE); - } - - debug = catalog.getCatalogManager().debug; - EntityResolver bResolver = catalog.getCatalogManager().getBootstrapResolver(); - - this.catalog = catalog; - - try { - if (parserFactory != null) { - SAXParser parser = parserFactory.newSAXParser(); - SAXParserHandler spHandler = new SAXParserHandler(); - spHandler.setContentHandler(this); - if (bResolver != null) { - spHandler.setEntityResolver(bResolver); } - parser.parse(new InputSource(is), spHandler); - } else { - Parser parser = (Parser) ReflectUtil.forName(parserClass).newInstance(); - parser.setDocumentHandler(this); - if (bResolver != null) { - parser.setEntityResolver(bResolver); + } + + /** + * Parse an XML Catalog stream. + * + * @param catalog The catalog to which this catalog file belongs + * @param is The input stream from which the catalog will be read + * + * @throws MalformedURLException Improper fileUrl + * @throws IOException Error reading catalog file + * @throws CatalogException A Catalog exception + */ + public void readCatalog(Catalog catalog, InputStream is) + throws IOException, CatalogException { + + // Create an instance of the parser + if (parserFactory == null && parserClass == null) { + debug.message(1, "Cannot read SAX catalog without a parser"); + throw new CatalogException(CatalogException.UNPARSEABLE); } - parser.parse(new InputSource(is)); - } - } catch (ClassNotFoundException cnfe) { - throw new CatalogException(CatalogException.UNPARSEABLE); - } catch (IllegalAccessException iae) { - throw new CatalogException(CatalogException.UNPARSEABLE); - } catch (InstantiationException ie) { - throw new CatalogException(CatalogException.UNPARSEABLE); - } catch (ParserConfigurationException pce) { - throw new CatalogException(CatalogException.UNKNOWN_FORMAT); - } catch (SAXException se) { - Exception e = se.getException(); - // FIXME: there must be a better way - UnknownHostException uhe = new UnknownHostException(); - FileNotFoundException fnfe = new FileNotFoundException(); - if (e != null) { - if (e.getClass() == uhe.getClass()) { - throw new CatalogException(CatalogException.PARSE_FAILED, - e.toString()); - } else if (e.getClass() == fnfe.getClass()) { - throw new CatalogException(CatalogException.PARSE_FAILED, - e.toString()); + + debug = catalog.getCatalogManager().debug; + EntityResolver bResolver = catalog.getCatalogManager().getBootstrapResolver(); + + this.catalog = catalog; + + try { + if (parserFactory != null) { + SAXParser parser = parserFactory.newSAXParser(); + SAXParserHandler spHandler = new SAXParserHandler(); + spHandler.setContentHandler(this); + if (bResolver != null) { + spHandler.setEntityResolver(bResolver); + } + parser.parse(new InputSource(is), spHandler); + } else { + Parser parser = (Parser) ReflectUtil.forName(parserClass).newInstance(); + parser.setDocumentHandler(this); + if (bResolver != null) { + parser.setEntityResolver(bResolver); + } + parser.parse(new InputSource(is)); + } + } catch (ClassNotFoundException cnfe) { + throw new CatalogException(CatalogException.UNPARSEABLE); + } catch (IllegalAccessException iae) { + throw new CatalogException(CatalogException.UNPARSEABLE); + } catch (InstantiationException ie) { + throw new CatalogException(CatalogException.UNPARSEABLE); + } catch (ParserConfigurationException pce) { + throw new CatalogException(CatalogException.UNKNOWN_FORMAT); + } catch (SAXException se) { + Exception e = se.getException(); + // FIXME: there must be a better way + UnknownHostException uhe = new UnknownHostException(); + FileNotFoundException fnfe = new FileNotFoundException(); + if (e != null) { + if (e.getClass() == uhe.getClass()) { + throw new CatalogException(CatalogException.PARSE_FAILED, + e.toString()); + } else if (e.getClass() == fnfe.getClass()) { + throw new CatalogException(CatalogException.PARSE_FAILED, + e.toString()); + } + } + throw new CatalogException(se); } - } - throw new CatalogException(se); - } - } - - // ---------------------------------------------------------------------- - // Implement the SAX ContentHandler interface - - /** The SAX setDocumentLocator method. Does nothing. */ - public void setDocumentLocator (Locator locator) { - if (saxParser != null) { - saxParser.setDocumentLocator(locator); - } - } - - /** The SAX startDocument method. Does nothing. */ - public void startDocument () throws SAXException { - saxParser = null; - abandonHope = false; - return; - } - - /** The SAX endDocument method. Does nothing. */ - public void endDocument ()throws SAXException { - if (saxParser != null) { - saxParser.endDocument(); - } - } - - /** - * The SAX startElement method. - * - *

The catalog parser is selected based on the namespace of the - * first element encountered in the catalog.

- */ - public void startElement (String name, - AttributeList atts) - throws SAXException { - - if (abandonHope) { - return; } - if (saxParser == null) { - String prefix = ""; - if (name.indexOf(':') > 0) { - prefix = name.substring(0, name.indexOf(':')); - } + // ---------------------------------------------------------------------- + // Implement the SAX ContentHandler interface - String localName = name; - if (localName.indexOf(':') > 0) { - localName = localName.substring(localName.indexOf(':')+1); - } - - String namespaceURI = null; - if (prefix.equals("")) { - namespaceURI = atts.getValue("xmlns"); - } else { - namespaceURI = atts.getValue("xmlns:" + prefix); - } - - String saxParserClass = getCatalogParser(namespaceURI, - localName); - - if (saxParserClass == null) { - abandonHope = true; - if (namespaceURI == null) { - debug.message(2, "No Catalog parser for " + name); - } else { - debug.message(2, "No Catalog parser for " - + "{" + namespaceURI + "}" - + name); + /** The SAX setDocumentLocator method. Does nothing. */ + public void setDocumentLocator (Locator locator) { + if (saxParser != null) { + saxParser.setDocumentLocator(locator); } + } + + /** The SAX startDocument method. Does nothing. */ + public void startDocument () throws SAXException { + saxParser = null; + abandonHope = false; return; - } - - try { - saxParser = (SAXCatalogParser) - ReflectUtil.forName(saxParserClass).newInstance(); - - saxParser.setCatalog(catalog); - saxParser.startDocument(); - saxParser.startElement(name, atts); - } catch (ClassNotFoundException cnfe) { - saxParser = null; - abandonHope = true; - debug.message(2, cnfe.toString()); - } catch (InstantiationException ie) { - saxParser = null; - abandonHope = true; - debug.message(2, ie.toString()); - } catch (IllegalAccessException iae) { - saxParser = null; - abandonHope = true; - debug.message(2, iae.toString()); - } catch (ClassCastException cce ) { - saxParser = null; - abandonHope = true; - debug.message(2, cce.toString()); - } - } else { - saxParser.startElement(name, atts); - } - } - - /** - * The SAX2 startElement method. - * - *

The catalog parser is selected based on the namespace of the - * first element encountered in the catalog.

- */ - public void startElement (String namespaceURI, - String localName, - String qName, - Attributes atts) - throws SAXException { - - if (abandonHope) { - return; } - if (saxParser == null) { - String saxParserClass = getCatalogParser(namespaceURI, - localName); - - if (saxParserClass == null) { - abandonHope = true; - if (namespaceURI == null) { - debug.message(2, "No Catalog parser for " + localName); - } else { - debug.message(2, "No Catalog parser for " - + "{" + namespaceURI + "}" - + localName); + /** The SAX endDocument method. Does nothing. */ + public void endDocument ()throws SAXException { + if (saxParser != null) { + saxParser.endDocument(); } - return; - } - - try { - saxParser = (SAXCatalogParser) - ReflectUtil.forName(saxParserClass).newInstance(); - - saxParser.setCatalog(catalog); - saxParser.startDocument(); - saxParser.startElement(namespaceURI, localName, qName, atts); - } catch (ClassNotFoundException cnfe) { - saxParser = null; - abandonHope = true; - debug.message(2, cnfe.toString()); - } catch (InstantiationException ie) { - saxParser = null; - abandonHope = true; - debug.message(2, ie.toString()); - } catch (IllegalAccessException iae) { - saxParser = null; - abandonHope = true; - debug.message(2, iae.toString()); - } catch (ClassCastException cce ) { - saxParser = null; - abandonHope = true; - debug.message(2, cce.toString()); - } - } else { - saxParser.startElement(namespaceURI, localName, qName, atts); } - } - /** The SAX endElement method. Does nothing. */ - public void endElement (String name) throws SAXException { - if (saxParser != null) { - saxParser.endElement(name); - } - } + /** + * The SAX startElement method. + * + *

The catalog parser is selected based on the namespace of the + * first element encountered in the catalog.

+ */ + public void startElement (String name, + AttributeList atts) + throws SAXException { - /** The SAX2 endElement method. Does nothing. */ - public void endElement (String namespaceURI, - String localName, - String qName) throws SAXException { - if (saxParser != null) { - saxParser.endElement(namespaceURI, localName, qName); - } - } + if (abandonHope) { + return; + } - /** The SAX characters method. Does nothing. */ - public void characters (char ch[], int start, int length) - throws SAXException { - if (saxParser != null) { - saxParser.characters(ch, start, length); - } - } + if (saxParser == null) { + String prefix = ""; + if (name.indexOf(':') > 0) { + prefix = name.substring(0, name.indexOf(':')); + } - /** The SAX ignorableWhitespace method. Does nothing. */ - public void ignorableWhitespace (char ch[], int start, int length) - throws SAXException { - if (saxParser != null) { - saxParser.ignorableWhitespace(ch, start, length); - } - } + String localName = name; + if (localName.indexOf(':') > 0) { + localName = localName.substring(localName.indexOf(':')+1); + } - /** The SAX processingInstruction method. Does nothing. */ - public void processingInstruction (String target, String data) - throws SAXException { - if (saxParser != null) { - saxParser.processingInstruction(target, data); - } - } + String namespaceURI = null; + if (prefix.length() == 0) { + namespaceURI = atts.getValue("xmlns"); + } else { + namespaceURI = atts.getValue("xmlns:" + prefix); + } - /** The SAX startPrefixMapping method. Does nothing. */ - public void startPrefixMapping (String prefix, String uri) - throws SAXException { - if (saxParser != null) { - saxParser.startPrefixMapping (prefix, uri); - } - } + String saxParserClass = getCatalogParser(namespaceURI, + localName); - /** The SAX endPrefixMapping method. Does nothing. */ - public void endPrefixMapping (String prefix) - throws SAXException { - if (saxParser != null) { - saxParser.endPrefixMapping (prefix); - } - } + if (saxParserClass == null) { + abandonHope = true; + if (namespaceURI == null) { + debug.message(2, "No Catalog parser for " + name); + } else { + debug.message(2, "No Catalog parser for " + + "{" + namespaceURI + "}" + + name); + } + return; + } - /** The SAX skippedentity method. Does nothing. */ - public void skippedEntity (String name) - throws SAXException { - if (saxParser != null) { - saxParser.skippedEntity(name); + try { + saxParser = (SAXCatalogParser) + ReflectUtil.forName(saxParserClass).newInstance(); + + saxParser.setCatalog(catalog); + saxParser.startDocument(); + saxParser.startElement(name, atts); + } catch (ClassNotFoundException cnfe) { + saxParser = null; + abandonHope = true; + debug.message(2, cnfe.toString()); + } catch (InstantiationException ie) { + saxParser = null; + abandonHope = true; + debug.message(2, ie.toString()); + } catch (IllegalAccessException iae) { + saxParser = null; + abandonHope = true; + debug.message(2, iae.toString()); + } catch (ClassCastException cce ) { + saxParser = null; + abandonHope = true; + debug.message(2, cce.toString()); + } + } else { + saxParser.startElement(name, atts); + } + } + + /** + * The SAX2 startElement method. + * + *

The catalog parser is selected based on the namespace of the + * first element encountered in the catalog.

+ */ + public void startElement (String namespaceURI, + String localName, + String qName, + Attributes atts) + throws SAXException { + + if (abandonHope) { + return; + } + + if (saxParser == null) { + String saxParserClass = getCatalogParser(namespaceURI, + localName); + + if (saxParserClass == null) { + abandonHope = true; + if (namespaceURI == null) { + debug.message(2, "No Catalog parser for " + localName); + } else { + debug.message(2, "No Catalog parser for " + + "{" + namespaceURI + "}" + + localName); + } + return; + } + + try { + saxParser = (SAXCatalogParser) + ReflectUtil.forName(saxParserClass).newInstance(); + + saxParser.setCatalog(catalog); + saxParser.startDocument(); + saxParser.startElement(namespaceURI, localName, qName, atts); + } catch (ClassNotFoundException cnfe) { + saxParser = null; + abandonHope = true; + debug.message(2, cnfe.toString()); + } catch (InstantiationException ie) { + saxParser = null; + abandonHope = true; + debug.message(2, ie.toString()); + } catch (IllegalAccessException iae) { + saxParser = null; + abandonHope = true; + debug.message(2, iae.toString()); + } catch (ClassCastException cce ) { + saxParser = null; + abandonHope = true; + debug.message(2, cce.toString()); + } + } else { + saxParser.startElement(namespaceURI, localName, qName, atts); + } + } + + /** The SAX endElement method. Does nothing. */ + public void endElement (String name) throws SAXException { + if (saxParser != null) { + saxParser.endElement(name); + } + } + + /** The SAX2 endElement method. Does nothing. */ + public void endElement (String namespaceURI, + String localName, + String qName) throws SAXException { + if (saxParser != null) { + saxParser.endElement(namespaceURI, localName, qName); + } + } + + /** The SAX characters method. Does nothing. */ + public void characters (char ch[], int start, int length) + throws SAXException { + if (saxParser != null) { + saxParser.characters(ch, start, length); + } + } + + /** The SAX ignorableWhitespace method. Does nothing. */ + public void ignorableWhitespace (char ch[], int start, int length) + throws SAXException { + if (saxParser != null) { + saxParser.ignorableWhitespace(ch, start, length); + } + } + + /** The SAX processingInstruction method. Does nothing. */ + public void processingInstruction (String target, String data) + throws SAXException { + if (saxParser != null) { + saxParser.processingInstruction(target, data); + } + } + + /** The SAX startPrefixMapping method. Does nothing. */ + public void startPrefixMapping (String prefix, String uri) + throws SAXException { + if (saxParser != null) { + saxParser.startPrefixMapping (prefix, uri); + } + } + + /** The SAX endPrefixMapping method. Does nothing. */ + public void endPrefixMapping (String prefix) + throws SAXException { + if (saxParser != null) { + saxParser.endPrefixMapping (prefix); + } + } + + /** The SAX skippedentity method. Does nothing. */ + public void skippedEntity (String name) + throws SAXException { + if (saxParser != null) { + saxParser.skippedEntity(name); + } } - } } diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXParserHandler.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXParserHandler.java index 0d07510aafb..efcf32950df 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXParserHandler.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/SAXParserHandler.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// SAXParserHandler.java - An entity-resolving DefaultHandler - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/TR9401CatalogReader.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/TR9401CatalogReader.java index b27481049f6..fb39a433063 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/TR9401CatalogReader.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/TR9401CatalogReader.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// TR9401CatalogReader.java - Read OASIS Catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -23,13 +17,14 @@ package com.sun.org.apache.xml.internal.resolver.readers; -import java.io.InputStream; -import java.io.IOException; -import java.net.MalformedURLException; -import java.util.Vector; import com.sun.org.apache.xml.internal.resolver.Catalog; import com.sun.org.apache.xml.internal.resolver.CatalogEntry; import com.sun.org.apache.xml.internal.resolver.CatalogException; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.util.Locale; +import java.util.Vector; /** * Parses OASIS Open Catalog files. @@ -97,7 +92,7 @@ public class TR9401CatalogReader extends TextCatalogReader { if (caseSensitive) { entryToken = token; } else { - entryToken = token.toUpperCase(); + entryToken = token.toUpperCase(Locale.ENGLISH); } if (entryToken.equals("DELEGATE")) { diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/TextCatalogReader.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/TextCatalogReader.java index 3493e48f91b..9440e3370cb 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/TextCatalogReader.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/TextCatalogReader.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// TextCatalogReader.java - Read text/plain Catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -23,18 +17,19 @@ package com.sun.org.apache.xml.internal.resolver.readers; -import java.io.InputStream; -import java.io.IOException; -import java.io.FileNotFoundException; -import java.net.URL; -import java.net.URLConnection; -import java.net.MalformedURLException; -import java.util.Vector; -import java.util.Stack; import com.sun.org.apache.xml.internal.resolver.Catalog; import com.sun.org.apache.xml.internal.resolver.CatalogEntry; import com.sun.org.apache.xml.internal.resolver.CatalogException; import com.sun.org.apache.xml.internal.resolver.readers.CatalogReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.Locale; +import java.util.Stack; +import java.util.Vector; /** * Parses plain text Catalog files. @@ -140,7 +135,7 @@ public class TextCatalogReader implements CatalogReader { if (caseSensitive) { entryToken = token; } else { - entryToken = token.toUpperCase(); + entryToken = token.toUpperCase(Locale.ENGLISH); } try { diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/XCatalogReader.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/XCatalogReader.java index 3d94545ae23..ce8cc40d6a8 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/XCatalogReader.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/readers/XCatalogReader.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// XCatalogReader.java - Read XML Catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -34,7 +28,7 @@ import org.xml.sax.*; import javax.xml.parsers.*; /** - * Parse "xcatalog" XML Catalog files, this is the XML Catalog format + * Parse "XCatalog" XML Catalog files, this is the XML Catalog format * developed by John Cowan and supported by Apache. * * @see Catalog @@ -52,6 +46,7 @@ public class XCatalogReader extends SAXCatalogReader implements SAXCatalogParser /** Set the current catalog. */ public void setCatalog (Catalog catalog) { this.catalog = catalog; + debug = catalog.getCatalogManager().debug; } /** Get the current catalog. */ @@ -59,13 +54,19 @@ public class XCatalogReader extends SAXCatalogReader implements SAXCatalogParser return catalog; } - /** The constructor */ - public XCatalogReader(SAXParserFactory parserFactory) { + /** Default constructor */ + public XCatalogReader() { + super(); + } + + /** Constructor allowing for providing custom SAX parser factory */ + public XCatalogReader(SAXParserFactory parserFactory, Catalog catalog) { super(parserFactory); + setCatalog(catalog); } // ---------------------------------------------------------------------- - // Implement the SAX DocumentHandler interface + // Implement the SAX ContentHandler interface /** The SAX setDocumentLocator method does nothing. */ public void setDocumentLocator (Locator locator) { @@ -106,44 +107,44 @@ public class XCatalogReader extends SAXCatalogReader implements SAXCatalogParser Vector entryArgs = new Vector(); if (localName.equals("Base")) { - entryType = catalog.BASE; + entryType = Catalog.BASE; entryArgs.add(atts.getValue("HRef")); - catalog.getCatalogManager().debug.message(4, "Base", atts.getValue("HRef")); + debug.message(4, "Base", atts.getValue("HRef")); } else if (localName.equals("Delegate")) { - entryType = catalog.DELEGATE_PUBLIC; - entryArgs.add(atts.getValue("PublicId")); + entryType = Catalog.DELEGATE_PUBLIC; + entryArgs.add(atts.getValue("PublicID")); entryArgs.add(atts.getValue("HRef")); - catalog.getCatalogManager().debug.message(4, "Delegate", - PublicId.normalize(atts.getValue("PublicId")), + debug.message(4, "Delegate", + PublicId.normalize(atts.getValue("PublicID")), atts.getValue("HRef")); } else if (localName.equals("Extend")) { - entryType = catalog.CATALOG; + entryType = Catalog.CATALOG; entryArgs.add(atts.getValue("HRef")); - catalog.getCatalogManager().debug.message(4, "Extend", atts.getValue("HRef")); + debug.message(4, "Extend", atts.getValue("HRef")); } else if (localName.equals("Map")) { - entryType = catalog.PUBLIC; - entryArgs.add(atts.getValue("PublicId")); + entryType = Catalog.PUBLIC; + entryArgs.add(atts.getValue("PublicID")); entryArgs.add(atts.getValue("HRef")); - catalog.getCatalogManager().debug.message(4, "Map", - PublicId.normalize(atts.getValue("PublicId")), + debug.message(4, "Map", + PublicId.normalize(atts.getValue("PublicID")), atts.getValue("HRef")); } else if (localName.equals("Remap")) { - entryType = catalog.SYSTEM; - entryArgs.add(atts.getValue("SystemId")); + entryType = Catalog.SYSTEM; + entryArgs.add(atts.getValue("SystemID")); entryArgs.add(atts.getValue("HRef")); - catalog.getCatalogManager().debug.message(4, "Remap", - atts.getValue("SystemId"), + debug.message(4, "Remap", + atts.getValue("SystemID"), atts.getValue("HRef")); - } else if (localName.equals("XMLCatalog")) { + } else if (localName.equals("XCatalog")) { // nop, start of catalog } else { // This is equivalent to an invalid catalog entry type - catalog.getCatalogManager().debug.message(1, "Invalid catalog entry type", localName); + debug.message(1, "Invalid catalog entry type", localName); } if (entryType >= 0) { @@ -152,21 +153,21 @@ public class XCatalogReader extends SAXCatalogReader implements SAXCatalogParser catalog.addEntry(ce); } catch (CatalogException cex) { if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) { - catalog.getCatalogManager().debug.message(1, "Invalid catalog entry type", localName); + debug.message(1, "Invalid catalog entry type", localName); } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) { - catalog.getCatalogManager().debug.message(1, "Invalid catalog entry", localName); + debug.message(1, "Invalid catalog entry", localName); } } } - } + } - /** The SAX endElement method does nothing. */ - public void endElement (String namespaceURI, - String localName, - String qName) - throws SAXException { - return; - } + /** The SAX endElement method does nothing. */ + public void endElement (String namespaceURI, + String localName, + String qName) + throws SAXException { + return; + } /** The SAX characters method does nothing. */ public void characters (char ch[], int start, int length) @@ -185,4 +186,23 @@ public class XCatalogReader extends SAXCatalogReader implements SAXCatalogParser throws SAXException { return; } + + /** The SAX skippedEntity method does nothing. */ + public void skippedEntity (String name) + throws SAXException { + return; + } + + /** The SAX startPrefixMapping method does nothing. */ + public void startPrefixMapping(String prefix, String uri) + throws SAXException { + return; + } + + /** The SAX endPrefixMapping method does nothing. */ + public void endPrefixMapping(String prefix) + throws SAXException { + return; + } + } diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/CatalogResolver.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/CatalogResolver.java index 25951aa490b..bf82547188b 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/CatalogResolver.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/CatalogResolver.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// CatalogResolver.java - A SAX EntityResolver/JAXP URI Resolver - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -218,7 +212,10 @@ public class CatalogResolver implements EntityResolver, URIResolver { return iSource; } catch (Exception e) { - catalogManager.debug.message(1, "Failed to create InputSource", resolved); + catalogManager.debug.message(1, + "Failed to create InputSource (" + + e.toString() + + ")", resolved); return null; } } diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingParser.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingParser.java index 0d90d573a39..354ac1e60c0 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingParser.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingParser.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// ResolvingParser.java - An interface for reading catalog files - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -67,17 +61,12 @@ import com.sun.org.apache.xml.internal.resolver.helpers.FileURL; */ public class ResolvingParser implements Parser, DTDHandler, DocumentHandler, EntityResolver { - /** Make the parser Namespace aware? */ - public static boolean namespaceAware = true; - - /** Make the parser validating? */ - public static boolean validating = false; /** Suppress explanatory message? * * @see #parse(InputSource) */ - public static boolean suppressExplanation = false; + private static final boolean suppressExplanation = false; /** The underlying parser. */ private SAXParser saxParser = null; @@ -103,9 +92,6 @@ public class ResolvingParser /** Are we in the prolog? Is an oasis-xml-catalog PI valid now? */ private boolean allowXMLCatalogPI = false; - /** Has an oasis-xml-catalog PI been seen? */ - private boolean oasisXMLCatalogPI = false; - /** The base URI of the input document, if known. */ private URL baseURL = null; @@ -125,8 +111,8 @@ public class ResolvingParser catalogResolver = new CatalogResolver(catalogManager); SAXParserFactory spf = catalogManager.useServicesMechanism() ? SAXParserFactory.newInstance() : new SAXParserFactoryImpl(); - spf.setNamespaceAware(namespaceAware); - spf.setValidating(validating); + spf.setNamespaceAware(true); + spf.setValidating(false); try { saxParser = spf.newSAXParser(); @@ -289,7 +275,6 @@ public class ResolvingParser if (catalog != null) { try { catalogManager.debug.message(4,"oasis-xml-catalog", catalog.toString()); - oasisXMLCatalogPI = true; if (piCatalogResolver == null) { piCatalogResolver = new CatalogResolver(true); @@ -396,7 +381,10 @@ public class ResolvingParser return iSource; } catch (Exception e) { - catalogManager.debug.message(1, "Failed to create InputSource", resolved); + catalogManager.debug.message(1, + "Failed to create InputSource (" + + e.toString() + + ")", resolved); return null; } } else { diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingXMLFilter.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingXMLFilter.java index 660e4af6a13..f196e3726fe 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingXMLFilter.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingXMLFilter.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// ResolvingXMLFilter.java - An XMLFilter that performs catalog resolution - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -61,7 +55,7 @@ public class ResolvingXMLFilter extends XMLFilterImpl { * * @see #parse(InputSource) */ - public static boolean suppressExplanation = false; + private static boolean suppressExplanation = false; /** The manager for the underlying resolver. */ CatalogManager catalogManager = CatalogManager.getStaticManager(); @@ -75,9 +69,6 @@ public class ResolvingXMLFilter extends XMLFilterImpl { /** Are we in the prolog? Is an oasis-xml-catalog PI valid now? */ private boolean allowXMLCatalogPI = false; - /** Has an oasis-xml-catalog PI been seen? */ - private boolean oasisXMLCatalogPI = false; - /** The base URI of the input document, if known. */ private URL baseURL = null; @@ -202,7 +193,10 @@ public class ResolvingXMLFilter extends XMLFilterImpl { return iSource; } catch (Exception e) { - catalogManager.debug.message(1, "Failed to create InputSource", resolved); + catalogManager.debug.message(1, + "Failed to create InputSource (" + + e.toString() + + ")", resolved); return null; } } else { @@ -289,7 +283,6 @@ public class ResolvingXMLFilter extends XMLFilterImpl { if (catalog != null) { try { catalogManager.debug.message(4,"oasis-xml-catalog", catalog.toString()); - oasisXMLCatalogPI = true; if (piCatalogResolver == null) { piCatalogResolver = new CatalogResolver(true); diff --git a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingXMLReader.java b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingXMLReader.java index 0a98cd8dbe6..ad9bd7c6e19 100644 --- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingXMLReader.java +++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xml/internal/resolver/tools/ResolvingXMLReader.java @@ -1,16 +1,10 @@ /* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -// ResolvingXMLReader.java - An XMLReader that performs catalog resolution - -/* - * Copyright 2001-2004 The Apache Software Foundation or its licensors, - * as applicable. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -23,7 +17,6 @@ package com.sun.org.apache.xml.internal.resolver.tools; -import org.xml.sax.*; import javax.xml.parsers.*; @@ -48,10 +41,10 @@ import com.sun.org.apache.xml.internal.resolver.*; */ public class ResolvingXMLReader extends ResolvingXMLFilter { /** Make the parser Namespace aware? */ - public static boolean namespaceAware = true; + private static final boolean namespaceAware = true; /** Make the parser validating? */ - public static boolean validating = false; + private static final boolean validating = false; /** * Construct a new reader from the JAXP factory. diff --git a/jaxws/.hgignore b/jaxws/.hgignore index 36b21b16e0c..94668d195b7 100644 --- a/jaxws/.hgignore +++ b/jaxws/.hgignore @@ -2,7 +2,7 @@ ^dist/ ^drop/ ^drop_included/ -^webrev/ +^webrev /nbproject/private/ ^.hgtip .DS_Store diff --git a/jaxws/.hgtags b/jaxws/.hgtags index 121910c3483..e768a03efa3 100644 --- a/jaxws/.hgtags +++ b/jaxws/.hgtags @@ -272,3 +272,7 @@ ce19e0403ec8a80223cc47320c905b2a4d45881d jdk9-b23 0b35542d6bf32af23a9fc59a37c52aa091fee388 jdk9-b24 ab8726a0fcde897e4cfd1ea581ed90b0d8a7cbb7 jdk9-b25 9b43f3993b96c2213428e95127a2d3fd0e709d3a jdk9-b26 +dcaa586ab756420e9a62643793bacef2c84bf637 jdk9-b27 +5282a14f131f897cc9575872c0fae72d47dc4e65 jdk9-b28 +3d1a4bfb6abbf5011ba6d8896301ee3b6ef3ba72 jdk9-b29 +e58d3ea638c3824f01547596b2a98aa5f77c4a5c jdk9-b30 diff --git a/jdk/.hgignore b/jdk/.hgignore index d8754b13e1f..1ed4344d03f 100644 --- a/jdk/.hgignore +++ b/jdk/.hgignore @@ -1,5 +1,6 @@ ^build/ ^dist/ +^webrev ^testoutput/ /nbproject/private/ ^make/netbeans/.*/build/ diff --git a/jdk/.hgtags b/jdk/.hgtags index 9b0360a42ef..bea594c1bfc 100644 --- a/jdk/.hgtags +++ b/jdk/.hgtags @@ -269,3 +269,7 @@ f87c5be90e01a7ffb47947108eb3e0b0b1920880 jdk9-b20 875450e7ef8dde8f59db662ec1351ea30b8cb35d jdk9-b24 a31efe49556a7c12f9ea2c9ee8b4fae8aa67723a jdk9-b25 dde9f5cfde5f46e62ceb5fab81151578e5277aef jdk9-b26 +f0870554049807d3392bd7976ab114f7f2b7bafa jdk9-b27 +1828f73b35cfe35e460e41fd6e087ab1f83e0621 jdk9-b28 +2da27e8e2c865e154f0c2eb9009f011a44649b11 jdk9-b29 +8d24fb4493f13d380a2adf62d444e1e5a4451f37 jdk9-b30 diff --git a/jdk/make/copy/Copy-java.base.gmk b/jdk/make/copy/Copy-java.base.gmk index c624ff80874..97fc8193ef7 100644 --- a/jdk/make/copy/Copy-java.base.gmk +++ b/jdk/make/copy/Copy-java.base.gmk @@ -208,20 +208,6 @@ BASE_CONF_FILES += $(CACERTS_DST) ################################################################################ -ifeq ($(OPENJDK_TARGET_OS), solaris) - - SUNPKCS11_CFG_SRC := $(JDK_TOPDIR)/src/java.base/share/conf/security/sunpkcs11-solaris.cfg - SUNPKCS11_CFG_DST := $(JDK_OUTPUTDIR)/lib/security/sunpkcs11-solaris.cfg - - $(SUNPKCS11_CFG_DST): $(SUNPKCS11_CFG_SRC) - $(call install-file) - - BASE_CONF_FILES += $(SUNPKCS11_CFG_DST) - -endif - -################################################################################ - $(JDK_OUTPUTDIR)/lib/net.properties: $(JDK_TOPDIR)/src/java.base/share/conf/net.properties $(ECHO) $(LOG_INFO) Copying $(@F) $(call install-file) diff --git a/jdk/make/copy/Copy-jdk.crypto.pkcs11.gmk b/jdk/make/copy/Copy-jdk.crypto.pkcs11.gmk new file mode 100644 index 00000000000..6db3dc97a8e --- /dev/null +++ b/jdk/make/copy/Copy-jdk.crypto.pkcs11.gmk @@ -0,0 +1,50 @@ +# +# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +include CopyCommon.gmk + +################################################################################ + +ifeq ($(OPENJDK_TARGET_OS), solaris) + + SUNPKCS11_CFG_SRC := \ + $(JDK_TOPDIR)/src/jdk.crypto.pkcs11/solaris/conf/security/sunpkcs11-solaris.cfg + SUNPKCS11_CFG_DST := $(JDK_OUTPUTDIR)/lib/security/sunpkcs11-solaris.cfg + + $(SUNPKCS11_CFG_DST): $(SUNPKCS11_CFG_SRC) + $(call install-file) + + SECURITY_PKCS11_CONF_FILES += $(SUNPKCS11_CFG_DST) + +endif + +################################################################################ + +jdk.crypto.pkcs11: $(SECURITY_PKCS11_CONF_FILES) + +all: jdk.crypto.pkcs11 + +.PHONY: all jdk.crypto.pkcs11 + diff --git a/jdk/src/java.base/share/classes/java/lang/Integer.java b/jdk/src/java.base/share/classes/java/lang/Integer.java index 94b0aff7970..d22ce9bef35 100644 --- a/jdk/src/java.base/share/classes/java/lang/Integer.java +++ b/jdk/src/java.base/share/classes/java/lang/Integer.java @@ -592,37 +592,6 @@ public final class Integer extends Number implements Comparable { } } - /** - * Parses the {@link CharSequence} argument as a signed {@code int} in the - * specified {@code radix}, beginning at the specified {@code beginIndex} - * and extending to the end of the sequence. - * - *

The method does not take steps to guard against the - * {@code CharSequence} being mutated while parsing. - * - * @param s the {@code CharSequence} containing the {@code int} - * representation to be parsed - * @param radix the radix to be used while parsing {@code s}. - * @param beginIndex the beginning index, inclusive. - * @return the signed {@code int} represented by the subsequence in - * the specified radix. - * @throws NullPointerException if {@code s} is null. - * @throws IndexOutOfBoundsException if {@code beginIndex} is - * negative, or if {@code beginIndex} is greater than - * {@code s.length()}. - * @throws NumberFormatException if the {@code CharSequence} does not - * contain a parsable {@code int} in the specified - * {@code radix}, or if {@code radix} is either smaller than - * {@link java.lang.Character#MIN_RADIX} or larger than - * {@link java.lang.Character#MAX_RADIX}. - * @since 1.9 - */ - public static int parseInt(CharSequence s, int radix, int beginIndex) - throws NumberFormatException { - // forces an implicit null check of s - return parseInt(s, radix, beginIndex, s.length()); - } - /** * Parses the {@link CharSequence} argument as a signed {@code int} in the * specified {@code radix}, beginning at the specified {@code beginIndex} @@ -633,9 +602,9 @@ public final class Integer extends Number implements Comparable { * * @param s the {@code CharSequence} containing the {@code int} * representation to be parsed - * @param radix the radix to be used while parsing {@code s}. * @param beginIndex the beginning index, inclusive. * @param endIndex the ending index, exclusive. + * @param radix the radix to be used while parsing {@code s}. * @return the signed {@code int} represented by the subsequence in * the specified radix. * @throws NullPointerException if {@code s} is null. @@ -650,7 +619,7 @@ public final class Integer extends Number implements Comparable { * {@link java.lang.Character#MAX_RADIX}. * @since 1.9 */ - public static int parseInt(CharSequence s, int radix, int beginIndex, int endIndex) + public static int parseInt(CharSequence s, int beginIndex, int endIndex, int radix) throws NumberFormatException { s = Objects.requireNonNull(s); @@ -690,7 +659,7 @@ public final class Integer extends Number implements Comparable { int result = 0; while (i < endIndex) { // Accumulating negatively avoids surprises near MAX_VALUE - int digit = Character.digit(s.charAt(i++), radix); + int digit = Character.digit(s.charAt(i), radix); if (digit < 0 || result < multmin) { throw NumberFormatException.forCharSequence(s, beginIndex, endIndex, i); @@ -700,6 +669,7 @@ public final class Integer extends Number implements Comparable { throw NumberFormatException.forCharSequence(s, beginIndex, endIndex, i); } + i++; result -= digit; } return negative ? result : -result; @@ -805,37 +775,6 @@ public final class Integer extends Number implements Comparable { } } - /** - * Parses the {@link CharSequence} argument as an unsigned {@code int} in - * the specified {@code radix}, beginning at the specified - * {@code beginIndex} and extending to the end of the sequence. - * - *

The method does not take steps to guard against the - * {@code CharSequence} being mutated while parsing. - * - * @param s the {@code CharSequence} containing the unsigned - * {@code int} representation to be parsed - * @param radix the radix to be used while parsing {@code s}. - * @param beginIndex the beginning index, inclusive. - * @return the unsigned {@code int} represented by the subsequence in - * the specified radix. - * @throws NullPointerException if {@code s} is null. - * @throws IndexOutOfBoundsException if {@code beginIndex} is - * negative, or if {@code beginIndex} is greater than - * {@code s.length()}. - * @throws NumberFormatException if the {@code CharSequence} does not - * contain a parsable unsigned {@code int} in the specified - * {@code radix}, or if {@code radix} is either smaller than - * {@link java.lang.Character#MIN_RADIX} or larger than - * {@link java.lang.Character#MAX_RADIX}. - * @since 1.9 - */ - public static int parseUnsignedInt(CharSequence s, int radix, int beginIndex) - throws NumberFormatException { - // forces an implicit null check of s - return parseUnsignedInt(s, radix, beginIndex, s.length()); - } - /** * Parses the {@link CharSequence} argument as an unsigned {@code int} in * the specified {@code radix}, beginning at the specified @@ -846,9 +785,9 @@ public final class Integer extends Number implements Comparable { * * @param s the {@code CharSequence} containing the unsigned * {@code int} representation to be parsed - * @param radix the radix to be used while parsing {@code s}. * @param beginIndex the beginning index, inclusive. * @param endIndex the ending index, exclusive. + * @param radix the radix to be used while parsing {@code s}. * @return the unsigned {@code int} represented by the subsequence in * the specified radix. * @throws NullPointerException if {@code s} is null. @@ -863,7 +802,7 @@ public final class Integer extends Number implements Comparable { * {@link java.lang.Character#MAX_RADIX}. * @since 1.9 */ - public static int parseUnsignedInt(CharSequence s, int radix, int beginIndex, int endIndex) + public static int parseUnsignedInt(CharSequence s, int beginIndex, int endIndex, int radix) throws NumberFormatException { s = Objects.requireNonNull(s); @@ -881,9 +820,9 @@ public final class Integer extends Number implements Comparable { } else { if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits (radix == 10 && len <= 9)) { // Integer.MAX_VALUE in base 10 is 10 digits - return parseInt(s, radix, start, start + len); + return parseInt(s, start, start + len, radix); } else { - long ell = Long.parseLong(s, radix, start, start + len); + long ell = Long.parseLong(s, start, start + len, radix); if ((ell & 0xffff_ffff_0000_0000L) == 0) { return (int) ell; } else { diff --git a/jdk/src/java.base/share/classes/java/lang/Long.java b/jdk/src/java.base/share/classes/java/lang/Long.java index 76caa77fac7..bcf33fccff8 100644 --- a/jdk/src/java.base/share/classes/java/lang/Long.java +++ b/jdk/src/java.base/share/classes/java/lang/Long.java @@ -604,37 +604,6 @@ public final class Long extends Number implements Comparable { } } - /** - * Parses the {@link CharSequence} argument as a signed {@code long} in - * the specified {@code radix}, beginning at the specified {@code beginIndex} - * and extending to the end of the sequence. - * - *

The method does not take steps to guard against the - * {@code CharSequence} being mutated while parsing. - * - * @param s the {@code CharSequence} containing the {@code long} - * representation to be parsed - * @param radix the radix to be used while parsing {@code s}. - * @param beginIndex the beginning index, inclusive. - * @return the signed {@code long} represented by the subsequence in - * the specified radix. - * @throws NullPointerException if {@code s} is null. - * @throws IndexOutOfBoundsException if {@code beginIndex} is - * negative, or if {@code beginIndex} is greater than - * {@code s.length()}. - * @throws NumberFormatException if the {@code CharSequence} does not - * contain a parsable {@code long} in the specified - * {@code radix}, or if {@code radix} is either smaller than - * {@link java.lang.Character#MIN_RADIX} or larger than - * {@link java.lang.Character#MAX_RADIX}. - * @since 1.9 - */ - public static long parseLong(CharSequence s, int radix, int beginIndex) - throws NumberFormatException { - // forces a null check of s - return parseLong(s, radix, beginIndex, s.length()); - } - /** * Parses the {@link CharSequence} argument as a signed {@code long} in * the specified {@code radix}, beginning at the specified @@ -645,9 +614,9 @@ public final class Long extends Number implements Comparable { * * @param s the {@code CharSequence} containing the {@code long} * representation to be parsed - * @param radix the radix to be used while parsing {@code s}. * @param beginIndex the beginning index, inclusive. * @param endIndex the ending index, exclusive. + * @param radix the radix to be used while parsing {@code s}. * @return the signed {@code long} represented by the subsequence in * the specified radix. * @throws NullPointerException if {@code s} is null. @@ -662,7 +631,7 @@ public final class Long extends Number implements Comparable { * {@link java.lang.Character#MAX_RADIX}. * @since 1.9 */ - public static long parseLong(CharSequence s, int radix, int beginIndex, int endIndex) + public static long parseLong(CharSequence s, int beginIndex, int endIndex, int radix) throws NumberFormatException { s = Objects.requireNonNull(s); @@ -702,7 +671,7 @@ public final class Long extends Number implements Comparable { long result = 0; while (i < endIndex) { // Accumulating negatively avoids surprises near MAX_VALUE - int digit = Character.digit(s.charAt(i++), radix); + int digit = Character.digit(s.charAt(i), radix); if (digit < 0 || result < multmin) { throw NumberFormatException.forCharSequence(s, beginIndex, endIndex, i); @@ -712,6 +681,7 @@ public final class Long extends Number implements Comparable { throw NumberFormatException.forCharSequence(s, beginIndex, endIndex, i); } + i++; result -= digit; } return negative ? result : -result; @@ -811,7 +781,7 @@ public final class Long extends Number implements Comparable { } // No need for range checks on len due to testing above. - long first = parseLong(s, radix, 0, len - 1); + long first = parseLong(s, 0, len - 1, radix); int second = Character.digit(s.charAt(len - 1), radix); if (second < 0) { throw new NumberFormatException("Bad digit at end of " + s); @@ -880,37 +850,6 @@ public final class Long extends Number implements Comparable { } } - /** - * Parses the {@link CharSequence} argument as an unsigned {@code long} in - * the specified {@code radix}, beginning at the specified - * {@code beginIndex} and extending to the end of the sequence. - * - *

The method does not take steps to guard against the - * {@code CharSequence} being mutated while parsing. - * - * @param s the {@code CharSequence} containing the unsigned - * {@code long} representation to be parsed - * @param radix the radix to be used while parsing {@code s}. - * @param beginIndex the beginning index, inclusive. - * @return the unsigned {@code long} represented by the subsequence in - * the specified radix. - * @throws NullPointerException if {@code s} is null. - * @throws IndexOutOfBoundsException if {@code beginIndex} is - * negative, or if {@code beginIndex} is greater than - * {@code s.length()}. - * @throws NumberFormatException if the {@code CharSequence} does not - * contain a parsable unsigned {@code long} in the specified - * {@code radix}, or if {@code radix} is either smaller than - * {@link java.lang.Character#MIN_RADIX} or larger than - * {@link java.lang.Character#MAX_RADIX}. - * @since 1.9 - */ - public static long parseUnsignedLong(CharSequence s, int radix, int beginIndex) - throws NumberFormatException { - // forces a null check of s - return parseUnsignedLong(s, radix, beginIndex, s.length()); - } - /** * Parses the {@link CharSequence} argument as an unsigned {@code long} in * the specified {@code radix}, beginning at the specified @@ -921,9 +860,9 @@ public final class Long extends Number implements Comparable { * * @param s the {@code CharSequence} containing the unsigned * {@code long} representation to be parsed - * @param radix the radix to be used while parsing {@code s}. * @param beginIndex the beginning index, inclusive. * @param endIndex the ending index, exclusive. + * @param radix the radix to be used while parsing {@code s}. * @return the unsigned {@code long} represented by the subsequence in * the specified radix. * @throws NullPointerException if {@code s} is null. @@ -938,7 +877,7 @@ public final class Long extends Number implements Comparable { * {@link java.lang.Character#MAX_RADIX}. * @since 1.9 */ - public static long parseUnsignedLong(CharSequence s, int radix, int beginIndex, int endIndex) + public static long parseUnsignedLong(CharSequence s, int beginIndex, int endIndex, int radix) throws NumberFormatException { s = Objects.requireNonNull(s); @@ -955,11 +894,11 @@ public final class Long extends Number implements Comparable { } else { if (len <= 12 || // Long.MAX_VALUE in Character.MAX_RADIX is 13 digits (radix == 10 && len <= 18) ) { // Long.MAX_VALUE in base 10 is 19 digits - return parseLong(s, radix, start, start + len); + return parseLong(s, start, start + len, radix); } // No need for range checks on end due to testing above. - long first = parseLong(s, radix, start, start + len - 1); + long first = parseLong(s, start, start + len - 1, radix); int second = Character.digit(s.charAt(start + len - 1), radix); if (second < 0) { throw new NumberFormatException("Bad digit at end of " + diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java index f860c77e5e2..74f53e47906 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java @@ -50,31 +50,31 @@ import jdk.internal.org.objectweb.asm.Type; * * All bound arguments are encapsulated in dedicated species. */ -/* non-public */ abstract class BoundMethodHandle extends MethodHandle { +/*non-public*/ abstract class BoundMethodHandle extends MethodHandle { - /* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) { + /*non-public*/ BoundMethodHandle(MethodType type, LambdaForm form) { super(type, form); + assert(speciesData() == speciesData(form)); } // // BMH API and internals // - static MethodHandle bindSingle(MethodType type, LambdaForm form, BasicType xtype, Object x) { + static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, BasicType xtype, Object x) { // for some type signatures, there exist pre-defined concrete BMH classes try { switch (xtype) { case L_TYPE: - if (true) return bindSingle(type, form, x); // Use known fast path. - return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(L_TYPE).constructor[0].invokeBasic(type, form, x); + return bindSingle(type, form, x); // Use known fast path. case I_TYPE: - return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x)); + return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(I_TYPE).constructor().invokeBasic(type, form, ValueConversions.widenSubword(x)); case J_TYPE: - return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor[0].invokeBasic(type, form, (long) x); + return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(J_TYPE).constructor().invokeBasic(type, form, (long) x); case F_TYPE: - return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor[0].invokeBasic(type, form, (float) x); + return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(F_TYPE).constructor().invokeBasic(type, form, (float) x); case D_TYPE: - return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor[0].invokeBasic(type, form, (double) x); + return (BoundMethodHandle) SpeciesData.EMPTY.extendWith(D_TYPE).constructor().invokeBasic(type, form, (double) x); default : throw newInternalError("unexpected xtype: " + xtype); } } catch (Throwable t) { @@ -82,49 +82,61 @@ import jdk.internal.org.objectweb.asm.Type; } } - static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) { - return new Species_L(type, form, x); + /*non-public*/ + LambdaFormEditor editor() { + return form.editor(); } - MethodHandle cloneExtend(MethodType type, LambdaForm form, BasicType xtype, Object x) { - try { - switch (xtype) { - case L_TYPE: return copyWithExtendL(type, form, x); - case I_TYPE: return copyWithExtendI(type, form, ValueConversions.widenSubword(x)); - case J_TYPE: return copyWithExtendJ(type, form, (long) x); - case F_TYPE: return copyWithExtendF(type, form, (float) x); - case D_TYPE: return copyWithExtendD(type, form, (double) x); - } - } catch (Throwable t) { - throw newInternalError(t); + static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, Object x) { + return Species_L.make(type, form, x); + } + + @Override // there is a default binder in the super class, for 'L' types only + /*non-public*/ + BoundMethodHandle bindArgumentL(int pos, Object value) { + return editor().bindArgumentL(this, pos, value); + } + /*non-public*/ + BoundMethodHandle bindArgumentI(int pos, int value) { + return editor().bindArgumentI(this, pos, value); + } + /*non-public*/ + BoundMethodHandle bindArgumentJ(int pos, long value) { + return editor().bindArgumentJ(this, pos, value); + } + /*non-public*/ + BoundMethodHandle bindArgumentF(int pos, float value) { + return editor().bindArgumentF(this, pos, value); + } + /*non-public*/ + BoundMethodHandle bindArgumentD(int pos, double value) { + return editor().bindArgumentD(this, pos, value); + } + + @Override + BoundMethodHandle rebind() { + if (!tooComplex()) { + return this; } - throw newInternalError("unexpected type: " + xtype); + return makeReinvoker(this); } - @Override - MethodHandle bindArgument(int pos, BasicType basicType, Object value) { - MethodType type = type().dropParameterTypes(pos, pos+1); - LambdaForm form = internalForm().bind(1+pos, speciesData()); - return cloneExtend(type, form, basicType, value); + private boolean tooComplex() { + return (fieldCount() > FIELD_COUNT_THRESHOLD || + form.expressionCount() > FORM_EXPRESSION_THRESHOLD); } + private static final int FIELD_COUNT_THRESHOLD = 12; // largest convenient BMH field count + private static final int FORM_EXPRESSION_THRESHOLD = 24; // largest convenient BMH expression count - @Override - MethodHandle dropArguments(MethodType srcType, int pos, int drops) { - LambdaForm form = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos + drops)); - try { - return copyWith(srcType, form); - } catch (Throwable t) { - throw newInternalError(t); - } - } - - @Override - MethodHandle permuteArguments(MethodType newType, int[] reorder) { - try { - return copyWith(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList()))); - } catch (Throwable t) { - throw newInternalError(t); - } + /** + * A reinvoker MH has this form: + * {@code lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }} + */ + static BoundMethodHandle makeReinvoker(MethodHandle target) { + LambdaForm form = DelegatingMethodHandle.makeReinvokerForm( + target, MethodTypeForm.LF_REBIND, + Species_L.SPECIES_DATA, Species_L.SPECIES_DATA.getterFunction(0)); + return Species_L.make(target.type(), form, target); } /** @@ -133,14 +145,22 @@ import jdk.internal.org.objectweb.asm.Type; */ /*non-public*/ abstract SpeciesData speciesData(); + /*non-public*/ static SpeciesData speciesData(LambdaForm form) { + Object c = form.names[0].constraint; + if (c instanceof SpeciesData) + return (SpeciesData) c; + // if there is no BMH constraint, then use the null constraint + return SpeciesData.EMPTY; + } + /** * Return the number of fields in this BMH. Equivalent to speciesData().fieldCount(). */ /*non-public*/ abstract int fieldCount(); @Override - final Object internalProperties() { - return "/BMH="+internalValues(); + Object internalProperties() { + return "\n& BMH="+internalValues(); } @Override @@ -178,15 +198,6 @@ import jdk.internal.org.objectweb.asm.Type; /*non-public*/ abstract BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg); /*non-public*/ abstract BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg); - // The following is a grossly irregular hack: - @Override MethodHandle reinvokerTarget() { - try { - return (MethodHandle) arg(0); - } catch (Throwable ex) { - throw newInternalError(ex); - } - } - // // concrete BMH classes required to close bootstrap loops // @@ -198,8 +209,6 @@ import jdk.internal.org.objectweb.asm.Type; super(mt, lf); this.argL0 = argL0; } - // The following is a grossly irregular hack: - @Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; } @Override /*non-public*/ SpeciesData speciesData() { return SPECIES_DATA; @@ -219,7 +228,7 @@ import jdk.internal.org.objectweb.asm.Type; @Override /*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) { try { - return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg); + return (BoundMethodHandle) SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, narg); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -227,7 +236,7 @@ import jdk.internal.org.objectweb.asm.Type; @Override /*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) { try { - return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg); + return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, narg); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -235,7 +244,7 @@ import jdk.internal.org.objectweb.asm.Type; @Override /*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) { try { - return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg); + return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, narg); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -243,7 +252,7 @@ import jdk.internal.org.objectweb.asm.Type; @Override /*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) { try { - return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg); + return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, narg); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -251,7 +260,7 @@ import jdk.internal.org.objectweb.asm.Type; @Override /*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) { try { - return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, narg); + return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, narg); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -268,18 +277,20 @@ import jdk.internal.org.objectweb.asm.Type; * The fields are immutable; their values are fully specified at object construction. * Each BMH type supplies an array of getter functions which may be used in lambda forms. * A BMH is constructed by cloning a shorter BMH and adding one or more new field values. - * As a degenerate and common case, the "shorter BMH" can be missing, and contributes zero prior fields. + * The shortest possible BMH has zero fields; its class is SimpleMethodHandle. + * BMH species are not interrelated by subtyping, even though it would appear that + * a shorter BMH could serve as a supertype of a longer one which extends it. */ static class SpeciesData { - final String typeChars; - final BasicType[] typeCodes; - final Class clazz; + private final String typeChars; + private final BasicType[] typeCodes; + private final Class clazz; // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH // Therefore, we need a non-final link in the chain. Use array elements. - final MethodHandle[] constructor; - final MethodHandle[] getters; - final NamedFunction[] nominalGetters; - final SpeciesData[] extensions; + @Stable private final MethodHandle[] constructor; + @Stable private final MethodHandle[] getters; + @Stable private final NamedFunction[] nominalGetters; + @Stable private final SpeciesData[] extensions; /*non-public*/ int fieldCount() { return typeCodes.length; @@ -290,9 +301,14 @@ import jdk.internal.org.objectweb.asm.Type; /*non-public*/ char fieldTypeChar(int i) { return typeChars.charAt(i); } - + Object fieldSignature() { + return typeChars; + } + public Class fieldHolder() { + return clazz; + } public String toString() { - return "SpeciesData["+(isPlaceholder() ? "" : clazz.getSimpleName())+":"+typeChars+"]"; + return "SpeciesData<"+fieldSignature()+">"; } /** @@ -301,7 +317,20 @@ import jdk.internal.org.objectweb.asm.Type; * getter. */ NamedFunction getterFunction(int i) { - return nominalGetters[i]; + NamedFunction nf = nominalGetters[i]; + assert(nf.memberDeclaringClassOrNull() == fieldHolder()); + assert(nf.returnType() == fieldType(i)); + return nf; + } + + NamedFunction[] getterFunctions() { + return nominalGetters; + } + + MethodHandle[] getterHandles() { return getters; } + + MethodHandle constructor() { + return constructor[0]; } static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class); @@ -324,7 +353,7 @@ import jdk.internal.org.objectweb.asm.Type; private void initForBootstrap() { assert(!INIT_DONE); - if (constructor[0] == null) { + if (constructor() == null) { String types = typeChars; Factory.makeCtors(clazz, types, this.constructor); Factory.makeGetters(clazz, types, this.getters); @@ -508,19 +537,19 @@ import jdk.internal.org.objectweb.asm.Type; * return new Species_LLI(mt, lf, argL0, argL1, argI2); * } * final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) { - * return SPECIES_DATA.extendWith(L_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); + * return SPECIES_DATA.extendWith(L_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg); * } * final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) { - * return SPECIES_DATA.extendWith(I_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); + * return SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg); * } * final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) { - * return SPECIES_DATA.extendWith(J_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); + * return SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg); * } * final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) { - * return SPECIES_DATA.extendWith(F_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); + * return SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg); * } * public final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) { - * return SPECIES_DATA.extendWith(D_TYPE).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg); + * return SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, argL0, argL1, argI2, narg); * } * } * @@ -575,16 +604,6 @@ import jdk.internal.org.objectweb.asm.Type; mv.visitMaxs(0, 0); mv.visitEnd(); - // emit implementation of reinvokerTarget() - mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG); - mv.visitTypeInsn(CHECKCAST, MH); - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - // emit implementation of speciesData() mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null); mv.visitCode(); @@ -653,16 +672,14 @@ import jdk.internal.org.objectweb.asm.Type; char btChar = type.basicTypeChar(); mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_FINAL, "copyWithExtend" + btChar, makeSignature(String.valueOf(btChar), false), null, E_THROWABLE); mv.visitCode(); - // return SPECIES_DATA.extendWith(t).constructor[0].invokeBasic(mt, lf, argL0, ..., narg) + // return SPECIES_DATA.extendWith(t).constructor().invokeBasic(mt, lf, argL0, ..., narg) // obtain constructor mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG); int iconstInsn = ICONST_0 + ord; assert(iconstInsn <= ICONST_5); mv.visitInsn(iconstInsn); mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWith", BMHSPECIES_DATA_EWI_SIG, false); - mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG); - mv.visitInsn(ICONST_0); - mv.visitInsn(AALOAD); + mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "constructor", "()" + MH_SIG, false); // load mt, lf mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 2); diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java b/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java index 949364bcec4..10ac1c0716e 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java @@ -102,7 +102,7 @@ public class CallSite { */ /*package-private*/ CallSite(MethodType type) { - target = type.invokers().uninitializedCallSite(); + target = makeUninitializedCallSite(type); } /** @@ -211,27 +211,40 @@ public class CallSite { public abstract MethodHandle dynamicInvoker(); /*non-public*/ MethodHandle makeDynamicInvoker() { - MethodHandle getTarget = GET_TARGET.bindReceiver(this); + MethodHandle getTarget = GET_TARGET.bindArgumentL(0, this); MethodHandle invoker = MethodHandles.exactInvoker(this.type()); return MethodHandles.foldArguments(invoker, getTarget); } private static final MethodHandle GET_TARGET; + private static final MethodHandle THROW_UCS; static { try { GET_TARGET = IMPL_LOOKUP. findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); + THROW_UCS = IMPL_LOOKUP. + findStatic(CallSite.class, "uninitializedCallSite", MethodType.methodType(Object.class, Object[].class)); } catch (ReflectiveOperationException e) { throw newInternalError(e); } } /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */ - /*package-private*/ - static Empty uninitializedCallSite() { + private static Object uninitializedCallSite(Object... ignore) { throw new IllegalStateException("uninitialized call site"); } + private MethodHandle makeUninitializedCallSite(MethodType targetType) { + MethodType basicType = targetType.basicType(); + MethodHandle invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_UNINIT_CS); + if (invoker == null) { + invoker = THROW_UCS.asType(basicType); + invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_UNINIT_CS, invoker); + } + // unchecked view is OK since no values will be received or returned + return invoker.viewAsType(targetType, false); + } + // unsafe stuff: private static final long TARGET_OFFSET; static { @@ -319,7 +332,7 @@ public class CallSite { throw new ClassCastException("bootstrap method failed to produce a CallSite"); } if (!site.getTarget().type().equals(type)) - throw new WrongMethodTypeException("wrong type: "+site.getTarget()); + throw wrongTargetType(site.getTarget(), type); } catch (Throwable ex) { BootstrapMethodError bex; if (ex instanceof BootstrapMethodError) diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java b/jdk/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java new file mode 100644 index 00000000000..63ba8fea5b2 --- /dev/null +++ b/jdk/src/java.base/share/classes/java/lang/invoke/DelegatingMethodHandle.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2014, 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 java.lang.invoke; + +import java.util.Arrays; +import static java.lang.invoke.LambdaForm.*; +import static java.lang.invoke.MethodHandleStatics.*; + +/** + * A method handle whose invocation behavior is determined by a target. + * The delegating MH itself can hold extra "intentions" beyond the simple behavior. + * @author jrose + */ +/*non-public*/ +abstract class DelegatingMethodHandle extends MethodHandle { + protected DelegatingMethodHandle(MethodHandle target) { + this(target.type(), target); + } + + protected DelegatingMethodHandle(MethodType type, MethodHandle target) { + super(type, chooseDelegatingForm(target)); + } + + /** Define this to extract the delegated target which supplies the invocation behavior. */ + abstract protected MethodHandle getTarget(); + + @Override + abstract MethodHandle asTypeUncached(MethodType newType); + + @Override + MemberName internalMemberName() { + return getTarget().internalMemberName(); + } + + @Override + boolean isInvokeSpecial() { + return getTarget().isInvokeSpecial(); + } + + @Override + Class internalCallerClass() { + return getTarget().internalCallerClass(); + } + + @Override + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + // FIXME: rethink 'copyWith' protocol; it is too low-level for use on all MHs + throw newIllegalArgumentException("do not use this"); + } + + @Override + String internalProperties() { + return "\n& Class="+getClass().getSimpleName()+ + "\n& Target="+getTarget().debugString(); + } + + @Override + BoundMethodHandle rebind() { + return getTarget().rebind(); + } + + private static LambdaForm chooseDelegatingForm(MethodHandle target) { + if (target instanceof SimpleMethodHandle) + return target.internalForm(); // no need for an indirection + return makeReinvokerForm(target, MethodTypeForm.LF_DELEGATE, DelegatingMethodHandle.class, NF_getTarget); + } + + /** Create a LF which simply reinvokes a target of the given basic type. */ + static LambdaForm makeReinvokerForm(MethodHandle target, + int whichCache, + Object constraint, + NamedFunction getTargetFn) { + MethodType mtype = target.type().basicType(); + boolean customized = (whichCache < 0 || + mtype.parameterSlotCount() > MethodType.MAX_MH_INVOKER_ARITY); + LambdaForm form; + if (!customized) { + form = mtype.form().cachedLambdaForm(whichCache); + if (form != null) return form; + } + final int THIS_DMH = 0; + final int ARG_BASE = 1; + final int ARG_LIMIT = ARG_BASE + mtype.parameterCount(); + int nameCursor = ARG_LIMIT; + final int NEXT_MH = customized ? -1 : nameCursor++; + final int REINVOKE = nameCursor++; + LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); + assert(names.length == nameCursor); + names[THIS_DMH] = names[THIS_DMH].withConstraint(constraint); + Object[] targetArgs; + if (customized) { + targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class); + names[REINVOKE] = new LambdaForm.Name(target, targetArgs); // the invoker is the target itself + } else { + names[NEXT_MH] = new LambdaForm.Name(getTargetFn, names[THIS_DMH]); + targetArgs = Arrays.copyOfRange(names, THIS_DMH, ARG_LIMIT, Object[].class); + targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH + names[REINVOKE] = new LambdaForm.Name(mtype, targetArgs); + } + String debugString; + switch(whichCache) { + case MethodTypeForm.LF_REBIND: debugString = "BMH.reinvoke"; break; + case MethodTypeForm.LF_DELEGATE: debugString = "MH.delegate"; break; + default: debugString = "MH.reinvoke"; break; + } + form = new LambdaForm(debugString, ARG_LIMIT, names); + if (!customized) { + form = mtype.form().setCachedLambdaForm(whichCache, form); + } + return form; + } + + private static final NamedFunction NF_getTarget; + static { + try { + NF_getTarget = new NamedFunction(DelegatingMethodHandle.class + .getDeclaredMethod("getTarget")); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } +} diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java index 0fbc4619e22..e9d2526cd8d 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java @@ -59,6 +59,7 @@ class DirectMethodHandle extends MethodHandle { MemberName m = new MemberName(Object.class, member.getName(), member.getMethodType(), member.getReferenceKind()); m = MemberName.getFactory().resolveOrNull(m.getReferenceKind(), m, null); if (m != null && m.isPublic()) { + assert(member.getReferenceKind() == m.getReferenceKind()); // else this.form is wrong member = m; } } @@ -125,61 +126,31 @@ class DirectMethodHandle extends MethodHandle { return new Constructor(mtype, lform, ctor, init, instanceClass); } + @Override + BoundMethodHandle rebind() { + return BoundMethodHandle.makeReinvoker(this); + } + + @Override + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + assert(this.getClass() == DirectMethodHandle.class); // must override in subclasses + return new DirectMethodHandle(mt, lf, member); + } + @Override String internalProperties() { - return "/DMH="+member.toString(); + return "\n& DMH.MN="+internalMemberName(); } //// Implementation methods. @Override - MethodHandle viewAsType(MethodType newType) { - return new DirectMethodHandle(newType, form, member); - } - @Override @ForceInline MemberName internalMemberName() { return member; } - @Override - MethodHandle bindArgument(int pos, BasicType basicType, Object value) { - // If the member needs dispatching, do so. - if (pos == 0 && basicType == L_TYPE) { - DirectMethodHandle concrete = maybeRebind(value); - if (concrete != null) - return concrete.bindReceiver(value); - } - return super.bindArgument(pos, basicType, value); - } - - @Override - MethodHandle bindReceiver(Object receiver) { - // If the member needs dispatching, do so. - DirectMethodHandle concrete = maybeRebind(receiver); - if (concrete != null) - return concrete.bindReceiver(receiver); - return super.bindReceiver(receiver); - } - private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory(); - private DirectMethodHandle maybeRebind(Object receiver) { - if (receiver != null) { - switch (member.getReferenceKind()) { - case REF_invokeInterface: - case REF_invokeVirtual: - // Pre-dispatch the member. - Class concreteClass = receiver.getClass(); - MemberName concrete = new MemberName(concreteClass, member.getName(), member.getMethodType(), REF_invokeSpecial); - concrete = IMPL_NAMES.resolveOrNull(REF_invokeSpecial, concrete, concreteClass); - if (concrete != null) - return new DirectMethodHandle(type(), preparedLambdaForm(concrete), concrete); - break; - } - } - return null; - } - /** * Create a LF which can invoke the given method. * Cache and share this structure among all methods with @@ -260,9 +231,10 @@ class DirectMethodHandle extends MethodHandle { } else { names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]); } + assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]); Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class); assert(outArgs[outArgs.length-1] == names[GET_MEMBER]); // look, shifted args! - int result = LambdaForm.LAST_RESULT; + int result = LAST_RESULT; if (doesAlloc) { assert(outArgs[outArgs.length-2] == names[NEW_OBJ]); // got to move this one System.arraycopy(outArgs, 0, outArgs, 1, outArgs.length-2); @@ -277,6 +249,16 @@ class DirectMethodHandle extends MethodHandle { return lform; } + static Object findDirectMethodHandle(Name name) { + if (name.function == Lazy.NF_internalMemberName || + name.function == Lazy.NF_internalMemberNameEnsureInit || + name.function == Lazy.NF_constructorMethod) { + assert(name.arguments.length == 1); + return name.arguments[0]; + } + return null; + } + private static void maybeCompile(LambdaForm lform, MemberName m) { if (VerifyAccess.isSamePackage(m.getDeclaringClass(), MethodHandle.class)) // Help along bootstrapping... @@ -389,8 +371,8 @@ class DirectMethodHandle extends MethodHandle { return true; } @Override - MethodHandle viewAsType(MethodType newType) { - return new Special(newType, form, member); + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + return new Special(mt, lf, member); } } @@ -407,8 +389,8 @@ class DirectMethodHandle extends MethodHandle { assert(initMethod.isResolved()); } @Override - MethodHandle viewAsType(MethodType newType) { - return new Constructor(newType, form, member, initMethod, instanceClass); + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + return new Constructor(mt, lf, member, initMethod, instanceClass); } } @@ -437,8 +419,8 @@ class DirectMethodHandle extends MethodHandle { return fieldType.cast(obj); } @Override - MethodHandle viewAsType(MethodType newType) { - return new Accessor(newType, form, member, fieldOffset); + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + return new Accessor(mt, lf, member, fieldOffset); } } @@ -480,8 +462,8 @@ class DirectMethodHandle extends MethodHandle { return fieldType.cast(obj); } @Override - MethodHandle viewAsType(MethodType newType) { - return new StaticAccessor(newType, form, member, staticBase, staticOffset); + MethodHandle copyWith(MethodType mt, LambdaForm lf) { + return new StaticAccessor(mt, lf, member, staticBase, staticOffset); } } diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index 58e56dd246a..0bfa6e32a98 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -25,21 +25,20 @@ package java.lang.invoke; -import sun.invoke.util.VerifyAccess; -import static java.lang.invoke.LambdaForm.*; - -import sun.invoke.util.Wrapper; - import java.io.*; import java.util.*; +import java.lang.reflect.Modifier; import jdk.internal.org.objectweb.asm.*; -import java.lang.reflect.*; +import static java.lang.invoke.LambdaForm.*; +import static java.lang.invoke.LambdaForm.BasicType.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; -import static java.lang.invoke.LambdaForm.BasicType.*; + +import sun.invoke.util.VerifyAccess; import sun.invoke.util.VerifyType; +import sun.invoke.util.Wrapper; import sun.reflect.misc.ReflectUtil; /** @@ -74,7 +73,11 @@ class InvokerBytecodeGenerator { private final LambdaForm lambdaForm; private final String invokerName; private final MethodType invokerType; - private final int[] localsMap; + + /** Info about local variables in compiled lambda form */ + private final int[] localsMap; // index + private final BasicType[] localTypes; // basic type + private final Class[] localClasses; // type /** ASM bytecode generation. */ private ClassWriter cw; @@ -83,6 +86,7 @@ class InvokerBytecodeGenerator { private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory(); private static final Class HOST_CLASS = LambdaForm.class; + /** Main constructor; other constructors delegate to this one. */ private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize, String className, String invokerName, MethodType invokerType) { if (invokerName.contains(".")) { @@ -98,18 +102,26 @@ class InvokerBytecodeGenerator { this.lambdaForm = lambdaForm; this.invokerName = invokerName; this.invokerType = invokerType; - this.localsMap = new int[localsMapSize]; + this.localsMap = new int[localsMapSize+1]; + // last entry of localsMap is count of allocated local slots + this.localTypes = new BasicType[localsMapSize+1]; + this.localClasses = new Class[localsMapSize+1]; } + /** For generating LambdaForm interpreter entry points. */ private InvokerBytecodeGenerator(String className, String invokerName, MethodType invokerType) { this(null, invokerType.parameterCount(), className, invokerName, invokerType); // Create an array to map name indexes to locals indexes. + localTypes[localTypes.length - 1] = V_TYPE; for (int i = 0; i < localsMap.length; i++) { localsMap[i] = invokerType.parameterSlotCount() - invokerType.parameterSlotDepth(i); + if (i < invokerType.parameterCount()) + localTypes[i] = basicType(invokerType.parameterType(i)); } } + /** For generating customized code for a single LambdaForm. */ private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) { this(form, form.names.length, className, form.debugName, invokerType); @@ -117,7 +129,11 @@ class InvokerBytecodeGenerator { Name[] names = form.names; for (int i = 0, index = 0; i < localsMap.length; i++) { localsMap[i] = index; - index += names[i].type.basicTypeSlots(); + if (i < names.length) { + BasicType type = names[i].type(); + index += type.basicTypeSlots(); + localTypes[i] = type; + } } } @@ -148,7 +164,6 @@ class InvokerBytecodeGenerator { static void maybeDump(final String className, final byte[] classFile) { if (DUMP_CLASS_FILES) { - System.out.println("dump: " + className); java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Void run() { @@ -156,6 +171,7 @@ class InvokerBytecodeGenerator { String dumpName = className; //dumpName = dumpName.replace('/', '-'); File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class"); + System.out.println("dump: " + dumpFile); dumpFile.getParentFile().mkdirs(); FileOutputStream file = new FileOutputStream(dumpFile); file.write(classFile); @@ -204,7 +220,7 @@ class InvokerBytecodeGenerator { String constantPlaceholder(Object arg) { String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++; - if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + arg.toString() + ">>"; // debugging aid + if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(arg) + ">>"; // debugging aid if (cpPatches.containsKey(cpPlaceholder)) { throw new InternalError("observed CP placeholder twice: " + cpPlaceholder); } @@ -225,6 +241,17 @@ class InvokerBytecodeGenerator { return res; } + private static String debugString(Object arg) { + if (arg instanceof MethodHandle) { + MethodHandle mh = (MethodHandle) arg; + MemberName member = mh.internalMemberName(); + if (member != null) + return member.toString(); + return mh.debugString(); + } + return arg.toString(); + } + /** * Extract the number of constant pool entries from a given class file. * @@ -400,6 +427,64 @@ class InvokerBytecodeGenerator { emitStoreInsn(L_TYPE, index); } + private byte arrayTypeCode(Wrapper elementType) { + switch (elementType) { + case BOOLEAN: return Opcodes.T_BOOLEAN; + case BYTE: return Opcodes.T_BYTE; + case CHAR: return Opcodes.T_CHAR; + case SHORT: return Opcodes.T_SHORT; + case INT: return Opcodes.T_INT; + case LONG: return Opcodes.T_LONG; + case FLOAT: return Opcodes.T_FLOAT; + case DOUBLE: return Opcodes.T_DOUBLE; + case OBJECT: return 0; // in place of Opcodes.T_OBJECT + default: throw new InternalError(); + } + } + + private int arrayInsnOpcode(byte tcode, int aaop) throws InternalError { + assert(aaop == Opcodes.AASTORE || aaop == Opcodes.AALOAD); + int xas; + switch (tcode) { + case Opcodes.T_BOOLEAN: xas = Opcodes.BASTORE; break; + case Opcodes.T_BYTE: xas = Opcodes.BASTORE; break; + case Opcodes.T_CHAR: xas = Opcodes.CASTORE; break; + case Opcodes.T_SHORT: xas = Opcodes.SASTORE; break; + case Opcodes.T_INT: xas = Opcodes.IASTORE; break; + case Opcodes.T_LONG: xas = Opcodes.LASTORE; break; + case Opcodes.T_FLOAT: xas = Opcodes.FASTORE; break; + case Opcodes.T_DOUBLE: xas = Opcodes.DASTORE; break; + case 0: xas = Opcodes.AASTORE; break; + default: throw new InternalError(); + } + return xas - Opcodes.AASTORE + aaop; + } + + + private void freeFrameLocal(int oldFrameLocal) { + int i = indexForFrameLocal(oldFrameLocal); + if (i < 0) return; + BasicType type = localTypes[i]; + int newFrameLocal = makeLocalTemp(type); + mv.visitVarInsn(loadInsnOpcode(type), oldFrameLocal); + mv.visitVarInsn(storeInsnOpcode(type), newFrameLocal); + assert(localsMap[i] == oldFrameLocal); + localsMap[i] = newFrameLocal; + assert(indexForFrameLocal(oldFrameLocal) < 0); + } + private int indexForFrameLocal(int frameLocal) { + for (int i = 0; i < localsMap.length; i++) { + if (localsMap[i] == frameLocal && localTypes[i] != V_TYPE) + return i; + } + return -1; + } + private int makeLocalTemp(BasicType type) { + int frameLocal = localsMap[localsMap.length - 1]; + localsMap[localsMap.length - 1] = frameLocal + type.basicTypeSlots(); + return frameLocal; + } + /** * Emit a boxing call. * @@ -421,41 +506,79 @@ class InvokerBytecodeGenerator { String owner = "java/lang/" + wrapper.wrapperType().getSimpleName(); String name = wrapper.primitiveSimpleName() + "Value"; String desc = "()" + wrapper.basicTypeChar(); - mv.visitTypeInsn(Opcodes.CHECKCAST, owner); + emitReferenceCast(wrapper.wrapperType(), null); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false); } /** - * Emit an implicit conversion. + * Emit an implicit conversion for an argument which must be of the given pclass. + * This is usually a no-op, except when pclass is a subword type or a reference other than Object or an interface. * * @param ptype type of value present on stack * @param pclass type of value required on stack + * @param arg compile-time representation of value on stack (Node, constant) or null if none */ - private void emitImplicitConversion(BasicType ptype, Class pclass) { + private void emitImplicitConversion(BasicType ptype, Class pclass, Object arg) { assert(basicType(pclass) == ptype); // boxing/unboxing handled by caller if (pclass == ptype.basicTypeClass() && ptype != L_TYPE) return; // nothing to do switch (ptype) { - case L_TYPE: - if (VerifyType.isNullConversion(Object.class, pclass)) + case L_TYPE: + if (VerifyType.isNullConversion(Object.class, pclass, false)) { + if (PROFILE_LEVEL > 0) + emitReferenceCast(Object.class, arg); + return; + } + emitReferenceCast(pclass, arg); + return; + case I_TYPE: + if (!VerifyType.isNullConversion(int.class, pclass, false)) + emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass)); return; - if (isStaticallyNameable(pclass)) { - mv.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(pclass)); - } else { - mv.visitLdcInsn(constantPlaceholder(pclass)); - mv.visitTypeInsn(Opcodes.CHECKCAST, CLS); - mv.visitInsn(Opcodes.SWAP); - mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "castReference", CLL_SIG, false); - if (pclass.isArray()) - mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY); - } - return; - case I_TYPE: - if (!VerifyType.isNullConversion(int.class, pclass)) - emitPrimCast(ptype.basicTypeWrapper(), Wrapper.forPrimitiveType(pclass)); - return; } - throw new InternalError("bad implicit conversion: tc="+ptype+": "+pclass); + throw newInternalError("bad implicit conversion: tc="+ptype+": "+pclass); + } + + /** Update localClasses type map. Return true if the information is already present. */ + private boolean assertStaticType(Class cls, Name n) { + int local = n.index(); + Class aclass = localClasses[local]; + if (aclass != null && (aclass == cls || cls.isAssignableFrom(aclass))) { + return true; // type info is already present + } else if (aclass == null || aclass.isAssignableFrom(cls)) { + localClasses[local] = cls; // type info can be improved + } + return false; + } + + private void emitReferenceCast(Class cls, Object arg) { + Name writeBack = null; // local to write back result + if (arg instanceof Name) { + Name n = (Name) arg; + if (assertStaticType(cls, n)) + return; // this cast was already performed + if (lambdaForm.useCount(n) > 1) { + // This guy gets used more than once. + writeBack = n; + } + } + if (isStaticallyNameable(cls)) { + String sig = getInternalName(cls); + mv.visitTypeInsn(Opcodes.CHECKCAST, sig); + } else { + mv.visitLdcInsn(constantPlaceholder(cls)); + mv.visitTypeInsn(Opcodes.CHECKCAST, CLS); + mv.visitInsn(Opcodes.SWAP); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, MHI, "castReference", CLL_SIG, false); + if (Object[].class.isAssignableFrom(cls)) + mv.visitTypeInsn(Opcodes.CHECKCAST, OBJARY); + else if (PROFILE_LEVEL > 0) + mv.visitTypeInsn(Opcodes.CHECKCAST, OBJ); + } + if (writeBack != null) { + mv.visitInsn(Opcodes.DUP); + emitAstoreInsn(writeBack.index()); + } } /** @@ -477,7 +600,11 @@ class InvokerBytecodeGenerator { } private static String getInternalName(Class c) { - assert(VerifyAccess.isTypeVisible(c, Object.class)); + if (c == Object.class) return OBJ; + else if (c == Object[].class) return OBJARY; + else if (c == Class.class) return CLS; + else if (c == MethodHandle.class) return MH; + assert(VerifyAccess.isTypeVisible(c, Object.class)) : c.getName(); return c.getName().replace('.', '/'); } @@ -506,39 +633,62 @@ class InvokerBytecodeGenerator { // iterate over the form's names, generating bytecode instructions for each // start iterating at the first name following the arguments + Name onStack = null; for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { Name name = lambdaForm.names[i]; - MemberName member = name.function.member(); - if (isSelectAlternative(i)) { - emitSelectAlternative(name, lambdaForm.names[i + 1]); - i++; // skip MH.invokeBasic of the selectAlternative result - } else if (isGuardWithCatch(i)) { - emitGuardWithCatch(i); - i = i+2; // Jump to the end of GWC idiom - } else if (isStaticallyInvocable(member)) { + emitStoreResult(onStack); + onStack = name; // unless otherwise modified below + MethodHandleImpl.Intrinsic intr = name.function.intrinsicName(); + switch (intr) { + case SELECT_ALTERNATIVE: + assert isSelectAlternative(i); + onStack = emitSelectAlternative(name, lambdaForm.names[i+1]); + i++; // skip MH.invokeBasic of the selectAlternative result + continue; + case GUARD_WITH_CATCH: + assert isGuardWithCatch(i); + onStack = emitGuardWithCatch(i); + i = i+2; // Jump to the end of GWC idiom + continue; + case NEW_ARRAY: + Class rtype = name.function.methodType().returnType(); + if (isStaticallyNameable(rtype)) { + emitNewArray(name); + continue; + } + break; + case ARRAY_LOAD: + emitArrayLoad(name); + continue; + case ARRAY_STORE: + emitArrayStore(name); + continue; + case IDENTITY: + assert(name.arguments.length == 1); + emitPushArguments(name); + continue; + case ZERO: + assert(name.arguments.length == 0); + emitConst(name.type.basicTypeWrapper().zero()); + continue; + case NONE: + // no intrinsic associated + break; + default: + throw newInternalError("Unknown intrinsic: "+intr); + } + + MemberName member = name.function.member(); + if (isStaticallyInvocable(member)) { emitStaticInvoke(member, name); } else { emitInvoke(name); } - - // Update cached form name's info in case an intrinsic spanning multiple names was encountered. - name = lambdaForm.names[i]; - member = name.function.member(); - - // store the result from evaluating to the target name in a local if required - // (if this is the last value, i.e., the one that is going to be returned, - // avoid store/load/return and just return) - if (i == lambdaForm.names.length - 1 && i == lambdaForm.result) { - // return value - do nothing - } else if (name.type != V_TYPE) { - // non-void: actually assign - emitStoreInsn(name.type, name.index()); - } } // return statement - emitReturn(); + emitReturn(onStack); classFileEpilogue(); bogusMethod(lambdaForm); @@ -548,29 +698,43 @@ class InvokerBytecodeGenerator { return classFile; } + void emitArrayLoad(Name name) { emitArrayOp(name, Opcodes.AALOAD); } + void emitArrayStore(Name name) { emitArrayOp(name, Opcodes.AASTORE); } + + void emitArrayOp(Name name, int arrayOpcode) { + assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE; + Class elementType = name.function.methodType().parameterType(0).getComponentType(); + assert elementType != null; + emitPushArguments(name); + if (elementType.isPrimitive()) { + Wrapper w = Wrapper.forPrimitiveType(elementType); + arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode); + } + mv.visitInsn(arrayOpcode); + } + /** * Emit an invoke for the given name. */ void emitInvoke(Name name) { + assert(!isLinkerMethodInvoke(name)); // should use the static path for these if (true) { // push receiver MethodHandle target = name.function.resolvedHandle; assert(target != null) : name.exprString(); mv.visitLdcInsn(constantPlaceholder(target)); - mv.visitTypeInsn(Opcodes.CHECKCAST, MH); + emitReferenceCast(MethodHandle.class, target); } else { // load receiver emitAloadInsn(0); - mv.visitTypeInsn(Opcodes.CHECKCAST, MH); + emitReferenceCast(MethodHandle.class, null); mv.visitFieldInsn(Opcodes.GETFIELD, MH, "form", LF_SIG); mv.visitFieldInsn(Opcodes.GETFIELD, LF, "names", LFN_SIG); // TODO more to come } // push arguments - for (int i = 0; i < name.arguments.length; i++) { - emitPushArgument(name, i); - } + emitPushArguments(name); // invocation MethodType type = name.function.methodType(); @@ -585,6 +749,10 @@ class InvokerBytecodeGenerator { //MethodHandle.class already covered }; + static boolean isStaticallyInvocable(Name name) { + return isStaticallyInvocable(name.function.member()); + } + static boolean isStaticallyInvocable(MemberName member) { if (member == null) return false; if (member.isConstructor()) return false; @@ -611,6 +779,8 @@ class InvokerBytecodeGenerator { } static boolean isStaticallyNameable(Class cls) { + if (cls == Object.class) + return true; while (cls.isArray()) cls = cls.getComponentType(); if (cls.isPrimitive()) @@ -631,12 +801,17 @@ class InvokerBytecodeGenerator { return false; } + void emitStaticInvoke(Name name) { + emitStaticInvoke(name.function.member(), name); + } + /** * Emit an invoke for the given name, using the MemberName directly. */ void emitStaticInvoke(MemberName member, Name name) { assert(member.equals(name.function.member())); - String cname = getInternalName(member.getDeclaringClass()); + Class defc = member.getDeclaringClass(); + String cname = getInternalName(defc); String mname = member.getName(); String mtype; byte refKind = member.getReferenceKind(); @@ -653,9 +828,7 @@ class InvokerBytecodeGenerator { } // push arguments - for (int i = 0; i < name.arguments.length; i++) { - emitPushArgument(name, i); - } + emitPushArguments(name); // invocation if (member.isMethod()) { @@ -666,6 +839,52 @@ class InvokerBytecodeGenerator { mtype = MethodType.toFieldDescriptorString(member.getFieldType()); mv.visitFieldInsn(refKindOpcode(refKind), cname, mname, mtype); } + // Issue a type assertion for the result, so we can avoid casts later. + if (name.type == L_TYPE) { + Class rtype = member.getInvocationType().returnType(); + assert(!rtype.isPrimitive()); + if (rtype != Object.class && !rtype.isInterface()) { + assertStaticType(rtype, name); + } + } + } + + void emitNewArray(Name name) throws InternalError { + Class rtype = name.function.methodType().returnType(); + if (name.arguments.length == 0) { + // The array will be a constant. + Object emptyArray; + try { + emptyArray = name.function.resolvedHandle.invoke(); + } catch (Throwable ex) { + throw newInternalError(ex); + } + assert(java.lang.reflect.Array.getLength(emptyArray) == 0); + assert(emptyArray.getClass() == rtype); // exact typing + mv.visitLdcInsn(constantPlaceholder(emptyArray)); + emitReferenceCast(rtype, emptyArray); + return; + } + Class arrayElementType = rtype.getComponentType(); + assert(arrayElementType != null); + emitIconstInsn(name.arguments.length); + int xas = Opcodes.AASTORE; + if (!arrayElementType.isPrimitive()) { + mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType)); + } else { + byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType)); + xas = arrayInsnOpcode(tc, xas); + mv.visitIntInsn(Opcodes.NEWARRAY, tc); + } + // store arguments + for (int i = 0; i < name.arguments.length; i++) { + mv.visitInsn(Opcodes.DUP); + emitIconstInsn(i); + emitPushArgument(name, i); + mv.visitInsn(xas); + } + // the array is left on the stack + assertStaticType(rtype, name); } int refKindOpcode(byte refKind) { switch (refKind) { @@ -707,6 +926,21 @@ class InvokerBytecodeGenerator { !member.isPublic() && !member.isStatic(); } + /** + * Check if MemberName is a call to MethodHandle.linkToStatic, etc. + */ + private boolean isLinkerMethodInvoke(Name name) { + if (name.function == null) + return false; + if (name.arguments.length < 1) + return false; // must have MH argument + MemberName member = name.function.member(); + return member != null && + member.getDeclaringClass() == MethodHandle.class && + !member.isPublic() && member.isStatic() && + member.getName().startsWith("linkTo"); + } + /** * Check if i-th name is a call to MethodHandleImpl.selectAlternative. */ @@ -755,7 +989,9 @@ class InvokerBytecodeGenerator { * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I} * } */ - private void emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { + private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) { + assert isStaticallyInvocable(invokeBasicName); + Name receiver = (Name) invokeBasicName.arguments[0]; Label L_fallback = new Label(); @@ -763,15 +999,15 @@ class InvokerBytecodeGenerator { // load test result emitPushArgument(selectAlternativeName, 0); - mv.visitInsn(Opcodes.ICONST_1); // if_icmpne L_fallback - mv.visitJumpInsn(Opcodes.IF_ICMPNE, L_fallback); + mv.visitJumpInsn(Opcodes.IFEQ, L_fallback); // invoke selectAlternativeName.arguments[1] + Class[] preForkClasses = localClasses.clone(); emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot - emitInvoke(invokeBasicName); + emitStaticInvoke(invokeBasicName); // goto L_done mv.visitJumpInsn(Opcodes.GOTO, L_done); @@ -780,12 +1016,17 @@ class InvokerBytecodeGenerator { mv.visitLabel(L_fallback); // invoke selectAlternativeName.arguments[2] + System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative emitAstoreInsn(receiver.index()); // store the MH in the receiver slot - emitInvoke(invokeBasicName); + emitStaticInvoke(invokeBasicName); // L_done: mv.visitLabel(L_done); + // for now do not bother to merge typestate; just reset to the dominator state + System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); + + return invokeBasicName; // return what's on stack } /** @@ -808,7 +1049,7 @@ class InvokerBytecodeGenerator { * return a3.invokeBasic(ex, a6, a7); * }} */ - private void emitGuardWithCatch(int pos) { + private Name emitGuardWithCatch(int pos) { Name args = lambdaForm.names[pos]; Name invoker = lambdaForm.names[pos+1]; Name result = lambdaForm.names[pos+2]; @@ -859,6 +1100,12 @@ class InvokerBytecodeGenerator { mv.visitInsn(Opcodes.ATHROW); mv.visitLabel(L_done); + + return result; + } + + private void emitPushArguments(Name args) { + emitPushArguments(args, 0); } private void emitPushArguments(Name args, int start) { @@ -878,7 +1125,7 @@ class InvokerBytecodeGenerator { if (arg instanceof Name) { Name n = (Name) arg; emitLoadInsn(n.type, n.index()); - emitImplicitConversion(n.type, ptype); + emitImplicitConversion(n.type, ptype, n); } else if ((arg == null || arg instanceof String) && bptype == L_TYPE) { emitConst(arg); } else { @@ -886,15 +1133,25 @@ class InvokerBytecodeGenerator { emitConst(arg); } else { mv.visitLdcInsn(constantPlaceholder(arg)); - emitImplicitConversion(L_TYPE, ptype); + emitImplicitConversion(L_TYPE, ptype, arg); } } } + /** + * Store the name to its local, if necessary. + */ + private void emitStoreResult(Name name) { + if (name != null && name.type != V_TYPE) { + // non-void: actually assign + emitStoreInsn(name.type, name.index()); + } + } + /** * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type. */ - private void emitReturn() { + private void emitReturn(Name onStack) { // return statement Class rclass = invokerType.returnType(); BasicType rtype = lambdaForm.returnType(); @@ -907,12 +1164,11 @@ class InvokerBytecodeGenerator { LambdaForm.Name rn = lambdaForm.names[lambdaForm.result]; // put return value on the stack if it is not already there - if (lambdaForm.result != lambdaForm.names.length - 1 || - lambdaForm.result < lambdaForm.arity) { - emitLoadInsn(rn.type, lambdaForm.result); + if (rn != onStack) { + emitLoadInsn(rtype, lambdaForm.result); } - emitImplicitConversion(rtype, rclass); + emitImplicitConversion(rtype, rclass, rn); // generate actual return statement emitReturnInsn(rtype); diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java index d3116d12963..12a118a7576 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java @@ -25,8 +25,9 @@ package java.lang.invoke; +import java.lang.reflect.Array; import java.util.Arrays; -import sun.invoke.empty.Empty; + import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; @@ -40,52 +41,63 @@ class Invokers { // exact type (sans leading target MH) for the outgoing call private final MethodType targetType; - // FIXME: Get rid of the invokers that are not useful. - - // exact invoker for the outgoing call - private /*lazy*/ MethodHandle exactInvoker; - private /*lazy*/ MethodHandle basicInvoker; // invokeBasic (unchecked exact) - - // erased (partially untyped but with primitives) invoker for the outgoing call - // FIXME: get rid of - private /*lazy*/ MethodHandle erasedInvoker; - // FIXME: get rid of - /*lazy*/ MethodHandle erasedInvokerWithDrops; // for InvokeGeneric - - // general invoker for the outgoing call - private /*lazy*/ MethodHandle generalInvoker; - - // general invoker for the outgoing call, uses varargs - private /*lazy*/ MethodHandle varargsInvoker; - - // general invoker for the outgoing call; accepts a trailing Object[] - private final /*lazy*/ MethodHandle[] spreadInvokers; - - // invoker for an unbound callsite - private /*lazy*/ MethodHandle uninitializedCallSite; + // Cached adapter information: + private final @Stable MethodHandle[] invokers = new MethodHandle[INV_LIMIT]; + // Indexes into invokers: + static final int + INV_EXACT = 0, // MethodHandles.exactInvoker + INV_GENERIC = 1, // MethodHandles.invoker (generic invocation) + INV_BASIC = 2, // MethodHandles.basicInvoker + INV_LIMIT = 3; /** Compute and cache information common to all collecting adapters * that implement members of the erasure-family of the given erased type. */ /*non-public*/ Invokers(MethodType targetType) { this.targetType = targetType; - this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1]; } /*non-public*/ MethodHandle exactInvoker() { - MethodHandle invoker = exactInvoker; + MethodHandle invoker = cachedInvoker(INV_EXACT); if (invoker != null) return invoker; invoker = makeExactOrGeneralInvoker(true); - exactInvoker = invoker; - return invoker; + return setCachedInvoker(INV_EXACT, invoker); } - /*non-public*/ MethodHandle generalInvoker() { - MethodHandle invoker = generalInvoker; + /*non-public*/ MethodHandle genericInvoker() { + MethodHandle invoker = cachedInvoker(INV_GENERIC); if (invoker != null) return invoker; invoker = makeExactOrGeneralInvoker(false); - generalInvoker = invoker; - return invoker; + return setCachedInvoker(INV_GENERIC, invoker); + } + + /*non-public*/ MethodHandle basicInvoker() { + MethodHandle invoker = cachedInvoker(INV_BASIC); + if (invoker != null) return invoker; + MethodType basicType = targetType.basicType(); + if (basicType != targetType) { + // double cache; not used significantly + return setCachedInvoker(INV_BASIC, basicType.invokers().basicInvoker()); + } + invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_BASIC_INV); + if (invoker == null) { + MemberName method = invokeBasicMethod(basicType); + invoker = DirectMethodHandle.make(method); + assert(checkInvoker(invoker)); + invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_BASIC_INV, invoker); + } + return setCachedInvoker(INV_BASIC, invoker); + } + + private MethodHandle cachedInvoker(int idx) { + return invokers[idx]; + } + + private synchronized MethodHandle setCachedInvoker(int idx, final MethodHandle invoker) { + // Simulate a CAS, to avoid racy duplication of results. + MethodHandle prev = invokers[idx]; + if (prev != null) return prev; + return invokers[idx] = invoker; } private MethodHandle makeExactOrGeneralInvoker(boolean isExact) { @@ -95,7 +107,7 @@ class Invokers { LambdaForm lform = invokeHandleForm(mtype, false, which); MethodHandle invoker = BoundMethodHandle.bindSingle(invokerType, lform, mtype); String whichName = (isExact ? "invokeExact" : "invoke"); - invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke(whichName, mtype)); + invoker = invoker.withInternalMemberName(MemberName.makeMethodHandleInvoke(whichName, mtype), false); assert(checkInvoker(invoker)); maybeCompileToBytecode(invoker); return invoker; @@ -110,21 +122,6 @@ class Invokers { } } - /*non-public*/ MethodHandle basicInvoker() { - MethodHandle invoker = basicInvoker; - if (invoker != null) return invoker; - MethodType basicType = targetType.basicType(); - if (basicType != targetType) { - // double cache; not used significantly - return basicInvoker = basicType.invokers().basicInvoker(); - } - MemberName method = invokeBasicMethod(basicType); - invoker = DirectMethodHandle.make(method); - assert(checkInvoker(invoker)); - basicInvoker = invoker; - return invoker; - } - // This next one is called from LambdaForm.NamedFunction.. /*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) { assert(basicType == basicType.basicType()); @@ -145,87 +142,42 @@ class Invokers { return true; } - // FIXME: get rid of - /*non-public*/ MethodHandle erasedInvoker() { - MethodHandle xinvoker = exactInvoker(); - MethodHandle invoker = erasedInvoker; - if (invoker != null) return invoker; - MethodType erasedType = targetType.erase(); - invoker = xinvoker.asType(erasedType.invokerType()); - erasedInvoker = invoker; - return invoker; - } - + /** + * Find or create an invoker which passes unchanged a given number of arguments + * and spreads the rest from a trailing array argument. + * The invoker target type is the post-spread type {@code (TYPEOF(uarg*), TYPEOF(sarg*))=>RT}. + * All the {@code sarg}s must have a common type {@code C}. (If there are none, {@code Object} is assumed.} + * @param leadingArgCount the number of unchanged (non-spread) arguments + * @return {@code invoker.invokeExact(mh, uarg*, C[]{sarg*}) := (RT)mh.invoke(uarg*, sarg*)} + */ /*non-public*/ MethodHandle spreadInvoker(int leadingArgCount) { - MethodHandle vaInvoker = spreadInvokers[leadingArgCount]; - if (vaInvoker != null) return vaInvoker; int spreadArgCount = targetType.parameterCount() - leadingArgCount; - MethodType spreadInvokerType = targetType - .replaceParameterTypes(leadingArgCount, targetType.parameterCount(), Object[].class); - if (targetType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) { - // Factor sinvoker.invoke(mh, a) into ginvoker.asSpreader().invoke(mh, a) - // where ginvoker.invoke(mh, a*) => mh.invoke(a*). - MethodHandle genInvoker = generalInvoker(); - vaInvoker = genInvoker.asSpreader(Object[].class, spreadArgCount); - } else { - // Cannot build a general invoker here of type ginvoker.invoke(mh, a*[254]). - // Instead, factor sinvoker.invoke(mh, a) into ainvoker.invoke(filter(mh), a) - // where filter(mh) == mh.asSpreader(Object[], spreadArgCount) - MethodHandle arrayInvoker = MethodHandles.exactInvoker(spreadInvokerType); - MethodHandle makeSpreader; - try { - makeSpreader = IMPL_LOOKUP - .findVirtual(MethodHandle.class, "asSpreader", - MethodType.methodType(MethodHandle.class, Class.class, int.class)); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - makeSpreader = MethodHandles.insertArguments(makeSpreader, 1, Object[].class, spreadArgCount); - vaInvoker = MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader); + MethodType postSpreadType = targetType; + Class argArrayType = impliedRestargType(postSpreadType, leadingArgCount); + if (postSpreadType.parameterSlotCount() <= MethodType.MAX_MH_INVOKER_ARITY) { + return genericInvoker().asSpreader(argArrayType, spreadArgCount); } - assert(vaInvoker.type().equals(spreadInvokerType.invokerType())); - maybeCompileToBytecode(vaInvoker); - spreadInvokers[leadingArgCount] = vaInvoker; - return vaInvoker; + // Cannot build a generic invoker here of type ginvoker.invoke(mh, a*[254]). + // Instead, factor sinvoker.invoke(mh, a) into ainvoker.invoke(filter(mh), a) + // where filter(mh) == mh.asSpreader(Object[], spreadArgCount) + MethodType preSpreadType = postSpreadType + .replaceParameterTypes(leadingArgCount, postSpreadType.parameterCount(), argArrayType); + MethodHandle arrayInvoker = MethodHandles.invoker(preSpreadType); + MethodHandle makeSpreader = MethodHandles.insertArguments(Lazy.MH_asSpreader, 1, argArrayType, spreadArgCount); + return MethodHandles.filterArgument(arrayInvoker, 0, makeSpreader); } - /*non-public*/ MethodHandle varargsInvoker() { - MethodHandle vaInvoker = varargsInvoker; - if (vaInvoker != null) return vaInvoker; - vaInvoker = spreadInvoker(0).asType(MethodType.genericMethodType(0, true).invokerType()); - varargsInvoker = vaInvoker; - return vaInvoker; - } - - private static MethodHandle THROW_UCS = null; - - /*non-public*/ MethodHandle uninitializedCallSite() { - MethodHandle invoker = uninitializedCallSite; - if (invoker != null) return invoker; - if (targetType.parameterCount() > 0) { - MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount()); - Invokers invokers0 = type0.invokers(); - invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(), - 0, targetType.parameterList()); - assert(invoker.type().equals(targetType)); - uninitializedCallSite = invoker; - return invoker; + private static Class impliedRestargType(MethodType restargType, int fromPos) { + if (restargType.isGeneric()) return Object[].class; // can be nothing else + int maxPos = restargType.parameterCount(); + if (fromPos >= maxPos) return Object[].class; // reasonable default + Class argType = restargType.parameterType(fromPos); + for (int i = fromPos+1; i < maxPos; i++) { + if (argType != restargType.parameterType(i)) + throw newIllegalArgumentException("need homogeneous rest arguments", restargType); } - invoker = THROW_UCS; - if (invoker == null) { - try { - THROW_UCS = invoker = IMPL_LOOKUP - .findStatic(CallSite.class, "uninitializedCallSite", - MethodType.methodType(Empty.class)); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - } - invoker = MethodHandles.explicitCastArguments(invoker, MethodType.methodType(targetType.returnType())); - invoker = invoker.dropArguments(targetType, 0, targetType.parameterCount()); - assert(invoker.type().equals(targetType)); - uninitializedCallSite = invoker; - return invoker; + if (argType == Object.class) return Object[].class; + return Array.newInstance(argType, 0).getClass(); } public String toString() { @@ -308,7 +260,9 @@ class Invokers { : Arrays.asList(mtype, customized, which, nameCursor, names.length); if (MTYPE_ARG >= INARG_LIMIT) { assert(names[MTYPE_ARG] == null); - NamedFunction getter = BoundMethodHandle.getSpeciesData("L").getterFunction(0); + BoundMethodHandle.SpeciesData speciesData = BoundMethodHandle.speciesData_L(); + names[THIS_MH] = names[THIS_MH].withConstraint(speciesData); + NamedFunction getter = speciesData.getterFunction(0); names[MTYPE_ARG] = new Name(getter, names[THIS_MH]); // else if isLinker, then MTYPE is passed in from the caller (e.g., the JVM) } @@ -360,9 +314,6 @@ class Invokers { Object checkGenericType(Object mhObj, Object expectedObj) { MethodHandle mh = (MethodHandle) mhObj; MethodType expected = (MethodType) expectedObj; - if (mh.type() == expected) return mh; - MethodHandle atc = mh.asTypeCache; - if (atc != null && atc.type() == expected) return atc; return mh.asType(expected); /* Maybe add more paths here. Possible optimizations: * for (R)MH.invoke(a*), @@ -436,27 +387,40 @@ class Invokers { } // Local constant functions: - private static final NamedFunction NF_checkExactType; - private static final NamedFunction NF_checkGenericType; - private static final NamedFunction NF_asType; - private static final NamedFunction NF_getCallSiteTarget; + private static final NamedFunction + NF_checkExactType, + NF_checkGenericType, + NF_getCallSiteTarget; static { try { - NF_checkExactType = new NamedFunction(Invokers.class - .getDeclaredMethod("checkExactType", Object.class, Object.class)); - NF_checkGenericType = new NamedFunction(Invokers.class - .getDeclaredMethod("checkGenericType", Object.class, Object.class)); - NF_asType = new NamedFunction(MethodHandle.class - .getDeclaredMethod("asType", MethodType.class)); - NF_getCallSiteTarget = new NamedFunction(Invokers.class - .getDeclaredMethod("getCallSiteTarget", Object.class)); - NF_checkExactType.resolve(); - NF_checkGenericType.resolve(); - NF_getCallSiteTarget.resolve(); - // bound + NamedFunction nfs[] = { + NF_checkExactType = new NamedFunction(Invokers.class + .getDeclaredMethod("checkExactType", Object.class, Object.class)), + NF_checkGenericType = new NamedFunction(Invokers.class + .getDeclaredMethod("checkGenericType", Object.class, Object.class)), + NF_getCallSiteTarget = new NamedFunction(Invokers.class + .getDeclaredMethod("getCallSiteTarget", Object.class)) + }; + for (NamedFunction nf : nfs) { + // Each nf must be statically invocable or we get tied up in our bootstraps. + assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf; + nf.resolve(); + } } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } } + private static class Lazy { + private static final MethodHandle MH_asSpreader; + + static { + try { + MH_asSpreader = IMPL_LOOKUP.findVirtual(MethodHandle.class, "asSpreader", + MethodType.methodType(MethodHandle.class, Class.class, int.class)); + } catch (ReflectiveOperationException ex) { + throw newInternalError(ex); + } + } + } } diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java index 0676b7b3ab0..b83040647c5 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java @@ -27,11 +27,10 @@ package java.lang.invoke; import java.lang.annotation.*; import java.lang.reflect.Method; -import java.util.Map; import java.util.List; import java.util.Arrays; import java.util.HashMap; -import java.util.concurrent.ConcurrentHashMap; + import sun.invoke.util.Wrapper; import java.lang.reflect.Field; @@ -125,8 +124,7 @@ class LambdaForm { MemberName vmentry; // low-level behavior, or null if not yet prepared private boolean isCompiled; - // Caches for common structural transforms: - LambdaForm[] bindCache; + Object transformCache; // managed by LambdaFormEditor public static final int VOID_RESULT = -1, LAST_RESULT = -2; @@ -214,6 +212,13 @@ class LambdaForm { } return btypes; } + static byte[] basicTypesOrd(BasicType[] btypes) { + byte[] ords = new byte[btypes.length]; + for (int i = 0; i < btypes.length; i++) { + ords[i] = (byte)btypes[i].ordinal(); + } + return ords; + } static boolean isBasicTypeChar(char c) { return "LIJFDV".indexOf(c) >= 0; } @@ -243,7 +248,12 @@ class LambdaForm { this.result = fixResult(result, names); this.names = names.clone(); this.debugName = fixDebugName(debugName); - normalize(); + int maxOutArity = normalize(); + if (maxOutArity > MethodType.MAX_MH_INVOKER_ARITY) { + // Cannot use LF interpreter on very high arity expressions. + assert(maxOutArity <= MethodType.MAX_JVM_ARITY); + compileToBytecode(); + } } LambdaForm(String debugName, @@ -348,9 +358,12 @@ class LambdaForm { return true; } - /** Renumber and/or replace params so that they are interned and canonically numbered. */ - private void normalize() { + /** Renumber and/or replace params so that they are interned and canonically numbered. + * @return maximum argument list length among the names (since we have to pass over them anyway) + */ + private int normalize() { Name[] oldNames = null; + int maxOutArity = 0; int changesStart = 0; for (int i = 0; i < names.length; i++) { Name n = names[i]; @@ -361,6 +374,8 @@ class LambdaForm { } names[i] = n.cloneWithIndex(i); } + if (n.arguments != null && maxOutArity < n.arguments.length) + maxOutArity = n.arguments.length; } if (oldNames != null) { int startFixing = arity; @@ -387,6 +402,7 @@ class LambdaForm { } assert(nameRefsAreLegal()); } + return maxOutArity; } /** @@ -398,7 +414,7 @@ class LambdaForm { * This allows Name references to be freely reused to construct * fresh lambdas, without confusion. */ - private boolean nameRefsAreLegal() { + boolean nameRefsAreLegal() { assert(arity >= 0 && arity <= names.length); assert(result >= -1 && result < names.length); // Do all names possess an index consistent with their local definition order? @@ -439,8 +455,20 @@ class LambdaForm { /** Report the N-th argument type. */ BasicType parameterType(int n) { + return parameter(n).type; + } + + /** Report the N-th argument name. */ + Name parameter(int n) { assert(n < arity); - return names[n].type; + Name param = names[n]; + assert(param.isParam()); + return param; + } + + /** Report the N-th argument type constraint. */ + Object parameterConstraint(int n) { + return parameter(n).constraint; } /** Report the arity. */ @@ -448,6 +476,11 @@ class LambdaForm { return arity; } + /** Report the number of expressions (non-parameter names). */ + int expressionCount() { + return names.length - arity; + } + /** Return the method type corresponding to my basic type signature. */ MethodType methodType() { return signatureType(basicTypeSignature()); @@ -464,7 +497,7 @@ class LambdaForm { return sig.indexOf('_'); } static BasicType signatureReturn(String sig) { - return basicType(sig.charAt(signatureArity(sig)+1)); + return basicType(sig.charAt(signatureArity(sig) + 1)); } static boolean isValidSignature(String sig) { int arity = sig.indexOf('_'); @@ -582,21 +615,12 @@ class LambdaForm { isCompiled = true; return vmentry; } catch (Error | Exception ex) { - throw newInternalError("compileToBytecode", ex); + throw newInternalError(this.toString(), ex); } } - private static final ConcurrentHashMap PREPARED_FORMS; - static { - int capacity = 512; // expect many distinct signatures over time - float loadFactor = 0.75f; // normal default - int writers = 1; - PREPARED_FORMS = new ConcurrentHashMap<>(capacity, loadFactor, writers); - } - - private static Map computeInitialPreparedForms() { + private static void computeInitialPreparedForms() { // Find all predefined invokers and associate them with canonical empty lambda forms. - HashMap forms = new HashMap<>(); for (MemberName m : MemberName.getFactory().getMethods(LambdaForm.class, false, null, null, null)) { if (!m.isStatic() || !m.isPackage()) continue; MethodType mt = m.getMethodType(); @@ -607,13 +631,9 @@ class LambdaForm { assert(m.getName().equals("interpret" + sig.substring(sig.indexOf('_')))); LambdaForm form = new LambdaForm(sig); form.vmentry = m; - form = mt.form().setCachedLambdaForm(MethodTypeForm.LF_COUNTER, form); - // FIXME: get rid of PREPARED_FORMS; use MethodTypeForm cache only - forms.put(sig, form); + form = mt.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, form); } } - //System.out.println("computeInitialPreparedForms => "+forms); - return forms; } // Set this false to disable use of the interpret_L methods defined in this file. @@ -647,13 +667,11 @@ class LambdaForm { } private static LambdaForm getPreparedForm(String sig) { MethodType mtype = signatureType(sig); - //LambdaForm prep = PREPARED_FORMS.get(sig); LambdaForm prep = mtype.form().cachedLambdaForm(MethodTypeForm.LF_INTERPRET); if (prep != null) return prep; assert(isValidSignature(sig)); prep = new LambdaForm(sig); prep.vmentry = InvokerBytecodeGenerator.generateLambdaFormInterpreterEntryPoint(sig); - //LambdaForm prep2 = PREPARED_FORMS.putIfAbsent(sig.intern(), prep); return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_INTERPRET, prep); } @@ -709,10 +727,7 @@ class LambdaForm { /** If the invocation count hits the threshold we spin bytecodes and call that subsequently. */ private static final int COMPILE_THRESHOLD; static { - if (MethodHandleStatics.COMPILE_THRESHOLD != null) - COMPILE_THRESHOLD = MethodHandleStatics.COMPILE_THRESHOLD; - else - COMPILE_THRESHOLD = 30; // default value + COMPILE_THRESHOLD = Math.max(-1, MethodHandleStatics.COMPILE_THRESHOLD); } private int invocationCounter = 0; @@ -728,7 +743,9 @@ class LambdaForm { for (int i = argumentValues.length; i < values.length; i++) { values[i] = interpretName(names[i], values); } - return (result < 0) ? null : values[result]; + Object rv = (result < 0) ? null : values[result]; + assert(resultCheck(argumentValues, rv)); + return rv; } @Hidden @@ -785,28 +802,6 @@ class LambdaForm { return rval; } - //** This transform is applied (statically) to every name.function. */ - /* - private static MethodHandle eraseSubwordTypes(MethodHandle mh) { - MethodType mt = mh.type(); - if (mt.hasPrimitives()) { - mt = mt.changeReturnType(eraseSubwordType(mt.returnType())); - for (int i = 0; i < mt.parameterCount(); i++) { - mt = mt.changeParameterType(i, eraseSubwordType(mt.parameterType(i))); - } - mh = MethodHandles.explicitCastArguments(mh, mt); - } - return mh; - } - private static Class eraseSubwordType(Class type) { - if (!type.isPrimitive()) return type; - if (type == int.class) return type; - Wrapper w = Wrapper.forPrimitiveType(type); - if (w.isSubwordOrInt()) return int.class; - return type; - } - */ - static void traceInterpreter(String event, Object obj, Object... args) { if (TRACE_INTERPRETER) { System.out.println("LFI: "+event+" "+(obj != null ? obj : "")+(args != null && args.length != 0 ? Arrays.asList(args) : "")); @@ -819,8 +814,16 @@ class LambdaForm { assert(argumentValues.length == arity) : arity+"!="+Arrays.asList(argumentValues)+".length"; // also check that the leading (receiver) argument is somehow bound to this LF: assert(argumentValues[0] instanceof MethodHandle) : "not MH: " + argumentValues[0]; - assert(((MethodHandle)argumentValues[0]).internalForm() == this); + MethodHandle mh = (MethodHandle) argumentValues[0]; + assert(mh.internalForm() == this); // note: argument #0 could also be an interface wrapper, in the future + argumentTypesMatch(basicTypeSignature(), argumentValues); + return true; + } + private boolean resultCheck(Object[] argumentValues, Object result) { + MethodHandle mh = (MethodHandle) argumentValues[0]; + MethodType mt = mh.type(); + assert(valueMatches(returnType(), mt.returnType(), result)); return true; } @@ -839,7 +842,7 @@ class LambdaForm { if (i == arity) buf.append(")=>{"); Name n = names[i]; if (i >= arity) buf.append("\n "); - buf.append(n); + buf.append(n.paramString()); if (i < arity) { if (i+1 < arity) buf.append(","); continue; @@ -847,6 +850,7 @@ class LambdaForm { buf.append("=").append(n.exprString()); buf.append(";"); } + if (arity == names.length) buf.append(")=>{"); buf.append(result < 0 ? "void" : names[result]).append("}"); if (TRACE_INTERPRETER) { // Extra verbosity: @@ -856,135 +860,19 @@ class LambdaForm { return buf.toString(); } - /** - * Apply immediate binding for a Name in this form indicated by its position relative to the form. - * The first parameter to a LambdaForm, a0:L, always represents the form's method handle, so 0 is not - * accepted as valid. - */ - LambdaForm bindImmediate(int pos, BasicType basicType, Object value) { - // must be an argument, and the types must match - assert pos > 0 && pos < arity && names[pos].type == basicType && Name.typesMatch(basicType, value); - - int arity2 = arity - 1; - Name[] names2 = new Name[names.length - 1]; - for (int r = 0, w = 0; r < names.length; ++r, ++w) { // (r)ead from names, (w)rite to names2 - Name n = names[r]; - if (n.isParam()) { - if (n.index == pos) { - // do not copy over the argument that is to be replaced with a literal, - // but adjust the write index - --w; - } else { - names2[w] = new Name(w, n.type); - } - } else { - Object[] arguments2 = new Object[n.arguments.length]; - for (int i = 0; i < n.arguments.length; ++i) { - Object arg = n.arguments[i]; - if (arg instanceof Name) { - int ni = ((Name) arg).index; - if (ni == pos) { - arguments2[i] = value; - } else if (ni < pos) { - // replacement position not yet passed - arguments2[i] = names2[ni]; - } else { - // replacement position passed - arguments2[i] = names2[ni - 1]; - } - } else { - arguments2[i] = arg; - } - } - names2[w] = new Name(n.function, arguments2); - names2[w].initIndex(w); - } - } - - int result2 = result == -1 ? -1 : result - 1; - return new LambdaForm(debugName, arity2, names2, result2); + @Override + public boolean equals(Object obj) { + return obj instanceof LambdaForm && equals((LambdaForm)obj); } - - LambdaForm bind(int namePos, BoundMethodHandle.SpeciesData oldData) { - Name name = names[namePos]; - BoundMethodHandle.SpeciesData newData = oldData.extendWith(name.type); - return bind(name, new Name(newData.getterFunction(oldData.fieldCount()), names[0]), oldData, newData); + public boolean equals(LambdaForm that) { + if (this.result != that.result) return false; + return Arrays.equals(this.names, that.names); } - LambdaForm bind(Name name, Name binding, - BoundMethodHandle.SpeciesData oldData, - BoundMethodHandle.SpeciesData newData) { - int pos = name.index; - assert(name.isParam()); - assert(!binding.isParam()); - assert(name.type == binding.type); - assert(0 <= pos && pos < arity && names[pos] == name); - assert(binding.function.memberDeclaringClassOrNull() == newData.clazz); - assert(oldData.getters.length == newData.getters.length-1); - if (bindCache != null) { - LambdaForm form = bindCache[pos]; - if (form != null) { - assert(form.contains(binding)) : "form << " + form + " >> does not contain binding << " + binding + " >>"; - return form; - } - } else { - bindCache = new LambdaForm[arity]; - } - assert(nameRefsAreLegal()); - int arity2 = arity-1; - Name[] names2 = names.clone(); - names2[pos] = binding; // we might move this in a moment - - // The newly created LF will run with a different BMH. - // Switch over any pre-existing BMH field references to the new BMH class. - int firstOldRef = -1; - for (int i = 0; i < names2.length; i++) { - Name n = names[i]; - if (n.function != null && - n.function.memberDeclaringClassOrNull() == oldData.clazz) { - MethodHandle oldGetter = n.function.resolvedHandle; - MethodHandle newGetter = null; - for (int j = 0; j < oldData.getters.length; j++) { - if (oldGetter == oldData.getters[j]) - newGetter = newData.getters[j]; - } - if (newGetter != null) { - if (firstOldRef < 0) firstOldRef = i; - Name n2 = new Name(newGetter, n.arguments); - names2[i] = n2; - } - } - } - - // Walk over the new list of names once, in forward order. - // Replace references to 'name' with 'binding'. - // Replace data structure references to the old BMH species with the new. - // This might cause a ripple effect, but it will settle in one pass. - assert(firstOldRef < 0 || firstOldRef > pos); - for (int i = pos+1; i < names2.length; i++) { - if (i <= arity2) continue; - names2[i] = names2[i].replaceNames(names, names2, pos, i); - } - - // (a0, a1, name=a2, a3, a4) => (a0, a1, a3, a4, binding) - int insPos = pos; - for (; insPos+1 < names2.length; insPos++) { - Name n = names2[insPos+1]; - if (n.isSiblingBindingBefore(binding)) { - names2[insPos] = n; - } else { - break; - } - } - names2[insPos] = binding; - - // Since we moved some stuff, maybe update the result reference: - int result2 = result; - if (result2 == pos) - result2 = insPos; - else if (result2 > pos && result2 <= insPos) - result2 -= 1; - - return bindCache[pos] = new LambdaForm(debugName, arity2, names2, result2); + public int hashCode() { + return result + 31 * Arrays.hashCode(names); + } + LambdaFormEditor editor() { + return LambdaFormEditor.lambdaFormEditor(this); } boolean contains(Name name) { @@ -1000,16 +888,16 @@ class LambdaForm { } LambdaForm addArguments(int pos, BasicType... types) { - assert(pos <= arity); + // names array has MH in slot 0; skip it. + int argpos = pos + 1; + assert(argpos <= arity); int length = names.length; int inTypes = types.length; Name[] names2 = Arrays.copyOf(names, length + inTypes); int arity2 = arity + inTypes; int result2 = result; - if (result2 >= arity) + if (result2 >= argpos) result2 += inTypes; - // names array has MH in slot 0; skip it. - int argpos = pos + 1; // Note: The LF constructor will rename names2[argpos...]. // Make space for new arguments (shift temporaries). System.arraycopy(names, argpos, names2, argpos + inTypes, length - argpos); @@ -1102,8 +990,9 @@ class LambdaForm { } NamedFunction(MemberName member, MethodHandle resolvedHandle) { this.member = member; - //resolvedHandle = eraseSubwordTypes(resolvedHandle); this.resolvedHandle = resolvedHandle; + // The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest. + //assert(!isInvokeBasic()); } NamedFunction(MethodType basicInvokerType) { assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType; @@ -1114,6 +1003,13 @@ class LambdaForm { // necessary to pass BigArityTest this.member = Invokers.invokeBasicMethod(basicInvokerType); } + assert(isInvokeBasic()); + } + + private boolean isInvokeBasic() { + return member != null && + member.isMethodHandleInvoke() && + "invokeBasic".equals(member.getName()); } // The next 3 constructors are used to break circular dependencies on MH.invokeStatic, etc. @@ -1169,7 +1065,7 @@ class LambdaForm { if (LambdaForm.signatureReturn(sig) == V_TYPE) srcType = srcType.changeReturnType(void.class); MethodTypeForm typeForm = srcType.form(); - typeForm.namedFunctionInvoker = DirectMethodHandle.make(m); + typeForm.setCachedMethodHandle(MethodTypeForm.MH_NF_INV, DirectMethodHandle.make(m)); } } } @@ -1179,85 +1075,104 @@ class LambdaForm { /** void return type invokers. */ @Hidden static Object invoke__V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 0); + assert(arityCheck(0, void.class, mh, a)); mh.invokeBasic(); return null; } @Hidden static Object invoke_L_V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 1); + assert(arityCheck(1, void.class, mh, a)); mh.invokeBasic(a[0]); return null; } @Hidden static Object invoke_LL_V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 2); + assert(arityCheck(2, void.class, mh, a)); mh.invokeBasic(a[0], a[1]); return null; } @Hidden static Object invoke_LLL_V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 3); + assert(arityCheck(3, void.class, mh, a)); mh.invokeBasic(a[0], a[1], a[2]); return null; } @Hidden static Object invoke_LLLL_V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 4); + assert(arityCheck(4, void.class, mh, a)); mh.invokeBasic(a[0], a[1], a[2], a[3]); return null; } @Hidden static Object invoke_LLLLL_V(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 5); + assert(arityCheck(5, void.class, mh, a)); mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]); return null; } /** Object return type invokers. */ @Hidden static Object invoke__L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 0); + assert(arityCheck(0, mh, a)); return mh.invokeBasic(); } @Hidden static Object invoke_L_L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 1); + assert(arityCheck(1, mh, a)); return mh.invokeBasic(a[0]); } @Hidden static Object invoke_LL_L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 2); + assert(arityCheck(2, mh, a)); return mh.invokeBasic(a[0], a[1]); } @Hidden static Object invoke_LLL_L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 3); + assert(arityCheck(3, mh, a)); return mh.invokeBasic(a[0], a[1], a[2]); } @Hidden static Object invoke_LLLL_L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 4); + assert(arityCheck(4, mh, a)); return mh.invokeBasic(a[0], a[1], a[2], a[3]); } @Hidden static Object invoke_LLLLL_L(MethodHandle mh, Object[] a) throws Throwable { - assert(a.length == 5); + assert(arityCheck(5, mh, a)); return mh.invokeBasic(a[0], a[1], a[2], a[3], a[4]); } + private static boolean arityCheck(int arity, MethodHandle mh, Object[] a) { + return arityCheck(arity, Object.class, mh, a); + } + private static boolean arityCheck(int arity, Class rtype, MethodHandle mh, Object[] a) { + assert(a.length == arity) + : Arrays.asList(a.length, arity); + assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype)) + : Arrays.asList(mh, rtype, arity); + MemberName member = mh.internalMemberName(); + if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) { + assert(arity > 0); + assert(a[0] instanceof MethodHandle); + MethodHandle mh2 = (MethodHandle) a[0]; + assert(mh2.type().basicType() == MethodType.genericMethodType(arity-1).changeReturnType(rtype)) + : Arrays.asList(member, mh2, rtype, arity); + } + return true; + } static final MethodType INVOKER_METHOD_TYPE = MethodType.methodType(Object.class, MethodHandle.class, Object[].class); private static MethodHandle computeInvoker(MethodTypeForm typeForm) { - MethodHandle mh = typeForm.namedFunctionInvoker; + typeForm = typeForm.basicType().form(); // normalize to basic type + MethodHandle mh = typeForm.cachedMethodHandle(MethodTypeForm.MH_NF_INV); if (mh != null) return mh; MemberName invoker = InvokerBytecodeGenerator.generateNamedFunctionInvoker(typeForm); // this could take a while mh = DirectMethodHandle.make(invoker); - MethodHandle mh2 = typeForm.namedFunctionInvoker; + MethodHandle mh2 = typeForm.cachedMethodHandle(MethodTypeForm.MH_NF_INV); if (mh2 != null) return mh2; // benign race if (!mh.type().equals(INVOKER_METHOD_TYPE)) throw newInternalError(mh.debugString()); - return typeForm.namedFunctionInvoker = mh; + return typeForm.setCachedMethodHandle(MethodTypeForm.MH_NF_INV, mh); } @Hidden @@ -1365,6 +1280,11 @@ class LambdaForm { public boolean isConstantZero() { return this.equals(constantZero(returnType())); } + + public MethodHandleImpl.Intrinsic intrinsicName() { + return resolvedHandle == null ? MethodHandleImpl.Intrinsic.NONE + : resolvedHandle.intrinsicName(); + } } public static String basicTypeSignature(MethodType type) { @@ -1411,6 +1331,7 @@ class LambdaForm { final BasicType type; private short index; final NamedFunction function; + final Object constraint; // additional type information, if not null @Stable final Object[] arguments; private Name(int index, BasicType type, NamedFunction function, Object[] arguments) { @@ -1418,8 +1339,18 @@ class LambdaForm { this.type = type; this.function = function; this.arguments = arguments; + this.constraint = null; assert(this.index == index); } + private Name(Name that, Object constraint) { + this.index = that.index; + this.type = that.type; + this.function = that.function; + this.arguments = that.arguments; + this.constraint = constraint; + assert(constraint == null || isParam()); // only params have constraints + assert(constraint == null || constraint instanceof BoundMethodHandle.SpeciesData || constraint instanceof Class); + } Name(MethodHandle function, Object... arguments) { this(new NamedFunction(function), arguments); } @@ -1431,7 +1362,7 @@ class LambdaForm { this(new NamedFunction(function), arguments); } Name(NamedFunction function, Object... arguments) { - this(-1, function.returnType(), function, arguments = arguments.clone()); + this(-1, function.returnType(), function, arguments = Arrays.copyOf(arguments, arguments.length, Object[].class)); assert(arguments.length == function.arity()) : "arity mismatch: arguments.length=" + arguments.length + " == function.arity()=" + function.arity() + " in " + debugString(); for (int i = 0; i < arguments.length; i++) assert(typesMatch(function.parameterType(i), arguments[i])) : "types don't match: function.parameterType(" + i + ")=" + function.parameterType(i) + ", arguments[" + i + "]=" + arguments[i] + " in " + debugString(); @@ -1467,7 +1398,11 @@ class LambdaForm { } Name cloneWithIndex(int i) { Object[] newArguments = (arguments == null) ? null : arguments.clone(); - return new Name(i, type, function, newArguments); + return new Name(i, type, function, newArguments).withConstraint(constraint); + } + Name withConstraint(Object constraint) { + if (constraint == this.constraint) return this; + return new Name(this, constraint); } Name replaceName(Name oldName, Name newName) { // FIXME: use replaceNames uniformly if (oldName == newName) return this; @@ -1487,7 +1422,11 @@ class LambdaForm { if (!replaced) return this; return new Name(function, arguments); } + /** In the arguments of this Name, replace oldNames[i] pairwise by newNames[i]. + * Limit such replacements to {@code start<=i= end) return this; @SuppressWarnings("LocalVariableHidesMemberVariable") Object[] arguments = this.arguments; boolean replaced = false; @@ -1539,9 +1478,17 @@ class LambdaForm { return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+typeChar(); } public String debugString() { - String s = toString(); + String s = paramString(); return (function == null) ? s : s + "=" + exprString(); } + public String paramString() { + String s = toString(); + Object c = constraint; + if (c == null) + return s; + if (c instanceof Class) c = ((Class)c).getSimpleName(); + return s + "/" + c; + } public String exprString() { if (function == null) return toString(); StringBuilder buf = new StringBuilder(function.toString()); @@ -1572,34 +1519,6 @@ class LambdaForm { return true; } - /** - * Does this Name precede the given binding node in some canonical order? - * This predicate is used to order data bindings (via insertion sort) - * with some stability. - */ - boolean isSiblingBindingBefore(Name binding) { - assert(!binding.isParam()); - if (isParam()) return true; - if (function.equals(binding.function) && - arguments.length == binding.arguments.length) { - boolean sawInt = false; - for (int i = 0; i < arguments.length; i++) { - Object a1 = arguments[i]; - Object a2 = binding.arguments[i]; - if (!a1.equals(a2)) { - if (a1 instanceof Integer && a2 instanceof Integer) { - if (sawInt) continue; - sawInt = true; - if ((int)a1 < (int)a2) continue; // still might be true - } - return false; - } - } - return sawInt; - } - return false; - } - /** Return the index of the last occurrence of n in the argument array. * Return -1 if the name is not used. */ @@ -1690,6 +1609,7 @@ class LambdaForm { static Name internArgument(Name n) { assert(n.isParam()) : "not param: " + n; assert(n.index < INTERNED_ARGUMENT_LIMIT); + if (n.constraint != null) return n; return argument(n.index, n.type); } static Name[] arguments(int extra, String types) { @@ -1858,37 +1778,6 @@ class LambdaForm { @interface Hidden { } - -/* - // Smoke-test for the invokers used in this file. - static void testMethodHandleLinkers() throws Throwable { - MemberName.Factory lookup = MemberName.getFactory(); - MemberName asList_MN = new MemberName(Arrays.class, "asList", - MethodType.methodType(List.class, Object[].class), - REF_invokeStatic); - //MethodHandleNatives.resolve(asList_MN, null); - asList_MN = lookup.resolveOrFail(asList_MN, REF_invokeStatic, null, NoSuchMethodException.class); - System.out.println("about to call "+asList_MN); - Object[] abc = { "a", "bc" }; - List lst = (List) MethodHandle.linkToStatic(abc, asList_MN); - System.out.println("lst="+lst); - MemberName toString_MN = new MemberName(Object.class.getMethod("toString")); - String s1 = (String) MethodHandle.linkToVirtual(lst, toString_MN); - toString_MN = new MemberName(Object.class.getMethod("toString"), true); - String s2 = (String) MethodHandle.linkToSpecial(lst, toString_MN); - System.out.println("[s1,s2,lst]="+Arrays.asList(s1, s2, lst.toString())); - MemberName toArray_MN = new MemberName(List.class.getMethod("toArray")); - Object[] arr = (Object[]) MethodHandle.linkToInterface(lst, toArray_MN); - System.out.println("toArray="+Arrays.toString(arr)); - } - static { try { testMethodHandleLinkers(); } catch (Throwable ex) { throw new RuntimeException(ex); } } - // Requires these definitions in MethodHandle: - static final native Object linkToStatic(Object x1, MemberName mn) throws Throwable; - static final native Object linkToVirtual(Object x1, MemberName mn) throws Throwable; - static final native Object linkToSpecial(Object x1, MemberName mn) throws Throwable; - static final native Object linkToInterface(Object x1, MemberName mn) throws Throwable; - */ - private static final HashMap DEBUG_NAME_COUNTERS; static { if (debugEnabled()) @@ -1901,7 +1790,7 @@ class LambdaForm { static { createIdentityForms(); if (USE_PREDEFINED_INTERPRET_METHODS) - PREPARED_FORMS.putAll(computeInitialPreparedForms()); + computeInitialPreparedForms(); NamedFunction.initializeInvokers(); } diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java new file mode 100644 index 00000000000..872a2a59921 --- /dev/null +++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2013, 2014, 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 java.lang.invoke; + +import java.util.ArrayList; +import java.util.Arrays; +import static java.lang.invoke.LambdaForm.*; +import static java.lang.invoke.LambdaForm.BasicType.*; + +/** Working storage for an LF that is being transformed. + * Similarly to a StringBuffer, the editing can take place in multiple steps. + */ +final class LambdaFormBuffer { + private int arity, length; + private Name[] names; + private Name[] originalNames; // snapshot of pre-transaction names + private byte flags; + private int firstChange; + private Name resultName; + private String debugName; + private ArrayList dups; + + private static final int F_TRANS = 0x10, F_OWNED = 0x03; + + LambdaFormBuffer(LambdaForm lf) { + this(lf.arity, lf.names, lf.result); + debugName = lf.debugName; + assert(lf.nameRefsAreLegal()); + } + + private LambdaFormBuffer(int arity, Name[] names, int result) { + this.arity = arity; + setNames(names); + if (result == LAST_RESULT) result = length - 1; + if (result >= 0 && names[result].type != V_TYPE) + resultName = names[result]; + } + + private LambdaForm lambdaForm() { + assert(!inTrans()); // need endEdit call to tidy things up + return new LambdaForm(debugName, arity, nameArray(), resultIndex()); + } + + Name name(int i) { + assert(i < length); + return names[i]; + } + + Name[] nameArray() { + return Arrays.copyOf(names, length); + } + + int resultIndex() { + if (resultName == null) return VOID_RESULT; + int index = indexOf(resultName, names); + assert(index >= 0); + return index; + } + + void setNames(Name[] names2) { + names = originalNames = names2; // keep a record of where everything was to start with + length = names2.length; + flags = 0; + } + + private boolean verifyArity() { + for (int i = 0; i < arity && i < firstChange; i++) { + assert(names[i].isParam()) : "#" + i + "=" + names[i]; + } + for (int i = arity; i < length; i++) { + assert(!names[i].isParam()) : "#" + i + "=" + names[i]; + } + for (int i = length; i < names.length; i++) { + assert(names[i] == null) : "#" + i + "=" + names[i]; + } + // check resultName also + if (resultName != null) { + int resultIndex = indexOf(resultName, names); + assert(resultIndex >= 0) : "not found: " + resultName.exprString() + Arrays.asList(names); + assert(names[resultIndex] == resultName); + } + return true; + } + + private boolean verifyFirstChange() { + assert(inTrans()); + for (int i = 0; i < length; i++) { + if (names[i] != originalNames[i]) { + assert(firstChange == i) : Arrays.asList(firstChange, i, originalNames[i].exprString(), Arrays.asList(names)); + return true; + } + } + assert(firstChange == length) : Arrays.asList(firstChange, Arrays.asList(names)); + return true; + } + + private static int indexOf(NamedFunction fn, NamedFunction[] fns) { + for (int i = 0; i < fns.length; i++) { + if (fns[i] == fn) return i; + } + return -1; + } + + private static int indexOf(Name n, Name[] ns) { + for (int i = 0; i < ns.length; i++) { + if (ns[i] == n) return i; + } + return -1; + } + + boolean inTrans() { + return (flags & F_TRANS) != 0; + } + + int ownedCount() { + return flags & F_OWNED; + } + + void growNames(int insertPos, int growLength) { + int oldLength = length; + int newLength = oldLength + growLength; + int oc = ownedCount(); + if (oc == 0 || newLength > names.length) { + names = Arrays.copyOf(names, (names.length + growLength) * 5 / 4); + if (oc == 0) { + flags++; + oc++; + assert(ownedCount() == oc); + } + } + if (originalNames != null && originalNames.length < names.length) { + originalNames = Arrays.copyOf(originalNames, names.length); + if (oc == 1) { + flags++; + oc++; + assert(ownedCount() == oc); + } + } + if (growLength == 0) return; + int insertEnd = insertPos + growLength; + int tailLength = oldLength - insertPos; + System.arraycopy(names, insertPos, names, insertEnd, tailLength); + Arrays.fill(names, insertPos, insertEnd, null); + if (originalNames != null) { + System.arraycopy(originalNames, insertPos, originalNames, insertEnd, tailLength); + Arrays.fill(originalNames, insertPos, insertEnd, null); + } + length = newLength; + if (firstChange >= insertPos) { + firstChange += growLength; + } + } + + int lastIndexOf(Name n) { + int result = -1; + for (int i = 0; i < length; i++) { + if (names[i] == n) result = i; + } + return result; + } + + /** We have just overwritten the name at pos1 with the name at pos2. + * This means that there are two copies of the name, which we will have to fix later. + */ + private void noteDuplicate(int pos1, int pos2) { + Name n = names[pos1]; + assert(n == names[pos2]); + assert(originalNames[pos1] != null); // something was replaced at pos1 + assert(originalNames[pos2] == null || originalNames[pos2] == n); + if (dups == null) { + dups = new ArrayList<>(); + } + dups.add(n); + } + + /** Replace duplicate names by nulls, and remove all nulls. */ + private void clearDuplicatesAndNulls() { + if (dups != null) { + // Remove duplicates. + assert(ownedCount() >= 1); + for (Name dup : dups) { + for (int i = firstChange; i < length; i++) { + if (names[i] == dup && originalNames[i] != dup) { + names[i] = null; + assert(Arrays.asList(names).contains(dup)); + break; // kill only one dup + } + } + } + dups.clear(); + } + // Now that we are done with originalNames, remove "killed" names. + int oldLength = length; + for (int i = firstChange; i < length; i++) { + if (names[i] == null) { + System.arraycopy(names, i + 1, names, i, (--length - i)); + --i; // restart loop at this position + } + } + if (length < oldLength) { + Arrays.fill(names, length, oldLength, null); + } + assert(!Arrays.asList(names).subList(0, length).contains(null)); + } + + /** Create a private, writable copy of names. + * Preserve the original copy, for reference. + */ + void startEdit() { + assert(verifyArity()); + int oc = ownedCount(); + assert(!inTrans()); // no nested transactions + flags |= F_TRANS; + Name[] oldNames = names; + Name[] ownBuffer = (oc == 2 ? originalNames : null); + assert(ownBuffer != oldNames); + if (ownBuffer != null && ownBuffer.length >= length) { + names = copyNamesInto(ownBuffer); + } else { + // make a new buffer to hold the names + final int SLOP = 2; + names = Arrays.copyOf(oldNames, Math.max(length + SLOP, oldNames.length)); + if (oc < 2) ++flags; + assert(ownedCount() == oc + 1); + } + originalNames = oldNames; + assert(originalNames != names); + firstChange = length; + assert(inTrans()); + } + + private void changeName(int i, Name name) { + assert(inTrans()); + assert(i < length); + Name oldName = names[i]; + assert(oldName == originalNames[i]); // no multiple changes + assert(verifyFirstChange()); + if (ownedCount() == 0) + growNames(0, 0); + names[i] = name; + if (firstChange > i) { + firstChange = i; + } + if (resultName != null && resultName == oldName) { + resultName = name; + } + } + + /** Change the result name. Null means a void result. */ + void setResult(Name name) { + assert(name == null || lastIndexOf(name) >= 0); + resultName = name; + } + + /** Finish a transaction. */ + LambdaForm endEdit() { + assert(verifyFirstChange()); + // Assuming names have been changed pairwise from originalNames[i] to names[i], + // update arguments to ensure referential integrity. + for (int i = Math.max(firstChange, arity); i < length; i++) { + Name name = names[i]; + if (name == null) continue; // space for removed duplicate + Name newName = name.replaceNames(originalNames, names, firstChange, i); + if (newName != name) { + names[i] = newName; + if (resultName == name) { + resultName = newName; + } + } + } + assert(inTrans()); + flags &= ~F_TRANS; + clearDuplicatesAndNulls(); + originalNames = null; + // If any parameters have been changed, then reorder them as needed. + // This is a "sheep-and-goats" stable sort, pushing all non-parameters + // to the right of all parameters. + if (firstChange < arity) { + Name[] exprs = new Name[arity - firstChange]; + int argp = firstChange, exprp = 0; + for (int i = firstChange; i < arity; i++) { + Name name = names[i]; + if (name.isParam()) { + names[argp++] = name; + } else { + exprs[exprp++] = name; + } + } + assert(exprp == (arity - argp)); + // copy the exprs just after the last remaining param + System.arraycopy(exprs, 0, names, argp, exprp); + // adjust arity + arity -= exprp; + } + assert(verifyArity()); + return lambdaForm(); + } + + private Name[] copyNamesInto(Name[] buffer) { + System.arraycopy(names, 0, buffer, 0, length); + Arrays.fill(buffer, length, buffer.length, null); + return buffer; + } + + /** Replace any Name whose function is in oldFns with a copy + * whose function is in the corresponding position in newFns. + * Only do this if the arguments are exactly equal to the given. + */ + LambdaFormBuffer replaceFunctions(NamedFunction[] oldFns, NamedFunction[] newFns, + Object... forArguments) { + assert(inTrans()); + if (oldFns.length == 0) return this; + for (int i = arity; i < length; i++) { + Name n = names[i]; + int nfi = indexOf(n.function, oldFns); + if (nfi >= 0 && Arrays.equals(n.arguments, forArguments)) { + changeName(i, new Name(newFns[nfi], n.arguments)); + } + } + return this; + } + + private void replaceName(int pos, Name binding) { + assert(inTrans()); + assert(verifyArity()); + assert(pos < arity); + Name param = names[pos]; + assert(param.isParam()); + assert(param.type == binding.type); + changeName(pos, binding); + } + + /** Replace a parameter by a fresh parameter. */ + LambdaFormBuffer renameParameter(int pos, Name newParam) { + assert(newParam.isParam()); + replaceName(pos, newParam); + return this; + } + + /** Replace a parameter by a fresh expression. */ + LambdaFormBuffer replaceParameterByNewExpression(int pos, Name binding) { + assert(!binding.isParam()); + assert(lastIndexOf(binding) < 0); // else use replaceParameterByCopy + replaceName(pos, binding); + return this; + } + + /** Replace a parameter by another parameter or expression already in the form. */ + LambdaFormBuffer replaceParameterByCopy(int pos, int valuePos) { + assert(pos != valuePos); + replaceName(pos, names[valuePos]); + noteDuplicate(pos, valuePos); // temporarily, will occur twice in the names array + return this; + } + + private void insertName(int pos, Name expr, boolean isParameter) { + assert(inTrans()); + assert(verifyArity()); + assert(isParameter ? pos <= arity : pos >= arity); + growNames(pos, 1); + if (isParameter) arity += 1; + changeName(pos, expr); + } + + /** Insert a fresh expression. */ + LambdaFormBuffer insertExpression(int pos, Name expr) { + assert(!expr.isParam()); + insertName(pos, expr, false); + return this; + } + + /** Insert a fresh parameter. */ + LambdaFormBuffer insertParameter(int pos, Name param) { + assert(param.isParam()); + insertName(pos, param, true); + return this; + } +} diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java new file mode 100644 index 00000000000..1c29d73b4cc --- /dev/null +++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java @@ -0,0 +1,825 @@ +/* + * Copyright (c) 2014, 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 java.lang.invoke; + +import java.util.Arrays; +import static java.lang.invoke.LambdaForm.*; +import static java.lang.invoke.LambdaForm.BasicType.*; +import static java.lang.invoke.MethodHandleImpl.Intrinsic; +import java.util.Collections; +import java.util.concurrent.ConcurrentHashMap; + +import sun.invoke.util.Wrapper; + +/** Transforms on LFs. + * A lambda-form editor can derive new LFs from its base LF. + * The editor can cache derived LFs, which simplifies the reuse of their underlying bytecodes. + * To support this caching, a LF has an optional pointer to its editor. + */ +class LambdaFormEditor { + final LambdaForm lambdaForm; + + private LambdaFormEditor(LambdaForm lambdaForm) { + this.lambdaForm = lambdaForm; + } + + // Factory method. + static LambdaFormEditor lambdaFormEditor(LambdaForm lambdaForm) { + // TO DO: Consider placing intern logic here, to cut down on duplication. + // lambdaForm = findPreexistingEquivalent(lambdaForm) + return new LambdaFormEditor(lambdaForm); + } + + /** A description of a cached transform, possibly associated with the result of the transform. + * The logical content is a sequence of byte values, starting with a Kind.ordinal value. + * The sequence is unterminated, ending with an indefinite number of zero bytes. + * Sequences that are simple (short enough and with small enough values) pack into a 64-bit long. + */ + private static final class Transform { + final long packedBytes; + final byte[] fullBytes; + final LambdaForm result; // result of transform, or null, if there is none available + + private enum Kind { + NO_KIND, // necessary because ordinal must be greater than zero + BIND_ARG, ADD_ARG, DUP_ARG, + SPREAD_ARGS, + FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO, + COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY, + FOLD_ARGS, FOLD_ARGS_TO_VOID, + PERMUTE_ARGS + //maybe add more for guard with test, catch exception, pointwise type conversions + } + + private static final boolean STRESS_TEST = false; // turn on to disable most packing + private static final int + PACKED_BYTE_SIZE = (STRESS_TEST ? 2 : 4), + PACKED_BYTE_MASK = (1 << PACKED_BYTE_SIZE) - 1, + PACKED_BYTE_MAX_LENGTH = (STRESS_TEST ? 3 : 64 / PACKED_BYTE_SIZE); + + private static long packedBytes(byte[] bytes) { + if (bytes.length > PACKED_BYTE_MAX_LENGTH) return 0; + long pb = 0; + int bitset = 0; + for (int i = 0; i < bytes.length; i++) { + int b = bytes[i] & 0xFF; + bitset |= b; + pb |= (long)b << (i * PACKED_BYTE_SIZE); + } + if (!inRange(bitset)) + return 0; + return pb; + } + private static long packedBytes(int b0, int b1) { + assert(inRange(b0 | b1)); + return ( (b0 << 0*PACKED_BYTE_SIZE) + | (b1 << 1*PACKED_BYTE_SIZE)); + } + private static long packedBytes(int b0, int b1, int b2) { + assert(inRange(b0 | b1 | b2)); + return ( (b0 << 0*PACKED_BYTE_SIZE) + | (b1 << 1*PACKED_BYTE_SIZE) + | (b2 << 2*PACKED_BYTE_SIZE)); + } + private static long packedBytes(int b0, int b1, int b2, int b3) { + assert(inRange(b0 | b1 | b2 | b3)); + return ( (b0 << 0*PACKED_BYTE_SIZE) + | (b1 << 1*PACKED_BYTE_SIZE) + | (b2 << 2*PACKED_BYTE_SIZE) + | (b3 << 3*PACKED_BYTE_SIZE)); + } + private static boolean inRange(int bitset) { + assert((bitset & 0xFF) == bitset); // incoming values must fit in *unsigned* byte + return ((bitset & ~PACKED_BYTE_MASK) == 0); + } + private static byte[] fullBytes(int... byteValues) { + byte[] bytes = new byte[byteValues.length]; + int i = 0; + for (int bv : byteValues) { + bytes[i++] = bval(bv); + } + assert(packedBytes(bytes) == 0); + return bytes; + } + + private byte byteAt(int i) { + long pb = packedBytes; + if (pb == 0) { + if (i >= fullBytes.length) return 0; + return fullBytes[i]; + } + assert(fullBytes == null); + if (i > PACKED_BYTE_MAX_LENGTH) return 0; + int pos = (i * PACKED_BYTE_SIZE); + return (byte)((pb >>> pos) & PACKED_BYTE_MASK); + } + + Kind kind() { return Kind.values()[byteAt(0)]; } + + private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) { + this.packedBytes = packedBytes; + this.fullBytes = fullBytes; + this.result = result; + } + private Transform(long packedBytes) { + this(packedBytes, null, null); + assert(packedBytes != 0); + } + private Transform(byte[] fullBytes) { + this(0, fullBytes, null); + } + + private static byte bval(int b) { + assert((b & 0xFF) == b); // incoming value must fit in *unsigned* byte + return (byte)b; + } + private static byte bval(Kind k) { + return bval(k.ordinal()); + } + static Transform of(Kind k, int b1) { + byte b0 = bval(k); + if (inRange(b0 | b1)) + return new Transform(packedBytes(b0, b1)); + else + return new Transform(fullBytes(b0, b1)); + } + static Transform of(Kind k, int b1, int b2) { + byte b0 = (byte) k.ordinal(); + if (inRange(b0 | b1 | b2)) + return new Transform(packedBytes(b0, b1, b2)); + else + return new Transform(fullBytes(b0, b1, b2)); + } + static Transform of(Kind k, int b1, int b2, int b3) { + byte b0 = (byte) k.ordinal(); + if (inRange(b0 | b1 | b2 | b3)) + return new Transform(packedBytes(b0, b1, b2, b3)); + else + return new Transform(fullBytes(b0, b1, b2, b3)); + } + private static final byte[] NO_BYTES = {}; + static Transform of(Kind k, int... b123) { + return ofBothArrays(k, b123, NO_BYTES); + } + static Transform of(Kind k, int b1, byte[] b234) { + return ofBothArrays(k, new int[]{ b1 }, b234); + } + static Transform of(Kind k, int b1, int b2, byte[] b345) { + return ofBothArrays(k, new int[]{ b1, b2 }, b345); + } + private static Transform ofBothArrays(Kind k, int[] b123, byte[] b456) { + byte[] fullBytes = new byte[1 + b123.length + b456.length]; + int i = 0; + fullBytes[i++] = bval(k); + for (int bv : b123) { + fullBytes[i++] = bval(bv); + } + for (byte bv : b456) { + fullBytes[i++] = bv; + } + long packedBytes = packedBytes(fullBytes); + if (packedBytes != 0) + return new Transform(packedBytes); + else + return new Transform(fullBytes); + } + + Transform withResult(LambdaForm result) { + return new Transform(this.packedBytes, this.fullBytes, result); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Transform && equals((Transform)obj); + } + public boolean equals(Transform that) { + return this.packedBytes == that.packedBytes && Arrays.equals(this.fullBytes, that.fullBytes); + } + @Override + public int hashCode() { + if (packedBytes != 0) { + assert(fullBytes == null); + return Long.hashCode(packedBytes); + } + return Arrays.hashCode(fullBytes); + } + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + long bits = packedBytes; + if (bits != 0) { + buf.append("("); + while (bits != 0) { + buf.append(bits & PACKED_BYTE_MASK); + bits >>>= PACKED_BYTE_SIZE; + if (bits != 0) buf.append(","); + } + buf.append(")"); + } + if (fullBytes != null) { + buf.append("unpacked"); + buf.append(Arrays.toString(fullBytes)); + } + if (result != null) { + buf.append(" result="); + buf.append(result); + } + return buf.toString(); + } + } + + /** Find a previously cached transform equivalent to the given one, and return its result. */ + private LambdaForm getInCache(Transform key) { + assert(key.result == null); + // The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap. + Object c = lambdaForm.transformCache; + Transform k = null; + if (c instanceof ConcurrentHashMap) { + @SuppressWarnings("unchecked") + ConcurrentHashMap m = (ConcurrentHashMap) c; + k = m.get(key); + } else if (c == null) { + return null; + } else if (c instanceof Transform) { + // one-element cache avoids overhead of an array + Transform t = (Transform)c; + if (t.equals(key)) k = t; + } else { + Transform[] ta = (Transform[])c; + for (int i = 0; i < ta.length; i++) { + Transform t = ta[i]; + if (t == null) break; + if (t.equals(key)) { k = t; break; } + } + } + assert(k == null || key.equals(k)); + return k == null ? null : k.result; + } + + /** Arbitrary but reasonable limits on Transform[] size for cache. */ + private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16; + + /** Cache a transform with its result, and return that result. + * But if an equivalent transform has already been cached, return its result instead. + */ + private LambdaForm putInCache(Transform key, LambdaForm form) { + key = key.withResult(form); + for (int pass = 0; ; pass++) { + Object c = lambdaForm.transformCache; + if (c instanceof ConcurrentHashMap) { + @SuppressWarnings("unchecked") + ConcurrentHashMap m = (ConcurrentHashMap) c; + Transform k = m.putIfAbsent(key, key); + return k != null ? k.result : form; + } + assert(pass == 0); + synchronized (lambdaForm) { + c = lambdaForm.transformCache; + if (c instanceof ConcurrentHashMap) + continue; + if (c == null) { + lambdaForm.transformCache = key; + return form; + } + Transform[] ta; + if (c instanceof Transform) { + Transform k = (Transform)c; + if (k.equals(key)) { + return k.result; + } + // expand one-element cache to small array + ta = new Transform[MIN_CACHE_ARRAY_SIZE]; + ta[0] = k; + lambdaForm.transformCache = c = ta; + } else { + // it is already expanded + ta = (Transform[])c; + } + int len = ta.length; + int i; + for (i = 0; i < len; i++) { + Transform k = ta[i]; + if (k == null) { + break; + } + if (k.equals(key)) { + return k.result; + } + } + if (i < len) { + // just fall through to cache update + } else if (len < MAX_CACHE_ARRAY_SIZE) { + len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE); + ta = Arrays.copyOf(ta, len); + lambdaForm.transformCache = ta; + } else { + ConcurrentHashMap m = new ConcurrentHashMap<>(MAX_CACHE_ARRAY_SIZE * 2); + for (Transform k : ta) { + m.put(k, k); + } + lambdaForm.transformCache = m; + // The second iteration will update for this query, concurrently. + continue; + } + ta[i] = key; + return form; + } + } + } + + private LambdaFormBuffer buffer() { + return new LambdaFormBuffer(lambdaForm); + } + + /// Editing methods for method handles. These need to have fast paths. + + private BoundMethodHandle.SpeciesData oldSpeciesData() { + return BoundMethodHandle.speciesData(lambdaForm); + } + private BoundMethodHandle.SpeciesData newSpeciesData(BasicType type) { + return oldSpeciesData().extendWith(type); + } + + BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) { + assert(mh.speciesData() == oldSpeciesData()); + BasicType bt = L_TYPE; + MethodType type2 = bindArgumentType(mh, pos, bt); + LambdaForm form2 = bindArgumentForm(1+pos); + return mh.copyWithExtendL(type2, form2, value); + } + BoundMethodHandle bindArgumentI(BoundMethodHandle mh, int pos, int value) { + assert(mh.speciesData() == oldSpeciesData()); + BasicType bt = I_TYPE; + MethodType type2 = bindArgumentType(mh, pos, bt); + LambdaForm form2 = bindArgumentForm(1+pos); + return mh.copyWithExtendI(type2, form2, value); + } + + BoundMethodHandle bindArgumentJ(BoundMethodHandle mh, int pos, long value) { + assert(mh.speciesData() == oldSpeciesData()); + BasicType bt = J_TYPE; + MethodType type2 = bindArgumentType(mh, pos, bt); + LambdaForm form2 = bindArgumentForm(1+pos); + return mh.copyWithExtendJ(type2, form2, value); + } + + BoundMethodHandle bindArgumentF(BoundMethodHandle mh, int pos, float value) { + assert(mh.speciesData() == oldSpeciesData()); + BasicType bt = F_TYPE; + MethodType type2 = bindArgumentType(mh, pos, bt); + LambdaForm form2 = bindArgumentForm(1+pos); + return mh.copyWithExtendF(type2, form2, value); + } + + BoundMethodHandle bindArgumentD(BoundMethodHandle mh, int pos, double value) { + assert(mh.speciesData() == oldSpeciesData()); + BasicType bt = D_TYPE; + MethodType type2 = bindArgumentType(mh, pos, bt); + LambdaForm form2 = bindArgumentForm(1+pos); + return mh.copyWithExtendD(type2, form2, value); + } + + private MethodType bindArgumentType(BoundMethodHandle mh, int pos, BasicType bt) { + assert(mh.form == lambdaForm); + assert(mh.form.names[1+pos].type == bt); + assert(BasicType.basicType(mh.type().parameterType(pos)) == bt); + return mh.type().dropParameterTypes(pos, pos+1); + } + + /// Editing methods for lambda forms. + // Each editing method can (potentially) cache the edited LF so that it can be reused later. + + LambdaForm bindArgumentForm(int pos) { + Transform key = Transform.of(Transform.Kind.BIND_ARG, pos); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.parameterConstraint(0) == newSpeciesData(lambdaForm.parameterType(pos))); + return form; + } + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + + BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); + BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos)); + Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values + Name newBaseAddress; + NamedFunction getter = newData.getterFunction(oldData.fieldCount()); + + if (pos != 0) { + // The newly created LF will run with a different BMH. + // Switch over any pre-existing BMH field references to the new BMH class. + buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); + newBaseAddress = oldBaseAddress.withConstraint(newData); + buf.renameParameter(0, newBaseAddress); + buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress)); + } else { + // cannot bind the MH arg itself, unless oldData is empty + assert(oldData == BoundMethodHandle.SpeciesData.EMPTY); + newBaseAddress = new Name(L_TYPE).withConstraint(newData); + buf.replaceParameterByNewExpression(0, new Name(getter, newBaseAddress)); + buf.insertParameter(0, newBaseAddress); + } + + form = buf.endEdit(); + return putInCache(key, form); + } + + LambdaForm addArgumentForm(int pos, BasicType type) { + Transform key = Transform.of(Transform.Kind.ADD_ARG, pos, type.ordinal()); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity+1); + assert(form.parameterType(pos) == type); + return form; + } + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + + buf.insertParameter(pos, new Name(type)); + + form = buf.endEdit(); + return putInCache(key, form); + } + + LambdaForm dupArgumentForm(int srcPos, int dstPos) { + Transform key = Transform.of(Transform.Kind.DUP_ARG, srcPos, dstPos); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity-1); + return form; + } + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + + assert(lambdaForm.parameter(srcPos).constraint == null); + assert(lambdaForm.parameter(dstPos).constraint == null); + buf.replaceParameterByCopy(dstPos, srcPos); + + form = buf.endEdit(); + return putInCache(key, form); + } + + LambdaForm spreadArgumentsForm(int pos, Class arrayType, int arrayLength) { + Class elementType = arrayType.getComponentType(); + Class erasedArrayType = arrayType; + if (!elementType.isPrimitive()) + erasedArrayType = Object[].class; + BasicType bt = basicType(elementType); + int elementTypeKey = bt.ordinal(); + if (bt.basicTypeClass() != elementType) { + if (elementType.isPrimitive()) { + elementTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal(); + } + } + Transform key = Transform.of(Transform.Kind.SPREAD_ARGS, pos, elementTypeKey, arrayLength); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity - arrayLength + 1); + return form; + } + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + + assert(pos <= MethodType.MAX_JVM_ARITY); + assert(pos + arrayLength <= lambdaForm.arity); + assert(pos > 0); // cannot spread the MH arg itself + + Name spreadParam = new Name(L_TYPE); + Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength); + + // insert the new expressions + int exprPos = lambdaForm.arity(); + buf.insertExpression(exprPos++, checkSpread); + // adjust the arguments + MethodHandle aload = MethodHandles.arrayElementGetter(erasedArrayType); + for (int i = 0; i < arrayLength; i++) { + Name loadArgument = new Name(aload, spreadParam, i); + buf.insertExpression(exprPos + i, loadArgument); + buf.replaceParameterByCopy(pos + i, exprPos + i); + } + buf.insertParameter(pos, spreadParam); + + form = buf.endEdit(); + return putInCache(key, form); + } + + LambdaForm collectArgumentsForm(int pos, MethodType collectorType) { + int collectorArity = collectorType.parameterCount(); + boolean dropResult = (collectorType.returnType() == void.class); + if (collectorArity == 1 && !dropResult) { + return filterArgumentForm(pos, basicType(collectorType.parameterType(0))); + } + BasicType[] newTypes = BasicType.basicTypes(collectorType.parameterList()); + Transform.Kind kind = (dropResult + ? Transform.Kind.COLLECT_ARGS_TO_VOID + : Transform.Kind.COLLECT_ARGS); + if (dropResult && collectorArity == 0) pos = 1; // pure side effect + Transform key = Transform.of(kind, pos, collectorArity, BasicType.basicTypesOrd(newTypes)); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity - (dropResult ? 0 : 1) + collectorArity); + return form; + } + form = makeArgumentCombinationForm(pos, collectorType, false, dropResult); + return putInCache(key, form); + } + + LambdaForm collectArgumentArrayForm(int pos, MethodHandle arrayCollector) { + MethodType collectorType = arrayCollector.type(); + int collectorArity = collectorType.parameterCount(); + assert(arrayCollector.intrinsicName() == Intrinsic.NEW_ARRAY); + Class arrayType = collectorType.returnType(); + Class elementType = arrayType.getComponentType(); + BasicType argType = basicType(elementType); + int argTypeKey = argType.ordinal(); + if (argType.basicTypeClass() != elementType) { + // return null if it requires more metadata (like String[].class) + if (!elementType.isPrimitive()) + return null; + argTypeKey = TYPE_LIMIT + Wrapper.forPrimitiveType(elementType).ordinal(); + } + assert(collectorType.parameterList().equals(Collections.nCopies(collectorArity, elementType))); + Transform.Kind kind = Transform.Kind.COLLECT_ARGS_TO_ARRAY; + Transform key = Transform.of(kind, pos, collectorArity, argTypeKey); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity - 1 + collectorArity); + return form; + } + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + + assert(pos + 1 <= lambdaForm.arity); + assert(pos > 0); // cannot filter the MH arg itself + + Name[] newParams = new Name[collectorArity]; + for (int i = 0; i < collectorArity; i++) { + newParams[i] = new Name(pos + i, argType); + } + Name callCombiner = new Name(arrayCollector, (Object[]) /*...*/ newParams); + + // insert the new expression + int exprPos = lambdaForm.arity(); + buf.insertExpression(exprPos, callCombiner); + + // insert new arguments + int argPos = pos + 1; // skip result parameter + for (Name newParam : newParams) { + buf.insertParameter(argPos++, newParam); + } + assert(buf.lastIndexOf(callCombiner) == exprPos+newParams.length); + buf.replaceParameterByCopy(pos, exprPos+newParams.length); + + form = buf.endEdit(); + return putInCache(key, form); + } + + LambdaForm filterArgumentForm(int pos, BasicType newType) { + Transform key = Transform.of(Transform.Kind.FILTER_ARG, pos, newType.ordinal()); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity); + assert(form.parameterType(pos) == newType); + return form; + } + + BasicType oldType = lambdaForm.parameterType(pos); + MethodType filterType = MethodType.methodType(oldType.basicTypeClass(), + newType.basicTypeClass()); + form = makeArgumentCombinationForm(pos, filterType, false, false); + return putInCache(key, form); + } + + private LambdaForm makeArgumentCombinationForm(int pos, + MethodType combinerType, + boolean keepArguments, boolean dropResult) { + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + int combinerArity = combinerType.parameterCount(); + int resultArity = (dropResult ? 0 : 1); + + assert(pos <= MethodType.MAX_JVM_ARITY); + assert(pos + resultArity + (keepArguments ? combinerArity : 0) <= lambdaForm.arity); + assert(pos > 0); // cannot filter the MH arg itself + assert(combinerType == combinerType.basicType()); + assert(combinerType.returnType() != void.class || dropResult); + + BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); + BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); + + // The newly created LF will run with a different BMH. + // Switch over any pre-existing BMH field references to the new BMH class. + Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values + buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); + Name newBaseAddress = oldBaseAddress.withConstraint(newData); + buf.renameParameter(0, newBaseAddress); + + Name getCombiner = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); + Object[] combinerArgs = new Object[1 + combinerArity]; + combinerArgs[0] = getCombiner; + Name[] newParams; + if (keepArguments) { + newParams = new Name[0]; + System.arraycopy(lambdaForm.names, pos + resultArity, + combinerArgs, 1, combinerArity); + } else { + newParams = new Name[combinerArity]; + BasicType[] newTypes = basicTypes(combinerType.parameterList()); + for (int i = 0; i < newTypes.length; i++) { + newParams[i] = new Name(pos + i, newTypes[i]); + } + System.arraycopy(newParams, 0, + combinerArgs, 1, combinerArity); + } + Name callCombiner = new Name(combinerType, combinerArgs); + + // insert the two new expressions + int exprPos = lambdaForm.arity(); + buf.insertExpression(exprPos+0, getCombiner); + buf.insertExpression(exprPos+1, callCombiner); + + // insert new arguments, if needed + int argPos = pos + resultArity; // skip result parameter + for (Name newParam : newParams) { + buf.insertParameter(argPos++, newParam); + } + assert(buf.lastIndexOf(callCombiner) == exprPos+1+newParams.length); + if (!dropResult) { + buf.replaceParameterByCopy(pos, exprPos+1+newParams.length); + } + + return buf.endEdit(); + } + + LambdaForm filterReturnForm(BasicType newType, boolean constantZero) { + Transform.Kind kind = (constantZero ? Transform.Kind.FILTER_RETURN_TO_ZERO : Transform.Kind.FILTER_RETURN); + Transform key = Transform.of(kind, newType.ordinal()); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity); + assert(form.returnType() == newType); + return form; + } + LambdaFormBuffer buf = buffer(); + buf.startEdit(); + + int insPos = lambdaForm.names.length; + Name callFilter; + if (constantZero) { + // Synthesize a constant zero value for the given type. + if (newType == V_TYPE) + callFilter = null; + else + callFilter = new Name(constantZero(newType)); + } else { + BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); + BoundMethodHandle.SpeciesData newData = newSpeciesData(L_TYPE); + + // The newly created LF will run with a different BMH. + // Switch over any pre-existing BMH field references to the new BMH class. + Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values + buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); + Name newBaseAddress = oldBaseAddress.withConstraint(newData); + buf.renameParameter(0, newBaseAddress); + + Name getFilter = new Name(newData.getterFunction(oldData.fieldCount()), newBaseAddress); + buf.insertExpression(insPos++, getFilter); + BasicType oldType = lambdaForm.returnType(); + if (oldType == V_TYPE) { + MethodType filterType = MethodType.methodType(newType.basicTypeClass()); + callFilter = new Name(filterType, getFilter); + } else { + MethodType filterType = MethodType.methodType(newType.basicTypeClass(), oldType.basicTypeClass()); + callFilter = new Name(filterType, getFilter, lambdaForm.names[lambdaForm.result]); + } + } + + if (callFilter != null) + buf.insertExpression(insPos++, callFilter); + buf.setResult(callFilter); + + form = buf.endEdit(); + return putInCache(key, form); + } + + LambdaForm foldArgumentsForm(int foldPos, boolean dropResult, MethodType combinerType) { + int combinerArity = combinerType.parameterCount(); + Transform.Kind kind = (dropResult ? Transform.Kind.FOLD_ARGS_TO_VOID : Transform.Kind.FOLD_ARGS); + Transform key = Transform.of(kind, foldPos, combinerArity); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == lambdaForm.arity - (kind == Transform.Kind.FOLD_ARGS ? 1 : 0)); + return form; + } + form = makeArgumentCombinationForm(foldPos, combinerType, true, dropResult); + return putInCache(key, form); + } + + LambdaForm permuteArgumentsForm(int skip, int[] reorder) { + assert(skip == 1); // skip only the leading MH argument, names[0] + int length = lambdaForm.names.length; + int outArgs = reorder.length; + int inTypes = 0; + boolean nullPerm = true; + for (int i = 0; i < reorder.length; i++) { + int inArg = reorder[i]; + if (inArg != i) nullPerm = false; + inTypes = Math.max(inTypes, inArg+1); + } + assert(skip + reorder.length == lambdaForm.arity); + if (nullPerm) return lambdaForm; // do not bother to cache + Transform key = Transform.of(Transform.Kind.PERMUTE_ARGS, reorder); + LambdaForm form = getInCache(key); + if (form != null) { + assert(form.arity == skip+inTypes) : form; + return form; + } + + BasicType[] types = new BasicType[inTypes]; + for (int i = 0; i < outArgs; i++) { + int inArg = reorder[i]; + types[inArg] = lambdaForm.names[skip + i].type; + } + assert (skip + outArgs == lambdaForm.arity); + assert (permutedTypesMatch(reorder, types, lambdaForm.names, skip)); + int pos = 0; + while (pos < outArgs && reorder[pos] == pos) { + pos += 1; + } + Name[] names2 = new Name[length - outArgs + inTypes]; + System.arraycopy(lambdaForm.names, 0, names2, 0, skip + pos); + int bodyLength = length - lambdaForm.arity; + System.arraycopy(lambdaForm.names, skip + outArgs, names2, skip + inTypes, bodyLength); + int arity2 = names2.length - bodyLength; + int result2 = lambdaForm.result; + if (result2 >= 0) { + if (result2 < skip + outArgs) { + result2 = reorder[result2 - skip]; + } else { + result2 = result2 - outArgs + inTypes; + } + } + for (int j = pos; j < outArgs; j++) { + Name n = lambdaForm.names[skip + j]; + int i = reorder[j]; + Name n2 = names2[skip + i]; + if (n2 == null) { + names2[skip + i] = n2 = new Name(types[i]); + } else { + assert (n2.type == types[i]); + } + for (int k = arity2; k < names2.length; k++) { + names2[k] = names2[k].replaceName(n, n2); + } + } + for (int i = skip + pos; i < arity2; i++) { + if (names2[i] == null) { + names2[i] = argument(i, types[i - skip]); + } + } + for (int j = lambdaForm.arity; j < lambdaForm.names.length; j++) { + int i = j - lambdaForm.arity + arity2; + Name n = lambdaForm.names[j]; + Name n2 = names2[i]; + if (n != n2) { + for (int k = i + 1; k < names2.length; k++) { + names2[k] = names2[k].replaceName(n, n2); + } + } + } + + form = new LambdaForm(lambdaForm.debugName, arity2, names2, result2); + return putInCache(key, form); + } + + static boolean permutedTypesMatch(int[] reorder, BasicType[] types, Name[] names, int skip) { + for (int i = 0; i < reorder.length; i++) { + assert (names[skip + i].isParam()); + assert (names[skip + i].type == types[reorder[i]]); + } + return true; + } +} diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java index 75b26be4e0c..d4634c84a8e 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java @@ -327,10 +327,6 @@ import java.util.Objects; assert(getReferenceKind() == oldKind); assert(MethodHandleNatives.refKindIsValid(refKind)); flags += (((int)refKind - oldKind) << MN_REFERENCE_KIND_SHIFT); -// if (isConstructor() && refKind != REF_newInvokeSpecial) -// flags += (IS_METHOD - IS_CONSTRUCTOR); -// else if (refKind == REF_newInvokeSpecial && isMethod()) -// flags += (IS_CONSTRUCTOR - IS_METHOD); return this; } @@ -344,9 +340,11 @@ import java.util.Objects; return !testFlags(mask, 0); } - /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). */ + /** Utility method to query if this member is a method handle invocation (invoke or invokeExact). + * Also returns true for the non-public MH.invokeBasic. + */ public boolean isMethodHandleInvoke() { - final int bits = MH_INVOKE_MODS; + final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC; final int negs = Modifier.STATIC; if (testFlags(bits | negs, bits) && clazz == MethodHandle.class) { @@ -355,7 +353,14 @@ import java.util.Objects; return false; } public static boolean isMethodHandleInvokeName(String name) { - return name.equals("invoke") || name.equals("invokeExact"); + switch (name) { + case "invoke": + case "invokeExact": + case "invokeBasic": // internal sig-poly method + return true; + default: + return false; + } } private static final int MH_INVOKE_MODS = Modifier.NATIVE | Modifier.FINAL | Modifier.PUBLIC; @@ -720,16 +725,8 @@ import java.util.Objects; init(defClass, name, type, flagsMods(IS_FIELD, 0, refKind)); initResolved(false); } - /** Create a field or type name from the given components: Declaring class, name, type. - * The declaring class may be supplied as null if this is to be a bare name and type. - * The modifier flags default to zero. - * The resulting name will in an unresolved state. - */ - public MemberName(Class defClass, String name, Class type, Void unused) { - this(defClass, name, type, REF_NONE); - initResolved(false); - } - /** Create a method or constructor name from the given components: Declaring class, name, type, modifiers. + /** Create a method or constructor name from the given components: + * Declaring class, name, type, reference kind. * It will be a constructor if and only if the name is {@code "<init>"}. * The declaring class may be supplied as null if this is to be a bare name and type. * The last argument is optional, a boolean which requests REF_invokeSpecial. diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java index 5ed091e408f..e72a07a2aff 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java @@ -27,12 +27,8 @@ package java.lang.invoke; import java.util.*; -import java.lang.invoke.LambdaForm.BasicType; -import sun.invoke.util.*; -import sun.misc.Unsafe; import static java.lang.invoke.MethodHandleStatics.*; -import static java.lang.invoke.LambdaForm.BasicType.*; /** * A method handle is a typed, directly executable reference to an underlying method, @@ -625,15 +621,8 @@ public abstract class MethodHandle { * @see MethodHandles#spreadInvoker */ public Object invokeWithArguments(Object... arguments) throws Throwable { - int argc = arguments == null ? 0 : arguments.length; - @SuppressWarnings("LocalVariableHidesMemberVariable") - MethodType type = type(); - if (type.parameterCount() != argc || isVarargsCollector()) { - // simulate invoke - return asType(MethodType.genericMethodType(argc)).invokeWithArguments(arguments); - } - MethodHandle invoker = type.invokers().varargsInvoker(); - return invoker.invokeExact(this, arguments); + MethodType invocationType = MethodType.genericMethodType(arguments == null ? 0 : arguments.length); + return invocationType.invokers().spreadInvoker(0).invokeExact(asType(invocationType), arguments); } /** @@ -763,18 +752,26 @@ public abstract class MethodHandle { return this; } // Return 'this.asTypeCache' if the conversion is already memoized. + MethodHandle atc = asTypeCached(newType); + if (atc != null) { + return atc; + } + return asTypeUncached(newType); + } + + private MethodHandle asTypeCached(MethodType newType) { MethodHandle atc = asTypeCache; if (atc != null && newType == atc.type) { return atc; } - return asTypeUncached(newType); + return null; } /** Override this to change asType behavior. */ /*non-public*/ MethodHandle asTypeUncached(MethodType newType) { if (!type.isConvertibleTo(newType)) throw new WrongMethodTypeException("cannot convert "+this+" to "+newType); - return asTypeCache = convertArguments(newType); + return asTypeCache = MethodHandleImpl.makePairwiseConvert(this, newType, true); } /** @@ -867,34 +864,48 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray * @see #asCollector */ public MethodHandle asSpreader(Class arrayType, int arrayLength) { - asSpreaderChecks(arrayType, arrayLength); - int spreadArgPos = type.parameterCount() - arrayLength; - return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength); + MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength); + int arity = type().parameterCount(); + int spreadArgPos = arity - arrayLength; + if (USE_LAMBDA_FORM_EDITOR) { + MethodHandle afterSpread = this.asType(postSpreadType); + BoundMethodHandle mh = afterSpread.rebind(); + LambdaForm lform = mh.editor().spreadArgumentsForm(1 + spreadArgPos, arrayType, arrayLength); + MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, arity, arrayType); + return mh.copyWith(preSpreadType, lform); + } else { + return MethodHandleImpl.makeSpreadArguments(this, arrayType, spreadArgPos, arrayLength); + } } - private void asSpreaderChecks(Class arrayType, int arrayLength) { + /** + * See if {@code asSpreader} can be validly called with the given arguments. + * Return the type of the method handle call after spreading but before conversions. + */ + private MethodType asSpreaderChecks(Class arrayType, int arrayLength) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); if (nargs < arrayLength || arrayLength < 0) throw newIllegalArgumentException("bad spread array length"); - if (arrayType != Object[].class && arrayLength != 0) { - boolean sawProblem = false; - Class arrayElement = arrayType.getComponentType(); - for (int i = nargs - arrayLength; i < nargs; i++) { - if (!MethodType.canConvert(arrayElement, type().parameterType(i))) { - sawProblem = true; + Class arrayElement = arrayType.getComponentType(); + MethodType mtype = type(); + boolean match = true, fail = false; + for (int i = nargs - arrayLength; i < nargs; i++) { + Class ptype = mtype.parameterType(i); + if (ptype != arrayElement) { + match = false; + if (!MethodType.canConvert(arrayElement, ptype)) { + fail = true; break; } } - if (sawProblem) { - ArrayList> ptypes = new ArrayList<>(type().parameterList()); - for (int i = nargs - arrayLength; i < nargs; i++) { - ptypes.set(i, arrayElement); - } - // elicit an error: - this.asType(MethodType.methodType(type().returnType(), ptypes)); - } } + if (match) return mtype; + MethodType needType = mtype.asSpreaderType(arrayType, arrayLength); + if (!fail) return needType; + // elicit an error: + this.asType(needType); + throw newInternalError("should not return", null); } private void spreadArrayChecks(Class arrayType, int arrayLength) { @@ -984,16 +995,31 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123)); */ public MethodHandle asCollector(Class arrayType, int arrayLength) { asCollectorChecks(arrayType, arrayLength); - int collectArgPos = type().parameterCount()-1; - MethodHandle target = this; - if (arrayType != type().parameterType(collectArgPos)) - target = convertArguments(type().changeParameterType(collectArgPos, arrayType)); - MethodHandle collector = ValueConversions.varargsArray(arrayType, arrayLength); - return MethodHandles.collectArguments(target, collectArgPos, collector); + int collectArgPos = type().parameterCount() - 1; + if (USE_LAMBDA_FORM_EDITOR) { + BoundMethodHandle mh = rebind(); + MethodType resultType = type().asCollectorType(arrayType, arrayLength); + MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength); + LambdaForm lform = mh.editor().collectArgumentArrayForm(1 + collectArgPos, newArray); + if (lform != null) { + return mh.copyWith(resultType, lform); + } + lform = mh.editor().collectArgumentsForm(1 + collectArgPos, newArray.type().basicType()); + return mh.copyWithExtendL(resultType, lform, newArray); + } else { + MethodHandle target = this; + if (arrayType != type().parameterType(collectArgPos)) + target = MethodHandleImpl.makePairwiseConvert(this, type().changeParameterType(collectArgPos, arrayType), true); + MethodHandle collector = MethodHandleImpl.varargsArray(arrayType, arrayLength); + return MethodHandles.collectArguments(target, collectArgPos, collector); + } } - // private API: return true if last param exactly matches arrayType - private boolean asCollectorChecks(Class arrayType, int arrayLength) { + /** + * See if {@code asCollector} can be validly called with the given arguments. + * Return false if the last parameter is not an exact match to arrayType. + */ + /*non-public*/ boolean asCollectorChecks(Class arrayType, int arrayLength) { spreadArrayChecks(arrayType, arrayLength); int nargs = type().parameterCount(); if (nargs != 0) { @@ -1155,7 +1181,7 @@ assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0))); * @see #asFixedArity */ public MethodHandle asVarargsCollector(Class arrayType) { - Class arrayElement = arrayType.getComponentType(); + arrayType.getClass(); // explicit NPE boolean lastMatch = asCollectorChecks(arrayType, 0); if (isVarargsCollector() && lastMatch) return this; @@ -1257,14 +1283,8 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); * @see MethodHandles#insertArguments */ public MethodHandle bindTo(Object x) { - Class ptype; - @SuppressWarnings("LocalVariableHidesMemberVariable") - MethodType type = type(); - if (type.parameterCount() == 0 || - (ptype = type.parameterType(0)).isPrimitive()) - throw newIllegalArgumentException("no leading reference parameter", x); - x = ptype.cast(x); // throw CCE if needed - return bindReceiver(x); + x = type.leadingReferenceParameter().cast(x); // throw CCE if needed + return bindArgumentL(0, x); } /** @@ -1284,14 +1304,17 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); */ @Override public String toString() { - if (DEBUG_METHOD_HANDLE_NAMES) return debugString(); + if (DEBUG_METHOD_HANDLE_NAMES) return "MethodHandle"+debugString(); return standardString(); } String standardString() { return "MethodHandle"+type; } + /** Return a string with a several lines describing the method handle structure. + * This string would be suitable for display in an IDE debugger. + */ String debugString() { - return standardString()+"/LF="+internalForm()+internalProperties(); + return type+" : "+internalForm()+internalProperties(); } //// Implementation methods. @@ -1300,22 +1323,42 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); // Other transforms to do: convert, explicitCast, permute, drop, filter, fold, GWT, catch + BoundMethodHandle bindArgumentL(int pos, Object value) { + return rebind().bindArgumentL(pos, value); + } + /*non-public*/ MethodHandle setVarargs(MemberName member) throws IllegalAccessException { if (!member.isVarargs()) return this; - int argc = type().parameterCount(); - if (argc != 0) { - Class arrayType = type().parameterType(argc-1); - if (arrayType.isArray()) { - return MethodHandleImpl.makeVarargsCollector(this, arrayType); - } + Class arrayType = type().lastParameterType(); + if (arrayType.isArray()) { + return MethodHandleImpl.makeVarargsCollector(this, arrayType); } throw member.makeAccessException("cannot make variable arity", null); } + /*non-public*/ - MethodHandle viewAsType(MethodType newType) { + MethodHandle viewAsType(MethodType newType, boolean strict) { // No actual conversions, just a new view of the same method. - return MethodHandleImpl.makePairwiseConvert(this, newType, 0); + // Note that this operation must not produce a DirectMethodHandle, + // because retyped DMHs, like any transformed MHs, + // cannot be cracked into MethodHandleInfo. + assert viewAsTypeChecks(newType, strict); + BoundMethodHandle mh = rebind(); + assert(!((MethodHandle)mh instanceof DirectMethodHandle)); + return mh.copyWith(newType, mh.form); + } + + /*non-public*/ + boolean viewAsTypeChecks(MethodType newType, boolean strict) { + if (strict) { + assert(type().isViewableAs(newType, true)) + : Arrays.asList(this, newType); + } else { + assert(type().basicType().isViewableAs(newType.basicType(), true)) + : Arrays.asList(this, newType); + } + return true; } // Decoding @@ -1336,9 +1379,15 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); } /*non-public*/ - MethodHandle withInternalMemberName(MemberName member) { + MethodHandleImpl.Intrinsic intrinsicName() { + // no special intrinsic meaning to most MHs + return MethodHandleImpl.Intrinsic.NONE; + } + + /*non-public*/ + MethodHandle withInternalMemberName(MemberName member, boolean isInvokeSpecial) { if (member != null) { - return MethodHandleImpl.makeWrappedMember(this, member); + return MethodHandleImpl.makeWrappedMember(this, member, isInvokeSpecial); } else if (internalMemberName() == null) { // The required internaMemberName is null, and this MH (like most) doesn't have one. return this; @@ -1362,7 +1411,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); /*non-public*/ Object internalProperties() { - // Override to something like "/FOO=bar" + // Override to something to follow this.form, like "\n& FOO=bar" return ""; } @@ -1370,95 +1419,14 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); //// Sub-classes can override these default implementations. //// All these methods assume arguments are already validated. - /*non-public*/ MethodHandle convertArguments(MethodType newType) { - // Override this if it can be improved. - return MethodHandleImpl.makePairwiseConvert(this, newType, 1); - } - /*non-public*/ - MethodHandle bindArgument(int pos, BasicType basicType, Object value) { - // Override this if it can be improved. - return rebind().bindArgument(pos, basicType, value); - } + abstract MethodHandle copyWith(MethodType mt, LambdaForm lf); - /*non-public*/ - MethodHandle bindReceiver(Object receiver) { - // Override this if it can be improved. - return bindArgument(0, L_TYPE, receiver); - } - - /*non-public*/ - MethodHandle dropArguments(MethodType srcType, int pos, int drops) { - // Override this if it can be improved. - return rebind().dropArguments(srcType, pos, drops); - } - - /*non-public*/ - MethodHandle permuteArguments(MethodType newType, int[] reorder) { - // Override this if it can be improved. - return rebind().permuteArguments(newType, reorder); - } - - /*non-public*/ - MethodHandle rebind() { - // Bind 'this' into a new invoker, of the known class BMH. - MethodType type2 = type(); - LambdaForm form2 = reinvokerForm(this); - // form2 = lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) } - return BoundMethodHandle.bindSingle(type2, form2, this); - } - - /*non-public*/ - MethodHandle reinvokerTarget() { - throw new InternalError("not a reinvoker MH: "+this.getClass().getName()+": "+this); - } - - /** Create a LF which simply reinvokes a target of the given basic type. - * The target MH must override {@link #reinvokerTarget} to provide the target. + /** Require this method handle to be a BMH, or else replace it with a "wrapper" BMH. + * Many transforms are implemented only for BMHs. + * @return a behaviorally equivalent BMH */ - static LambdaForm reinvokerForm(MethodHandle target) { - MethodType mtype = target.type().basicType(); - LambdaForm reinvoker = mtype.form().cachedLambdaForm(MethodTypeForm.LF_REINVOKE); - if (reinvoker != null) return reinvoker; - if (mtype.parameterSlotCount() >= MethodType.MAX_MH_ARITY) - return makeReinvokerForm(target.type(), target); // cannot cache this - reinvoker = makeReinvokerForm(mtype, null); - return mtype.form().setCachedLambdaForm(MethodTypeForm.LF_REINVOKE, reinvoker); - } - private static LambdaForm makeReinvokerForm(MethodType mtype, MethodHandle customTargetOrNull) { - boolean customized = (customTargetOrNull != null); - MethodHandle MH_invokeBasic = customized ? null : MethodHandles.basicInvoker(mtype); - final int THIS_BMH = 0; - final int ARG_BASE = 1; - final int ARG_LIMIT = ARG_BASE + mtype.parameterCount(); - int nameCursor = ARG_LIMIT; - final int NEXT_MH = customized ? -1 : nameCursor++; - final int REINVOKE = nameCursor++; - LambdaForm.Name[] names = LambdaForm.arguments(nameCursor - ARG_LIMIT, mtype.invokerType()); - Object[] targetArgs; - MethodHandle targetMH; - if (customized) { - targetArgs = Arrays.copyOfRange(names, ARG_BASE, ARG_LIMIT, Object[].class); - targetMH = customTargetOrNull; - } else { - names[NEXT_MH] = new LambdaForm.Name(NF_reinvokerTarget, names[THIS_BMH]); - targetArgs = Arrays.copyOfRange(names, THIS_BMH, ARG_LIMIT, Object[].class); - targetArgs[0] = names[NEXT_MH]; // overwrite this MH with next MH - targetMH = MethodHandles.basicInvoker(mtype); - } - names[REINVOKE] = new LambdaForm.Name(targetMH, targetArgs); - return new LambdaForm("BMH.reinvoke", ARG_LIMIT, names); - } - - private static final LambdaForm.NamedFunction NF_reinvokerTarget; - static { - try { - NF_reinvokerTarget = new LambdaForm.NamedFunction(MethodHandle.class - .getDeclaredMethod("reinvokerTarget")); - } catch (ReflectiveOperationException ex) { - throw newInternalError(ex); - } - } + abstract BoundMethodHandle rebind(); /** * Replace the old lambda form of this method handle with a new one. @@ -1470,6 +1438,7 @@ assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString()); /*non-public*/ void updateForm(LambdaForm newForm) { if (form == newForm) return; + assert(this instanceof DirectMethodHandle && this.internalMemberName().isStatic()); // ISSUE: Should we have a memory fence here? UNSAFE.putObject(this, FORM_OFFSET, newForm); this.form.prepare(); // as in MethodHandle. diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index 3e4ee65e161..7b018f036cf 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -27,8 +27,10 @@ package java.lang.invoke; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; +import java.util.Collections; + import sun.invoke.empty.Empty; import sun.invoke.util.ValueConversions; import sun.invoke.util.VerifyType; @@ -44,6 +46,20 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; * @author jrose */ /*non-public*/ abstract class MethodHandleImpl { + // Do not adjust this except for special platforms: + private static final int MAX_ARITY; + static { + final Object[] values = { 255 }; + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + values[0] = Integer.getInteger(MethodHandleImpl.class.getName()+".MAX_ARITY", 255); + return null; + } + }); + MAX_ARITY = (Integer) values[0]; + } + /// Factory methods to create method handles: static void initStatics() { @@ -52,27 +68,55 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } static MethodHandle makeArrayElementAccessor(Class arrayClass, boolean isSetter) { + if (arrayClass == Object[].class) + return (isSetter ? ArrayAccessor.OBJECT_ARRAY_SETTER : ArrayAccessor.OBJECT_ARRAY_GETTER); if (!arrayClass.isArray()) throw newIllegalArgumentException("not an array: "+arrayClass); - MethodHandle accessor = ArrayAccessor.getAccessor(arrayClass, isSetter); - MethodType srcType = accessor.type().erase(); - MethodType lambdaType = srcType.invokerType(); - Name[] names = arguments(1, lambdaType); - Name[] args = Arrays.copyOfRange(names, 1, 1 + srcType.parameterCount()); - names[names.length - 1] = new Name(accessor.asType(srcType), (Object[]) args); - LambdaForm form = new LambdaForm("getElement", lambdaType.parameterCount(), names); - MethodHandle mh = SimpleMethodHandle.make(srcType, form); - if (ArrayAccessor.needCast(arrayClass)) { - mh = mh.bindTo(arrayClass); + MethodHandle[] cache = ArrayAccessor.TYPED_ACCESSORS.get(arrayClass); + int cacheIndex = (isSetter ? ArrayAccessor.SETTER_INDEX : ArrayAccessor.GETTER_INDEX); + MethodHandle mh = cache[cacheIndex]; + if (mh != null) return mh; + mh = ArrayAccessor.getAccessor(arrayClass, isSetter); + MethodType correctType = ArrayAccessor.correctType(arrayClass, isSetter); + if (mh.type() != correctType) { + assert(mh.type().parameterType(0) == Object[].class); + assert((isSetter ? mh.type().parameterType(2) : mh.type().returnType()) == Object.class); + assert(isSetter || correctType.parameterType(0).getComponentType() == correctType.returnType()); + // safe to view non-strictly, because element type follows from array type + mh = mh.viewAsType(correctType, false); + } + mh = makeIntrinsic(mh, (isSetter ? Intrinsic.ARRAY_STORE : Intrinsic.ARRAY_LOAD)); + // Atomically update accessor cache. + synchronized(cache) { + if (cache[cacheIndex] == null) { + cache[cacheIndex] = mh; + } else { + // Throw away newly constructed accessor and use cached version. + mh = cache[cacheIndex]; + } } - mh = mh.asType(ArrayAccessor.correctType(arrayClass, isSetter)); return mh; } static final class ArrayAccessor { /// Support for array element access - static final HashMap, MethodHandle> GETTER_CACHE = new HashMap<>(); // TODO use it - static final HashMap, MethodHandle> SETTER_CACHE = new HashMap<>(); // TODO use it + static final int GETTER_INDEX = 0, SETTER_INDEX = 1, INDEX_LIMIT = 2; + static final ClassValue TYPED_ACCESSORS + = new ClassValue() { + @Override + protected MethodHandle[] computeValue(Class type) { + return new MethodHandle[INDEX_LIMIT]; + } + }; + static final MethodHandle OBJECT_ARRAY_GETTER, OBJECT_ARRAY_SETTER; + static { + MethodHandle[] cache = TYPED_ACCESSORS.get(Object[].class); + cache[GETTER_INDEX] = OBJECT_ARRAY_GETTER = makeIntrinsic(getAccessor(Object[].class, false), Intrinsic.ARRAY_LOAD); + cache[SETTER_INDEX] = OBJECT_ARRAY_SETTER = makeIntrinsic(getAccessor(Object[].class, true), Intrinsic.ARRAY_STORE); + + assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_GETTER.internalMemberName())); + assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_SETTER.internalMemberName())); + } static int getElementI(int[] a, int i) { return a[i]; } static long getElementJ(long[] a, int i) { return a[i]; } @@ -94,45 +138,21 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; static void setElementC(char[] a, int i, char x) { a[i] = x; } static void setElementL(Object[] a, int i, Object x) { a[i] = x; } - static Object getElementL(Class arrayClass, Object[] a, int i) { arrayClass.cast(a); return a[i]; } - static void setElementL(Class arrayClass, Object[] a, int i, Object x) { arrayClass.cast(a); a[i] = x; } - - // Weakly typed wrappers of Object[] accessors: - static Object getElementL(Object a, int i) { return getElementL((Object[])a, i); } - static void setElementL(Object a, int i, Object x) { setElementL((Object[]) a, i, x); } - static Object getElementL(Object arrayClass, Object a, int i) { return getElementL((Class) arrayClass, (Object[])a, i); } - static void setElementL(Object arrayClass, Object a, int i, Object x) { setElementL((Class) arrayClass, (Object[])a, i, x); } - - static boolean needCast(Class arrayClass) { - Class elemClass = arrayClass.getComponentType(); - return !elemClass.isPrimitive() && elemClass != Object.class; - } static String name(Class arrayClass, boolean isSetter) { Class elemClass = arrayClass.getComponentType(); - if (elemClass == null) throw new IllegalArgumentException(); + if (elemClass == null) throw newIllegalArgumentException("not an array", arrayClass); return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass); } - static final boolean USE_WEAKLY_TYPED_ARRAY_ACCESSORS = false; // FIXME: decide static MethodType type(Class arrayClass, boolean isSetter) { Class elemClass = arrayClass.getComponentType(); Class arrayArgClass = arrayClass; if (!elemClass.isPrimitive()) { arrayArgClass = Object[].class; - if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS) - arrayArgClass = Object.class; + elemClass = Object.class; } - if (!needCast(arrayClass)) { - return !isSetter ? + return !isSetter ? MethodType.methodType(elemClass, arrayArgClass, int.class) : MethodType.methodType(void.class, arrayArgClass, int.class, elemClass); - } else { - Class classArgClass = Class.class; - if (USE_WEAKLY_TYPED_ARRAY_ACCESSORS) - classArgClass = Object.class; - return !isSetter ? - MethodType.methodType(Object.class, classArgClass, arrayArgClass, int.class) : - MethodType.methodType(void.class, classArgClass, arrayArgClass, int.class, Object.class); - } } static MethodType correctType(Class arrayClass, boolean isSetter) { Class elemClass = arrayClass.getComponentType(); @@ -159,40 +179,110 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; * integral widening or narrowing, and floating point widening or narrowing. * @param srcType required call type * @param target original method handle - * @param level which strength of conversion is allowed + * @param strict if true, only asType conversions are allowed; if false, explicitCastArguments conversions allowed + * @param monobox if true, unboxing conversions are assumed to be exactly typed (Integer to int only, not long or double) * @return an adapter to the original handle with the desired new type, * or the original target if the types are already identical * or null if the adaptation cannot be made */ - static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, int level) { - assert(level >= 0 && level <= 2); + static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, + boolean strict, boolean monobox) { MethodType dstType = target.type(); assert(dstType.parameterCount() == target.type().parameterCount()); if (srcType == dstType) return target; + if (USE_LAMBDA_FORM_EDITOR) { + return makePairwiseConvertByEditor(target, srcType, strict, monobox); + } else { + return makePairwiseConvertIndirect(target, srcType, strict, monobox); + } + } - // Calculate extra arguments (temporaries) required in the names array. - // FIXME: Use an ArrayList. Some arguments require more than one conversion step. - final int INARG_COUNT = srcType.parameterCount(); - int conversions = 0; - boolean[] needConv = new boolean[1+INARG_COUNT]; - for (int i = 0; i <= INARG_COUNT; i++) { - Class src = (i == INARG_COUNT) ? dstType.returnType() : srcType.parameterType(i); - Class dst = (i == INARG_COUNT) ? srcType.returnType() : dstType.parameterType(i); - if (!VerifyType.isNullConversion(src, dst) || - level <= 1 && dst.isInterface() && !dst.isAssignableFrom(src)) { - needConv[i] = true; - conversions++; + private static int countNonNull(Object[] array) { + int count = 0; + for (Object x : array) { + if (x != null) ++count; + } + return count; + } + + static MethodHandle makePairwiseConvertByEditor(MethodHandle target, MethodType srcType, + boolean strict, boolean monobox) { + Object[] convSpecs = computeValueConversions(srcType, target.type(), strict, monobox); + int convCount = countNonNull(convSpecs); + if (convCount == 0) + return target.viewAsType(srcType, strict); + MethodType basicSrcType = srcType.basicType(); + MethodType midType = target.type().basicType(); + BoundMethodHandle mh = target.rebind(); + // FIXME: Reduce number of bindings when there is more than one Class conversion. + // FIXME: Reduce number of bindings when there are repeated conversions. + for (int i = 0; i < convSpecs.length-1; i++) { + Object convSpec = convSpecs[i]; + if (convSpec == null) continue; + MethodHandle fn; + if (convSpec instanceof Class) { + fn = Lazy.MH_castReference.bindTo(convSpec); + } else { + fn = (MethodHandle) convSpec; + } + Class newType = basicSrcType.parameterType(i); + if (--convCount == 0) + midType = srcType; + else + midType = midType.changeParameterType(i, newType); + LambdaForm form2 = mh.editor().filterArgumentForm(1+i, BasicType.basicType(newType)); + mh = mh.copyWithExtendL(midType, form2, fn); + mh = mh.rebind(); + } + Object convSpec = convSpecs[convSpecs.length-1]; + if (convSpec != null) { + MethodHandle fn; + if (convSpec instanceof Class) { + if (convSpec == void.class) + fn = null; + else + fn = Lazy.MH_castReference.bindTo(convSpec); + } else { + fn = (MethodHandle) convSpec; + } + Class newType = basicSrcType.returnType(); + assert(--convCount == 0); + midType = srcType; + if (fn != null) { + mh = mh.rebind(); // rebind if too complex + LambdaForm form2 = mh.editor().filterReturnForm(BasicType.basicType(newType), false); + mh = mh.copyWithExtendL(midType, form2, fn); + } else { + LambdaForm form2 = mh.editor().filterReturnForm(BasicType.basicType(newType), true); + mh = mh.copyWith(midType, form2); } } - boolean retConv = needConv[INARG_COUNT]; + assert(convCount == 0); + assert(mh.type().equals(srcType)); + return mh; + } + + static MethodHandle makePairwiseConvertIndirect(MethodHandle target, MethodType srcType, + boolean strict, boolean monobox) { + // Calculate extra arguments (temporaries) required in the names array. + Object[] convSpecs = computeValueConversions(srcType, target.type(), strict, monobox); + final int INARG_COUNT = srcType.parameterCount(); + int convCount = countNonNull(convSpecs); + boolean retConv = (convSpecs[INARG_COUNT] != null); + boolean retVoid = srcType.returnType() == void.class; + if (retConv && retVoid) { + convCount -= 1; + retConv = false; + } final int IN_MH = 0; final int INARG_BASE = 1; final int INARG_LIMIT = INARG_BASE + INARG_COUNT; - final int NAME_LIMIT = INARG_LIMIT + conversions + 1; + final int NAME_LIMIT = INARG_LIMIT + convCount + 1; final int RETURN_CONV = (!retConv ? -1 : NAME_LIMIT - 1); final int OUT_CALL = (!retConv ? NAME_LIMIT : RETURN_CONV) - 1; + final int RESULT = (retVoid ? -1 : NAME_LIMIT - 1); // Now build a LambdaForm. MethodType lambdaType = srcType.basicType().invokerType(); @@ -204,59 +294,21 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; int nameCursor = INARG_LIMIT; for (int i = 0; i < INARG_COUNT; i++) { - Class src = srcType.parameterType(i); - Class dst = dstType.parameterType(i); - - if (!needConv[i]) { + Object convSpec = convSpecs[i]; + if (convSpec == null) { // do nothing: difference is trivial outArgs[OUTARG_BASE + i] = names[INARG_BASE + i]; continue; } - // Tricky case analysis follows. - MethodHandle fn = null; - if (src.isPrimitive()) { - if (dst.isPrimitive()) { - fn = ValueConversions.convertPrimitive(src, dst); - } else { - Wrapper w = Wrapper.forPrimitiveType(src); - MethodHandle boxMethod = ValueConversions.box(w); - if (dst == w.wrapperType()) - fn = boxMethod; - else - fn = boxMethod.asType(MethodType.methodType(dst, src)); - } + Name conv; + if (convSpec instanceof Class) { + Class convClass = (Class) convSpec; + conv = new Name(Lazy.MH_castReference, convClass, names[INARG_BASE + i]); } else { - if (dst.isPrimitive()) { - // Caller has boxed a primitive. Unbox it for the target. - Wrapper w = Wrapper.forPrimitiveType(dst); - if (level == 0 || VerifyType.isNullConversion(src, w.wrapperType())) { - fn = ValueConversions.unbox(dst); - } else if (src == Object.class || !Wrapper.isWrapperType(src)) { - // Examples: Object->int, Number->int, Comparable->int; Byte->int, Character->int - // must include additional conversions - // src must be examined at runtime, to detect Byte, Character, etc. - MethodHandle unboxMethod = (level == 1 - ? ValueConversions.unbox(dst) - : ValueConversions.unboxCast(dst)); - fn = unboxMethod; - } else { - // Example: Byte->int - // Do this by reformulating the problem to Byte->byte. - Class srcPrim = Wrapper.forWrapperType(src).primitiveType(); - MethodHandle unbox = ValueConversions.unbox(srcPrim); - // Compose the two conversions. FIXME: should make two Names for this job - fn = unbox.asType(MethodType.methodType(dst, src)); - } - } else { - // Simple reference conversion. - // Note: Do not check for a class hierarchy relation - // between src and dst. In all cases a 'null' argument - // will pass the cast conversion. - fn = ValueConversions.cast(dst, Lazy.MH_castReference); - } + MethodHandle fn = (MethodHandle) convSpec; + conv = new Name(fn, names[INARG_BASE + i]); } - Name conv = new Name(fn, names[INARG_BASE + i]); assert(names[nameCursor] == null); names[nameCursor++] = conv; assert(outArgs[OUTARG_BASE + i] == null); @@ -267,29 +319,29 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; assert(nameCursor == OUT_CALL); names[OUT_CALL] = new Name(target, outArgs); - if (RETURN_CONV < 0) { + Object convSpec = convSpecs[INARG_COUNT]; + if (!retConv) { assert(OUT_CALL == names.length-1); } else { - Class needReturn = srcType.returnType(); - Class haveReturn = dstType.returnType(); - MethodHandle fn; - Object[] arg = { names[OUT_CALL] }; - if (haveReturn == void.class) { - // synthesize a zero value for the given void - Object zero = Wrapper.forBasicType(needReturn).zero(); - fn = MethodHandles.constant(needReturn, zero); - arg = new Object[0]; // don't pass names[OUT_CALL] to conversion + Name conv; + if (convSpec == void.class) { + conv = new Name(LambdaForm.constantZero(BasicType.basicType(srcType.returnType()))); + } else if (convSpec instanceof Class) { + Class convClass = (Class) convSpec; + conv = new Name(Lazy.MH_castReference, convClass, names[OUT_CALL]); } else { - MethodHandle identity = MethodHandles.identity(needReturn); - MethodType needConversion = identity.type().changeParameterType(0, haveReturn); - fn = makePairwiseConvert(identity, needConversion, level); + MethodHandle fn = (MethodHandle) convSpec; + if (fn.type().parameterCount() == 0) + conv = new Name(fn); // don't pass retval to void conversion + else + conv = new Name(fn, names[OUT_CALL]); } assert(names[RETURN_CONV] == null); - names[RETURN_CONV] = new Name(fn, arg); + names[RETURN_CONV] = conv; assert(RETURN_CONV == names.length-1); } - LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names); + LambdaForm form = new LambdaForm("convert", lambdaType.parameterCount(), names, RESULT); return SimpleMethodHandle.make(srcType, form); } @@ -312,12 +364,79 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; return new ClassCastException("Cannot cast " + obj.getClass().getName() + " to " + t.getName()); } - static MethodHandle makeReferenceIdentity(Class refType) { - MethodType lambdaType = MethodType.genericMethodType(1).invokerType(); - Name[] names = arguments(1, lambdaType); - names[names.length - 1] = new Name(ValueConversions.identity(), names[1]); - LambdaForm form = new LambdaForm("identity", lambdaType.parameterCount(), names); - return SimpleMethodHandle.make(MethodType.methodType(refType, refType), form); + static Object[] computeValueConversions(MethodType srcType, MethodType dstType, + boolean strict, boolean monobox) { + final int INARG_COUNT = srcType.parameterCount(); + Object[] convSpecs = new Object[INARG_COUNT+1]; + for (int i = 0; i <= INARG_COUNT; i++) { + boolean isRet = (i == INARG_COUNT); + Class src = isRet ? dstType.returnType() : srcType.parameterType(i); + Class dst = isRet ? srcType.returnType() : dstType.parameterType(i); + if (!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict)) { + convSpecs[i] = valueConversion(src, dst, strict, monobox); + } + } + return convSpecs; + } + static MethodHandle makePairwiseConvert(MethodHandle target, MethodType srcType, + boolean strict) { + return makePairwiseConvert(target, srcType, strict, /*monobox=*/ false); + } + + /** + * Find a conversion function from the given source to the given destination. + * This conversion function will be used as a LF NamedFunction. + * Return a Class object if a simple cast is needed. + * Return void.class if void is involved. + */ + static Object valueConversion(Class src, Class dst, boolean strict, boolean monobox) { + assert(!VerifyType.isNullConversion(src, dst, /*keepInterfaces=*/ strict)); // caller responsibility + if (dst == void.class) + return dst; + MethodHandle fn; + if (src.isPrimitive()) { + if (src == void.class) { + return void.class; // caller must recognize this specially + } else if (dst.isPrimitive()) { + // Examples: int->byte, byte->int, boolean->int (!strict) + fn = ValueConversions.convertPrimitive(src, dst); + } else { + // Examples: int->Integer, boolean->Object, float->Number + Wrapper wsrc = Wrapper.forPrimitiveType(src); + fn = ValueConversions.boxExact(wsrc); + assert(fn.type().parameterType(0) == wsrc.primitiveType()); + assert(fn.type().returnType() == wsrc.wrapperType()); + if (!VerifyType.isNullConversion(wsrc.wrapperType(), dst, strict)) { + // Corner case, such as int->Long, which will probably fail. + MethodType mt = MethodType.methodType(dst, src); + if (strict) + fn = fn.asType(mt); + else + fn = MethodHandleImpl.makePairwiseConvert(fn, mt, /*strict=*/ false); + } + } + } else if (dst.isPrimitive()) { + Wrapper wdst = Wrapper.forPrimitiveType(dst); + if (monobox || src == wdst.wrapperType()) { + // Use a strongly-typed unboxer, if possible. + fn = ValueConversions.unboxExact(wdst, strict); + } else { + // Examples: Object->int, Number->int, Comparable->int, Byte->int + // must include additional conversions + // src must be examined at runtime, to detect Byte, Character, etc. + fn = (strict + ? ValueConversions.unboxWiden(wdst) + : ValueConversions.unboxCast(wdst)); + } + } else { + // Simple reference conversion. + // Note: Do not check for a class hierarchy relation + // between src and dst. In all cases a 'null' argument + // will pass the cast conversion. + return dst; + } + assert(fn.type().parameterCount() <= 1) : "pc"+Arrays.asList(src.getSimpleName(), dst.getSimpleName(), fn); + return fn; } static MethodHandle makeVarargsCollector(MethodHandle target, Class arrayType) { @@ -326,33 +445,45 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; if (type.parameterType(last) != arrayType) target = target.asType(type.changeParameterType(last, arrayType)); target = target.asFixedArity(); // make sure this attribute is turned off - return new AsVarargsCollector(target, target.type(), arrayType); + return new AsVarargsCollector(target, arrayType); } - static class AsVarargsCollector extends MethodHandle { + private static final class AsVarargsCollector extends DelegatingMethodHandle { private final MethodHandle target; private final Class arrayType; - private /*@Stable*/ MethodHandle asCollectorCache; + private @Stable MethodHandle asCollectorCache; - AsVarargsCollector(MethodHandle target, MethodType type, Class arrayType) { - super(type, reinvokerForm(target)); + AsVarargsCollector(MethodHandle target, Class arrayType) { + this(target.type(), target, arrayType); + } + AsVarargsCollector(MethodType type, MethodHandle target, Class arrayType) { + super(type, target); this.target = target; this.arrayType = arrayType; this.asCollectorCache = target.asCollector(arrayType, 0); } - @Override MethodHandle reinvokerTarget() { return target; } - @Override public boolean isVarargsCollector() { return true; } + @Override + protected MethodHandle getTarget() { + return target; + } + @Override public MethodHandle asFixedArity() { return target; } + @Override + MethodHandle setVarargs(MemberName member) { + if (member.isVarargs()) return this; + return asFixedArity(); + } + @Override public MethodHandle asTypeUncached(MethodType newType) { MethodType type = this.type(); @@ -381,54 +512,15 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } @Override - MethodHandle setVarargs(MemberName member) { - if (member.isVarargs()) return this; - return asFixedArity(); - } - - @Override - MethodHandle viewAsType(MethodType newType) { - if (newType.lastParameterType() != type().lastParameterType()) - throw new InternalError(); - MethodHandle newTarget = asFixedArity().viewAsType(newType); - // put back the varargs bit: - return new AsVarargsCollector(newTarget, newType, arrayType); - } - - @Override - MemberName internalMemberName() { - return asFixedArity().internalMemberName(); - } - @Override - Class internalCallerClass() { - return asFixedArity().internalCallerClass(); - } - - /*non-public*/ - @Override - boolean isInvokeSpecial() { - return asFixedArity().isInvokeSpecial(); - } - - - @Override - MethodHandle bindArgument(int pos, BasicType basicType, Object value) { - return asFixedArity().bindArgument(pos, basicType, value); - } - - @Override - MethodHandle bindReceiver(Object receiver) { - return asFixedArity().bindReceiver(receiver); - } - - @Override - MethodHandle dropArguments(MethodType srcType, int pos, int drops) { - return asFixedArity().dropArguments(srcType, pos, drops); - } - - @Override - MethodHandle permuteArguments(MethodType newType, int[] reorder) { - return asFixedArity().permuteArguments(newType, reorder); + boolean viewAsTypeChecks(MethodType newType, boolean strict) { + super.viewAsTypeChecks(newType, true); + if (strict) return true; + // extra assertion for non-strict checks: + assert (type().lastParameterType().getComponentType() + .isAssignableFrom( + newType.lastParameterType().getComponentType())) + : Arrays.asList(this, newType); + return true; } } @@ -499,32 +591,46 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; * Pre-initialized NamedFunctions for bootstrapping purposes. * Factored in an inner class to delay initialization until first usage. */ - private static class Lazy { + static class Lazy { private static final Class MHI = MethodHandleImpl.class; static final NamedFunction NF_checkSpreadArgument; static final NamedFunction NF_guardWithCatch; - static final NamedFunction NF_selectAlternative; static final NamedFunction NF_throwException; static final MethodHandle MH_castReference; + static final MethodHandle MH_selectAlternative; + static final MethodHandle MH_copyAsPrimitiveArray; + static final MethodHandle MH_fillNewTypedArray; + static final MethodHandle MH_fillNewArray; + static final MethodHandle MH_arrayIdentity; static { try { NF_checkSpreadArgument = new NamedFunction(MHI.getDeclaredMethod("checkSpreadArgument", Object.class, int.class)); NF_guardWithCatch = new NamedFunction(MHI.getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class, MethodHandle.class, Object[].class)); - NF_selectAlternative = new NamedFunction(MHI.getDeclaredMethod("selectAlternative", boolean.class, MethodHandle.class, - MethodHandle.class)); NF_throwException = new NamedFunction(MHI.getDeclaredMethod("throwException", Throwable.class)); NF_checkSpreadArgument.resolve(); NF_guardWithCatch.resolve(); - NF_selectAlternative.resolve(); NF_throwException.resolve(); - MethodType mt = MethodType.methodType(Object.class, Class.class, Object.class); - MH_castReference = IMPL_LOOKUP.findStatic(MHI, "castReference", mt); + MH_castReference = IMPL_LOOKUP.findStatic(MHI, "castReference", + MethodType.methodType(Object.class, Class.class, Object.class)); + MH_copyAsPrimitiveArray = IMPL_LOOKUP.findStatic(MHI, "copyAsPrimitiveArray", + MethodType.methodType(Object.class, Wrapper.class, Object[].class)); + MH_arrayIdentity = IMPL_LOOKUP.findStatic(MHI, "identity", + MethodType.methodType(Object[].class, Object[].class)); + MH_fillNewArray = IMPL_LOOKUP.findStatic(MHI, "fillNewArray", + MethodType.methodType(Object[].class, Integer.class, Object[].class)); + MH_fillNewTypedArray = IMPL_LOOKUP.findStatic(MHI, "fillNewTypedArray", + MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class)); + + MH_selectAlternative = makeIntrinsic( + IMPL_LOOKUP.findStatic(MHI, "selectAlternative", + MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)), + Intrinsic.SELECT_ALTERNATIVE); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } @@ -595,29 +701,66 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; MethodHandle makeGuardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback) { - MethodType basicType = target.type().basicType(); - MethodHandle invokeBasic = MethodHandles.basicInvoker(basicType); - int arity = basicType.parameterCount(); - int extraNames = 3; - MethodType lambdaType = basicType.invokerType(); - Name[] names = arguments(extraNames, lambdaType); + MethodType type = target.type(); + assert(test.type().equals(type.changeReturnType(boolean.class)) && fallback.type().equals(type)); + MethodType basicType = type.basicType(); + LambdaForm form = makeGuardWithTestForm(basicType); + BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL(); + BoundMethodHandle mh; + try { + mh = (BoundMethodHandle) + data.constructor().invokeBasic(type, form, + (Object) test, (Object) target, (Object) fallback); + } catch (Throwable ex) { + throw uncaughtException(ex); + } + assert(mh.type() == type); + return mh; + } - Object[] testArgs = Arrays.copyOfRange(names, 1, 1 + arity, Object[].class); - Object[] targetArgs = Arrays.copyOfRange(names, 0, 1 + arity, Object[].class); + static + LambdaForm makeGuardWithTestForm(MethodType basicType) { + LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_GWT); + if (lform != null) return lform; + final int THIS_MH = 0; // the BMH_LLL + final int ARG_BASE = 1; // start of incoming arguments + final int ARG_LIMIT = ARG_BASE + basicType.parameterCount(); + int nameCursor = ARG_LIMIT; + final int GET_TEST = nameCursor++; + final int GET_TARGET = nameCursor++; + final int GET_FALLBACK = nameCursor++; + final int CALL_TEST = nameCursor++; + final int SELECT_ALT = nameCursor++; + final int CALL_TARGET = nameCursor++; + assert(CALL_TARGET == SELECT_ALT+1); // must be true to trigger IBG.emitSelectAlternative + + MethodType lambdaType = basicType.invokerType(); + Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); + + BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLL(); + names[THIS_MH] = names[THIS_MH].withConstraint(data); + names[GET_TEST] = new Name(data.getterFunction(0), names[THIS_MH]); + names[GET_TARGET] = new Name(data.getterFunction(1), names[THIS_MH]); + names[GET_FALLBACK] = new Name(data.getterFunction(2), names[THIS_MH]); + + Object[] invokeArgs = Arrays.copyOfRange(names, 0, ARG_LIMIT, Object[].class); // call test - names[arity + 1] = new Name(test, testArgs); + MethodType testType = basicType.changeReturnType(boolean.class).basicType(); + invokeArgs[0] = names[GET_TEST]; + names[CALL_TEST] = new Name(testType, invokeArgs); // call selectAlternative - Object[] selectArgs = { names[arity + 1], target, fallback }; - names[arity + 2] = new Name(Lazy.NF_selectAlternative, selectArgs); - targetArgs[0] = names[arity + 2]; + names[SELECT_ALT] = new Name(Lazy.MH_selectAlternative, names[CALL_TEST], + names[GET_TARGET], names[GET_FALLBACK]); // call target or fallback - names[arity + 3] = new Name(new NamedFunction(invokeBasic), targetArgs); + invokeArgs[0] = names[SELECT_ALT]; + names[CALL_TARGET] = new Name(basicType, invokeArgs); - LambdaForm form = new LambdaForm("guard", lambdaType.parameterCount(), names); - return SimpleMethodHandle.make(target.type(), form); + lform = new LambdaForm("guard", lambdaType.parameterCount(), names); + + return basicType.form().setCachedLambdaForm(MethodTypeForm.LF_GWT, lform); } /** @@ -665,6 +808,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType); BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); + names[THIS_MH] = names[THIS_MH].withConstraint(data); names[GET_TARGET] = new Name(data.getterFunction(0), names[THIS_MH]); names[GET_CLASS] = new Name(data.getterFunction(1), names[THIS_MH]); names[GET_CATCHER] = new Name(data.getterFunction(2), names[THIS_MH]); @@ -679,7 +823,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; Object[] args = new Object[invokeBasic.type().parameterCount()]; args[0] = names[GET_COLLECT_ARGS]; System.arraycopy(names, ARG_BASE, args, 1, ARG_LIMIT-ARG_BASE); - names[BOXED_ARGS] = new Name(new NamedFunction(invokeBasic), args); + names[BOXED_ARGS] = new Name(makeIntrinsic(invokeBasic, Intrinsic.GUARD_WITH_CATCH), args); // t_{i+1}:L=MethodHandleImpl.guardWithCatch(target:L,exType:L,catcher:L,t_{i}:L); Object[] gwcArgs = new Object[] {names[GET_TARGET], names[GET_CLASS], names[GET_CATCHER], names[BOXED_ARGS]}; @@ -688,7 +832,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L); MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class)); Object[] unboxArgs = new Object[] {names[GET_UNBOX_RESULT], names[TRY_CATCH]}; - names[UNBOX_RESULT] = new Name(new NamedFunction(invokeBasicUnbox), unboxArgs); + names[UNBOX_RESULT] = new Name(invokeBasicUnbox, unboxArgs); lform = new LambdaForm("guardWithCatch", lambdaType.parameterCount(), names); @@ -705,22 +849,27 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; // Prepare auxiliary method handles used during LambdaForm interpretation. // Box arguments and wrap them into Object[]: ValueConversions.array(). MethodType varargsType = type.changeReturnType(Object[].class); - MethodHandle collectArgs = ValueConversions.varargsArray(type.parameterCount()) - .asType(varargsType); + MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType); // Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore(). MethodHandle unboxResult; - if (type.returnType().isPrimitive()) { - unboxResult = ValueConversions.unbox(type.returnType()); + Class rtype = type.returnType(); + if (rtype.isPrimitive()) { + if (rtype == void.class) { + unboxResult = ValueConversions.ignore(); + } else { + Wrapper w = Wrapper.forPrimitiveType(type.returnType()); + unboxResult = ValueConversions.unboxExact(w); + } } else { - unboxResult = ValueConversions.identity(); + unboxResult = MethodHandles.identity(Object.class); } BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL(); BoundMethodHandle mh; try { mh = (BoundMethodHandle) - data.constructor[0].invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher, - (Object) collectArgs, (Object) unboxResult); + data.constructor().invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher, + (Object) collectArgs, (Object) unboxResult); } catch (Throwable ex) { throw uncaughtException(ex); } @@ -758,9 +907,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; assert(Throwable.class.isAssignableFrom(type.parameterType(0))); int arity = type.parameterCount(); if (arity > 1) { - return throwException(type.dropParameterTypes(1, arity)).dropArguments(type, 1, arity-1); + MethodHandle mh = throwException(type.dropParameterTypes(1, arity)); + mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity)); + return mh; } - return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, 2); + return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, false, true); } static Empty throwException(T t) throws T { throw t; } @@ -782,7 +933,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; mh = mh.bindTo(new UnsupportedOperationException("cannot reflectively invoke MethodHandle")); if (!method.getInvocationType().equals(mh.type())) throw new InternalError(method.toString()); - mh = mh.withInternalMemberName(method); + mh = mh.withInternalMemberName(method, false); mh = mh.asVarargsCollector(Object[].class); assert(method.isVarargs()); FAKE_METHOD_HANDLE_INVOKE[idx] = mh; @@ -819,7 +970,7 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; MethodHandle vamh = prepareForInvoker(mh); // Cache the result of makeInjectedInvoker once per argument class. MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass); - return restoreToType(bccInvoker.bindTo(vamh), mh.type(), mh.internalMemberName(), hostClass); + return restoreToType(bccInvoker.bindTo(vamh), mh, hostClass); } private static MethodHandle makeInjectedInvoker(Class hostClass) { @@ -874,12 +1025,14 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } // Undo the adapter effect of prepareForInvoker: - private static MethodHandle restoreToType(MethodHandle vamh, MethodType type, - MemberName member, + private static MethodHandle restoreToType(MethodHandle vamh, + MethodHandle original, Class hostClass) { + MethodType type = original.type(); MethodHandle mh = vamh.asCollector(Object[].class, type.parameterCount()); + MemberName member = original.internalMemberName(); mh = mh.asType(type); - mh = new WrappedMember(mh, type, member, hostClass); + mh = new WrappedMember(mh, type, member, original.isInvokeSpecial(), hostClass); return mh; } @@ -945,28 +1098,22 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; /** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */ - static class WrappedMember extends MethodHandle { + private static final class WrappedMember extends DelegatingMethodHandle { private final MethodHandle target; private final MemberName member; private final Class callerClass; + private final boolean isInvokeSpecial; - private WrappedMember(MethodHandle target, MethodType type, MemberName member, Class callerClass) { - super(type, reinvokerForm(target)); + private WrappedMember(MethodHandle target, MethodType type, + MemberName member, boolean isInvokeSpecial, + Class callerClass) { + super(type, target); this.target = target; this.member = member; this.callerClass = callerClass; + this.isInvokeSpecial = isInvokeSpecial; } - @Override - MethodHandle reinvokerTarget() { - return target; - } - @Override - public MethodHandle asTypeUncached(MethodType newType) { - // This MH is an alias for target, except for the MemberName - // Drop the MemberName if there is any conversion. - return asTypeCache = target.asType(newType); - } @Override MemberName internalMemberName() { return member; @@ -977,18 +1124,367 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; } @Override boolean isInvokeSpecial() { - return target.isInvokeSpecial(); + return isInvokeSpecial; } @Override - MethodHandle viewAsType(MethodType newType) { - return new WrappedMember(target, newType, member, callerClass); + protected MethodHandle getTarget() { + return target; + } + @Override + public MethodHandle asTypeUncached(MethodType newType) { + // This MH is an alias for target, except for the MemberName + // Drop the MemberName if there is any conversion. + return asTypeCache = target.asType(newType); } } - static MethodHandle makeWrappedMember(MethodHandle target, MemberName member) { - if (member.equals(target.internalMemberName())) + static MethodHandle makeWrappedMember(MethodHandle target, MemberName member, boolean isInvokeSpecial) { + if (member.equals(target.internalMemberName()) && isInvokeSpecial == target.isInvokeSpecial()) return target; - return new WrappedMember(target, target.type(), member, null); + return new WrappedMember(target, target.type(), member, isInvokeSpecial, null); } + /** Intrinsic IDs */ + /*non-public*/ + enum Intrinsic { + SELECT_ALTERNATIVE, + GUARD_WITH_CATCH, + NEW_ARRAY, + ARRAY_LOAD, + ARRAY_STORE, + IDENTITY, + ZERO, + NONE // no intrinsic associated + } + + /** Mark arbitrary method handle as intrinsic. + * InvokerBytecodeGenerator uses this info to produce more efficient bytecode shape. */ + private static final class IntrinsicMethodHandle extends DelegatingMethodHandle { + private final MethodHandle target; + private final Intrinsic intrinsicName; + + IntrinsicMethodHandle(MethodHandle target, Intrinsic intrinsicName) { + super(target.type(), target); + this.target = target; + this.intrinsicName = intrinsicName; + } + + @Override + protected MethodHandle getTarget() { + return target; + } + + @Override + Intrinsic intrinsicName() { + return intrinsicName; + } + + @Override + public MethodHandle asTypeUncached(MethodType newType) { + // This MH is an alias for target, except for the intrinsic name + // Drop the name if there is any conversion. + return asTypeCache = target.asType(newType); + } + + @Override + String internalProperties() { + return super.internalProperties() + + "\n& Intrinsic="+intrinsicName; + } + + @Override + public MethodHandle asCollector(Class arrayType, int arrayLength) { + if (intrinsicName == Intrinsic.IDENTITY) { + MethodType resultType = type().asCollectorType(arrayType, arrayLength); + MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength); + return newArray.asType(resultType); + } + return super.asCollector(arrayType, arrayLength); + } + } + + static MethodHandle makeIntrinsic(MethodHandle target, Intrinsic intrinsicName) { + if (intrinsicName == target.intrinsicName()) + return target; + return new IntrinsicMethodHandle(target, intrinsicName); + } + + static MethodHandle makeIntrinsic(MethodType type, LambdaForm form, Intrinsic intrinsicName) { + return new IntrinsicMethodHandle(SimpleMethodHandle.make(type, form), intrinsicName); + } + + /// Collection of multiple arguments. + + private static MethodHandle findCollector(String name, int nargs, Class rtype, Class... ptypes) { + MethodType type = MethodType.genericMethodType(nargs) + .changeReturnType(rtype) + .insertParameterTypes(0, ptypes); + try { + return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, name, type); + } catch (ReflectiveOperationException ex) { + return null; + } + } + + private static final Object[] NO_ARGS_ARRAY = {}; + private static Object[] makeArray(Object... args) { return args; } + private static Object[] array() { return NO_ARGS_ARRAY; } + private static Object[] array(Object a0) + { return makeArray(a0); } + private static Object[] array(Object a0, Object a1) + { return makeArray(a0, a1); } + private static Object[] array(Object a0, Object a1, Object a2) + { return makeArray(a0, a1, a2); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3) + { return makeArray(a0, a1, a2, a3); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4) + { return makeArray(a0, a1, a2, a3, a4); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5) + { return makeArray(a0, a1, a2, a3, a4, a5); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6) + { return makeArray(a0, a1, a2, a3, a4, a5, a6); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); } + private static Object[] array(Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8, Object a9) + { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } + private static MethodHandle[] makeArrays() { + ArrayList mhs = new ArrayList<>(); + for (;;) { + MethodHandle mh = findCollector("array", mhs.size(), Object[].class); + if (mh == null) break; + mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); + mhs.add(mh); + } + assert(mhs.size() == 11); // current number of methods + return mhs.toArray(new MethodHandle[MAX_ARITY+1]); + } + private static final MethodHandle[] ARRAYS = makeArrays(); + + // filling versions of the above: + // using Integer len instead of int len and no varargs to avoid bootstrapping problems + private static Object[] fillNewArray(Integer len, Object[] /*not ...*/ args) { + Object[] a = new Object[len]; + fillWithArguments(a, 0, args); + return a; + } + private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) { + Object[] a = Arrays.copyOf(example, len); + assert(a.getClass() != Object[].class); + fillWithArguments(a, 0, args); + return a; + } + private static void fillWithArguments(Object[] a, int pos, Object... args) { + System.arraycopy(args, 0, a, pos, args.length); + } + // using Integer pos instead of int pos to avoid bootstrapping problems + private static Object[] fillArray(Integer pos, Object[] a, Object a0) + { fillWithArguments(a, pos, a0); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1) + { fillWithArguments(a, pos, a0, a1); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2) + { fillWithArguments(a, pos, a0, a1, a2); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3) + { fillWithArguments(a, pos, a0, a1, a2, a3); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; } + private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, + Object a4, Object a5, Object a6, Object a7, + Object a8, Object a9) + { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; } + private static MethodHandle[] makeFillArrays() { + ArrayList mhs = new ArrayList<>(); + mhs.add(null); // there is no empty fill; at least a0 is required + for (;;) { + MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Integer.class, Object[].class); + if (mh == null) break; + mhs.add(mh); + } + assert(mhs.size() == 11); // current number of methods + return mhs.toArray(new MethodHandle[0]); + } + private static final MethodHandle[] FILL_ARRAYS = makeFillArrays(); + + private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) { + Object a = w.makeArray(boxes.length); + w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length); + return a; + } + + /** Return a method handle that takes the indicated number of Object + * arguments and returns an Object array of them, as if for varargs. + */ + static MethodHandle varargsArray(int nargs) { + MethodHandle mh = ARRAYS[nargs]; + if (mh != null) return mh; + mh = findCollector("array", nargs, Object[].class); + if (mh != null) mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); + if (mh != null) return ARRAYS[nargs] = mh; + mh = buildVarargsArray(Lazy.MH_fillNewArray, Lazy.MH_arrayIdentity, nargs); + assert(assertCorrectArity(mh, nargs)); + mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); + return ARRAYS[nargs] = mh; + } + + private static boolean assertCorrectArity(MethodHandle mh, int arity) { + assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh; + return true; + } + + // Array identity function (used as Lazy.MH_arrayIdentity). + static T[] identity(T[] x) { + return x; + } + + private static MethodHandle buildVarargsArray(MethodHandle newArray, MethodHandle finisher, int nargs) { + // Build up the result mh as a sequence of fills like this: + // finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23)) + // The various fill(_,10*I,___*[J]) are reusable. + int leftLen = Math.min(nargs, LEFT_ARGS); // absorb some arguments immediately + int rightLen = nargs - leftLen; + MethodHandle leftCollector = newArray.bindTo(nargs); + leftCollector = leftCollector.asCollector(Object[].class, leftLen); + MethodHandle mh = finisher; + if (rightLen > 0) { + MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen); + if (mh == Lazy.MH_arrayIdentity) + mh = rightFiller; + else + mh = MethodHandles.collectArguments(mh, 0, rightFiller); + } + if (mh == Lazy.MH_arrayIdentity) + mh = leftCollector; + else + mh = MethodHandles.collectArguments(mh, 0, leftCollector); + return mh; + } + + private static final int LEFT_ARGS = (FILL_ARRAYS.length - 1); + private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1]; + /** fill_array_to_right(N).invoke(a, argL..arg[N-1]) + * fills a[L]..a[N-1] with corresponding arguments, + * and then returns a. The value L is a global constant (LEFT_ARGS). + */ + private static MethodHandle fillToRight(int nargs) { + MethodHandle filler = FILL_ARRAY_TO_RIGHT[nargs]; + if (filler != null) return filler; + filler = buildFiller(nargs); + assert(assertCorrectArity(filler, nargs - LEFT_ARGS + 1)); + return FILL_ARRAY_TO_RIGHT[nargs] = filler; + } + private static MethodHandle buildFiller(int nargs) { + if (nargs <= LEFT_ARGS) + return Lazy.MH_arrayIdentity; // no args to fill; return the array unchanged + // we need room for both mh and a in mh.invoke(a, arg*[nargs]) + final int CHUNK = LEFT_ARGS; + int rightLen = nargs % CHUNK; + int midLen = nargs - rightLen; + if (rightLen == 0) { + midLen = nargs - (rightLen = CHUNK); + if (FILL_ARRAY_TO_RIGHT[midLen] == null) { + // build some precursors from left to right + for (int j = LEFT_ARGS % CHUNK; j < midLen; j += CHUNK) + if (j > LEFT_ARGS) fillToRight(j); + } + } + if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS); + assert(rightLen > 0); + MethodHandle midFill = fillToRight(midLen); // recursive fill + MethodHandle rightFill = FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1] + assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS); + assert(rightFill.type().parameterCount() == 1 + rightLen); + + // Combine the two fills: + // right(mid(a, x10..x19), x20..x23) + // The final product will look like this: + // right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23) + if (midLen == LEFT_ARGS) + return rightFill; + else + return MethodHandles.collectArguments(rightFill, 0, midFill); + } + + // Type-polymorphic version of varargs maker. + private static final ClassValue TYPED_COLLECTORS + = new ClassValue() { + @Override + protected MethodHandle[] computeValue(Class type) { + return new MethodHandle[256]; + } + }; + + static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM + + /** Return a method handle that takes the indicated number of + * typed arguments and returns an array of them. + * The type argument is the array type. + */ + static MethodHandle varargsArray(Class arrayType, int nargs) { + Class elemType = arrayType.getComponentType(); + if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType); + // FIXME: Need more special casing and caching here. + if (nargs >= MAX_JVM_ARITY/2 - 1) { + int slots = nargs; + final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH + if (slots <= MAX_ARRAY_SLOTS && elemType.isPrimitive()) + slots *= Wrapper.forPrimitiveType(elemType).stackSlots(); + if (slots > MAX_ARRAY_SLOTS) + throw new IllegalArgumentException("too many arguments: "+arrayType.getSimpleName()+", length "+nargs); + } + if (elemType == Object.class) + return varargsArray(nargs); + // other cases: primitive arrays, subtypes of Object[] + MethodHandle cache[] = TYPED_COLLECTORS.get(elemType); + MethodHandle mh = nargs < cache.length ? cache[nargs] : null; + if (mh != null) return mh; + if (nargs == 0) { + Object example = java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0); + mh = MethodHandles.constant(arrayType, example); + } else if (elemType.isPrimitive()) { + MethodHandle builder = Lazy.MH_fillNewArray; + MethodHandle producer = buildArrayProducer(arrayType); + mh = buildVarargsArray(builder, producer, nargs); + } else { + Class objArrayType = arrayType.asSubclass(Object[].class); + Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType); + MethodHandle builder = Lazy.MH_fillNewTypedArray.bindTo(example); + MethodHandle producer = Lazy.MH_arrayIdentity; // must be weakly typed + mh = buildVarargsArray(builder, producer, nargs); + } + mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); + mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); + assert(assertCorrectArity(mh, nargs)); + if (nargs < cache.length) + cache[nargs] = mh; + return mh; + } + + private static MethodHandle buildArrayProducer(Class arrayType) { + Class elemType = arrayType.getComponentType(); + assert(elemType.isPrimitive()); + return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType)); + } } diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java index 8fec887447b..1167eeb4a2b 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java @@ -33,6 +33,7 @@ import java.util.ArrayList; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; import sun.reflect.misc.ReflectUtil; +import static java.lang.invoke.MethodHandleStatics.*; /** * This class consists exclusively of static methods that help adapt @@ -148,7 +149,7 @@ public class MethodHandleProxies { public static T asInterfaceInstance(final Class intfc, final MethodHandle target) { if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers())) - throw new IllegalArgumentException("not a public interface: "+intfc.getName()); + throw newIllegalArgumentException("not a public interface", intfc.getName()); final MethodHandle mh; if (System.getSecurityManager() != null) { final Class caller = Reflection.getCallerClass(); @@ -165,7 +166,7 @@ public class MethodHandleProxies { } final Method[] methods = getSingleNameMethods(intfc); if (methods == null) - throw new IllegalArgumentException("not a single-method interface: "+intfc.getName()); + throw newIllegalArgumentException("not a single-method interface", intfc.getName()); final MethodHandle[] vaTargets = new MethodHandle[methods.length]; for (int i = 0; i < methods.length; i++) { Method sm = methods[i]; @@ -189,7 +190,7 @@ public class MethodHandleProxies { return getArg(method.getName()); if (isObjectMethod(method)) return callObjectMethod(proxy, method, args); - throw new InternalError("bad proxy method: "+method); + throw newInternalError("bad proxy method: "+method); } }; @@ -240,7 +241,7 @@ public class MethodHandleProxies { return (WrapperInstance) x; } catch (ClassCastException ex) { } - throw new IllegalArgumentException("not a wrapper instance"); + throw newIllegalArgumentException("not a wrapper instance"); } /** diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java index 50a14b35d52..c3d9ac12c09 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java @@ -45,16 +45,21 @@ import sun.misc.Unsafe; static final boolean DUMP_CLASS_FILES; static final boolean TRACE_INTERPRETER; static final boolean TRACE_METHOD_LINKAGE; - static final Integer COMPILE_THRESHOLD; + static final boolean USE_LAMBDA_FORM_EDITOR; + static final int COMPILE_THRESHOLD; + static final int PROFILE_LEVEL; + static { - final Object[] values = { false, false, false, false, null }; + final Object[] values = { false, false, false, false, false, null, null }; AccessController.doPrivileged(new PrivilegedAction() { public Void run() { values[0] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DEBUG_NAMES"); values[1] = Boolean.getBoolean("java.lang.invoke.MethodHandle.DUMP_CLASS_FILES"); values[2] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_INTERPRETER"); values[3] = Boolean.getBoolean("java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE"); - values[4] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD"); + values[4] = Boolean.getBoolean("java.lang.invoke.MethodHandle.USE_LF_EDITOR"); + values[5] = Integer.getInteger("java.lang.invoke.MethodHandle.COMPILE_THRESHOLD", 30); + values[6] = Integer.getInteger("java.lang.invoke.MethodHandle.PROFILE_LEVEL", 0); return null; } }); @@ -62,7 +67,9 @@ import sun.misc.Unsafe; DUMP_CLASS_FILES = (Boolean) values[1]; TRACE_INTERPRETER = (Boolean) values[2]; TRACE_METHOD_LINKAGE = (Boolean) values[3]; - COMPILE_THRESHOLD = (Integer) values[4]; + USE_LAMBDA_FORM_EDITOR = (Boolean) values[4]; + COMPILE_THRESHOLD = (Integer) values[5]; + PROFILE_LEVEL = (Integer) values[6]; } /** Tell if any of the debugging switches are turned on. @@ -127,7 +134,10 @@ import sun.misc.Unsafe; /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) { return new IllegalArgumentException(message(message, obj, obj2)); } + /** Propagate unchecked exceptions and errors, but wrap anything checked and throw that instead. */ /*non-public*/ static Error uncaughtException(Throwable ex) { + if (ex instanceof Error) throw (Error) ex; + if (ex instanceof RuntimeException) throw (RuntimeException) ex; throw newInternalError("uncaught exception", ex); } static Error NYI() { diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java index ee282752032..ea692345d66 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java @@ -26,8 +26,8 @@ package java.lang.invoke; import java.lang.reflect.*; +import java.util.BitSet; import java.util.List; -import java.util.ArrayList; import java.util.Arrays; import sun.invoke.util.ValueConversions; @@ -40,6 +40,7 @@ import sun.security.util.SecurityConstants; import java.lang.invoke.LambdaForm.BasicType; import static java.lang.invoke.LambdaForm.BasicType.*; import static java.lang.invoke.MethodHandleStatics.*; +import static java.lang.invoke.MethodHandleImpl.Intrinsic; import static java.lang.invoke.MethodHandleNatives.Constants.*; import java.util.concurrent.ConcurrentHashMap; @@ -862,6 +863,8 @@ assertEquals("", (String) MH_newString.invokeExact()); return invoker(type); if ("invokeExact".equals(name)) return exactInvoker(type); + if ("invokeBasic".equals(name)) + return basicInvoker(type); assert(!MemberName.isMethodHandleInvokeName(name)); return null; } @@ -1141,7 +1144,7 @@ return mh1; Class refc = receiver.getClass(); // may get NPE MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type); MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method, findBoundCallerClass(method)); - return mh.bindReceiver(receiver).setVarargs(method); + return mh.bindArgumentL(0, receiver).setVarargs(method); } /** @@ -1576,7 +1579,7 @@ return mh1; return false; return true; } - private MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class caller) throws IllegalAccessException { + private MethodHandle restrictReceiver(MemberName method, DirectMethodHandle mh, Class caller) throws IllegalAccessException { assert(!method.isStatic()); // receiver type of mh is too wide; narrow to caller if (!method.getDeclaringClass().isAssignableFrom(caller)) { @@ -1585,7 +1588,9 @@ return mh1; MethodType rawType = mh.type(); if (rawType.parameterType(0) == caller) return mh; MethodType narrowType = rawType.changeParameterType(0, caller); - return mh.viewAsType(narrowType); + assert(!mh.isVarargsCollector()); // viewAsType will lose varargs-ness + assert(mh.viewAsTypeChecks(narrowType, true)); + return mh.copyWith(narrowType, mh.form); } /** Check access and get the requested method. */ @@ -1647,15 +1652,17 @@ return mh1; checkMethod(refKind, refc, method); } - MethodHandle mh = DirectMethodHandle.make(refKind, refc, method); - mh = maybeBindCaller(method, mh, callerClass); - mh = mh.setVarargs(method); + DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method); + MethodHandle mh = dmh; // Optionally narrow the receiver argument to refc using restrictReceiver. if (doRestrict && (refKind == REF_invokeSpecial || (MethodHandleNatives.refKindHasReceiver(refKind) && - restrictProtectedReceiver(method)))) - mh = restrictReceiver(method, mh, lookupClass()); + restrictProtectedReceiver(method)))) { + mh = restrictReceiver(method, dmh, lookupClass()); + } + mh = maybeBindCaller(method, mh, callerClass); + mh = mh.setVarargs(method); return mh; } private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh, @@ -1687,12 +1694,12 @@ return mh1; // Optionally check with the security manager; this isn't needed for unreflect* calls. if (checkSecurity) checkSecurityManager(refc, field); - MethodHandle mh = DirectMethodHandle.make(refc, field); + DirectMethodHandle dmh = DirectMethodHandle.make(refc, field); boolean doRestrict = (MethodHandleNatives.refKindHasReceiver(refKind) && restrictProtectedReceiver(field)); if (doRestrict) - mh = restrictReceiver(field, mh, lookupClass()); - return mh; + return restrictReceiver(field, dmh, lookupClass()); + return dmh; } /** Check access and get the requested constructor. */ private MethodHandle getDirectConstructor(Class refc, MemberName ctor) throws IllegalAccessException { @@ -1879,7 +1886,8 @@ return invoker; static public MethodHandle spreadInvoker(MethodType type, int leadingArgCount) { if (leadingArgCount < 0 || leadingArgCount > type.parameterCount()) - throw new IllegalArgumentException("bad argument count "+leadingArgCount); + throw newIllegalArgumentException("bad argument count", leadingArgCount); + type = type.asSpreaderType(Object[].class, type.parameterCount() - leadingArgCount); return type.invokers().spreadInvoker(leadingArgCount); } @@ -1959,12 +1967,12 @@ return invoker; */ static public MethodHandle invoker(MethodType type) { - return type.invokers().generalInvoker(); + return type.invokers().genericInvoker(); } static /*non-public*/ MethodHandle basicInvoker(MethodType type) { - return type.form().basicInvoker(); + return type.invokers().basicInvoker(); } /// method handle modification (creation from other method handles) @@ -2015,10 +2023,13 @@ return invoker; */ public static MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) { - if (!target.type().isCastableTo(newType)) { - throw new WrongMethodTypeException("cannot explicitly cast "+target+" to "+newType); + MethodType oldType = target.type(); + // use the asTypeCache when possible: + if (oldType == newType) return target; + if (oldType.explicitCastEquivalentToAsType(newType)) { + return target.asType(newType); } - return MethodHandleImpl.makePairwiseConvert(target, newType, 2); + return MethodHandleImpl.makePairwiseConvert(target, newType, false); } /** @@ -2082,12 +2093,165 @@ assert((int)twice.invokeExact(21) == 42); */ public static MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) { - reorder = reorder.clone(); - checkReorder(reorder, newType, target.type()); - return target.permuteArguments(newType, reorder); + reorder = reorder.clone(); // get a private copy + MethodType oldType = target.type(); + permuteArgumentChecks(reorder, newType, oldType); + if (USE_LAMBDA_FORM_EDITOR) { + // first detect dropped arguments and handle them separately + int[] originalReorder = reorder; + BoundMethodHandle result = target.rebind(); + LambdaForm form = result.form; + int newArity = newType.parameterCount(); + // Normalize the reordering into a real permutation, + // by removing duplicates and adding dropped elements. + // This somewhat improves lambda form caching, as well + // as simplifying the transform by breaking it up into steps. + for (int ddIdx; (ddIdx = findFirstDupOrDrop(reorder, newArity)) != 0; ) { + if (ddIdx > 0) { + // We found a duplicated entry at reorder[ddIdx]. + // Example: (x,y,z)->asList(x,y,z) + // permuted by [1*,0,1] => (a0,a1)=>asList(a1,a0,a1) + // permuted by [0,1,0*] => (a0,a1)=>asList(a0,a1,a0) + // The starred element corresponds to the argument + // deleted by the dupArgumentForm transform. + int srcPos = ddIdx, dstPos = srcPos, dupVal = reorder[srcPos]; + boolean killFirst = false; + for (int val; (val = reorder[--dstPos]) != dupVal; ) { + // Set killFirst if the dup is larger than an intervening position. + // This will remove at least one inversion from the permutation. + if (dupVal > val) killFirst = true; + } + if (!killFirst) { + srcPos = dstPos; + dstPos = ddIdx; + } + form = form.editor().dupArgumentForm(1 + srcPos, 1 + dstPos); + assert (reorder[srcPos] == reorder[dstPos]); + oldType = oldType.dropParameterTypes(dstPos, dstPos + 1); + // contract the reordering by removing the element at dstPos + int tailPos = dstPos + 1; + System.arraycopy(reorder, tailPos, reorder, dstPos, reorder.length - tailPos); + reorder = Arrays.copyOf(reorder, reorder.length - 1); + } else { + int dropVal = ~ddIdx, insPos = 0; + while (insPos < reorder.length && reorder[insPos] < dropVal) { + // Find first element of reorder larger than dropVal. + // This is where we will insert the dropVal. + insPos += 1; + } + Class ptype = newType.parameterType(dropVal); + form = form.editor().addArgumentForm(1 + insPos, BasicType.basicType(ptype)); + oldType = oldType.insertParameterTypes(insPos, ptype); + // expand the reordering by inserting an element at insPos + int tailPos = insPos + 1; + reorder = Arrays.copyOf(reorder, reorder.length + 1); + System.arraycopy(reorder, insPos, reorder, tailPos, reorder.length - tailPos); + reorder[insPos] = dropVal; + } + assert (permuteArgumentChecks(reorder, newType, oldType)); + } + assert (reorder.length == newArity); // a perfect permutation + // Note: This may cache too many distinct LFs. Consider backing off to varargs code. + form = form.editor().permuteArgumentsForm(1, reorder); + if (newType == result.type() && form == result.internalForm()) + return result; + return result.copyWith(newType, form); + } else { + // first detect dropped arguments and handle them separately + MethodHandle originalTarget = target; + int newArity = newType.parameterCount(); + for (int dropIdx; (dropIdx = findFirstDrop(reorder, newArity)) >= 0; ) { + // dropIdx is missing from reorder; add it in at the end + int oldArity = reorder.length; + target = dropArguments(target, oldArity, newType.parameterType(dropIdx)); + reorder = Arrays.copyOf(reorder, oldArity+1); + reorder[oldArity] = dropIdx; + } + assert(target == originalTarget || permuteArgumentChecks(reorder, newType, target.type())); + // Note: This may cache too many distinct LFs. Consider backing off to varargs code. + BoundMethodHandle result = target.rebind(); + LambdaForm form = result.form.permuteArguments(1, reorder, basicTypes(newType.parameterList())); + return result.copyWith(newType, form); + } } - private static void checkReorder(int[] reorder, MethodType newType, MethodType oldType) { + /** Return the first value in [0..newArity-1] that is not present in reorder. */ + private static int findFirstDrop(int[] reorder, int newArity) { + final int BIT_LIMIT = 63; // max number of bits in bit mask + if (newArity < BIT_LIMIT) { + long mask = 0; + for (int arg : reorder) { + assert(arg < newArity); + mask |= (1 << arg); + } + if (mask == (1 << newArity) - 1) { + assert(Long.numberOfTrailingZeros(Long.lowestOneBit(~mask)) == newArity); + return -1; + } + // find first zero + long zeroBit = Long.lowestOneBit(~mask); + int zeroPos = Long.numberOfTrailingZeros(zeroBit); + assert(zeroPos < newArity); + return zeroPos; + } + BitSet mask = new BitSet(newArity); + for (int arg : reorder) { + assert(arg < newArity); + mask.set(arg); + } + int zeroPos = mask.nextClearBit(0); + if (zeroPos == newArity) + return -1; + return zeroPos; + } + + /** + * Return an indication of any duplicate or omission in reorder. + * If the reorder contains a duplicate entry, return the index of the second occurrence. + * Otherwise, return ~(n), for the first n in [0..newArity-1] that is not present in reorder. + * Otherwise, return zero. + * If an element not in [0..newArity-1] is encountered, return reorder.length. + */ + private static int findFirstDupOrDrop(int[] reorder, int newArity) { + final int BIT_LIMIT = 63; // max number of bits in bit mask + if (newArity < BIT_LIMIT) { + long mask = 0; + for (int i = 0; i < reorder.length; i++) { + int arg = reorder[i]; + if (arg >= newArity) return reorder.length; + int bit = 1 << arg; + if ((mask & bit) != 0) + return i; // >0 indicates a dup + mask |= bit; + } + if (mask == (1 << newArity) - 1) { + assert(Long.numberOfTrailingZeros(Long.lowestOneBit(~mask)) == newArity); + return 0; + } + // find first zero + long zeroBit = Long.lowestOneBit(~mask); + int zeroPos = Long.numberOfTrailingZeros(zeroBit); + assert(zeroPos < newArity); + return ~zeroPos; + } else { + // same algorithm, different bit set + BitSet mask = new BitSet(newArity); + for (int i = 0; i < reorder.length; i++) { + int arg = reorder[i]; + if (arg >= newArity) return reorder.length; + if (mask.get(arg)) + return i; // >0 indicates a dup + mask.set(arg); + } + int zeroPos = mask.nextClearBit(0); + if (zeroPos == newArity) { + return 0; + } + return ~zeroPos; + } + } + + private static boolean permuteArgumentChecks(int[] reorder, MethodType newType, MethodType oldType) { if (newType.returnType() != oldType.returnType()) throw newIllegalArgumentException("return types do not match", oldType, newType); @@ -2105,7 +2269,7 @@ assert((int)twice.invokeExact(21) == 42); throw newIllegalArgumentException("parameter types do not match after reorder", oldType, newType); } - if (!bad) return; + if (!bad) return true; } throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder)); } @@ -2131,9 +2295,14 @@ assert((int)twice.invokeExact(21) == 42); if (type == void.class) throw newIllegalArgumentException("void type"); Wrapper w = Wrapper.forPrimitiveType(type); - return insertArguments(identity(type), 0, w.convert(value, type)); + value = w.convert(value, type); + if (w.zero().equals(value)) + return zero(w, type); + return insertArguments(identity(type), 0, value); } else { - return identity(type).bindTo(type.cast(value)); + if (value == null) + return zero(Wrapper.OBJECT, type); + return identity(type).bindTo(value); } } @@ -2146,14 +2315,48 @@ assert((int)twice.invokeExact(21) == 42); */ public static MethodHandle identity(Class type) { - if (type == void.class) - throw newIllegalArgumentException("void type"); - else if (type == Object.class) - return ValueConversions.identity(); - else if (type.isPrimitive()) - return ValueConversions.identity(Wrapper.forPrimitiveType(type)); - else - return MethodHandleImpl.makeReferenceIdentity(type); + Wrapper btw = (type.isPrimitive() ? Wrapper.forPrimitiveType(type) : Wrapper.OBJECT); + int pos = btw.ordinal(); + MethodHandle ident = IDENTITY_MHS[pos]; + if (ident == null) { + ident = setCachedMethodHandle(IDENTITY_MHS, pos, makeIdentity(btw.primitiveType())); + } + if (ident.type().returnType() == type) + return ident; + // something like identity(Foo.class); do not bother to intern these + assert(btw == Wrapper.OBJECT); + return makeIdentity(type); + } + private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.values().length]; + private static MethodHandle makeIdentity(Class ptype) { + MethodType mtype = MethodType.methodType(ptype, ptype); + LambdaForm lform = LambdaForm.identityForm(BasicType.basicType(ptype)); + return MethodHandleImpl.makeIntrinsic(mtype, lform, Intrinsic.IDENTITY); + } + + private static MethodHandle zero(Wrapper btw, Class rtype) { + int pos = btw.ordinal(); + MethodHandle zero = ZERO_MHS[pos]; + if (zero == null) { + zero = setCachedMethodHandle(ZERO_MHS, pos, makeZero(btw.primitiveType())); + } + if (zero.type().returnType() == rtype) + return zero; + assert(btw == Wrapper.OBJECT); + return makeZero(rtype); + } + private static final MethodHandle[] ZERO_MHS = new MethodHandle[Wrapper.values().length]; + private static MethodHandle makeZero(Class rtype) { + MethodType mtype = MethodType.methodType(rtype); + LambdaForm lform = LambdaForm.zeroForm(BasicType.basicType(rtype)); + return MethodHandleImpl.makeIntrinsic(mtype, lform, Intrinsic.ZERO); + } + + synchronized private static MethodHandle setCachedMethodHandle(MethodHandle[] cache, int pos, MethodHandle value) { + // Simulate a CAS, to avoid racy duplication of results. + MethodHandle prev = cache[pos]; + if (prev != null) return prev; + return cache[pos] = value; } /** @@ -2189,6 +2392,37 @@ assert((int)twice.invokeExact(21) == 42); public static MethodHandle insertArguments(MethodHandle target, int pos, Object... values) { int insCount = values.length; + Class[] ptypes = insertArgumentsChecks(target, insCount, pos); + if (insCount == 0) return target; + BoundMethodHandle result = target.rebind(); + for (int i = 0; i < insCount; i++) { + Object value = values[i]; + Class ptype = ptypes[pos+i]; + if (ptype.isPrimitive()) { + result = insertArgumentPrimitive(result, pos, ptype, value); + } else { + value = ptype.cast(value); // throw CCE if needed + result = result.bindArgumentL(pos, value); + } + } + return result; + } + + private static BoundMethodHandle insertArgumentPrimitive(BoundMethodHandle result, int pos, + Class ptype, Object value) { + Wrapper w = Wrapper.forPrimitiveType(ptype); + // perform unboxing and/or primitive conversion + value = w.convert(value, ptype); + switch (w) { + case INT: return result.bindArgumentI(pos, (int)value); + case LONG: return result.bindArgumentJ(pos, (long)value); + case FLOAT: return result.bindArgumentF(pos, (float)value); + case DOUBLE: return result.bindArgumentD(pos, (double)value); + default: return result.bindArgumentI(pos, ValueConversions.widenSubword(value)); + } + } + + private static Class[] insertArgumentsChecks(MethodHandle target, int insCount, int pos) throws RuntimeException { MethodType oldType = target.type(); int outargs = oldType.parameterCount(); int inargs = outargs - insCount; @@ -2196,31 +2430,7 @@ assert((int)twice.invokeExact(21) == 42); throw newIllegalArgumentException("too many values to insert"); if (pos < 0 || pos > inargs) throw newIllegalArgumentException("no argument type to append"); - MethodHandle result = target; - for (int i = 0; i < insCount; i++) { - Object value = values[i]; - Class ptype = oldType.parameterType(pos+i); - if (ptype.isPrimitive()) { - BasicType btype = I_TYPE; - Wrapper w = Wrapper.forPrimitiveType(ptype); - switch (w) { - case LONG: btype = J_TYPE; break; - case FLOAT: btype = F_TYPE; break; - case DOUBLE: btype = D_TYPE; break; - } - // perform unboxing and/or primitive conversion - value = w.convert(value, ptype); - result = result.bindArgument(pos, btype, value); - continue; - } - value = ptype.cast(value); // throw CCE if needed - if (pos == 0) { - result = result.bindReceiver(value); - } else { - result = result.bindArgument(pos, L_TYPE, value); - } - } - return result; + return oldType.ptypes(); } /** @@ -2268,18 +2478,33 @@ assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z")); public static MethodHandle dropArguments(MethodHandle target, int pos, List> valueTypes) { MethodType oldType = target.type(); // get NPE + int dropped = dropArgumentChecks(oldType, pos, valueTypes); + if (dropped == 0) return target; + BoundMethodHandle result = target.rebind(); + LambdaForm lform = result.form; + if (USE_LAMBDA_FORM_EDITOR) { + int insertFormArg = 1 + pos; + for (Class ptype : valueTypes) { + lform = lform.editor().addArgumentForm(insertFormArg++, BasicType.basicType(ptype)); + } + } else { + lform = lform.addArguments(pos, valueTypes); + } + MethodType newType = oldType.insertParameterTypes(pos, valueTypes); + result = result.copyWith(newType, lform); + return result; + } + + private static int dropArgumentChecks(MethodType oldType, int pos, List> valueTypes) { int dropped = valueTypes.size(); MethodType.checkSlotCount(dropped); - if (dropped == 0) return target; int outargs = oldType.parameterCount(); int inargs = outargs + dropped; - if (pos < 0 || pos >= inargs) - throw newIllegalArgumentException("no argument type to remove"); - ArrayList> ptypes = new ArrayList<>(oldType.parameterList()); - ptypes.addAll(pos, valueTypes); - if (ptypes.size() != inargs) throw newIllegalArgumentException("valueTypes"); - MethodType newType = MethodType.methodType(oldType.returnType(), ptypes); - return target.dropArguments(newType, pos, dropped); + if (pos < 0 || pos > outargs) + throw newIllegalArgumentException("no argument type to remove" + + Arrays.asList(oldType, pos, valueTypes, inargs, outargs) + ); + return dropped; } /** @@ -2401,32 +2626,47 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY */ public static MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) { - MethodType targetType = target.type(); + filterArgumentsCheckArity(target, pos, filters); MethodHandle adapter = target; - MethodType adapterType = null; - assert((adapterType = targetType) != null); - int maxPos = targetType.parameterCount(); - if (pos + filters.length > maxPos) - throw newIllegalArgumentException("too many filters"); int curPos = pos-1; // pre-incremented for (MethodHandle filter : filters) { curPos += 1; if (filter == null) continue; // ignore null elements of filters adapter = filterArgument(adapter, curPos, filter); - assert((adapterType = adapterType.changeParameterType(curPos, filter.type().parameterType(0))) != null); } - assert(adapterType.equals(adapter.type())); return adapter; } /*non-public*/ static MethodHandle filterArgument(MethodHandle target, int pos, MethodHandle filter) { + filterArgumentChecks(target, pos, filter); + if (USE_LAMBDA_FORM_EDITOR) { + MethodType targetType = target.type(); + MethodType filterType = filter.type(); + BoundMethodHandle result = target.rebind(); + Class newParamType = filterType.parameterType(0); + LambdaForm lform = result.editor().filterArgumentForm(1 + pos, BasicType.basicType(newParamType)); + MethodType newType = targetType.changeParameterType(pos, newParamType); + result = result.copyWithExtendL(newType, lform, filter); + return result; + } else { + return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); + } + } + + private static void filterArgumentsCheckArity(MethodHandle target, int pos, MethodHandle[] filters) { + MethodType targetType = target.type(); + int maxPos = targetType.parameterCount(); + if (pos + filters.length > maxPos) + throw newIllegalArgumentException("too many filters"); + } + + private static void filterArgumentChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException { MethodType targetType = target.type(); MethodType filterType = filter.type(); if (filterType.parameterCount() != 1 || filterType.returnType() != targetType.parameterType(pos)) throw newIllegalArgumentException("target and filter types do not match", targetType, filterType); - return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); } /** @@ -2537,12 +2777,36 @@ assertEquals("[top, [[up, down, strange], charm], bottom]", */ public static MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter) { + MethodType newType = collectArgumentsChecks(target, pos, filter); + if (USE_LAMBDA_FORM_EDITOR) { + MethodType collectorType = filter.type(); + BoundMethodHandle result = target.rebind(); + LambdaForm lform; + if (collectorType.returnType().isArray() && filter.intrinsicName() == Intrinsic.NEW_ARRAY) { + lform = result.editor().collectArgumentArrayForm(1 + pos, filter); + if (lform != null) { + return result.copyWith(newType, lform); + } + } + lform = result.editor().collectArgumentsForm(1 + pos, collectorType.basicType()); + return result.copyWithExtendL(newType, lform, filter); + } else { + return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); + } + } + + private static MethodType collectArgumentsChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException { MethodType targetType = target.type(); MethodType filterType = filter.type(); - if (filterType.returnType() != void.class && - filterType.returnType() != targetType.parameterType(pos)) + Class rtype = filterType.returnType(); + List> filterArgs = filterType.parameterList(); + if (rtype == void.class) { + return targetType.insertParameterTypes(pos, filterArgs); + } + if (rtype != targetType.parameterType(pos)) { throw newIllegalArgumentException("target and filter types do not match", targetType, filterType); - return MethodHandleImpl.makeCollectArguments(target, filter, pos, false); + } + return targetType.dropParameterTypes(pos, pos+1).insertParameterTypes(pos, filterArgs); } /** @@ -2606,15 +2870,26 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) { MethodType targetType = target.type(); MethodType filterType = filter.type(); + filterReturnValueChecks(targetType, filterType); + if (USE_LAMBDA_FORM_EDITOR) { + BoundMethodHandle result = target.rebind(); + BasicType rtype = BasicType.basicType(filterType.returnType()); + LambdaForm lform = result.editor().filterReturnForm(rtype, false); + MethodType newType = targetType.changeReturnType(filterType.returnType()); + result = result.copyWithExtendL(newType, lform, filter); + return result; + } else { + return MethodHandleImpl.makeCollectArguments(filter, target, 0, false); + } + } + + private static void filterReturnValueChecks(MethodType targetType, MethodType filterType) throws RuntimeException { Class rtype = targetType.returnType(); int filterValues = filterType.parameterCount(); if (filterValues == 0 ? (rtype != void.class) : (rtype != filterType.parameterType(0))) - throw newIllegalArgumentException("target and filter types do not match", target, filter); - // result = fold( lambda(retval, arg...) { filter(retval) }, - // lambda( arg...) { target(arg...) } ) - return MethodHandleImpl.makeCollectArguments(filter, target, 0, false); + throw newIllegalArgumentException("target and filter types do not match", targetType, filterType); } /** @@ -2695,24 +2970,40 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum")); */ public static MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) { - int pos = 0; + int foldPos = 0; MethodType targetType = target.type(); MethodType combinerType = combiner.type(); - int foldPos = pos; - int foldArgs = combinerType.parameterCount(); - int foldVals = combinerType.returnType() == void.class ? 0 : 1; + Class rtype = foldArgumentChecks(foldPos, targetType, combinerType); + if (USE_LAMBDA_FORM_EDITOR) { + BoundMethodHandle result = target.rebind(); + boolean dropResult = (rtype == void.class); + // Note: This may cache too many distinct LFs. Consider backing off to varargs code. + LambdaForm lform = result.editor().foldArgumentsForm(1 + foldPos, dropResult, combinerType.basicType()); + MethodType newType = targetType; + if (!dropResult) + newType = newType.dropParameterTypes(foldPos, foldPos + 1); + result = result.copyWithExtendL(newType, lform, combiner); + return result; + } else { + return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true); + } + } + + private static Class foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) { + int foldArgs = combinerType.parameterCount(); + Class rtype = combinerType.returnType(); + int foldVals = rtype == void.class ? 0 : 1; int afterInsertPos = foldPos + foldVals; boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs); if (ok && !(combinerType.parameterList() .equals(targetType.parameterList().subList(afterInsertPos, afterInsertPos + foldArgs)))) ok = false; - if (ok && foldVals != 0 && !combinerType.returnType().equals(targetType.parameterType(0))) + if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0)) ok = false; if (!ok) throw misMatchedTypes("target and combiner types", targetType, combinerType); - MethodType newType = targetType.dropParameterTypes(foldPos, afterInsertPos); - return MethodHandleImpl.makeCollectArguments(target, combiner, foldPos, true); + return rtype; } /** diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java index 2b20ae71935..2762227f5c2 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java @@ -467,6 +467,75 @@ class MethodType implements java.io.Serializable { return dropParameterTypes(start, end).insertParameterTypes(start, ptypesToInsert); } + /** Replace the last arrayLength parameter types with the component type of arrayType. + * @param arrayType any array type + * @param arrayLength the number of parameter types to change + * @return the resulting type + */ + /*non-public*/ MethodType asSpreaderType(Class arrayType, int arrayLength) { + assert(parameterCount() >= arrayLength); + int spreadPos = ptypes.length - arrayLength; + if (arrayLength == 0) return this; // nothing to change + if (arrayType == Object[].class) { + if (isGeneric()) return this; // nothing to change + if (spreadPos == 0) { + // no leading arguments to preserve; go generic + MethodType res = genericMethodType(arrayLength); + if (rtype != Object.class) { + res = res.changeReturnType(rtype); + } + return res; + } + } + Class elemType = arrayType.getComponentType(); + assert(elemType != null); + for (int i = spreadPos; i < ptypes.length; i++) { + if (ptypes[i] != elemType) { + Class[] fixedPtypes = ptypes.clone(); + Arrays.fill(fixedPtypes, i, ptypes.length, elemType); + return methodType(rtype, fixedPtypes); + } + } + return this; // arguments check out; no change + } + + /** Return the leading parameter type, which must exist and be a reference. + * @return the leading parameter type, after error checks + */ + /*non-public*/ Class leadingReferenceParameter() { + Class ptype; + if (ptypes.length == 0 || + (ptype = ptypes[0]).isPrimitive()) + throw newIllegalArgumentException("no leading reference parameter"); + return ptype; + } + + /** Delete the last parameter type and replace it with arrayLength copies of the component type of arrayType. + * @param arrayType any array type + * @param arrayLength the number of parameter types to insert + * @return the resulting type + */ + /*non-public*/ MethodType asCollectorType(Class arrayType, int arrayLength) { + assert(parameterCount() >= 1); + assert(lastParameterType().isAssignableFrom(arrayType)); + MethodType res; + if (arrayType == Object[].class) { + res = genericMethodType(arrayLength); + if (rtype != Object.class) { + res = res.changeReturnType(rtype); + } + } else { + Class elemType = arrayType.getComponentType(); + assert(elemType != null); + res = methodType(rtype, Collections.nCopies(arrayLength, elemType)); + } + if (ptypes.length == 1) { + return res; + } else { + return res.insertParameterTypes(0, parameterList().subList(0, ptypes.length-1)); + } + } + /** * Finds or creates a method type with some parameter types omitted. * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. @@ -574,6 +643,10 @@ class MethodType implements java.io.Serializable { return genericMethodType(parameterCount()); } + /*non-public*/ boolean isGeneric() { + return this == erase() && !hasPrimitives(); + } + /** * Converts all primitive types to their corresponding wrapper types. * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}. @@ -726,44 +799,137 @@ class MethodType implements java.io.Serializable { return sj.toString(); } - + /** True if the old return type can always be viewed (w/o casting) under new return type, + * and the new parameters can be viewed (w/o casting) under the old parameter types. + */ /*non-public*/ - boolean isViewableAs(MethodType newType) { - if (!VerifyType.isNullConversion(returnType(), newType.returnType())) + boolean isViewableAs(MethodType newType, boolean keepInterfaces) { + if (!VerifyType.isNullConversion(returnType(), newType.returnType(), keepInterfaces)) return false; + return parametersAreViewableAs(newType, keepInterfaces); + } + /** True if the new parameters can be viewed (w/o casting) under the old parameter types. */ + /*non-public*/ + boolean parametersAreViewableAs(MethodType newType, boolean keepInterfaces) { + if (form == newType.form && form.erasedType == this) + return true; // my reference parameters are all Object + if (ptypes == newType.ptypes) + return true; int argc = parameterCount(); if (argc != newType.parameterCount()) return false; for (int i = 0; i < argc; i++) { - if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i))) + if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i), keepInterfaces)) return false; } return true; } /*non-public*/ - boolean isCastableTo(MethodType newType) { - int argc = parameterCount(); - if (argc != newType.parameterCount()) - return false; - return true; - } - /*non-public*/ boolean isConvertibleTo(MethodType newType) { + MethodTypeForm oldForm = this.form(); + MethodTypeForm newForm = newType.form(); + if (oldForm == newForm) + // same parameter count, same primitive/object mix + return true; if (!canConvert(returnType(), newType.returnType())) return false; - int argc = parameterCount(); - if (argc != newType.parameterCount()) + Class[] srcTypes = newType.ptypes; + Class[] dstTypes = ptypes; + if (srcTypes == dstTypes) + return true; + int argc; + if ((argc = srcTypes.length) != dstTypes.length) return false; - for (int i = 0; i < argc; i++) { - if (!canConvert(newType.parameterType(i), parameterType(i))) + if (argc <= 1) { + if (argc == 1 && !canConvert(srcTypes[0], dstTypes[0])) return false; + return true; + } + if ((oldForm.primitiveParameterCount() == 0 && oldForm.erasedType == this) || + (newForm.primitiveParameterCount() == 0 && newForm.erasedType == newType)) { + // Somewhat complicated test to avoid a loop of 2 or more trips. + // If either type has only Object parameters, we know we can convert. + assert(canConvertParameters(srcTypes, dstTypes)); + return true; + } + return canConvertParameters(srcTypes, dstTypes); + } + + /** Returns true if MHs.explicitCastArguments produces the same result as MH.asType. + * If the type conversion is impossible for either, the result should be false. + */ + /*non-public*/ + boolean explicitCastEquivalentToAsType(MethodType newType) { + if (this == newType) return true; + if (!explicitCastEquivalentToAsType(rtype, newType.rtype)) { + return false; + } + Class[] srcTypes = newType.ptypes; + Class[] dstTypes = ptypes; + if (dstTypes == srcTypes) { + return true; + } + if (dstTypes.length != srcTypes.length) { + return false; + } + for (int i = 0; i < dstTypes.length; i++) { + if (!explicitCastEquivalentToAsType(srcTypes[i], dstTypes[i])) { + return false; + } } return true; } + + /** Reports true if the src can be converted to the dst, by both asType and MHs.eCE, + * and with the same effect. + * MHs.eCA has the following "upgrades" to MH.asType: + * 1. interfaces are unchecked (that is, treated as if aliased to Object) + * Therefore, {@code Object->CharSequence} is possible in both cases but has different semantics + * 2. the full matrix of primitive-to-primitive conversions is supported + * Narrowing like {@code long->byte} and basic-typing like {@code boolean->int} + * are not supported by asType, but anything supported by asType is equivalent + * with MHs.eCE. + * 3a. unboxing conversions can be followed by the full matrix of primitive conversions + * 3b. unboxing of null is permitted (creates a zero primitive value) + * Most unboxing conversions, like {@code Object->int}, has potentially + * different behaviors for asType vs. MHs.eCE, because the dynamic value + * might be a wrapper of a type that requires narrowing, like {@code (Object)1L->byte}. + * The equivalence is only certain if the static src type is a wrapper, + * and the conversion will be a widening one. + * Other than interfaces, reference-to-reference conversions are the same. + * Boxing primitives to references is the same for both operators. + */ + private static boolean explicitCastEquivalentToAsType(Class src, Class dst) { + if (src == dst || dst == Object.class || dst == void.class) return true; + if (src.isPrimitive()) { + // Could be a prim/prim conversion, where casting is a strict superset. + // Or a boxing conversion, which is always to an exact wrapper class. + return canConvert(src, dst); + } else if (dst.isPrimitive()) { + Wrapper dw = Wrapper.forPrimitiveType(dst); + // Watch out: If src is Number or Object, we could get dynamic narrowing conversion. + // The conversion is known to be widening only if the wrapper type is statically visible. + return (Wrapper.isWrapperType(src) && + dw.isConvertibleFrom(Wrapper.forWrapperType(src))); + } else { + // R->R always works, but we have to avoid a check-cast to an interface. + return !dst.isInterface() || dst.isAssignableFrom(src); + } + } + + private boolean canConvertParameters(Class[] srcTypes, Class[] dstTypes) { + for (int i = 0; i < srcTypes.length; i++) { + if (!canConvert(srcTypes[i], dstTypes[i])) { + return false; + } + } + return true; + } + /*non-public*/ static boolean canConvert(Class src, Class dst) { // short-circuit a few cases: - if (src == dst || dst == Object.class) return true; + if (src == dst || src == Object.class || dst == Object.class) return true; // the remainder of this logic is documented in MethodHandle.asType if (src.isPrimitive()) { // can force void to an explicit null, a la reflect.Method.invoke @@ -905,7 +1071,7 @@ class MethodType implements java.io.Serializable { if (!descriptor.startsWith("(") || // also generates NPE if needed descriptor.indexOf(')') < 0 || descriptor.indexOf('.') >= 0) - throw new IllegalArgumentException("not a method descriptor: "+descriptor); + throw newIllegalArgumentException("not a method descriptor: "+descriptor); List> types = BytecodeDescriptor.parseMethod(descriptor, loader); Class rtype = types.remove(types.size() - 1); checkSlotCount(types.size()); diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java index d34a2998cab..43e85589410 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java @@ -47,15 +47,17 @@ final class MethodTypeForm { final int[] argToSlotTable, slotToArgTable; final long argCounts; // packed slot & value counts final long primCounts; // packed prim & double counts - final int vmslots; // total number of parameter slots final MethodType erasedType; // the canonical erasure final MethodType basicType; // the canonical erasure, with primitives simplified // Cached adapter information: - @Stable String typeString; // argument type signature characters - @Stable MethodHandle genericInvoker; // JVM hook for inexact invoke - @Stable MethodHandle basicInvoker; // cached instance of MH.invokeBasic - @Stable MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction + @Stable final MethodHandle[] methodHandles; + // Indexes into methodHandles: + static final int + MH_BASIC_INV = 0, // cached instance of MH.invokeBasic + MH_NF_INV = 1, // cached helper for LF.NamedFunction + MH_UNINIT_CS = 2, // uninitialized call site + MH_LIMIT = 3; // Cached lambda form information, for basic types only: final @Stable LambdaForm[] lambdaForms; @@ -68,26 +70,55 @@ final class MethodTypeForm { LF_INVINTERFACE = 4, LF_INVSTATIC_INIT = 5, // DMH invokeStatic with barrier LF_INTERPRET = 6, // LF interpreter - LF_COUNTER = 7, // CMH wrapper - LF_REINVOKE = 8, // other wrapper - LF_EX_LINKER = 9, // invokeExact_MT - LF_EX_INVOKER = 10, // invokeExact MH - LF_GEN_LINKER = 11, - LF_GEN_INVOKER = 12, + LF_REBIND = 7, // BoundMethodHandle + LF_DELEGATE = 8, // DelegatingMethodHandle + LF_EX_LINKER = 9, // invokeExact_MT (for invokehandle) + LF_EX_INVOKER = 10, // MHs.invokeExact + LF_GEN_LINKER = 11, // generic invoke_MT (for invokehandle) + LF_GEN_INVOKER = 12, // generic MHs.invoke LF_CS_LINKER = 13, // linkToCallSite_CS LF_MH_LINKER = 14, // linkToCallSite_MH - LF_GWC = 15, - LF_LIMIT = 16; + LF_GWC = 15, // guardWithCatch (catchException) + LF_GWT = 16, // guardWithTest + LF_LIMIT = 17; + /** Return the type corresponding uniquely (1-1) to this MT-form. + * It might have any primitive returns or arguments, but will have no references except Object. + */ public MethodType erasedType() { return erasedType; } + /** Return the basic type derived from the erased type of this MT-form. + * A basic type is erased (all references Object) and also has all primitive + * types (except int, long, float, double, void) normalized to int. + * Such basic types correspond to low-level JVM calling sequences. + */ public MethodType basicType() { return basicType; } + private boolean assertIsBasicType() { + // primitives must be flattened also + assert(erasedType == basicType) + : "erasedType: " + erasedType + " != basicType: " + basicType; + return true; + } + + public MethodHandle cachedMethodHandle(int which) { + assert(assertIsBasicType()); + return methodHandles[which]; + } + + synchronized public MethodHandle setCachedMethodHandle(int which, MethodHandle mh) { + // Simulate a CAS, to avoid racy duplication of results. + MethodHandle prev = methodHandles[which]; + if (prev != null) return prev; + return methodHandles[which] = mh; + } + public LambdaForm cachedLambdaForm(int which) { + assert(assertIsBasicType()); return lambdaForms[which]; } @@ -98,28 +129,6 @@ final class MethodTypeForm { return lambdaForms[which] = form; } - public MethodHandle basicInvoker() { - assert(erasedType == basicType) : "erasedType: " + erasedType + " != basicType: " + basicType; // primitives must be flattened also - MethodHandle invoker = basicInvoker; - if (invoker != null) return invoker; - invoker = DirectMethodHandle.make(invokeBasicMethod(basicType)); - basicInvoker = invoker; - return invoker; - } - - // This next one is called from LambdaForm.NamedFunction.. - /*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) { - assert(basicType == basicType.basicType()); - try { - // Do approximately the same as this public API call: - // Lookup.findVirtual(MethodHandle.class, name, type); - // But bypass access and corner case checks, since we know exactly what we need. - return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, "invokeBasic", basicType); - } catch (ReflectiveOperationException ex) { - throw newInternalError("JVM cannot find invoker for "+basicType, ex); - } - } - /** * Build an MTF for a given type, which must have all references erased to Object. * This MTF will stand for that type and all un-erased variations. @@ -172,6 +181,16 @@ final class MethodTypeForm { this.basicType = erasedType; } else { this.basicType = MethodType.makeImpl(bt, bpts, true); + // fill in rest of data from the basic type: + MethodTypeForm that = this.basicType.form(); + assert(this != that); + this.primCounts = that.primCounts; + this.argCounts = that.argCounts; + this.argToSlotTable = that.argToSlotTable; + this.slotToArgTable = that.slotToArgTable; + this.methodHandles = null; + this.lambdaForms = null; + return; } if (lac != 0) { int slot = ptypeCount + lac; @@ -187,10 +206,14 @@ final class MethodTypeForm { argToSlotTab[1+i] = slot; } assert(slot == 0); // filled the table - } - this.primCounts = pack(lrc, prc, lac, pac); - this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount); - if (slotToArgTab == null) { + } else if (pac != 0) { + // have primitives but no long primitives; share slot counts with generic + assert(ptypeCount == pslotCount); + MethodTypeForm that = MethodType.genericMethodType(ptypeCount).form(); + assert(this != that); + slotToArgTab = that.slotToArgTable; + argToSlotTab = that.argToSlotTable; + } else { int slot = ptypeCount; // first arg is deepest in stack slotToArgTab = new int[slot+1]; argToSlotTab = new int[1+ptypeCount]; @@ -201,19 +224,17 @@ final class MethodTypeForm { argToSlotTab[1+i] = slot; } } + this.primCounts = pack(lrc, prc, lac, pac); + this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount); this.argToSlotTable = argToSlotTab; this.slotToArgTable = slotToArgTab; if (pslotCount >= 256) throw newIllegalArgumentException("too many arguments"); - // send a few bits down to the JVM: - this.vmslots = parameterSlotCount(); - - if (basicType == erasedType) { - lambdaForms = new LambdaForm[LF_LIMIT]; - } else { - lambdaForms = null; // could be basicType.form().lambdaForms; - } + // Initialize caches, but only for basic types + assert(basicType == erasedType); + this.lambdaForms = new LambdaForm[LF_LIMIT]; + this.methodHandles = new MethodHandle[MH_LIMIT]; } private static long pack(int a, int b, int c, int d) { @@ -300,7 +321,7 @@ final class MethodTypeForm { */ public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) { Class[] ptypes = mt.ptypes(); - Class[] ptc = MethodTypeForm.canonicalizes(ptypes, howArgs); + Class[] ptc = MethodTypeForm.canonicalizeAll(ptypes, howArgs); Class rtype = mt.returnType(); Class rtc = MethodTypeForm.canonicalize(rtype, howRet); if (ptc == null && rtc == null) { @@ -368,7 +389,7 @@ final class MethodTypeForm { /** Canonicalize each param type in the given array. * Return null if all types are already canonicalized. */ - static Class[] canonicalizes(Class[] ts, int how) { + static Class[] canonicalizeAll(Class[] ts, int how) { Class[] cs = null; for (int imax = ts.length, i = 0; i < imax; i++) { Class c = canonicalize(ts[i], how); diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java b/jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java index 752505ee68e..c9e37a81ff4 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java @@ -25,38 +25,77 @@ package java.lang.invoke; -import static java.lang.invoke.LambdaForm.*; import static java.lang.invoke.LambdaForm.BasicType.*; +import static java.lang.invoke.MethodHandleStatics.*; /** * A method handle whose behavior is determined only by its LambdaForm. * @author jrose */ -final class SimpleMethodHandle extends MethodHandle { +final class SimpleMethodHandle extends BoundMethodHandle { private SimpleMethodHandle(MethodType type, LambdaForm form) { super(type, form); } - /*non-public*/ static SimpleMethodHandle make(MethodType type, LambdaForm form) { + /*non-public*/ static BoundMethodHandle make(MethodType type, LambdaForm form) { return new SimpleMethodHandle(type, form); } - @Override - MethodHandle bindArgument(int pos, BasicType basicType, Object value) { - MethodType type2 = type().dropParameterTypes(pos, pos+1); - LambdaForm form2 = internalForm().bind(1+pos, BoundMethodHandle.SpeciesData.EMPTY); - return BoundMethodHandle.bindSingle(type2, form2, basicType, value); + /*non-public*/ static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY; + + /*non-public*/ public SpeciesData speciesData() { + return SPECIES_DATA; } @Override - MethodHandle dropArguments(MethodType srcType, int pos, int drops) { - LambdaForm newForm = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops)); - return new SimpleMethodHandle(srcType, newForm); + /*non-public*/ BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) { + return make(mt, lf); } @Override - MethodHandle permuteArguments(MethodType newType, int[] reorder) { - LambdaForm form2 = internalForm().permuteArguments(1, reorder, basicTypes(newType.parameterList())); - return new SimpleMethodHandle(newType, form2); + String internalProperties() { + return "\n& Class="+getClass().getSimpleName(); + } + + @Override + /*non-public*/ public int fieldCount() { + return 0; + } + + @Override + /*non-public*/ final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) { + return BoundMethodHandle.bindSingle(mt, lf, narg); // Use known fast path. + } + @Override + /*non-public*/ final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) { + try { + return (BoundMethodHandle) SPECIES_DATA.extendWith(I_TYPE).constructor().invokeBasic(mt, lf, narg); + } catch (Throwable ex) { + throw uncaughtException(ex); + } + } + @Override + /*non-public*/ final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) { + try { + return (BoundMethodHandle) SPECIES_DATA.extendWith(J_TYPE).constructor().invokeBasic(mt, lf, narg); + } catch (Throwable ex) { + throw uncaughtException(ex); + } + } + @Override + /*non-public*/ final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) { + try { + return (BoundMethodHandle) SPECIES_DATA.extendWith(F_TYPE).constructor().invokeBasic(mt, lf, narg); + } catch (Throwable ex) { + throw uncaughtException(ex); + } + } + @Override + /*non-public*/ final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) { + try { + return (BoundMethodHandle) SPECIES_DATA.extendWith(D_TYPE).constructor().invokeBasic(mt, lf, narg); + } catch (Throwable ex) { + throw uncaughtException(ex); + } } } diff --git a/jdk/src/java.base/share/classes/java/lang/reflect/Constructor.java b/jdk/src/java.base/share/classes/java/lang/reflect/Constructor.java index 4b112bf16b3..b86e1a4eae0 100644 --- a/jdk/src/java.base/share/classes/java/lang/reflect/Constructor.java +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Constructor.java @@ -94,8 +94,19 @@ public final class Constructor extends Executable { // For sharing of ConstructorAccessors. This branching structure // is currently only two levels deep (i.e., one root Constructor // and potentially many Constructor objects pointing to it.) + // + // If this branching structure would ever contain cycles, deadlocks can + // occur in annotation code. private Constructor root; + /** + * Used by Excecutable for annotation sharing. + */ + @Override + Executable getRoot() { + return root; + } + /** * Package-private constructor used by ReflectAccess to enable * instantiation of these objects in Java code from the java.lang @@ -132,6 +143,9 @@ public final class Constructor extends Executable { // which implicitly requires that new java.lang.reflect // objects be fabricated for each reflective call on Class // objects.) + if (this.root != null) + throw new IllegalArgumentException("Can not copy a non-root Constructor"); + Constructor res = new Constructor<>(clazz, parameterTypes, exceptionTypes, modifiers, slot, diff --git a/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java b/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java index 99eb1eb8b90..15c6574d078 100644 --- a/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java @@ -52,6 +52,11 @@ public abstract class Executable extends AccessibleObject */ abstract byte[] getAnnotationBytes(); + /** + * Accessor method to allow code sharing + */ + abstract Executable getRoot(); + /** * Does the Executable have generic information. */ @@ -540,11 +545,16 @@ public abstract class Executable extends AccessibleObject private synchronized Map, Annotation> declaredAnnotations() { if (declaredAnnotations == null) { - declaredAnnotations = AnnotationParser.parseAnnotations( - getAnnotationBytes(), - sun.misc.SharedSecrets.getJavaLangAccess(). - getConstantPool(getDeclaringClass()), - getDeclaringClass()); + Executable root = getRoot(); + if (root != null) { + declaredAnnotations = root.declaredAnnotations(); + } else { + declaredAnnotations = AnnotationParser.parseAnnotations( + getAnnotationBytes(), + sun.misc.SharedSecrets.getJavaLangAccess(). + getConstantPool(getDeclaringClass()), + getDeclaringClass()); + } } return declaredAnnotations; } diff --git a/jdk/src/java.base/share/classes/java/lang/reflect/Field.java b/jdk/src/java.base/share/classes/java/lang/reflect/Field.java index 6e243bc82b6..bb23b22dc94 100644 --- a/jdk/src/java.base/share/classes/java/lang/reflect/Field.java +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Field.java @@ -81,6 +81,9 @@ class Field extends AccessibleObject implements Member { // For sharing of FieldAccessors. This branching structure is // currently only two levels deep (i.e., one root Field and // potentially many Field objects pointing to it.) + // + // If this branching structure would ever contain cycles, deadlocks can + // occur in annotation code. private Field root; // Generics infrastructure @@ -141,6 +144,9 @@ class Field extends AccessibleObject implements Member { // which implicitly requires that new java.lang.reflect // objects be fabricated for each reflective call on Class // objects.) + if (this.root != null) + throw new IllegalArgumentException("Can not copy a non-root Field"); + Field res = new Field(clazz, name, type, modifiers, slot, signature, annotations); res.root = this; // Might as well eagerly propagate this if already present @@ -1137,10 +1143,15 @@ class Field extends AccessibleObject implements Member { private synchronized Map, Annotation> declaredAnnotations() { if (declaredAnnotations == null) { - declaredAnnotations = AnnotationParser.parseAnnotations( - annotations, sun.misc.SharedSecrets.getJavaLangAccess(). - getConstantPool(getDeclaringClass()), - getDeclaringClass()); + Field root = this.root; + if (root != null) { + declaredAnnotations = root.declaredAnnotations(); + } else { + declaredAnnotations = AnnotationParser.parseAnnotations( + annotations, + sun.misc.SharedSecrets.getJavaLangAccess().getConstantPool(getDeclaringClass()), + getDeclaringClass()); + } } return declaredAnnotations; } diff --git a/jdk/src/java.base/share/classes/java/lang/reflect/Method.java b/jdk/src/java.base/share/classes/java/lang/reflect/Method.java index 6c58138ee52..ddf0f3888ab 100644 --- a/jdk/src/java.base/share/classes/java/lang/reflect/Method.java +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Method.java @@ -79,6 +79,9 @@ public final class Method extends Executable { // For sharing of MethodAccessors. This branching structure is // currently only two levels deep (i.e., one root Method and // potentially many Method objects pointing to it.) + // + // If this branching structure would ever contain cycles, deadlocks can + // occur in annotation code. private Method root; // Generics infrastructure @@ -144,6 +147,9 @@ public final class Method extends Executable { // which implicitly requires that new java.lang.reflect // objects be fabricated for each reflective call on Class // objects.) + if (this.root != null) + throw new IllegalArgumentException("Can not copy a non-root Method"); + Method res = new Method(clazz, name, parameterTypes, returnType, exceptionTypes, modifiers, slot, signature, annotations, parameterAnnotations, annotationDefault); @@ -153,6 +159,14 @@ public final class Method extends Executable { return res; } + /** + * Used by Excecutable for annotation sharing. + */ + @Override + Executable getRoot() { + return root; + } + @Override boolean hasGenericInformation() { return (getGenericSignature() != null); diff --git a/jdk/src/java.base/share/classes/java/net/URLClassLoader.java b/jdk/src/java.base/share/classes/java/net/URLClassLoader.java index 6f4691404c3..7bb5fc54a9e 100644 --- a/jdk/src/java.base/share/classes/java/net/URLClassLoader.java +++ b/jdk/src/java.base/share/classes/java/net/URLClassLoader.java @@ -354,10 +354,11 @@ public class URLClassLoader extends SecureClassLoader implements Closeable { * @exception NullPointerException if {@code name} is {@code null}. */ protected Class findClass(final String name) - throws ClassNotFoundException + throws ClassNotFoundException { + final Class result; try { - return AccessController.doPrivileged( + result = AccessController.doPrivileged( new PrivilegedExceptionAction>() { public Class run() throws ClassNotFoundException { String path = name.replace('.', '/').concat(".class"); @@ -369,13 +370,17 @@ public class URLClassLoader extends SecureClassLoader implements Closeable { throw new ClassNotFoundException(name, e); } } else { - throw new ClassNotFoundException(name); + return null; } } }, acc); } catch (java.security.PrivilegedActionException pae) { throw (ClassNotFoundException) pae.getException(); } + if (result == null) { + throw new ClassNotFoundException(name); + } + return result; } /* diff --git a/jdk/src/java.base/share/classes/java/time/Duration.java b/jdk/src/java.base/share/classes/java/time/Duration.java index 30d146dbf64..d5a49956e90 100644 --- a/jdk/src/java.base/share/classes/java/time/Duration.java +++ b/jdk/src/java.base/share/classes/java/time/Duration.java @@ -424,7 +424,7 @@ public final class Duration return 0; } try { - long val = Long.parseLong(text, 10, start, end); + long val = Long.parseLong(text, start, end, 10); return Math.multiplyExact(val, multiplier); } catch (NumberFormatException | ArithmeticException ex) { throw (DateTimeParseException) new DateTimeParseException("Text cannot be parsed to a Duration: " + errorText, text, 0).initCause(ex); @@ -437,7 +437,7 @@ public final class Duration return 0; } try { - int fraction = Integer.parseInt(text, 10, start, end); + int fraction = Integer.parseInt(text, start, end, 10); // for number strings smaller than 9 digits, interpret as if there // were trailing zeros diff --git a/jdk/src/java.base/share/classes/java/time/Period.java b/jdk/src/java.base/share/classes/java/time/Period.java index 88adb958fa0..c7c04435262 100644 --- a/jdk/src/java.base/share/classes/java/time/Period.java +++ b/jdk/src/java.base/share/classes/java/time/Period.java @@ -358,7 +358,7 @@ public final class Period if (start < 0 || end < 0) { return 0; } - int val = Integer.parseInt(text, 10, start, end); + int val = Integer.parseInt(text, start, end, 10); try { return Math.multiplyExact(val, negate); } catch (ArithmeticException ex) { diff --git a/jdk/src/java.base/share/classes/java/util/UUID.java b/jdk/src/java.base/share/classes/java/util/UUID.java index 11e024678e0..39e507df40c 100644 --- a/jdk/src/java.base/share/classes/java/util/UUID.java +++ b/jdk/src/java.base/share/classes/java/util/UUID.java @@ -194,7 +194,8 @@ public final class UUID implements java.io.Serializable, Comparable { * */ public static UUID fromString(String name) { - if (name.length() > 36) { + int len = name.length(); + if (len > 36) { throw new IllegalArgumentException("UUID string too large"); } @@ -214,15 +215,14 @@ public final class UUID implements java.io.Serializable, Comparable { throw new IllegalArgumentException("Invalid UUID string: " + name); } - long mostSigBits = Long.parseLong(name, 16, 0, dash1) & 0xffffffffL; + long mostSigBits = Long.parseLong(name, 0, dash1, 16) & 0xffffffffL; mostSigBits <<= 16; - mostSigBits |= Long.parseLong(name, 16, dash1 + 1, dash2) & 0xffffL; + mostSigBits |= Long.parseLong(name, dash1 + 1, dash2, 16) & 0xffffL; mostSigBits <<= 16; - mostSigBits |= Long.parseLong(name, 16, dash2 + 1, dash3) & 0xffffL; - - long leastSigBits = Long.parseLong(name, 16, dash3 + 1, dash4) & 0xffffL; + mostSigBits |= Long.parseLong(name, dash2 + 1, dash3, 16) & 0xffffL; + long leastSigBits = Long.parseLong(name, dash3 + 1, dash4, 16) & 0xffffL; leastSigBits <<= 48; - leastSigBits |= Long.parseLong(name, 16, dash4 + 1) & 0xffffffffffffL; + leastSigBits |= Long.parseLong(name, dash4 + 1, len, 16) & 0xffffffffffffL; return new UUID(mostSigBits, leastSigBits); } diff --git a/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java b/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java index e3095fce2d7..64b52f4569a 100644 --- a/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java +++ b/jdk/src/java.base/share/classes/sun/invoke/util/ValueConversions.java @@ -29,38 +29,34 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.EnumMap; -import java.util.List; public class ValueConversions { private static final Class THIS_CLASS = ValueConversions.class; - // Do not adjust this except for special platforms: - private static final int MAX_ARITY; - static { - final Object[] values = { 255 }; - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - values[0] = Integer.getInteger(THIS_CLASS.getName()+".MAX_ARITY", 255); - return null; - } - }); - MAX_ARITY = (Integer) values[0]; - } - private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); - private static EnumMap[] newWrapperCaches(int n) { - @SuppressWarnings("unchecked") // generic array creation - EnumMap[] caches - = (EnumMap[]) new EnumMap[n]; + /** Thread-safe canonicalized mapping from Wrapper to MethodHandle + * with unsynchronized reads and synchronized writes. + * It's safe to publish MethodHandles by data race because they are immutable. */ + private static class WrapperCache { + /** EnumMap uses preconstructed array internally, which is constant during it's lifetime. */ + private final EnumMap map = new EnumMap<>(Wrapper.class); + + public MethodHandle get(Wrapper w) { + return map.get(w); + } + public synchronized MethodHandle put(final Wrapper w, final MethodHandle mh) { + // Simulate CAS to avoid racy duplication + MethodHandle prev = map.putIfAbsent(w, mh); + if (prev != null) return prev; + return mh; + } + } + + private static WrapperCache[] newWrapperCaches(int n) { + WrapperCache[] caches = new WrapperCache[n]; for (int i = 0; i < n; i++) - caches[i] = new EnumMap<>(Wrapper.class); + caches[i] = new WrapperCache(); return caches; } @@ -71,63 +67,92 @@ public class ValueConversions { // implicit conversions sanctioned by JLS 5.1.2, etc. // explicit conversions as allowed by explicitCastArguments + static int unboxInteger(Integer x) { + return x; + } static int unboxInteger(Object x, boolean cast) { if (x instanceof Integer) - return ((Integer) x).intValue(); + return (Integer) x; return primitiveConversion(Wrapper.INT, x, cast).intValue(); } + static byte unboxByte(Byte x) { + return x; + } static byte unboxByte(Object x, boolean cast) { if (x instanceof Byte) - return ((Byte) x).byteValue(); + return (Byte) x; return primitiveConversion(Wrapper.BYTE, x, cast).byteValue(); } + static short unboxShort(Short x) { + return x; + } static short unboxShort(Object x, boolean cast) { if (x instanceof Short) - return ((Short) x).shortValue(); + return (Short) x; return primitiveConversion(Wrapper.SHORT, x, cast).shortValue(); } + static boolean unboxBoolean(Boolean x) { + return x; + } static boolean unboxBoolean(Object x, boolean cast) { if (x instanceof Boolean) - return ((Boolean) x).booleanValue(); + return (Boolean) x; return (primitiveConversion(Wrapper.BOOLEAN, x, cast).intValue() & 1) != 0; } + static char unboxCharacter(Character x) { + return x; + } static char unboxCharacter(Object x, boolean cast) { if (x instanceof Character) - return ((Character) x).charValue(); + return (Character) x; return (char) primitiveConversion(Wrapper.CHAR, x, cast).intValue(); } + static long unboxLong(Long x) { + return x; + } static long unboxLong(Object x, boolean cast) { if (x instanceof Long) - return ((Long) x).longValue(); + return (Long) x; return primitiveConversion(Wrapper.LONG, x, cast).longValue(); } + static float unboxFloat(Float x) { + return x; + } static float unboxFloat(Object x, boolean cast) { if (x instanceof Float) - return ((Float) x).floatValue(); + return (Float) x; return primitiveConversion(Wrapper.FLOAT, x, cast).floatValue(); } + static double unboxDouble(Double x) { + return x; + } static double unboxDouble(Object x, boolean cast) { if (x instanceof Double) - return ((Double) x).doubleValue(); + return (Double) x; return primitiveConversion(Wrapper.DOUBLE, x, cast).doubleValue(); } - private static MethodType unboxType(Wrapper wrap) { + private static MethodType unboxType(Wrapper wrap, int kind) { + if (kind == 0) + return MethodType.methodType(wrap.primitiveType(), wrap.wrapperType()); return MethodType.methodType(wrap.primitiveType(), Object.class, boolean.class); } - private static final EnumMap[] - UNBOX_CONVERSIONS = newWrapperCaches(2); + private static final WrapperCache[] UNBOX_CONVERSIONS = newWrapperCaches(4); - private static MethodHandle unbox(Wrapper wrap, boolean cast) { - EnumMap cache = UNBOX_CONVERSIONS[(cast?1:0)]; + private static MethodHandle unbox(Wrapper wrap, int kind) { + // kind 0 -> strongly typed with NPE + // kind 1 -> strongly typed but zero for null, + // kind 2 -> asType rules: accept multiple box types but only widening conversions with NPE + // kind 3 -> explicitCastArguments rules: allow narrowing conversions, zero for null + WrapperCache cache = UNBOX_CONVERSIONS[kind]; MethodHandle mh = cache.get(wrap); if (mh != null) { return mh; @@ -135,41 +160,59 @@ public class ValueConversions { // slow path switch (wrap) { case OBJECT: - mh = IDENTITY; break; case VOID: - mh = IGNORE; break; - } - if (mh != null) { - cache.put(wrap, mh); - return mh; + throw new IllegalArgumentException("unbox "+wrap); } // look up the method String name = "unbox" + wrap.wrapperSimpleName(); - MethodType type = unboxType(wrap); + MethodType type = unboxType(wrap, kind); try { mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); } catch (ReflectiveOperationException ex) { mh = null; } if (mh != null) { - mh = MethodHandles.insertArguments(mh, 1, cast); - cache.put(wrap, mh); - return mh; + if (kind > 0) { + boolean cast = (kind != 2); + mh = MethodHandles.insertArguments(mh, 1, cast); + } + if (kind == 1) { // casting but exact (null -> zero) + mh = mh.asType(unboxType(wrap, 0)); + } + return cache.put(wrap, mh); } throw new IllegalArgumentException("cannot find unbox adapter for " + wrap - + (cast ? " (cast)" : "")); + + (kind <= 1 ? " (exact)" : kind == 3 ? " (cast)" : "")); } + /** Return an exact unboxer for the given primitive type. */ + public static MethodHandle unboxExact(Wrapper type) { + return unbox(type, 0); + } + + /** Return an exact unboxer for the given primitive type, with optional null-to-zero conversion. + * The boolean says whether to throw an NPE on a null value (false means unbox a zero). + * The type of the unboxer is of a form like (Integer)int. + */ + public static MethodHandle unboxExact(Wrapper type, boolean throwNPE) { + return unbox(type, throwNPE ? 0 : 1); + } + + /** Return a widening unboxer for the given primitive type. + * Widen narrower primitive boxes to the given type. + * Do not narrow any primitive values or convert null to zero. + * The type of the unboxer is of a form like (Object)int. + */ + public static MethodHandle unboxWiden(Wrapper type) { + return unbox(type, 2); + } + + /** Return a casting unboxer for the given primitive type. + * Widen or narrow primitive values to the given type, or convert null to zero, as needed. + * The type of the unboxer is of a form like (Object)int. + */ public static MethodHandle unboxCast(Wrapper type) { - return unbox(type, true); - } - - public static MethodHandle unbox(Class type) { - return unbox(Wrapper.forPrimitiveType(type), false); - } - - public static MethodHandle unboxCast(Class type) { - return unbox(Wrapper.forPrimitiveType(type), true); + return unbox(type, 3); } static private final Integer ZERO_INT = 0, ONE_INT = 1; @@ -266,57 +309,26 @@ public class ValueConversions { return MethodType.methodType(boxType, wrap.primitiveType()); } - private static final EnumMap[] - BOX_CONVERSIONS = newWrapperCaches(2); + private static final WrapperCache[] BOX_CONVERSIONS = newWrapperCaches(1); - private static MethodHandle box(Wrapper wrap, boolean exact) { - EnumMap cache = BOX_CONVERSIONS[(exact?1:0)]; + public static MethodHandle boxExact(Wrapper wrap) { + WrapperCache cache = BOX_CONVERSIONS[0]; MethodHandle mh = cache.get(wrap); if (mh != null) { return mh; } - // slow path - switch (wrap) { - case OBJECT: - mh = IDENTITY; break; - case VOID: - mh = ZERO_OBJECT; - break; - } - if (mh != null) { - cache.put(wrap, mh); - return mh; - } // look up the method String name = "box" + wrap.wrapperSimpleName(); MethodType type = boxType(wrap); - if (exact) { - try { - mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); - } catch (ReflectiveOperationException ex) { - mh = null; - } - } else { - mh = box(wrap, !exact).asType(type.erase()); + try { + mh = IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); + } catch (ReflectiveOperationException ex) { + mh = null; } if (mh != null) { - cache.put(wrap, mh); - return mh; + return cache.put(wrap, mh); } - throw new IllegalArgumentException("cannot find box adapter for " - + wrap + (exact ? " (exact)" : "")); - } - - public static MethodHandle box(Class type) { - boolean exact = false; - // e.g., boxShort(short)Short if exact, - // e.g., boxShort(short)Object if !exact - return box(Wrapper.forPrimitiveType(type), exact); - } - - public static MethodHandle box(Wrapper type) { - boolean exact = false; - return box(type, exact); + throw new IllegalArgumentException("cannot find box adapter for " + wrap); } /// Constant functions @@ -348,11 +360,10 @@ public class ValueConversions { return 0; } - private static final EnumMap[] - CONSTANT_FUNCTIONS = newWrapperCaches(2); + private static final WrapperCache[] CONSTANT_FUNCTIONS = newWrapperCaches(2); public static MethodHandle zeroConstantFunction(Wrapper wrap) { - EnumMap cache = CONSTANT_FUNCTIONS[0]; + WrapperCache cache = CONSTANT_FUNCTIONS[0]; MethodHandle mh = cache.get(wrap); if (mh != null) { return mh; @@ -373,184 +384,37 @@ public class ValueConversions { break; } if (mh != null) { - cache.put(wrap, mh); - return mh; + return cache.put(wrap, mh); } // use zeroInt and cast the result if (wrap.isSubwordOrInt() && wrap != Wrapper.INT) { mh = MethodHandles.explicitCastArguments(zeroConstantFunction(Wrapper.INT), type); - cache.put(wrap, mh); - return mh; + return cache.put(wrap, mh); } throw new IllegalArgumentException("cannot find zero constant for " + wrap); } - /// Converting references to references. - - /** - * Identity function. - * @param x an arbitrary reference value - * @return the same value x - */ - static T identity(T x) { - return x; - } - - static T[] identity(T[] x) { - return x; - } - - /** - * Identity function on ints. - * @param x an arbitrary int value - * @return the same value x - */ - static int identity(int x) { - return x; - } - - static byte identity(byte x) { - return x; - } - - static short identity(short x) { - return x; - } - - static boolean identity(boolean x) { - return x; - } - - static char identity(char x) { - return x; - } - - /** - * Identity function on longs. - * @param x an arbitrary long value - * @return the same value x - */ - static long identity(long x) { - return x; - } - - static float identity(float x) { - return x; - } - - static double identity(double x) { - return x; - } - - private static ClassCastException newClassCastException(Class t, Object obj) { - return new ClassCastException("Cannot cast " + obj.getClass().getName() + " to " + t.getName()); - } - - private static final MethodHandle IDENTITY, CAST_REFERENCE, ZERO_OBJECT, IGNORE, EMPTY, - ARRAY_IDENTITY, FILL_NEW_TYPED_ARRAY, FILL_NEW_ARRAY; + private static final MethodHandle CAST_REFERENCE, IGNORE, EMPTY; static { try { MethodType idType = MethodType.genericMethodType(1); MethodType ignoreType = idType.changeReturnType(void.class); - MethodType zeroObjectType = MethodType.genericMethodType(0); - IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", idType); CAST_REFERENCE = IMPL_LOOKUP.findVirtual(Class.class, "cast", idType); - ZERO_OBJECT = IMPL_LOOKUP.findStatic(THIS_CLASS, "zeroObject", zeroObjectType); IGNORE = IMPL_LOOKUP.findStatic(THIS_CLASS, "ignore", ignoreType); EMPTY = IMPL_LOOKUP.findStatic(THIS_CLASS, "empty", ignoreType.dropParameterTypes(0, 1)); - ARRAY_IDENTITY = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", MethodType.methodType(Object[].class, Object[].class)); - FILL_NEW_ARRAY = IMPL_LOOKUP - .findStatic(THIS_CLASS, "fillNewArray", - MethodType.methodType(Object[].class, Integer.class, Object[].class)); - FILL_NEW_TYPED_ARRAY = IMPL_LOOKUP - .findStatic(THIS_CLASS, "fillNewTypedArray", - MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class)); } catch (NoSuchMethodException | IllegalAccessException ex) { throw newInternalError("uncaught exception", ex); } } - // Varargs methods need to be in a separately initialized class, to avoid bootstrapping problems. - static class LazyStatics { - private static final MethodHandle COPY_AS_REFERENCE_ARRAY, COPY_AS_PRIMITIVE_ARRAY, MAKE_LIST; - static { - try { - //MAKE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeArray", MethodType.methodType(Object[].class, Object[].class)); - COPY_AS_REFERENCE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsReferenceArray", MethodType.methodType(Object[].class, Class.class, Object[].class)); - COPY_AS_PRIMITIVE_ARRAY = IMPL_LOOKUP.findStatic(THIS_CLASS, "copyAsPrimitiveArray", MethodType.methodType(Object.class, Wrapper.class, Object[].class)); - MAKE_LIST = IMPL_LOOKUP.findStatic(THIS_CLASS, "makeList", MethodType.methodType(List.class, Object[].class)); - } catch (ReflectiveOperationException ex) { - throw newInternalError("uncaught exception", ex); - } - } + public static MethodHandle ignore() { + return IGNORE; } - private static final EnumMap[] WRAPPER_CASTS - = newWrapperCaches(1); - - /** Return a method that casts its sole argument (an Object) to the given type - * and returns it as the given type. - */ - public static MethodHandle cast(Class type) { - return cast(type, CAST_REFERENCE); - } - public static MethodHandle cast(Class type, MethodHandle castReference) { - if (type.isPrimitive()) throw new IllegalArgumentException("cannot cast primitive type "+type); - MethodHandle mh; - Wrapper wrap = null; - EnumMap cache = null; - if (Wrapper.isWrapperType(type)) { - wrap = Wrapper.forWrapperType(type); - cache = WRAPPER_CASTS[0]; - mh = cache.get(wrap); - if (mh != null) return mh; - } - mh = MethodHandles.insertArguments(castReference, 0, type); - if (cache != null) - cache.put(wrap, mh); - return mh; - } - - public static MethodHandle identity() { - return IDENTITY; - } - - public static MethodHandle identity(Class type) { - if (!type.isPrimitive()) - // Reference identity has been moved into MethodHandles: - return MethodHandles.identity(type); - return identity(Wrapper.findPrimitiveType(type)); - } - - public static MethodHandle identity(Wrapper wrap) { - EnumMap cache = CONSTANT_FUNCTIONS[1]; - MethodHandle mh = cache.get(wrap); - if (mh != null) { - return mh; - } - // slow path - MethodType type = MethodType.methodType(wrap.primitiveType()); - if (wrap != Wrapper.VOID) - type = type.appendParameterTypes(wrap.primitiveType()); - try { - mh = IMPL_LOOKUP.findStatic(THIS_CLASS, "identity", type); - } catch (ReflectiveOperationException ex) { - mh = null; - } - if (mh == null && wrap == Wrapper.VOID) { - mh = EMPTY; // #(){} : #()void - } - if (mh != null) { - cache.put(wrap, mh); - return mh; - } - - if (mh != null) { - cache.put(wrap, mh); - return mh; - } - throw new IllegalArgumentException("cannot find identity for " + wrap); + /** Return a method that casts its second argument (an Object) to the given type (a Class). */ + public static MethodHandle cast() { + return CAST_REFERENCE; } /// Primitive conversions. @@ -759,11 +623,10 @@ public class ValueConversions { return (x ? (byte)1 : (byte)0); } - private static final EnumMap[] - CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length); + private static final WrapperCache[] CONVERT_PRIMITIVE_FUNCTIONS = newWrapperCaches(Wrapper.values().length); public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) { - EnumMap cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()]; + WrapperCache cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()]; MethodHandle mh = cache.get(wdst); if (mh != null) { return mh; @@ -771,17 +634,9 @@ public class ValueConversions { // slow path Class src = wsrc.primitiveType(); Class dst = wdst.primitiveType(); - MethodType type = src == void.class ? MethodType.methodType(dst) : MethodType.methodType(dst, src); + MethodType type = MethodType.methodType(dst, src); if (wsrc == wdst) { - mh = identity(src); - } else if (wsrc == Wrapper.VOID) { - mh = zeroConstantFunction(wdst); - } else if (wdst == Wrapper.VOID) { - mh = MethodHandles.dropArguments(EMPTY, 0, src); // Defer back to MethodHandles. - } else if (wsrc == Wrapper.OBJECT) { - mh = unboxCast(dst); - } else if (wdst == Wrapper.OBJECT) { - mh = box(src); + mh = MethodHandles.identity(src); } else { assert(src.isPrimitive() && dst.isPrimitive()); try { @@ -792,8 +647,7 @@ public class ValueConversions { } if (mh != null) { assert(mh.type() == type) : mh; - cache.put(wdst, mh); - return mh; + return cache.put(wdst, mh); } throw new IllegalArgumentException("cannot find primitive conversion function for " + @@ -808,363 +662,6 @@ public class ValueConversions { return Character.toUpperCase(x.charAt(0))+x.substring(1); } - /// Collection of multiple arguments. - - public static Object convertArrayElements(Class arrayType, Object array) { - Class src = array.getClass().getComponentType(); - Class dst = arrayType.getComponentType(); - if (src == null || dst == null) throw new IllegalArgumentException("not array type"); - Wrapper sw = (src.isPrimitive() ? Wrapper.forPrimitiveType(src) : null); - Wrapper dw = (dst.isPrimitive() ? Wrapper.forPrimitiveType(dst) : null); - int length; - if (sw == null) { - Object[] a = (Object[]) array; - length = a.length; - if (dw == null) - return Arrays.copyOf(a, length, arrayType.asSubclass(Object[].class)); - Object res = dw.makeArray(length); - dw.copyArrayUnboxing(a, 0, res, 0, length); - return res; - } - length = java.lang.reflect.Array.getLength(array); - Object[] res; - if (dw == null) { - res = Arrays.copyOf(NO_ARGS_ARRAY, length, arrayType.asSubclass(Object[].class)); - } else { - res = new Object[length]; - } - sw.copyArrayBoxing(array, 0, res, 0, length); - if (dw == null) return res; - Object a = dw.makeArray(length); - dw.copyArrayUnboxing(res, 0, a, 0, length); - return a; - } - - private static MethodHandle findCollector(String name, int nargs, Class rtype, Class... ptypes) { - MethodType type = MethodType.genericMethodType(nargs) - .changeReturnType(rtype) - .insertParameterTypes(0, ptypes); - try { - return IMPL_LOOKUP.findStatic(THIS_CLASS, name, type); - } catch (ReflectiveOperationException ex) { - return null; - } - } - - private static final Object[] NO_ARGS_ARRAY = {}; - private static Object[] makeArray(Object... args) { return args; } - private static Object[] array() { return NO_ARGS_ARRAY; } - private static Object[] array(Object a0) - { return makeArray(a0); } - private static Object[] array(Object a0, Object a1) - { return makeArray(a0, a1); } - private static Object[] array(Object a0, Object a1, Object a2) - { return makeArray(a0, a1, a2); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3) - { return makeArray(a0, a1, a2, a3); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4) - { return makeArray(a0, a1, a2, a3, a4); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5) - { return makeArray(a0, a1, a2, a3, a4, a5); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6) - { return makeArray(a0, a1, a2, a3, a4, a5, a6); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7) - { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8) - { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); } - private static Object[] array(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8, Object a9) - { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } - private static MethodHandle[] makeArrays() { - ArrayList mhs = new ArrayList<>(); - for (;;) { - MethodHandle mh = findCollector("array", mhs.size(), Object[].class); - if (mh == null) break; - mhs.add(mh); - } - assert(mhs.size() == 11); // current number of methods - return mhs.toArray(new MethodHandle[MAX_ARITY+1]); - } - private static final MethodHandle[] ARRAYS = makeArrays(); - - // filling versions of the above: - // using Integer len instead of int len and no varargs to avoid bootstrapping problems - private static Object[] fillNewArray(Integer len, Object[] /*not ...*/ args) { - Object[] a = new Object[len]; - fillWithArguments(a, 0, args); - return a; - } - private static Object[] fillNewTypedArray(Object[] example, Integer len, Object[] /*not ...*/ args) { - Object[] a = Arrays.copyOf(example, len); - fillWithArguments(a, 0, args); - return a; - } - private static void fillWithArguments(Object[] a, int pos, Object... args) { - System.arraycopy(args, 0, a, pos, args.length); - } - // using Integer pos instead of int pos to avoid bootstrapping problems - private static Object[] fillArray(Integer pos, Object[] a, Object a0) - { fillWithArguments(a, pos, a0); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1) - { fillWithArguments(a, pos, a0, a1); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2) - { fillWithArguments(a, pos, a0, a1, a2); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3) - { fillWithArguments(a, pos, a0, a1, a2, a3); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8); return a; } - private static Object[] fillArray(Integer pos, Object[] a, Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8, Object a9) - { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; } - private static MethodHandle[] makeFillArrays() { - ArrayList mhs = new ArrayList<>(); - mhs.add(null); // there is no empty fill; at least a0 is required - for (;;) { - MethodHandle mh = findCollector("fillArray", mhs.size(), Object[].class, Integer.class, Object[].class); - if (mh == null) break; - mhs.add(mh); - } - assert(mhs.size() == 11); // current number of methods - return mhs.toArray(new MethodHandle[0]); - } - private static final MethodHandle[] FILL_ARRAYS = makeFillArrays(); - - private static Object[] copyAsReferenceArray(Class arrayType, Object... a) { - return Arrays.copyOf(a, a.length, arrayType); - } - private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) { - Object a = w.makeArray(boxes.length); - w.copyArrayUnboxing(boxes, 0, a, 0, boxes.length); - return a; - } - - /** Return a method handle that takes the indicated number of Object - * arguments and returns an Object array of them, as if for varargs. - */ - public static MethodHandle varargsArray(int nargs) { - MethodHandle mh = ARRAYS[nargs]; - if (mh != null) return mh; - mh = findCollector("array", nargs, Object[].class); - if (mh != null) return ARRAYS[nargs] = mh; - mh = buildVarargsArray(FILL_NEW_ARRAY, ARRAY_IDENTITY, nargs); - assert(assertCorrectArity(mh, nargs)); - return ARRAYS[nargs] = mh; - } - - private static boolean assertCorrectArity(MethodHandle mh, int arity) { - assert(mh.type().parameterCount() == arity) : "arity != "+arity+": "+mh; - return true; - } - - private static MethodHandle buildVarargsArray(MethodHandle newArray, MethodHandle finisher, int nargs) { - // Build up the result mh as a sequence of fills like this: - // finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23)) - // The various fill(_,10*I,___*[J]) are reusable. - int leftLen = Math.min(nargs, LEFT_ARGS); // absorb some arguments immediately - int rightLen = nargs - leftLen; - MethodHandle leftCollector = newArray.bindTo(nargs); - leftCollector = leftCollector.asCollector(Object[].class, leftLen); - MethodHandle mh = finisher; - if (rightLen > 0) { - MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen); - if (mh == ARRAY_IDENTITY) - mh = rightFiller; - else - mh = MethodHandles.collectArguments(mh, 0, rightFiller); - } - if (mh == ARRAY_IDENTITY) - mh = leftCollector; - else - mh = MethodHandles.collectArguments(mh, 0, leftCollector); - return mh; - } - - private static final int LEFT_ARGS = (FILL_ARRAYS.length - 1); - private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1]; - /** fill_array_to_right(N).invoke(a, argL..arg[N-1]) - * fills a[L]..a[N-1] with corresponding arguments, - * and then returns a. The value L is a global constant (LEFT_ARGS). - */ - private static MethodHandle fillToRight(int nargs) { - MethodHandle filler = FILL_ARRAY_TO_RIGHT[nargs]; - if (filler != null) return filler; - filler = buildFiller(nargs); - assert(assertCorrectArity(filler, nargs - LEFT_ARGS + 1)); - return FILL_ARRAY_TO_RIGHT[nargs] = filler; - } - private static MethodHandle buildFiller(int nargs) { - if (nargs <= LEFT_ARGS) - return ARRAY_IDENTITY; // no args to fill; return the array unchanged - // we need room for both mh and a in mh.invoke(a, arg*[nargs]) - final int CHUNK = LEFT_ARGS; - int rightLen = nargs % CHUNK; - int midLen = nargs - rightLen; - if (rightLen == 0) { - midLen = nargs - (rightLen = CHUNK); - if (FILL_ARRAY_TO_RIGHT[midLen] == null) { - // build some precursors from left to right - for (int j = LEFT_ARGS % CHUNK; j < midLen; j += CHUNK) - if (j > LEFT_ARGS) fillToRight(j); - } - } - if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS); - assert(rightLen > 0); - MethodHandle midFill = fillToRight(midLen); // recursive fill - MethodHandle rightFill = FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1] - assert(midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS); - assert(rightFill.type().parameterCount() == 1 + rightLen); - - // Combine the two fills: - // right(mid(a, x10..x19), x20..x23) - // The final product will look like this: - // right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23) - if (midLen == LEFT_ARGS) - return rightFill; - else - return MethodHandles.collectArguments(rightFill, 0, midFill); - } - - // Type-polymorphic version of varargs maker. - private static final ClassValue TYPED_COLLECTORS - = new ClassValue() { - @Override - protected MethodHandle[] computeValue(Class type) { - return new MethodHandle[256]; - } - }; - - static final int MAX_JVM_ARITY = 255; // limit imposed by the JVM - - /** Return a method handle that takes the indicated number of - * typed arguments and returns an array of them. - * The type argument is the array type. - */ - public static MethodHandle varargsArray(Class arrayType, int nargs) { - Class elemType = arrayType.getComponentType(); - if (elemType == null) throw new IllegalArgumentException("not an array: "+arrayType); - // FIXME: Need more special casing and caching here. - if (nargs >= MAX_JVM_ARITY/2 - 1) { - int slots = nargs; - final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH - if (arrayType == double[].class || arrayType == long[].class) - slots *= 2; - if (slots > MAX_ARRAY_SLOTS) - throw new IllegalArgumentException("too many arguments: "+arrayType.getSimpleName()+", length "+nargs); - } - if (elemType == Object.class) - return varargsArray(nargs); - // other cases: primitive arrays, subtypes of Object[] - MethodHandle cache[] = TYPED_COLLECTORS.get(elemType); - MethodHandle mh = nargs < cache.length ? cache[nargs] : null; - if (mh != null) return mh; - if (elemType.isPrimitive()) { - MethodHandle builder = FILL_NEW_ARRAY; - MethodHandle producer = buildArrayProducer(arrayType); - mh = buildVarargsArray(builder, producer, nargs); - } else { - @SuppressWarnings("unchecked") - Class objArrayType = (Class) arrayType; - Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType); - MethodHandle builder = FILL_NEW_TYPED_ARRAY.bindTo(example); - MethodHandle producer = ARRAY_IDENTITY; - mh = buildVarargsArray(builder, producer, nargs); - } - mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); - assert(assertCorrectArity(mh, nargs)); - if (nargs < cache.length) - cache[nargs] = mh; - return mh; - } - - private static MethodHandle buildArrayProducer(Class arrayType) { - Class elemType = arrayType.getComponentType(); - if (elemType.isPrimitive()) - return LazyStatics.COPY_AS_PRIMITIVE_ARRAY.bindTo(Wrapper.forPrimitiveType(elemType)); - else - return LazyStatics.COPY_AS_REFERENCE_ARRAY.bindTo(arrayType); - } - - // List version of varargs maker. - - private static final List NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY); - private static List makeList(Object... args) { return Arrays.asList(args); } - private static List list() { return NO_ARGS_LIST; } - private static List list(Object a0) - { return makeList(a0); } - private static List list(Object a0, Object a1) - { return makeList(a0, a1); } - private static List list(Object a0, Object a1, Object a2) - { return makeList(a0, a1, a2); } - private static List list(Object a0, Object a1, Object a2, Object a3) - { return makeList(a0, a1, a2, a3); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4) - { return makeList(a0, a1, a2, a3, a4); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5) - { return makeList(a0, a1, a2, a3, a4, a5); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6) - { return makeList(a0, a1, a2, a3, a4, a5, a6); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7) - { return makeList(a0, a1, a2, a3, a4, a5, a6, a7); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8) - { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8); } - private static List list(Object a0, Object a1, Object a2, Object a3, - Object a4, Object a5, Object a6, Object a7, - Object a8, Object a9) - { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } - private static MethodHandle[] makeLists() { - ArrayList mhs = new ArrayList<>(); - for (;;) { - MethodHandle mh = findCollector("list", mhs.size(), List.class); - if (mh == null) break; - mhs.add(mh); - } - assert(mhs.size() == 11); // current number of methods - return mhs.toArray(new MethodHandle[MAX_ARITY+1]); - } - private static final MethodHandle[] LISTS = makeLists(); - - /** Return a method handle that takes the indicated number of Object - * arguments and returns a List. - */ - public static MethodHandle varargsList(int nargs) { - MethodHandle mh = LISTS[nargs]; - if (mh != null) return mh; - mh = findCollector("list", nargs, List.class); - if (mh != null) return LISTS[nargs] = mh; - return LISTS[nargs] = buildVarargsList(nargs); - } - private static MethodHandle buildVarargsList(int nargs) { - return MethodHandles.filterReturnValue(varargsArray(nargs), LazyStatics.MAKE_LIST); - } - // handy shared exception makers (they simplify the common case code) private static InternalError newInternalError(String message, Throwable cause) { return new InternalError(message, cause); diff --git a/jdk/src/java.base/share/classes/sun/invoke/util/VerifyType.java b/jdk/src/java.base/share/classes/sun/invoke/util/VerifyType.java index 52b32c40e89..ce37b88c76d 100644 --- a/jdk/src/java.base/share/classes/sun/invoke/util/VerifyType.java +++ b/jdk/src/java.base/share/classes/sun/invoke/util/VerifyType.java @@ -40,18 +40,38 @@ public class VerifyType { /** * True if a value can be stacked as the source type and unstacked as the * destination type, without violating the JVM's type consistency. + *

+ * If both types are references, we apply the verifier's subclass check + * (or subtyping, if keepInterfaces). + * If the src type is a type guaranteed to be null (Void) it can be converted + * to any other reference type. + *

+ * If both types are primitives, we apply the verifier's primitive conversions. + * These do not include Java conversions such as long to double, since those + * require computation and (in general) stack depth changes. + * But very simple 32-bit viewing changes, such as byte to int, + * are null conversions, because they do not require any computation. + * These conversions are from any type to a wider type up to 32 bits, + * as long as the conversion is not signed to unsigned (byte to char). + *

+ * The primitive type 'void' does not interconvert with any other type, + * even though it is legal to drop any type from the stack and "return void". + * The stack effects, though are different between void and any other type, + * so it is safer to report a non-trivial conversion. * * @param src the type of a stacked value * @param dst the type by which we'd like to treat it + * @param keepInterfaces if false, we treat any interface as if it were Object * @return whether the retyping can be done without motion or reformatting */ - public static boolean isNullConversion(Class src, Class dst) { + public static boolean isNullConversion(Class src, Class dst, boolean keepInterfaces) { if (src == dst) return true; // Verifier allows any interface to be treated as Object: - if (dst.isInterface()) dst = Object.class; - if (src.isInterface()) src = Object.class; - if (src == dst) return true; // check again - if (dst == void.class) return true; // drop any return value + if (!keepInterfaces) { + if (dst.isInterface()) dst = Object.class; + if (src.isInterface()) src = Object.class; + if (src == dst) return true; // check again + } if (isNullType(src)) return !dst.isPrimitive(); if (!src.isPrimitive()) return dst.isAssignableFrom(src); if (!dst.isPrimitive()) return false; @@ -82,25 +102,13 @@ public class VerifyType { * Is the given type java.lang.Null or an equivalent null-only type? */ public static boolean isNullType(Class type) { - if (type == null) return false; - return type == NULL_CLASS - // This one may also be used as a null type. - // TO DO: Decide if we really want to legitimize it here. - // Probably we do, unless java.lang.Null really makes it into Java 7 - //|| type == Void.class - // Locally known null-only class: - || type == Empty.class - ; - } - private static final Class NULL_CLASS; - static { - Class nullClass = null; - try { - nullClass = Class.forName("java.lang.Null"); - } catch (ClassNotFoundException ex) { - // OK, we'll cope - } - NULL_CLASS = nullClass; + // Any reference statically typed as Void is guaranteed to be null. + // Therefore, it can be safely treated as a value of any + // other type that admits null, i.e., a reference type. + if (type == Void.class) return true; + // Locally known null-only class: + if (type == Empty.class) return true; + return false; } /** @@ -111,14 +119,14 @@ public class VerifyType { * @param recv the type of the method handle receiving the call * @return whether the retyping can be done without motion or reformatting */ - public static boolean isNullConversion(MethodType call, MethodType recv) { + public static boolean isNullConversion(MethodType call, MethodType recv, boolean keepInterfaces) { if (call == recv) return true; int len = call.parameterCount(); if (len != recv.parameterCount()) return false; for (int i = 0; i < len; i++) - if (!isNullConversion(call.parameterType(i), recv.parameterType(i))) + if (!isNullConversion(call.parameterType(i), recv.parameterType(i), keepInterfaces)) return false; - return isNullConversion(recv.returnType(), call.returnType()); + return isNullConversion(recv.returnType(), call.returnType(), keepInterfaces); } /** diff --git a/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java b/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java index 6c4be456aee..2732e28c8ed 100644 --- a/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java +++ b/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java @@ -230,14 +230,6 @@ public enum Wrapper { */ public T zero(Class type) { return convert(zero, type); } -// /** Produce a wrapper for the given wrapper or primitive type. */ -// public static Wrapper valueOf(Class type) { -// if (isPrimitiveType(type)) -// return forPrimitiveType(type); -// else -// return forWrapperType(type); -// } - /** Return the wrapper that wraps values of the given type. * The type may be {@code Object}, meaning the {@code OBJECT} wrapper. * Otherwise, the type must be a primitive. diff --git a/jdk/src/java.base/share/classes/sun/reflect/AccessorGenerator.java b/jdk/src/java.base/share/classes/sun/reflect/AccessorGenerator.java index 7f1ae50b126..96f64ec1847 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/AccessorGenerator.java +++ b/jdk/src/java.base/share/classes/sun/reflect/AccessorGenerator.java @@ -69,33 +69,34 @@ class AccessorGenerator implements ClassFileConstants { protected short codeIdx; protected short exceptionsIdx; // Boxing + protected short valueOfIdx; protected short booleanIdx; - protected short booleanCtorIdx; + protected short booleanBoxIdx; protected short booleanUnboxIdx; protected short byteIdx; - protected short byteCtorIdx; + protected short byteBoxIdx; protected short byteUnboxIdx; protected short characterIdx; - protected short characterCtorIdx; + protected short characterBoxIdx; protected short characterUnboxIdx; protected short doubleIdx; - protected short doubleCtorIdx; + protected short doubleBoxIdx; protected short doubleUnboxIdx; protected short floatIdx; - protected short floatCtorIdx; + protected short floatBoxIdx; protected short floatUnboxIdx; protected short integerIdx; - protected short integerCtorIdx; + protected short integerBoxIdx; protected short integerUnboxIdx; protected short longIdx; - protected short longCtorIdx; + protected short longBoxIdx; protected short longUnboxIdx; protected short shortIdx; - protected short shortCtorIdx; + protected short shortBoxIdx; protected short shortUnboxIdx; protected final short NUM_COMMON_CPOOL_ENTRIES = (short) 30; - protected final short NUM_BOXING_CPOOL_ENTRIES = (short) 72; + protected final short NUM_BOXING_CPOOL_ENTRIES = (short) 73; // Requires that superClass has been set up protected void emitCommonConstantPoolEntries() { @@ -181,9 +182,10 @@ class AccessorGenerator implements ClassFileConstants { /** Constant pool entries required to be able to box/unbox primitive types. Note that we don't emit these if we don't need them. */ protected void emitBoxingContantPoolEntries() { + // * [UTF-8] "valueOf" // * [UTF-8] "java/lang/Boolean" // * [CONSTANT_Class_info] for above - // * [UTF-8] "(Z)V" + // * [UTF-8] "(Z)Ljava/lang/Boolean;" // * [CONSTANT_NameAndType_info] for above // * [CONSTANT_Methodref_info] for above // * [UTF-8] "booleanValue" @@ -192,7 +194,7 @@ class AccessorGenerator implements ClassFileConstants { // * [CONSTANT_Methodref_info] for above // * [UTF-8] "java/lang/Byte" // * [CONSTANT_Class_info] for above - // * [UTF-8] "(B)V" + // * [UTF-8] "(B)Ljava/lang/Byte;" // * [CONSTANT_NameAndType_info] for above // * [CONSTANT_Methodref_info] for above // * [UTF-8] "byteValue" @@ -201,7 +203,7 @@ class AccessorGenerator implements ClassFileConstants { // * [CONSTANT_Methodref_info] for above // * [UTF-8] "java/lang/Character" // * [CONSTANT_Class_info] for above - // * [UTF-8] "(C)V" + // * [UTF-8] "(C)Ljava/lang/Character;" // * [CONSTANT_NameAndType_info] for above // * [CONSTANT_Methodref_info] for above // * [UTF-8] "charValue" @@ -210,7 +212,7 @@ class AccessorGenerator implements ClassFileConstants { // * [CONSTANT_Methodref_info] for above // * [UTF-8] "java/lang/Double" // * [CONSTANT_Class_info] for above - // * [UTF-8] "(D)V" + // * [UTF-8] "(D)Ljava/lang/Double;" // * [CONSTANT_NameAndType_info] for above // * [CONSTANT_Methodref_info] for above // * [UTF-8] "doubleValue" @@ -219,7 +221,7 @@ class AccessorGenerator implements ClassFileConstants { // * [CONSTANT_Methodref_info] for above // * [UTF-8] "java/lang/Float" // * [CONSTANT_Class_info] for above - // * [UTF-8] "(F)V" + // * [UTF-8] "(F)Ljava/lang/Float;" // * [CONSTANT_NameAndType_info] for above // * [CONSTANT_Methodref_info] for above // * [UTF-8] "floatValue" @@ -228,7 +230,7 @@ class AccessorGenerator implements ClassFileConstants { // * [CONSTANT_Methodref_info] for above // * [UTF-8] "java/lang/Integer" // * [CONSTANT_Class_info] for above - // * [UTF-8] "(I)V" + // * [UTF-8] "(I)Ljava/lang/Integer;" // * [CONSTANT_NameAndType_info] for above // * [CONSTANT_Methodref_info] for above // * [UTF-8] "intValue" @@ -237,7 +239,7 @@ class AccessorGenerator implements ClassFileConstants { // * [CONSTANT_Methodref_info] for above // * [UTF-8] "java/lang/Long" // * [CONSTANT_Class_info] for above - // * [UTF-8] "(J)V" + // * [UTF-8] "(J)Ljava/lang/Long;" // * [CONSTANT_NameAndType_info] for above // * [CONSTANT_Methodref_info] for above // * [UTF-8] "longValue" @@ -246,21 +248,26 @@ class AccessorGenerator implements ClassFileConstants { // * [CONSTANT_Methodref_info] for above // * [UTF-8] "java/lang/Short" // * [CONSTANT_Class_info] for above - // * [UTF-8] "(S)V" + // * [UTF-8] "(S)Ljava/lang/Short;" // * [CONSTANT_NameAndType_info] for above // * [CONSTANT_Methodref_info] for above // * [UTF-8] "shortValue" // * [UTF-8] "()S" // * [CONSTANT_NameAndType_info] for above // * [CONSTANT_Methodref_info] for above + + // valueOf-method name + asm.emitConstantPoolUTF8("valueOf"); + valueOfIdx = asm.cpi(); + // Boolean asm.emitConstantPoolUTF8("java/lang/Boolean"); asm.emitConstantPoolClass(asm.cpi()); booleanIdx = asm.cpi(); - asm.emitConstantPoolUTF8("(Z)V"); - asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolUTF8("(Z)Ljava/lang/Boolean;"); + asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi()); asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); - booleanCtorIdx = asm.cpi(); + booleanBoxIdx = asm.cpi(); asm.emitConstantPoolUTF8("booleanValue"); asm.emitConstantPoolUTF8("()Z"); asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); @@ -271,10 +278,10 @@ class AccessorGenerator implements ClassFileConstants { asm.emitConstantPoolUTF8("java/lang/Byte"); asm.emitConstantPoolClass(asm.cpi()); byteIdx = asm.cpi(); - asm.emitConstantPoolUTF8("(B)V"); - asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolUTF8("(B)Ljava/lang/Byte;"); + asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi()); asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); - byteCtorIdx = asm.cpi(); + byteBoxIdx = asm.cpi(); asm.emitConstantPoolUTF8("byteValue"); asm.emitConstantPoolUTF8("()B"); asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); @@ -285,10 +292,10 @@ class AccessorGenerator implements ClassFileConstants { asm.emitConstantPoolUTF8("java/lang/Character"); asm.emitConstantPoolClass(asm.cpi()); characterIdx = asm.cpi(); - asm.emitConstantPoolUTF8("(C)V"); - asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolUTF8("(C)Ljava/lang/Character;"); + asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi()); asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); - characterCtorIdx = asm.cpi(); + characterBoxIdx = asm.cpi(); asm.emitConstantPoolUTF8("charValue"); asm.emitConstantPoolUTF8("()C"); asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); @@ -299,10 +306,10 @@ class AccessorGenerator implements ClassFileConstants { asm.emitConstantPoolUTF8("java/lang/Double"); asm.emitConstantPoolClass(asm.cpi()); doubleIdx = asm.cpi(); - asm.emitConstantPoolUTF8("(D)V"); - asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolUTF8("(D)Ljava/lang/Double;"); + asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi()); asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); - doubleCtorIdx = asm.cpi(); + doubleBoxIdx = asm.cpi(); asm.emitConstantPoolUTF8("doubleValue"); asm.emitConstantPoolUTF8("()D"); asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); @@ -313,10 +320,10 @@ class AccessorGenerator implements ClassFileConstants { asm.emitConstantPoolUTF8("java/lang/Float"); asm.emitConstantPoolClass(asm.cpi()); floatIdx = asm.cpi(); - asm.emitConstantPoolUTF8("(F)V"); - asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolUTF8("(F)Ljava/lang/Float;"); + asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi()); asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); - floatCtorIdx = asm.cpi(); + floatBoxIdx = asm.cpi(); asm.emitConstantPoolUTF8("floatValue"); asm.emitConstantPoolUTF8("()F"); asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); @@ -327,10 +334,10 @@ class AccessorGenerator implements ClassFileConstants { asm.emitConstantPoolUTF8("java/lang/Integer"); asm.emitConstantPoolClass(asm.cpi()); integerIdx = asm.cpi(); - asm.emitConstantPoolUTF8("(I)V"); - asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolUTF8("(I)Ljava/lang/Integer;"); + asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi()); asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); - integerCtorIdx = asm.cpi(); + integerBoxIdx = asm.cpi(); asm.emitConstantPoolUTF8("intValue"); asm.emitConstantPoolUTF8("()I"); asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); @@ -341,10 +348,10 @@ class AccessorGenerator implements ClassFileConstants { asm.emitConstantPoolUTF8("java/lang/Long"); asm.emitConstantPoolClass(asm.cpi()); longIdx = asm.cpi(); - asm.emitConstantPoolUTF8("(J)V"); - asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolUTF8("(J)Ljava/lang/Long;"); + asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi()); asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); - longCtorIdx = asm.cpi(); + longBoxIdx = asm.cpi(); asm.emitConstantPoolUTF8("longValue"); asm.emitConstantPoolUTF8("()J"); asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); @@ -355,10 +362,10 @@ class AccessorGenerator implements ClassFileConstants { asm.emitConstantPoolUTF8("java/lang/Short"); asm.emitConstantPoolClass(asm.cpi()); shortIdx = asm.cpi(); - asm.emitConstantPoolUTF8("(S)V"); - asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolUTF8("(S)Ljava/lang/Short;"); + asm.emitConstantPoolNameAndType(valueOfIdx, asm.cpi()); asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); - shortCtorIdx = asm.cpi(); + shortBoxIdx = asm.cpi(); asm.emitConstantPoolUTF8("shortValue"); asm.emitConstantPoolUTF8("()S"); asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); @@ -515,23 +522,23 @@ class AccessorGenerator implements ClassFileConstants { throw new InternalError("Should have found primitive type"); } - protected short ctorIndexForPrimitiveType(Class type) { + protected short boxingMethodForPrimitiveType(Class type) { if (type == Boolean.TYPE) { - return booleanCtorIdx; + return booleanBoxIdx; } else if (type == Byte.TYPE) { - return byteCtorIdx; + return byteBoxIdx; } else if (type == Character.TYPE) { - return characterCtorIdx; + return characterBoxIdx; } else if (type == Double.TYPE) { - return doubleCtorIdx; + return doubleBoxIdx; } else if (type == Float.TYPE) { - return floatCtorIdx; + return floatBoxIdx; } else if (type == Integer.TYPE) { - return integerCtorIdx; + return integerBoxIdx; } else if (type == Long.TYPE) { - return longCtorIdx; + return longBoxIdx; } else if (type == Short.TYPE) { - return shortCtorIdx; + return shortBoxIdx; } throw new InternalError("Should have found primitive type"); } diff --git a/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java b/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java index 55a71c84248..dfecd17423e 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java +++ b/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java @@ -660,9 +660,9 @@ class MethodAccessorGenerator extends AccessorGenerator { if (!isConstructor) { // Box return value if necessary if (isPrimitive(returnType)) { - cb.opc_invokespecial(ctorIndexForPrimitiveType(returnType), - typeSizeInStackSlots(returnType), - 0); + cb.opc_invokestatic(boxingMethodForPrimitiveType(returnType), + typeSizeInStackSlots(returnType), + 0); } else if (returnType == Void.TYPE) { cb.opc_aconst_null(); } diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeBooleanFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeBooleanFieldAccessorImpl.java index 8c7cdd451b5..cf929b762a9 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeBooleanFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeBooleanFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeBooleanFieldAccessorImpl extends UnsafeFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Boolean(getBoolean(obj)); + return Boolean.valueOf(getBoolean(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeByteFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeByteFieldAccessorImpl.java index 8095fc299e3..15564220ecf 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeByteFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeByteFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeByteFieldAccessorImpl extends UnsafeFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Byte(getByte(obj)); + return Byte.valueOf(getByte(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeCharacterFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeCharacterFieldAccessorImpl.java index bdf83bc27c1..bd27f00b1bd 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeCharacterFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeCharacterFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeCharacterFieldAccessorImpl extends UnsafeFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Character(getChar(obj)); + return Character.valueOf(getChar(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeDoubleFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeDoubleFieldAccessorImpl.java index df43f5967a6..a43ac29368b 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeDoubleFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeDoubleFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeDoubleFieldAccessorImpl extends UnsafeFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Double(getDouble(obj)); + return Double.valueOf(getDouble(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeFloatFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeFloatFieldAccessorImpl.java index ca7789ac666..d2410987999 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeFloatFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeFloatFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeFloatFieldAccessorImpl extends UnsafeFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Float(getFloat(obj)); + return Float.valueOf(getFloat(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeIntegerFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeIntegerFieldAccessorImpl.java index 951a2202047..df802e3b2ad 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeIntegerFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeIntegerFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeIntegerFieldAccessorImpl extends UnsafeFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Integer(getInt(obj)); + return Integer.valueOf(getInt(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeLongFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeLongFieldAccessorImpl.java index 85fcf32847a..8e76ea193fe 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeLongFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeLongFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeLongFieldAccessorImpl extends UnsafeFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Long(getLong(obj)); + return Long.valueOf(getLong(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedBooleanFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedBooleanFieldAccessorImpl.java index 8e66c7fe0f1..cc31cb4910d 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedBooleanFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedBooleanFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedBooleanFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Boolean(getBoolean(obj)); + return Boolean.valueOf(getBoolean(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedByteFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedByteFieldAccessorImpl.java index f3cc92808e5..ae239a807f4 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedByteFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedByteFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedByteFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Byte(getByte(obj)); + return Byte.valueOf(getByte(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedCharacterFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedCharacterFieldAccessorImpl.java index 247e3b231e5..e322e1d3e04 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedCharacterFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedCharacterFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedCharacterFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Character(getChar(obj)); + return Character.valueOf(getChar(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedDoubleFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedDoubleFieldAccessorImpl.java index c7b2ae1f258..1fb71f5c447 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedDoubleFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedDoubleFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedDoubleFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Double(getDouble(obj)); + return Double.valueOf(getDouble(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedFloatFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedFloatFieldAccessorImpl.java index c06af79e501..0b7906d4830 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedFloatFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedFloatFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedFloatFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Float(getFloat(obj)); + return Float.valueOf(getFloat(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedIntegerFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedIntegerFieldAccessorImpl.java index b675158352d..df737fd0119 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedIntegerFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedIntegerFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedIntegerFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Integer(getInt(obj)); + return Integer.valueOf(getInt(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedLongFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedLongFieldAccessorImpl.java index 50fa38dcdd0..b77d4d1d9e9 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedLongFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedLongFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedLongFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Long(getLong(obj)); + return Long.valueOf(getLong(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedShortFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedShortFieldAccessorImpl.java index fddfef24f5c..c5ea9bb6caa 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedShortFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedShortFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedShortFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Short(getShort(obj)); + return Short.valueOf(getShort(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticBooleanFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticBooleanFieldAccessorImpl.java index 5b1e3f3ee8c..65dabb0fba6 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticBooleanFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticBooleanFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedStaticBooleanFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Boolean(getBoolean(obj)); + return Boolean.valueOf(getBoolean(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticByteFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticByteFieldAccessorImpl.java index a3e087e3469..136728b4594 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticByteFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticByteFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedStaticByteFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Byte(getByte(obj)); + return Byte.valueOf(getByte(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticCharacterFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticCharacterFieldAccessorImpl.java index c171064ec67..6015baf82a8 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticCharacterFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticCharacterFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedStaticCharacterFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Character(getChar(obj)); + return Character.valueOf(getChar(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticDoubleFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticDoubleFieldAccessorImpl.java index 4acd9f5221b..5433b4ec82a 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticDoubleFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticDoubleFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedStaticDoubleFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Double(getDouble(obj)); + return Double.valueOf(getDouble(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticFloatFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticFloatFieldAccessorImpl.java index 86eb74e89de..fe69684c1d8 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticFloatFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticFloatFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedStaticFloatFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Float(getFloat(obj)); + return Float.valueOf(getFloat(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticIntegerFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticIntegerFieldAccessorImpl.java index 7ddcd8c637c..3e420f92a39 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticIntegerFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticIntegerFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedStaticIntegerFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Integer(getInt(obj)); + return Integer.valueOf(getInt(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticLongFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticLongFieldAccessorImpl.java index 97da2f256c8..d25f4ac8a56 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticLongFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticLongFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedStaticLongFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Long(getLong(obj)); + return Long.valueOf(getLong(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticShortFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticShortFieldAccessorImpl.java index 4caf4d1da00..631bd7ae333 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticShortFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeQualifiedStaticShortFieldAccessorImpl.java @@ -35,7 +35,7 @@ class UnsafeQualifiedStaticShortFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Short(getShort(obj)); + return Short.valueOf(getShort(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeShortFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeShortFieldAccessorImpl.java index d8fe8dc96ec..05ea337f49c 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeShortFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeShortFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeShortFieldAccessorImpl extends UnsafeFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Short(getShort(obj)); + return Short.valueOf(getShort(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticBooleanFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticBooleanFieldAccessorImpl.java index 741c5ff48c2..b7572b54e31 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticBooleanFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticBooleanFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeStaticBooleanFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Boolean(getBoolean(obj)); + return Boolean.valueOf(getBoolean(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticByteFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticByteFieldAccessorImpl.java index 726961ba8a2..78eeb86842f 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticByteFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticByteFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeStaticByteFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Byte(getByte(obj)); + return Byte.valueOf(getByte(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticCharacterFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticCharacterFieldAccessorImpl.java index 29b030375a7..2dd3a597681 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticCharacterFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticCharacterFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeStaticCharacterFieldAccessorImpl extends UnsafeStaticFieldAccessorIm } public Object get(Object obj) throws IllegalArgumentException { - return new Character(getChar(obj)); + return Character.valueOf(getChar(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticDoubleFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticDoubleFieldAccessorImpl.java index 34c9784466b..b07df4f987f 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticDoubleFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticDoubleFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeStaticDoubleFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Double(getDouble(obj)); + return Double.valueOf(getDouble(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticFloatFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticFloatFieldAccessorImpl.java index 780a794cf75..2929727b6ad 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticFloatFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticFloatFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeStaticFloatFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Float(getFloat(obj)); + return Float.valueOf(getFloat(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticIntegerFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticIntegerFieldAccessorImpl.java index 1b21b766d09..963620a06fb 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticIntegerFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticIntegerFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeStaticIntegerFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl } public Object get(Object obj) throws IllegalArgumentException { - return new Integer(getInt(obj)); + return Integer.valueOf(getInt(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticLongFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticLongFieldAccessorImpl.java index 7d50ac2db9c..a8dedd060cc 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticLongFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticLongFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeStaticLongFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Long(getLong(obj)); + return Long.valueOf(getLong(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticShortFieldAccessorImpl.java b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticShortFieldAccessorImpl.java index d56d621e227..9ac6cd6533b 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticShortFieldAccessorImpl.java +++ b/jdk/src/java.base/share/classes/sun/reflect/UnsafeStaticShortFieldAccessorImpl.java @@ -33,7 +33,7 @@ class UnsafeStaticShortFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { } public Object get(Object obj) throws IllegalArgumentException { - return new Short(getShort(obj)); + return Short.valueOf(getShort(obj)); } public boolean getBoolean(Object obj) throws IllegalArgumentException { diff --git a/jdk/src/java.base/windows/native/libnet/NetworkInterface.c b/jdk/src/java.base/windows/native/libnet/NetworkInterface.c index 8ba5eda3c1f..56d822ddb8f 100644 --- a/jdk/src/java.base/windows/native/libnet/NetworkInterface.c +++ b/jdk/src/java.base/windows/native/libnet/NetworkInterface.c @@ -990,9 +990,11 @@ JNIEXPORT jbyteArray JNICALL Java_java_net_NetworkInterface_getMacAddr0 case MIB_IF_TYPE_FDDI: case IF_TYPE_IEEE80211: len = ifRowP->dwPhysAddrLen; - ret = (*env)->NewByteArray(env, len); - if (!IS_NULL(ret)) { - (*env)->SetByteArrayRegion(env, ret, 0, len, (jbyte *) ifRowP->bPhysAddr); + if (len > 0) { + ret = (*env)->NewByteArray(env, len); + if (!IS_NULL(ret)) { + (*env)->SetByteArrayRegion(env, ret, 0, len, (jbyte *) ifRowP->bPhysAddr); + } } break; } diff --git a/jdk/src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c b/jdk/src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c index 50c7630ebf8..abc9418a245 100644 --- a/jdk/src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c +++ b/jdk/src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c @@ -493,6 +493,9 @@ Java_java_net_TwoStacksPlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this, } } } else { + /* NET_BindV6() closes both sockets upon a failure */ + (*env)->SetObjectField(env, this, pdsi_fdID, NULL); + (*env)->SetObjectField(env, this, pdsi_fd1ID, NULL); NET_ThrowCurrent (env, "Cannot bind"); return; } diff --git a/jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c b/jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c index ff86b249c44..facab5923f9 100644 --- a/jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c +++ b/jdk/src/java.base/windows/native/libnet/TwoStacksPlainSocketImpl.c @@ -467,6 +467,10 @@ Java_java_net_TwoStacksPlainSocketImpl_socketBind(JNIEnv *env, jobject this, (*env)->SetIntField(env, fd1Obj, IO_fd_fdID, fd1); } } + } else { + /* NET_BindV6() closes both sockets upon a failure */ + (*env)->SetObjectField(env, this, psi_fdID, NULL); + (*env)->SetObjectField(env, this, psi_fd1ID, NULL); } } else { rv = NET_WinBind(fd, (struct sockaddr *)&him, len, exclBind); diff --git a/jdk/src/java.base/windows/native/libnet/net_util_md.c b/jdk/src/java.base/windows/native/libnet/net_util_md.c index 5f00f49dfd5..523973ba50f 100644 --- a/jdk/src/java.base/windows/native/libnet/net_util_md.c +++ b/jdk/src/java.base/windows/native/libnet/net_util_md.c @@ -627,7 +627,7 @@ void dumpAddr (char *str, void *addr) { * and returns SOCKET_ERROR. Used in NET_BindV6 only. */ -#define CLOSE_SOCKETS_AND_RETURN { \ +#define CLOSE_SOCKETS_AND_RETURN do { \ if (fd != -1) { \ closesocket (fd); \ fd = -1; \ @@ -646,7 +646,7 @@ void dumpAddr (char *str, void *addr) { } \ b->ipv4_fd = b->ipv6_fd = -1; \ return SOCKET_ERROR; \ -} +} while(0) /* * if ipv6 is available, call NET_BindV6 to bind to the required address/port. diff --git a/jdk/src/java.management/share/classes/javax/management/loading/MLet.java b/jdk/src/java.management/share/classes/javax/management/loading/MLet.java index f5689334ffe..1a3fb18527b 100644 --- a/jdk/src/java.management/share/classes/javax/management/loading/MLet.java +++ b/jdk/src/java.management/share/classes/javax/management/loading/MLet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2014, 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 @@ -1313,11 +1313,11 @@ public class MLet extends java.net.URLClassLoader if (type.compareTo("java.lang.Long") == 0) return Long.valueOf(param); if (type.compareTo("java.lang.Integer") == 0) - return param; + return Integer.valueOf(param); if (type.compareTo("java.lang.Float") == 0) - return new Float(param); + return Float.valueOf(param); if (type.compareTo("java.lang.Double") == 0) - return new Double(param); + return Double.valueOf(param); if (type.compareTo("java.lang.String") == 0) return param; diff --git a/jdk/src/java.management/share/classes/javax/management/remote/rmi/RMIConnector.java b/jdk/src/java.management/share/classes/javax/management/remote/rmi/RMIConnector.java index 545a79aeee1..ac54a1e7e8a 100644 --- a/jdk/src/java.management/share/classes/javax/management/remote/rmi/RMIConnector.java +++ b/jdk/src/java.management/share/classes/javax/management/remote/rmi/RMIConnector.java @@ -1335,66 +1335,94 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable int maxNotifications, long timeout) throws IOException, ClassNotFoundException { - IOException org; + boolean retried = false; while (true) { // used for a successful re-connection + // or a transient network problem try { return connection.fetchNotifications(clientSequenceNumber, maxNotifications, - timeout); + timeout); // return normally } catch (IOException ioe) { - org = ioe; + // Examine the chain of exceptions to determine whether this + // is a deserialization issue. If so - we propagate the + // appropriate exception to the caller, who will then + // proceed with fetching notifications one by one + rethrowDeserializationException(ioe); - // inform of IOException try { communicatorAdmin.gotIOException(ioe); - - // The connection should be re-established. - continue; + // reconnection OK, back to "while" to do again } catch (IOException ee) { - // No more fetch, the Exception will be re-thrown. - break; - } // never reached - } // never reached - } + boolean toClose = false; - // specially treating for an UnmarshalException - if (org instanceof UnmarshalException) { - UnmarshalException ume = (UnmarshalException)org; + synchronized (this) { + if (terminated) { + // the connection is closed. + throw ioe; + } else if (retried) { + toClose = true; + } + } - if (ume.detail instanceof ClassNotFoundException) - throw (ClassNotFoundException) ume.detail; + if (toClose) { + // JDK-8049303 + // We received an IOException - but the communicatorAdmin + // did not close the connection - possibly because + // the original exception was raised by a transient network + // problem? + // We already know that this exception is not due to a deserialization + // issue as we already took care of that before involving the + // communicatorAdmin. Moreover - we already made one retry attempt + // at fetching the same batch of notifications - and the + // problem persisted. + // Since trying again doesn't seem to solve the issue, we will now + // close the connection. Doing otherwise might cause the + // NotifFetcher thread to die silently. + final Notification failedNotif = + new JMXConnectionNotification( + JMXConnectionNotification.FAILED, + this, + connectionId, + clientNotifSeqNo++, + "Failed to communicate with the server: " + ioe.toString(), + ioe); - /* In Sun's RMI implementation, if a method return - contains an unserializable object, then we get - UnmarshalException wrapping WriteAbortedException - wrapping NotSerializableException. In that case we - extract the NotSerializableException so that our - caller can realize it should try to skip past the - notification that presumably caused it. It's not - certain that every other RMI implementation will - generate this exact exception sequence. If not, we - will not detect that the problem is due to an - unserializable object, and we will stop trying to - receive notifications from the server. It's not - clear we can do much better. */ - if (ume.detail instanceof WriteAbortedException) { - WriteAbortedException wae = - (WriteAbortedException) ume.detail; - if (wae.detail instanceof IOException) - throw (IOException) wae.detail; + sendNotification(failedNotif); + + try { + close(true); + } catch (Exception e) { + // OK. + // We are closing + } + throw ioe; // the connection is closed here. + } else { + // JDK-8049303 possible transient network problem, + // let's try one more time + retried = true; + } + } } - } else if (org instanceof MarshalException) { + } + } + + private void rethrowDeserializationException(IOException ioe) + throws ClassNotFoundException, IOException { + // specially treating for an UnmarshalException + if (ioe instanceof UnmarshalException) { + throw ioe; // the fix of 6937053 made ClientNotifForwarder.fetchNotifs + // fetch one by one with UnmarshalException + } else if (ioe instanceof MarshalException) { // IIOP will throw MarshalException wrapping a NotSerializableException // when a server fails to serialize a response. - MarshalException me = (MarshalException)org; + MarshalException me = (MarshalException)ioe; if (me.detail instanceof NotSerializableException) { throw (NotSerializableException)me.detail; } } - // Not serialization problem, simply re-throw the orginal exception - throw org; + // Not serialization problem, return. } protected Integer addListenerForMBeanRemovedNotif() diff --git a/jdk/src/java.management/share/classes/sun/management/Agent.java b/jdk/src/java.management/share/classes/sun/management/Agent.java index 01fb9423f64..6a8f710ecc0 100644 --- a/jdk/src/java.management/share/classes/sun/management/Agent.java +++ b/jdk/src/java.management/share/classes/sun/management/Agent.java @@ -210,6 +210,8 @@ public class Agent { } else { throw new AgentConfigurationError(INVALID_JMXREMOTE_PORT, "No port specified"); } + } catch (JdpException e) { + error(e); } catch (AgentConfigurationError err) { error(err.getError(), err.getParams()); } @@ -273,7 +275,7 @@ public class Agent { } private static void startDiscoveryService(Properties props) - throws IOException { + throws IOException, JdpException { // Start discovery service if requested String discoveryPort = props.getProperty("com.sun.management.jdp.port"); String discoveryAddress = props.getProperty("com.sun.management.jdp.address"); @@ -291,7 +293,7 @@ public class Agent { try{ shouldStart = Boolean.parseBoolean(discoveryShouldStart); } catch (NumberFormatException e) { - throw new AgentConfigurationError("Couldn't parse autodiscovery argument"); + throw new AgentConfigurationError(AGENT_EXCEPTION, "Couldn't parse autodiscovery argument"); } } @@ -302,7 +304,7 @@ public class Agent { address = (discoveryAddress == null) ? InetAddress.getByName(JDP_DEFAULT_ADDRESS) : InetAddress.getByName(discoveryAddress); } catch (UnknownHostException e) { - throw new AgentConfigurationError("Unable to broadcast to requested address", e); + throw new AgentConfigurationError(AGENT_EXCEPTION, e, "Unable to broadcast to requested address"); } int port = JDP_DEFAULT_PORT; @@ -310,7 +312,7 @@ public class Agent { try { port = Integer.parseInt(discoveryPort); } catch (NumberFormatException e) { - throw new AgentConfigurationError("Couldn't parse JDP port argument"); + throw new AgentConfigurationError(AGENT_EXCEPTION, "Couldn't parse JDP port argument"); } } @@ -330,12 +332,7 @@ public class Agent { String instanceName = props.getProperty("com.sun.management.jdp.name"); - try{ - JdpController.startDiscoveryService(address, port, instanceName, jmxUrlStr); - } - catch(JdpException e){ - throw new AgentConfigurationError("Couldn't start JDP service", e); - } + JdpController.startDiscoveryService(address, port, instanceName, jmxUrlStr); } } diff --git a/jdk/src/java.management/share/classes/sun/management/jdp/JdpBroadcaster.java b/jdk/src/java.management/share/classes/sun/management/jdp/JdpBroadcaster.java index b1766e6f7f6..cdb33ef7afa 100644 --- a/jdk/src/java.management/share/classes/sun/management/jdp/JdpBroadcaster.java +++ b/jdk/src/java.management/share/classes/sun/management/jdp/JdpBroadcaster.java @@ -35,6 +35,7 @@ import java.net.StandardSocketOptions; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.nio.channels.UnsupportedAddressTypeException; +import java.util.Enumeration; /** * JdpBroadcaster is responsible for sending pre-built JDP packet across a Net @@ -79,13 +80,49 @@ public final class JdpBroadcaster { if (srcAddress != null) { // User requests particular interface to bind to NetworkInterface interf = NetworkInterface.getByInetAddress(srcAddress); + + if (interf == null) { + throw new JdpException("Unable to get network interface for " + srcAddress.toString()); + } + + if (!interf.isUp()) { + throw new JdpException(interf.getName() + " is not up."); + } + + if (!interf.supportsMulticast()) { + throw new JdpException(interf.getName() + " does not support multicast."); + } + try { channel.bind(new InetSocketAddress(srcAddress, 0)); } catch (UnsupportedAddressTypeException ex) { throw new JdpException("Unable to bind to source address"); } channel.setOption(StandardSocketOptions.IP_MULTICAST_IF, interf); + } else { + Enumeration nics = NetworkInterface.getNetworkInterfaces(); + boolean succeed = false; + + while (nics.hasMoreElements()) { + NetworkInterface nic = nics.nextElement(); + + if (nic.isUp() && nic.supportsMulticast()) { + try { + channel.setOption(StandardSocketOptions.IP_MULTICAST_IF, nic); + succeed = true; + } catch (IOException ex) { + // pass + } + } + + } + + if (!succeed) { + throw new JdpException("Unable to bind to any interfaces."); + } + } + } /** diff --git a/jdk/src/java.sql/share/classes/java/sql/Date.java b/jdk/src/java.sql/share/classes/java/sql/Date.java index ab5121fca21..ccf450f985e 100644 --- a/jdk/src/java.sql/share/classes/java/sql/Date.java +++ b/jdk/src/java.sql/share/classes/java/sql/Date.java @@ -27,6 +27,8 @@ package java.sql; import java.time.Instant; import java.time.LocalDate; +import sun.misc.SharedSecrets; +import sun.misc.JavaLangAccess; /** *

A thin wrapper around a millisecond value that allows @@ -42,6 +44,8 @@ import java.time.LocalDate; */ public class Date extends java.util.Date { + private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); + /** * Constructs a Date object initialized with the given * year, month, and day. @@ -108,31 +112,27 @@ public class Date extends java.util.Date { * JDBC date escape format (yyyy-[m]m-[d]d) */ public static Date valueOf(String s) { + if (s == null) { + throw new java.lang.IllegalArgumentException(); + } final int YEAR_LENGTH = 4; final int MONTH_LENGTH = 2; final int DAY_LENGTH = 2; final int MAX_MONTH = 12; final int MAX_DAY = 31; - int firstDash; - int secondDash; Date d = null; - if (s == null) { - throw new java.lang.IllegalArgumentException(); - } - firstDash = s.indexOf('-'); - secondDash = s.indexOf('-', firstDash + 1); + int firstDash = s.indexOf('-'); + int secondDash = s.indexOf('-', firstDash + 1); + int len = s.length(); - if ((firstDash > 0) && (secondDash > 0) && (secondDash < s.length() - 1)) { - String yyyy = s.substring(0, firstDash); - String mm = s.substring(firstDash + 1, secondDash); - String dd = s.substring(secondDash + 1); - if (yyyy.length() == YEAR_LENGTH && - (mm.length() >= 1 && mm.length() <= MONTH_LENGTH) && - (dd.length() >= 1 && dd.length() <= DAY_LENGTH)) { - int year = Integer.parseInt(yyyy); - int month = Integer.parseInt(mm); - int day = Integer.parseInt(dd); + if ((firstDash > 0) && (secondDash > 0) && (secondDash < len - 1)) { + if (firstDash == YEAR_LENGTH && + (secondDash - firstDash > 1 && secondDash - firstDash <= MONTH_LENGTH + 1) && + (len - secondDash > 1 && len - secondDash <= DAY_LENGTH + 1)) { + int year = Integer.parseInt(s, 0, firstDash, 10); + int month = Integer.parseInt(s, firstDash + 1, secondDash, 10); + int day = Integer.parseInt(s, secondDash + 1, len, 10); if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) { d = new Date(year - 1900, month - 1, day); @@ -159,17 +159,30 @@ public class Date extends java.util.Date { int month = super.getMonth() + 1; int day = super.getDate(); - char buf[] = "2000-00-00".toCharArray(); - buf[0] = Character.forDigit(year/1000,10); - buf[1] = Character.forDigit((year/100)%10,10); - buf[2] = Character.forDigit((year/10)%10,10); - buf[3] = Character.forDigit(year%10,10); - buf[5] = Character.forDigit(month/10,10); - buf[6] = Character.forDigit(month%10,10); - buf[8] = Character.forDigit(day/10,10); - buf[9] = Character.forDigit(day%10,10); + char buf[] = new char[10]; + formatDecimalInt(year, buf, 0, 4); + buf[4] = '-'; + Date.formatDecimalInt(month, buf, 5, 2); + buf[7] = '-'; + Date.formatDecimalInt(day, buf, 8, 2); - return new String(buf); + return jla.newStringUnsafe(buf); + } + + /* + * Formats an unsigned integer into a char array in decimal output format. + * Numbers will be zero-padded or truncated if the string representation + * of the integer is smaller than or exceeds len, respectively. + * + * Should consider moving this to Integer and expose it through + * JavaLangAccess similar to Integer::formatUnsignedInt + */ + protected static void formatDecimalInt(int val, char[] buf, int offset, int len) { + int charPos = offset + len; + do { + buf[--charPos] = (char)('0' + (val % 10)); + val /= 10; + } while (charPos > offset); } // Override all the time operations inherited from java.util.Date; diff --git a/jdk/src/java.sql/share/classes/java/sql/Time.java b/jdk/src/java.sql/share/classes/java/sql/Time.java index 46a14b59a80..ec4863a7535 100644 --- a/jdk/src/java.sql/share/classes/java/sql/Time.java +++ b/jdk/src/java.sql/share/classes/java/sql/Time.java @@ -27,6 +27,8 @@ package java.sql; import java.time.Instant; import java.time.LocalTime; +import sun.misc.SharedSecrets; +import sun.misc.JavaLangAccess; /** *

A thin wrapper around the java.util.Date class that allows the JDBC @@ -39,6 +41,8 @@ import java.time.LocalTime; */ public class Time extends java.util.Date { + private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); + /** * Constructs a Time object initialized with the * given values for the hour, minute, and second. @@ -90,22 +94,19 @@ public class Time extends java.util.Date { * @return a corresponding Time object */ public static Time valueOf(String s) { + if (s == null) throw new java.lang.IllegalArgumentException(); + int hour; int minute; int second; - int firstColon; - int secondColon; - - if (s == null) throw new java.lang.IllegalArgumentException(); - - firstColon = s.indexOf(':'); - secondColon = s.indexOf(':', firstColon+1); - if ((firstColon > 0) & (secondColon > 0) & - (secondColon < s.length()-1)) { - hour = Integer.parseInt(s.substring(0, firstColon)); - minute = - Integer.parseInt(s.substring(firstColon+1, secondColon)); - second = Integer.parseInt(s.substring(secondColon+1)); + int firstColon = s.indexOf(':'); + int secondColon = s.indexOf(':', firstColon + 1); + int len = s.length(); + if (firstColon > 0 && secondColon > 0 && + secondColon < len - 1) { + hour = Integer.parseInt(s, 0, firstColon, 10); + minute = Integer.parseInt(s, firstColon + 1, secondColon, 10); + second = Integer.parseInt(s, secondColon + 1, len, 10); } else { throw new java.lang.IllegalArgumentException(); } @@ -123,26 +124,15 @@ public class Time extends java.util.Date { int hour = super.getHours(); int minute = super.getMinutes(); int second = super.getSeconds(); - String hourString; - String minuteString; - String secondString; - if (hour < 10) { - hourString = "0" + hour; - } else { - hourString = Integer.toString(hour); - } - if (minute < 10) { - minuteString = "0" + minute; - } else { - minuteString = Integer.toString(minute); - } - if (second < 10) { - secondString = "0" + second; - } else { - secondString = Integer.toString(second); - } - return (hourString + ":" + minuteString + ":" + secondString); + char[] buf = new char[8]; + Date.formatDecimalInt(hour, buf, 0, 2); + buf[2] = ':'; + Date.formatDecimalInt(minute, buf, 3, 2); + buf[5] = ':'; + Date.formatDecimalInt(second, buf, 6, 2); + + return jla.newStringUnsafe(buf); } // Override all the date operations inherited from java.util.Date; diff --git a/jdk/src/java.sql/share/classes/java/sql/Timestamp.java b/jdk/src/java.sql/share/classes/java/sql/Timestamp.java index 06ca2648814..e66831f85b9 100644 --- a/jdk/src/java.sql/share/classes/java/sql/Timestamp.java +++ b/jdk/src/java.sql/share/classes/java/sql/Timestamp.java @@ -27,7 +27,8 @@ package java.sql; import java.time.Instant; import java.time.LocalDateTime; -import java.util.StringTokenizer; +import sun.misc.SharedSecrets; +import sun.misc.JavaLangAccess; /** *

A thin wrapper around java.util.Date that allows @@ -71,6 +72,8 @@ import java.util.StringTokenizer; */ public class Timestamp extends java.util.Date { + private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); + /** * Constructs a Timestamp object initialized * with the given values. @@ -171,9 +174,6 @@ public class Timestamp extends java.util.Date { final int DAY_LENGTH = 2; final int MAX_MONTH = 12; final int MAX_DAY = 31; - String date_s; - String time_s; - String nanos_s; int year = 0; int month = 0; int day = 0; @@ -184,49 +184,38 @@ public class Timestamp extends java.util.Date { int firstDash; int secondDash; int dividingSpace; - int firstColon = 0; - int secondColon = 0; - int period = 0; + int firstColon; + int secondColon; + int period; String formatError = "Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]"; - String zeros = "000000000"; - String delimiterDate = "-"; - String delimiterTime = ":"; if (s == null) throw new java.lang.IllegalArgumentException("null string"); // Split the string into date and time components s = s.trim(); dividingSpace = s.indexOf(' '); - if (dividingSpace > 0) { - date_s = s.substring(0,dividingSpace); - time_s = s.substring(dividingSpace+1); - } else { + if (dividingSpace < 0) { throw new java.lang.IllegalArgumentException(formatError); } // Parse the date - firstDash = date_s.indexOf('-'); - secondDash = date_s.indexOf('-', firstDash+1); + firstDash = s.indexOf('-'); + secondDash = s.indexOf('-', firstDash+1); // Parse the time - if (time_s == null) - throw new java.lang.IllegalArgumentException(formatError); - firstColon = time_s.indexOf(':'); - secondColon = time_s.indexOf(':', firstColon+1); - period = time_s.indexOf('.', secondColon+1); + firstColon = s.indexOf(':', dividingSpace + 1); + secondColon = s.indexOf(':', firstColon + 1); + period = s.indexOf('.', secondColon + 1); // Convert the date boolean parsedDate = false; - if ((firstDash > 0) && (secondDash > 0) && (secondDash < date_s.length() - 1)) { - String yyyy = date_s.substring(0, firstDash); - String mm = date_s.substring(firstDash + 1, secondDash); - String dd = date_s.substring(secondDash + 1); - if (yyyy.length() == YEAR_LENGTH && - (mm.length() >= 1 && mm.length() <= MONTH_LENGTH) && - (dd.length() >= 1 && dd.length() <= DAY_LENGTH)) { - year = Integer.parseInt(yyyy); - month = Integer.parseInt(mm); - day = Integer.parseInt(dd); + if (firstDash > 0 && secondDash > 0 && secondDash < dividingSpace - 1) { + if (firstDash == YEAR_LENGTH && + (secondDash - firstDash > 1 && secondDash - firstDash <= MONTH_LENGTH + 1) && + (dividingSpace - secondDash > 1 && dividingSpace - secondDash <= DAY_LENGTH + 1)) { + year = Integer.parseInt(s, 0, firstDash, 10); + month = Integer.parseInt(s, firstDash + 1, secondDash, 10); + day = Integer.parseInt(s, secondDash + 1, dividingSpace, 10); if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) { parsedDate = true; @@ -238,25 +227,27 @@ public class Timestamp extends java.util.Date { } // Convert the time; default missing nanos - if ((firstColon > 0) & (secondColon > 0) & - (secondColon < time_s.length()-1)) { - hour = Integer.parseInt(time_s.substring(0, firstColon)); - minute = - Integer.parseInt(time_s.substring(firstColon+1, secondColon)); - if ((period > 0) & (period < time_s.length()-1)) { - second = - Integer.parseInt(time_s.substring(secondColon+1, period)); - nanos_s = time_s.substring(period+1); - if (nanos_s.length() > 9) + int len = s.length(); + if (firstColon > 0 && secondColon > 0 && secondColon < len - 1) { + hour = Integer.parseInt(s, dividingSpace + 1, firstColon, 10); + minute = Integer.parseInt(s, firstColon + 1, secondColon, 10); + if (period > 0 && period < len - 1) { + second = Integer.parseInt(s, secondColon + 1, period, 10); + int nanoPrecision = len - (period + 1); + if (nanoPrecision > 9) throw new java.lang.IllegalArgumentException(formatError); - if (!Character.isDigit(nanos_s.charAt(0))) + if (!Character.isDigit(s.charAt(period + 1))) throw new java.lang.IllegalArgumentException(formatError); - nanos_s = nanos_s + zeros.substring(0,9-nanos_s.length()); - a_nanos = Integer.parseInt(nanos_s); + int tmpNanos = Integer.parseInt(s, period + 1, len, 10); + while (nanoPrecision < 9) { + tmpNanos *= 10; + nanoPrecision++; + } + a_nanos = tmpNanos; } else if (period > 0) { throw new java.lang.IllegalArgumentException(formatError); } else { - second = Integer.parseInt(time_s.substring(secondColon+1)); + second = Integer.parseInt(s, secondColon + 1, len, 10); } } else { throw new java.lang.IllegalArgumentException(formatError); @@ -274,95 +265,41 @@ public class Timestamp extends java.util.Date { * yyyy-mm-dd hh:mm:ss.fffffffff format */ @SuppressWarnings("deprecation") - public String toString () { - + public String toString() { int year = super.getYear() + 1900; int month = super.getMonth() + 1; int day = super.getDate(); int hour = super.getHours(); int minute = super.getMinutes(); int second = super.getSeconds(); - String yearString; - String monthString; - String dayString; - String hourString; - String minuteString; - String secondString; - String nanosString; - String zeros = "000000000"; - String yearZeros = "0000"; - StringBuffer timestampBuf; - if (year < 1000) { - // Add leading zeros - yearString = "" + year; - yearString = yearZeros.substring(0, (4-yearString.length())) + - yearString; + int trailingZeros = 0; + int tmpNanos = nanos; + if (tmpNanos == 0) { + trailingZeros = 8; } else { - yearString = "" + year; - } - if (month < 10) { - monthString = "0" + month; - } else { - monthString = Integer.toString(month); - } - if (day < 10) { - dayString = "0" + day; - } else { - dayString = Integer.toString(day); - } - if (hour < 10) { - hourString = "0" + hour; - } else { - hourString = Integer.toString(hour); - } - if (minute < 10) { - minuteString = "0" + minute; - } else { - minuteString = Integer.toString(minute); - } - if (second < 10) { - secondString = "0" + second; - } else { - secondString = Integer.toString(second); - } - if (nanos == 0) { - nanosString = "0"; - } else { - nanosString = Integer.toString(nanos); - - // Add leading zeros - nanosString = zeros.substring(0, (9-nanosString.length())) + - nanosString; - - // Truncate trailing zeros - char[] nanosChar = new char[nanosString.length()]; - nanosString.getChars(0, nanosString.length(), nanosChar, 0); - int truncIndex = 8; - while (nanosChar[truncIndex] == '0') { - truncIndex--; + while (tmpNanos % 10 == 0) { + tmpNanos /= 10; + trailingZeros++; } - - nanosString = new String(nanosChar, 0, truncIndex + 1); } - // do a string buffer here instead. - timestampBuf = new StringBuffer(20+nanosString.length()); - timestampBuf.append(yearString); - timestampBuf.append("-"); - timestampBuf.append(monthString); - timestampBuf.append("-"); - timestampBuf.append(dayString); - timestampBuf.append(" "); - timestampBuf.append(hourString); - timestampBuf.append(":"); - timestampBuf.append(minuteString); - timestampBuf.append(":"); - timestampBuf.append(secondString); - timestampBuf.append("."); - timestampBuf.append(nanosString); + char[] buf = new char[29 - trailingZeros]; + Date.formatDecimalInt(year, buf, 0, 4); + buf[4] = '-'; + Date.formatDecimalInt(month, buf, 5, 2); + buf[7] = '-'; + Date.formatDecimalInt(day, buf, 8, 2); + buf[10] = ' '; + Date.formatDecimalInt(hour, buf, 11, 2); + buf[13] = ':'; + Date.formatDecimalInt(minute, buf, 14, 2); + buf[16] = ':'; + Date.formatDecimalInt(second, buf, 17, 2); + buf[19] = '.'; + Date.formatDecimalInt(tmpNanos, buf, 20, 9 - trailingZeros); - return (timestampBuf.toString()); + return jla.newStringUnsafe(buf); } /** diff --git a/jdk/src/jdk.attach/aix/classes/sun/tools/attach/AttachProviderImpl.java b/jdk/src/jdk.attach/aix/classes/sun/tools/attach/AttachProviderImpl.java index 532c3db0a3d..56d76734da5 100644 --- a/jdk/src/jdk.attach/aix/classes/sun/tools/attach/AttachProviderImpl.java +++ b/jdk/src/jdk.attach/aix/classes/sun/tools/attach/AttachProviderImpl.java @@ -28,8 +28,6 @@ package sun.tools.attach; import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.VirtualMachineDescriptor; import com.sun.tools.attach.AttachNotSupportedException; -import com.sun.tools.attach.spi.AttachProvider; - import java.io.IOException; // Based on linux/classes/sun/tools/attach/AttachProviderImpl.java. @@ -40,9 +38,6 @@ import java.io.IOException; */ public class AttachProviderImpl extends HotSpotAttachProvider { - // perf counter for the JVM version - private static final String JVM_VERSION = "java.property.java.vm.version"; - public AttachProviderImpl() { } diff --git a/jdk/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java b/jdk/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java index a76d0a99771..cf96c51e9a9 100644 --- a/jdk/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java +++ b/jdk/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java @@ -25,14 +25,12 @@ */ package sun.tools.attach; -import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.AgentLoadException; import com.sun.tools.attach.AttachNotSupportedException; import com.sun.tools.attach.spi.AttachProvider; import java.io.InputStream; import java.io.IOException; import java.io.File; -import java.util.Properties; // Based on linux/classes/sun/tools/attach/VirtualMachineImpl.java. diff --git a/jdk/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java b/jdk/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java index bf2e9516525..4a9562b069f 100644 --- a/jdk/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java +++ b/jdk/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java @@ -30,8 +30,10 @@ import com.sun.tools.attach.AgentLoadException; import com.sun.tools.attach.AgentInitializationException; import com.sun.tools.attach.spi.AttachProvider; +import java.io.BufferedReader; import java.io.InputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.util.Properties; import java.util.stream.Collectors; @@ -188,7 +190,7 @@ public abstract class HotSpotVirtualMachine extends VirtualMachine { .filter(entry -> checkedKeyName(entry.getKey())) .map(entry -> stripKeyName(entry.getKey()) + "=" + escape(entry.getValue())) .collect(Collectors.joining(" ")); - executeJCmd("ManagementAgent.start " + args); + executeJCmd("ManagementAgent.start " + args).close(); } private String escape(Object arg) { @@ -201,7 +203,7 @@ public abstract class HotSpotVirtualMachine extends VirtualMachine { @Override public String startLocalManagementAgent() throws IOException { - executeJCmd("ManagementAgent.start_local"); + executeJCmd("ManagementAgent.start_local").close(); return getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress"); } @@ -305,11 +307,11 @@ public abstract class HotSpotVirtualMachine extends VirtualMachine { * Utility method to read data into a String. */ String readErrorMessage(InputStream sis) throws IOException { - byte b[] = new byte[1024]; - int n; - StringBuffer message = new StringBuffer(); - while ((n = sis.read(b)) != -1) { - message.append(new String(b, 0, n, "UTF-8")); + String s; + StringBuilder message = new StringBuilder(); + BufferedReader br = new BufferedReader(new InputStreamReader(sis)); + while ((s = br.readLine()) != null) { + message.append(s); } return message.toString(); } diff --git a/jdk/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java b/jdk/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java index 8eabefa403c..84d2a36c38d 100644 --- a/jdk/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java +++ b/jdk/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java @@ -107,6 +107,7 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine { if (status != 0) { // read from the stream and use that as the error message String message = readErrorMessage(is); + is.close(); // special case the load command so that the right exception is thrown if (cmd.equals("load")) { throw new AgentLoadException("Failed to load agent library"); diff --git a/jdk/src/java.base/share/conf/security/sunpkcs11-solaris.cfg b/jdk/src/jdk.crypto.pkcs11/solaris/conf/security/sunpkcs11-solaris.cfg similarity index 100% rename from jdk/src/java.base/share/conf/security/sunpkcs11-solaris.cfg rename to jdk/src/jdk.crypto.pkcs11/solaris/conf/security/sunpkcs11-solaris.cfg diff --git a/jdk/src/jdk.runtime/share/native/common-unpack/unpack.cpp b/jdk/src/jdk.runtime/share/native/common-unpack/unpack.cpp index daf1b042428..5af0422661d 100644 --- a/jdk/src/jdk.runtime/share/native/common-unpack/unpack.cpp +++ b/jdk/src/jdk.runtime/share/native/common-unpack/unpack.cpp @@ -32,9 +32,12 @@ * the printf format %ld is correct and use of %lld will cause warning * errors from some compilers (gcc/g++). * _LP64 can be explicitly set (used on Linux). + * Should be checking for the Visual C++ since the _LP64 is set on the 64-bit + * systems but the correct format prefix for 64-bit integers is ll. * Solaris compilers will define __sparcv9 or __x86_64 on 64bit compilations. */ -#if defined(_LP64) || defined(__sparcv9) || defined(__x86_64) +#if !defined (_MSC_VER) && \ + (defined(_LP64) || defined(__sparcv9) || defined(__x86_64)) #define LONG_LONG_FORMAT "%ld" #define LONG_LONG_HEX_FORMAT "%lx" #else diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 39697b08227..9b69db7c116 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -139,6 +139,9 @@ com/sun/management/OperatingSystemMXBean/GetProcessCpuLoad.java aix-all com/sun/management/OperatingSystemMXBean/GetSystemCpuLoad.java aix-all javax/management/MBeanServer/OldMBeanServerTest.java aix-all +# 8050115 +javax/management/monitor/GaugeMonitorDeadlockTest.java generic-all + ############################################################################ # jdk_math @@ -272,6 +275,9 @@ com/sun/jdi/JdbReadTwiceTest.sh generic-all # jdk_util +# 8051641 +sun/util/calendar/zi/TestZoneInfo310.java generic-all + ############################################################################ # jdk_instrument diff --git a/jdk/test/TEST.groups b/jdk/test/TEST.groups index d361a3c5fcb..7c71d1e55cd 100644 --- a/jdk/test/TEST.groups +++ b/jdk/test/TEST.groups @@ -124,18 +124,29 @@ jdk_security2 = \ jdk_security3 = \ javax/security \ + -javax/security/auth/kerberos \ com/sun/security \ + -com/sun/security/jgss \ com/sun/org/apache/xml/internal/security \ sun/security \ + -sun/security/krb5 \ + -sun/security/jgss \ javax/net \ sun/net/www/protocol/https \ com/sun/net/ssl \ lib/security +jdk_security4 = \ + com/sun/security/jgss \ + javax/security/auth/kerberos \ + sun/security/krb5 \ + sun/security/jgss + jdk_security = \ :jdk_security1 \ :jdk_security2 \ - :jdk_security3 + :jdk_security3 \ + :jdk_security4 jdk_text = \ java/text \ diff --git a/jdk/test/java/lang/Integer/ParsingTest.java b/jdk/test/java/lang/Integer/ParsingTest.java index 920e81232a2..f5f64f70c84 100644 --- a/jdk/test/java/lang/Integer/ParsingTest.java +++ b/jdk/test/java/lang/Integer/ParsingTest.java @@ -23,24 +23,22 @@ /* * @test - * @bug 5017980 6576055 8041972 + * @bug 5017980 6576055 8041972 8055251 * @summary Test parsing methods * @author Joseph D. Darcy */ -import java.lang.IllegalArgumentException; import java.lang.IndexOutOfBoundsException; import java.lang.NullPointerException; import java.lang.RuntimeException; /** - * There are eight methods in java.lang.Integer which transform strings + * There are seven methods in java.lang.Integer which transform strings * into an int or Integer value: * * public Integer(String s) * public static Integer decode(String nm) - * public static int parseInt(CharSequence s, int radix, int beginIndex, int endIndex) - * public static int parseInt(CharSequence s, int radix, int beginIndex) + * public static int parseInt(CharSequence s, int beginIndex, int endIndex, int radix) * public static int parseInt(String s, int radix) * public static int parseInt(String s) * public static Integer valueOf(String s, int radix) @@ -55,20 +53,17 @@ import java.lang.RuntimeException; public class ParsingTest { public static void main(String... argv) { - check("+100", +100); - check("-100", -100); + check(+100, "+100"); + check(-100, "-100"); - check("+0", 0); - check("-0", 0); - check("+00000", 0); - check("-00000", 0); + check(0, "+0"); + check(0, "-0"); + check(0, "+00000"); + check(0, "-00000"); - check("+00000", 0, 0, 6); - check("-00000", 0, 0, 6); - - check("0", 0); - check("1", 1); - check("9", 9); + check(0, "0"); + check(1, "1"); + check(9, "9"); checkFailure(""); checkFailure("\u0000"); @@ -85,41 +80,37 @@ public class ParsingTest { checkFailure("-+6"); checkFailure("*100"); - check("test-00000", 0, 4, 10); - check("test-12345", -12345, 4, 10); - check("xx12345yy", 12345, 2, 7); + // check offset based methods + check(0, "+00000", 0, 6, 10); + check(0, "-00000", 0, 6, 10); + check(0, "test-00000", 4, 10, 10); + check(-12345, "test-12345", 4, 10, 10); + check(12345, "xx12345yy", 2, 7, 10); + check(15, "xxFyy", 2, 3, 16); - checkNumberFormatException("", 10, 0); - checkNumberFormatException("100", 10, 3); - checkNumberFormatException("+1000000", 10, 8); - checkNumberFormatException("-1000000", 10, 8); + checkNumberFormatException("", 0, 0, 10); + checkNumberFormatException("+-6", 0, 3, 10); + checkNumberFormatException("1000000", 7, 7, 10); + checkNumberFormatException("1000000", 0, 2, Character.MAX_RADIX + 1); + checkNumberFormatException("1000000", 0, 2, Character.MIN_RADIX - 1); - checkNumberFormatException("", 10, 0, 0); - checkNumberFormatException("+-6", 10, 0, 3); - checkNumberFormatException("1000000", 10, 7); - checkNumberFormatException("1000000", 10, 7, 7); - checkNumberFormatException("1000000", Character.MAX_RADIX + 1, 0, 2); - checkNumberFormatException("1000000", Character.MIN_RADIX - 1, 0, 2); + checkIndexOutOfBoundsException("1000000", 10, 4, 10); + checkIndexOutOfBoundsException("1000000", -1, 2, Character.MAX_RADIX + 1); + checkIndexOutOfBoundsException("1000000", -1, 2, Character.MIN_RADIX - 1); + checkIndexOutOfBoundsException("1000000", 10, 2, Character.MAX_RADIX + 1); + checkIndexOutOfBoundsException("1000000", 10, 2, Character.MIN_RADIX - 1); + checkIndexOutOfBoundsException("-1", 0, 3, 10); + checkIndexOutOfBoundsException("-1", 2, 3, 10); + checkIndexOutOfBoundsException("-1", -1, 2, 10); - checkIndexOutOfBoundsException("1000000", 10, 8); - checkIndexOutOfBoundsException("1000000", 10, -1); - checkIndexOutOfBoundsException("1000000", 10, 10, 4); - checkIndexOutOfBoundsException("1000000", Character.MAX_RADIX + 1, -1, 2); - checkIndexOutOfBoundsException("1000000", Character.MIN_RADIX - 1, -1, 2); - checkIndexOutOfBoundsException("1000000", Character.MAX_RADIX + 1, 10, 2); - checkIndexOutOfBoundsException("1000000", Character.MIN_RADIX - 1, 10, 2); - checkIndexOutOfBoundsException("-1", 10, 0, 3); - checkIndexOutOfBoundsException("-1", 10, 2, 3); - checkIndexOutOfBoundsException("-1", 10, -1, 2); - - checkNull(10, 0, 1); - checkNull(10, -1, 0); - checkNull(10, 0, 0); - checkNull(10, 0, -1); + checkNull(0, 1, 10); + checkNull(-1, 0, 10); + checkNull(0, 0, 10); + checkNull(0, -1, 10); checkNull(-1, -1, -1); } - private static void check(String val, int expected) { + private static void check(int expected, String val) { int n = Integer.parseInt(val); if (n != expected) throw new RuntimeException("Integer.parseInt failed. String:" + @@ -137,11 +128,11 @@ public class ParsingTest { } } - private static void checkNumberFormatException(String val, int radix, int start) { + private static void checkNumberFormatException(String val, int start, int end, int radix) { int n = 0; try { - n = Integer.parseInt(val, radix, start); - System.err.println("parseInt(" + val + ", " + radix + ", " + start + + n = Integer.parseInt(val, start, end, radix); + System.err.println("parseInt(" + val + ", " + start + ", " + end + ", " + radix + ") incorrectly returned " + n); throw new RuntimeException(); } catch (NumberFormatException nfe) { @@ -149,23 +140,11 @@ public class ParsingTest { } } - private static void checkNumberFormatException(String val, int radix, int start, int end) { + private static void checkIndexOutOfBoundsException(String val, int start, int end, int radix) { int n = 0; try { - n = Integer.parseInt(val, radix, start, end); - System.err.println("parseInt(" + val + ", " + radix + ", " + start + ", " + end + - ") incorrectly returned " + n); - throw new RuntimeException(); - } catch (NumberFormatException nfe) { - ; // Expected - } - } - - private static void checkIndexOutOfBoundsException(String val, int radix, int start) { - int n = 0; - try { - n = Integer.parseInt(val, radix, start); - System.err.println("parseInt(" + val + ", " + radix + ", " + start + + n = Integer.parseInt(val, start, end, radix); + System.err.println("parseInt(" + val + ", " + start + ", " + end + ", " + radix + ") incorrectly returned " + n); throw new RuntimeException(); } catch (IndexOutOfBoundsException ioob) { @@ -173,23 +152,11 @@ public class ParsingTest { } } - private static void checkIndexOutOfBoundsException(String val, int radix, int start, int end) { + private static void checkNull(int start, int end, int radix) { int n = 0; try { - n = Integer.parseInt(val, radix, start, end); - System.err.println("parseInt(" + val + ", " + radix + ", " + start + ", " + end + - ") incorrectly returned " + n); - throw new RuntimeException(); - } catch (IndexOutOfBoundsException ioob) { - ; // Expected - } - } - - private static void checkNull(int radix, int start, int end) { - int n = 0; - try { - n = Integer.parseInt(null, 10, start, end); - System.err.println("parseInt(null, " + radix + ", " + start + ", " + end + + n = Integer.parseInt(null, start, end, radix); + System.err.println("parseInt(null, " + start + ", " + end + ", " + radix + ") incorrectly returned " + n); throw new RuntimeException(); } catch (NullPointerException npe) { @@ -197,10 +164,10 @@ public class ParsingTest { } } - private static void check(String val, int expected, int start, int end) { - int n = Integer.parseInt(val, 10, start, end); + private static void check(int expected, String val, int start, int end, int radix) { + int n = Integer.parseInt(val, start, end, radix); if (n != expected) - throw new RuntimeException("Integer.parsedInt failed. String:" + - val + ", start: " + start + ", end: " + end + " Result:" + n); + throw new RuntimeException("Integer.parsedInt failed. Expected: " + expected + " String: \"" + + val + "\", start: " + start + ", end: " + end + ", radix: " + radix + " Result:" + n); } } diff --git a/jdk/test/java/lang/Integer/Unsigned.java b/jdk/test/java/lang/Integer/Unsigned.java index 09be7bfddb6..6c3aecc70c1 100644 --- a/jdk/test/java/lang/Integer/Unsigned.java +++ b/jdk/test/java/lang/Integer/Unsigned.java @@ -303,6 +303,17 @@ public class Unsigned { "\tconverting back ''%s'' resulted in %d%n", value, radix, longString, intResult); } + + // test offset based parse method + intResult = Integer.parseUnsignedInt("prefix" + longString + "suffix", + "prefix".length(), "prefix".length() + longString.length(), radix); + + if (Integer.toUnsignedLong(intResult) != value) { + errors++; + System.err.printf("Bad roundtrip conversion of %d in base %d" + + "\tconverting back ''%s'' resulted in %d%n", + value, radix, longString, intResult); + } } } diff --git a/jdk/test/java/lang/Long/ParsingTest.java b/jdk/test/java/lang/Long/ParsingTest.java index 42c49dddf3d..cbf83415d36 100644 --- a/jdk/test/java/lang/Long/ParsingTest.java +++ b/jdk/test/java/lang/Long/ParsingTest.java @@ -23,19 +23,18 @@ /* * @test - * @bug 5017980 6576055 8041972 + * @bug 5017980 6576055 8041972 8055251 * @summary Test parsing methods * @author Joseph D. Darcy */ /** - * There are eight methods in java.lang.Long which transform strings + * There are seven methods in java.lang.Long which transform strings * into a long or Long value: * * public Long(String s) * public static Long decode(String nm) * public static long parseLong(CharSequence s, int radix, int beginIndex, int endIndex) - * public static long parseLong(CharSequence s, int radix, int beginIndex) * public static long parseLong(String s, int radix) * public static long parseLong(String s) * public static Long valueOf(String s, int radix) @@ -49,17 +48,17 @@ public class ParsingTest { public static void main(String... argv) { - check("+100", +100L); - check("-100", -100L); + check(+100L, "+100"); + check(-100L, "-100"); - check("+0", 0L); - check("-0", 0L); - check("+00000", 0L); - check("-00000", 0L); + check(0L, "+0"); + check(0L, "-0"); + check(0L, "+00000"); + check(0L, "-00000"); - check("0", 0L); - check("1", 1L); - check("9", 9L); + check(0L, "0"); + check(1L, "1"); + check(9L, "9"); checkFailure(""); checkFailure("\u0000"); @@ -76,40 +75,36 @@ public class ParsingTest { checkFailure("-+6"); checkFailure("*100"); - check("test-00000", 0L, 4, 10); - check("test-12345", -12345L, 4, 10); - check("xx12345yy", 12345L, 2, 7); - check("xx123456789012345yy", 123456789012345L, 2, 17); + check(0L, "test-00000", 4, 10, 10); + check(-12345L, "test-12345", 4, 10, 10); + check(12345L, "xx12345yy", 2, 7, 10); + check(123456789012345L, "xx123456789012345yy", 2, 17, 10); + check(15L, "xxFyy", 2, 3, 16); - checkNumberFormatException("100", 10, 3); - checkNumberFormatException("", 10, 0); - checkNumberFormatException("+1000000", 10, 8); - checkNumberFormatException("-1000000", 10, 8); + checkNumberFormatException("", 0, 0, 10); + checkNumberFormatException("+-6", 0, 3, 10); + checkNumberFormatException("1000000", 7, 7, 10); + checkNumberFormatException("1000000", 0, 2, Character.MAX_RADIX + 1); + checkNumberFormatException("1000000", 0, 2, Character.MIN_RADIX - 1); - checkNumberFormatException("", 10, 0, 0); - checkNumberFormatException("+-6", 10, 0, 3); - checkNumberFormatException("1000000", 10, 7, 7); - checkNumberFormatException("1000000", Character.MAX_RADIX + 1, 0, 2); - checkNumberFormatException("1000000", Character.MIN_RADIX - 1, 0, 2); + checkIndexOutOfBoundsException("", 1, 1, 10); + checkIndexOutOfBoundsException("1000000", 10, 4, 10); + checkIndexOutOfBoundsException("1000000", 10, 2, Character.MAX_RADIX + 1); + checkIndexOutOfBoundsException("1000000", 10, 2, Character.MIN_RADIX - 1); + checkIndexOutOfBoundsException("1000000", -1, 2, Character.MAX_RADIX + 1); + checkIndexOutOfBoundsException("1000000", -1, 2, Character.MIN_RADIX - 1); + checkIndexOutOfBoundsException("-1", 0, 3, 10); + checkIndexOutOfBoundsException("-1", 2, 3, 10); + checkIndexOutOfBoundsException("-1", -1, 2, 10); - checkIndexOutOfBoundsException("", 10, 1, 1); - checkIndexOutOfBoundsException("1000000", 10, 10, 4); - checkIndexOutOfBoundsException("1000000", Character.MAX_RADIX + 1, 10, 2); - checkIndexOutOfBoundsException("1000000", Character.MIN_RADIX - 1, 10, 2); - checkIndexOutOfBoundsException("1000000", Character.MAX_RADIX + 1, -1, 2); - checkIndexOutOfBoundsException("1000000", Character.MIN_RADIX - 1, -1, 2); - checkIndexOutOfBoundsException("-1", 10, 0, 3); - checkIndexOutOfBoundsException("-1", 10, 2, 3); - checkIndexOutOfBoundsException("-1", 10, -1, 2); - - checkNull(10, 0, 1); - checkNull(10, -1, 0); - checkNull(10, 0, 0); - checkNull(10, 0, -1); + checkNull(0, 1, 10); + checkNull(-1, 0, 10); + checkNull(0, 0, 10); + checkNull(0, -1, 10); checkNull(-1, -1, -1); } - private static void check(String val, long expected) { + private static void check(long expected, String val) { long n = Long.parseLong(val); if (n != expected) throw new RuntimeException("Long.parseLong failed. String:" + @@ -127,11 +122,11 @@ public class ParsingTest { } } - private static void checkNumberFormatException(String val, int radix, int start) { + private static void checkNumberFormatException(String val, int start, int end, int radix) { long n = 0; try { - n = Long.parseLong(val, radix, start); - System.err.println("parseLong(" + val + ", " + radix + ", " + start + + n = Long.parseLong(val, start, end, radix); + System.err.println("parseLong(" + val + ", " + start + ", " + end + ", " + radix + ") incorrectly returned " + n); throw new RuntimeException(); } catch (NumberFormatException nfe) { @@ -139,23 +134,11 @@ public class ParsingTest { } } - private static void checkNumberFormatException(String val, int radix, int start, int end) { + private static void checkIndexOutOfBoundsException(String val, int start, int end, int radix) { long n = 0; try { - n = Long.parseLong(val, radix, start, end); - System.err.println("parseLong(" + val + ", " + radix + ", " + start + ", " + end + - ") incorrectly returned " + n); - throw new RuntimeException(); - } catch (NumberFormatException nfe) { - ; // Expected - } - } - - private static void checkIndexOutOfBoundsException(String val, int radix, int start) { - long n = 0; - try { - n = Long.parseLong(val, radix, start); - System.err.println("parseLong(" + val + ", " + radix + ", " + start + + n = Long.parseLong(val, start, end, radix); + System.err.println("parseLong(" + val + ", " + start + ", " + end + ", " + radix + ") incorrectly returned " + n); throw new RuntimeException(); } catch (IndexOutOfBoundsException ioob) { @@ -163,23 +146,11 @@ public class ParsingTest { } } - private static void checkIndexOutOfBoundsException(String val, int radix, int start, int end) { + private static void checkNull(int start, int end, int radix) { long n = 0; try { - n = Long.parseLong(val, radix, start, end); - System.err.println("parseLong(" + val + ", " + radix + ", " + start + ", " + end + - ") incorrectly returned " + n); - throw new RuntimeException(); - } catch (IndexOutOfBoundsException ioob) { - ; // Expected - } - } - - private static void checkNull(int radix, int start, int end) { - long n = 0; - try { - n = Long.parseLong(null, 10, start, end); - System.err.println("parseLong(null, " + radix + ", " + start + ", " + end + + n = Long.parseLong(null, start, end, radix); + System.err.println("parseLong(null, " + start + ", " + end + ", " + radix + ") incorrectly returned " + n); throw new RuntimeException(); } catch (NullPointerException npe) { @@ -187,10 +158,10 @@ public class ParsingTest { } } - private static void check(String val, long expected, int start, int end) { - long n = Long.parseLong(val, 10, start, end); + private static void check(long expected, String val, int start, int end, int radix) { + long n = Long.parseLong(val, start, end, radix); if (n != expected) - throw new RuntimeException("Long.parseLong failed. String:" + - val + ", start: " + start + ", end: " + end + " Result:" + n); + throw new RuntimeException("Long.parseLong failed. Expexted: " + expected + " String: \"" + + val + "\", start: " + start + ", end: " + end + " radix: " + radix + " Result: " + n); } } diff --git a/jdk/test/java/lang/Long/Unsigned.java b/jdk/test/java/lang/Long/Unsigned.java index 3686457ffe6..41f7692597c 100644 --- a/jdk/test/java/lang/Long/Unsigned.java +++ b/jdk/test/java/lang/Long/Unsigned.java @@ -289,6 +289,17 @@ public class Unsigned { "\tconverting back ''%s'' resulted in %d%n", value, radix, bigString, longResult); } + + // test offset based parse method + longResult = Long.parseUnsignedLong("prefix" + bigString + "suffix", "prefix".length(), + "prefix".length() + bigString.length(), radix); + + if (!toUnsignedBigInt(longResult).equals(value)) { + errors++; + System.err.printf("Bad roundtrip conversion of %d in base %d" + + "\tconverting back ''%s'' resulted in %d%n", + value, radix, bigString, longResult); + } } } diff --git a/jdk/test/java/lang/invoke/MethodHandlesTest.java b/jdk/test/java/lang/invoke/MethodHandlesTest.java index d607e06aad2..993eaefef0f 100644 --- a/jdk/test/java/lang/invoke/MethodHandlesTest.java +++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java @@ -2160,15 +2160,23 @@ public class MethodHandlesTest { else type = type.changeParameterType(j, argType); if (done.add(type)) - testInvokers(type); + testInvokersWithCatch(type); MethodType vtype = type.changeReturnType(void.class); if (done.add(vtype)) - testInvokers(vtype); + testInvokersWithCatch(vtype); } } } } + public void testInvokersWithCatch(MethodType type) throws Throwable { + try { + testInvokers(type); + } catch (Throwable ex) { + System.out.println("*** testInvokers on "+type+" => "); + ex.printStackTrace(System.out); + } + } public void testInvokers(MethodType type) throws Throwable { if (verbosity >= 3) System.out.println("test invokers for "+type); diff --git a/jdk/test/java/lang/invoke/VarargsArrayTest.java b/jdk/test/java/lang/invoke/VarargsArrayTest.java new file mode 100644 index 00000000000..91bf36485f2 --- /dev/null +++ b/jdk/test/java/lang/invoke/VarargsArrayTest.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2014, 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 java.lang.invoke; + +import sun.invoke.util.Wrapper; + +import java.util.Arrays; +import java.util.Collections; + +/* @test + * @summary unit tests for varargs array methods: MethodHandleInfo.varargsArray(int), + * MethodHandleInfo.varargsArray(Class,int) & MethodHandleInfo.varargsList(int) + * + * @run main/bootclasspath java.lang.invoke.VarargsArrayTest + * @run main/bootclasspath -DVarargsArrayTest.MAX_ARITY=255 -DVarargsArrayTest.START_ARITY=250 + * java.lang.invoke.VarargsArrayTest + */ + +/* This might take a while and burn lots of metadata: + * @run main/bootclasspath -DVarargsArrayTest.MAX_ARITY=255 -DVarargsArrayTest.EXHAUSTIVE=true java.lang.invoke.VarargsArrayTest + */ +public class VarargsArrayTest { + private static final Class CLASS = VarargsArrayTest.class; + private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40); + private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0); + private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE"); + + public static void main(String[] args) throws Throwable { + testVarargsArray(); + testVarargsReferenceArray(); + testVarargsPrimitiveArray(); + } + + public static void testVarargsArray() throws Throwable { + final int MIN = START_ARITY; + final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) { + MethodHandle target = MethodHandleImpl.varargsArray(nargs); + Object[] args = new Object[nargs]; + for (int i = 0; i < nargs; i++) + args[i] = "#"+i; + Object res = target.invokeWithArguments(args); + assertArrayEquals(args, (Object[])res); + } + } + + public static void testVarargsReferenceArray() throws Throwable { + testTypedVarargsArray(Object[].class); + testTypedVarargsArray(String[].class); + testTypedVarargsArray(Number[].class); + } + + public static void testVarargsPrimitiveArray() throws Throwable { + testTypedVarargsArray(int[].class); + testTypedVarargsArray(long[].class); + testTypedVarargsArray(byte[].class); + testTypedVarargsArray(boolean[].class); + testTypedVarargsArray(short[].class); + testTypedVarargsArray(char[].class); + testTypedVarargsArray(float[].class); + testTypedVarargsArray(double[].class); + } + + private static int nextArgCount(int nargs, int density, int MAX) { + if (EXHAUSTIVE) return nargs + 1; + if (nargs >= MAX) return Integer.MAX_VALUE; + int BOT = 20, TOP = MAX-5; + if (density < 10) { BOT = 10; MAX = TOP-2; } + if (nargs <= BOT || nargs >= TOP) { + ++nargs; + } else { + int bump = Math.max(1, 100 / density); + nargs += bump; + if (nargs > TOP) nargs = TOP; + } + return nargs; + } + + private static void testTypedVarargsArray(Class arrayType) throws Throwable { + Class elemType = arrayType.getComponentType(); + int MIN = START_ARITY; + int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added + int density = 3; + if (elemType == int.class || elemType == long.class) density = 7; + if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; } + for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) { + Object[] args = makeTestArray(elemType, nargs); + MethodHandle varargsArray = MethodHandleImpl.varargsArray(arrayType, nargs); + MethodType vaType = varargsArray.type(); + assertEquals(arrayType, vaType.returnType()); + if (nargs != 0) { + assertEquals(elemType, vaType.parameterType(0)); + assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1)); + } + assertEquals(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType)), + vaType); + Object res = varargsArray.invokeWithArguments(args); + assertEquals(res.getClass(), arrayType); + String resString = toArrayString(res); + assertEquals(Arrays.toString(args), resString); + + MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs); + MethodType stype = spreader.type(); + assert(stype == MethodType.methodType(arrayType, arrayType)); + if (nargs <= 5) { + // invoke target as a spreader also: + @SuppressWarnings("cast") + Object res2 = spreader.invokeWithArguments((Object)res); + String res2String = toArrayString(res2); + assertEquals(Arrays.toString(args), res2String); + // invoke the spreader on a generic Object[] array; check for error + try { + Object res3 = spreader.invokeWithArguments((Object)args); + String res3String = toArrayString(res3); + assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); + assertEquals(Arrays.toString(args), res3String); + } catch (ClassCastException ex) { + assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); + } + } + if (nargs == 0) { + // invoke spreader on null arglist + Object res3 = spreader.invokeWithArguments((Object)null); + String res3String = toArrayString(res3); + assertEquals(Arrays.toString(args), res3String); + } + } + } + + private static Object[] makeTestArray(Class elemType, int len) { + Wrapper elem = null; + if (elemType.isPrimitive()) + elem = Wrapper.forPrimitiveType(elemType); + else if (Wrapper.isWrapperType(elemType)) + elem = Wrapper.forWrapperType(elemType); + Object[] args = new Object[len]; + for (int i = 0; i < len; i++) { + Object arg = i * 100; + if (elem == null) { + if (elemType == String.class) + arg = "#"+arg; + arg = elemType.cast(arg); // just to make sure + } else { + switch (elem) { + case BOOLEAN: arg = (i % 3 == 0); break; + case CHAR: arg = 'a' + i; break; + case LONG: arg = (long)i * 1000_000_000; break; + case FLOAT: arg = (float)i / 100; break; + case DOUBLE: arg = (double)i / 1000_000; break; + } + arg = elem.cast(arg, elemType); + } + args[i] = arg; + } + return args; + } + + private static String toArrayString(Object a) { + if (a == null) return "null"; + Class elemType = a.getClass().getComponentType(); + if (elemType == null) return a.toString(); + if (elemType.isPrimitive()) { + switch (Wrapper.forPrimitiveType(elemType)) { + case INT: return Arrays.toString((int[])a); + case BYTE: return Arrays.toString((byte[])a); + case BOOLEAN: return Arrays.toString((boolean[])a); + case SHORT: return Arrays.toString((short[])a); + case CHAR: return Arrays.toString((char[])a); + case FLOAT: return Arrays.toString((float[])a); + case LONG: return Arrays.toString((long[])a); + case DOUBLE: return Arrays.toString((double[])a); + } + } + return Arrays.toString((Object[])a); + } + + public static void assertArrayEquals(Object[] arr1, Object[] arr2) { + if (arr1 == null && arr2 == null) return; + if (arr1 != null && arr2 != null && arr1.length == arr2.length) { + for (int i = 0; i < arr1.length; i++) { + assertEquals(arr1[i], arr2[i]); + } + return; + } + throw new AssertionError(Arrays.deepToString(arr1) + " != " + Arrays.deepToString(arr2)); + } + + public static void assertEquals(Object o1, Object o2) { + if (o1 == null && o2 == null) return; + if (o1 != null && o1.equals(o2)) return; + throw new AssertionError(o1 + " != " + o2); + } + + public static void assertTrue(String msg, boolean b) { + if (!b) { + throw new AssertionError(msg); + } + } + + public static void assertFalse(String msg, boolean b) { + assertTrue(msg, !b); + } +} diff --git a/jdk/test/java/lang/management/MemoryMXBean/LowMemoryTest.java b/jdk/test/java/lang/management/MemoryMXBean/LowMemoryTest.java index 450cc870296..c269c398bc8 100644 --- a/jdk/test/java/lang/management/MemoryMXBean/LowMemoryTest.java +++ b/jdk/test/java/lang/management/MemoryMXBean/LowMemoryTest.java @@ -63,11 +63,16 @@ public class LowMemoryTest { // Use a low young gen size to ensure that the // allocated objects are put in the old gen. final String nmFlag = "-Xmn" + YOUNG_GEN_SIZE; - RunUtil.runTestKeepGcOpts(main, nmFlag); - RunUtil.runTestClearGcOpts(main, nmFlag, "-XX:+UseSerialGC"); - RunUtil.runTestClearGcOpts(main, nmFlag, "-XX:+UseParallelGC"); - RunUtil.runTestClearGcOpts(main, nmFlag, "-XX:+UseG1GC"); - RunUtil.runTestClearGcOpts(main, nmFlag, "-XX:+UseConcMarkSweepGC"); + // Using large pages will change the young gen size, + // make sure we don't use them for this test. + final String lpFlag = "-XX:-UseLargePages"; + // Prevent G1 from selecting a large heap region size, + // since that would change the young gen size. + final String g1Flag = "-XX:G1HeapRegionSize=1m"; + RunUtil.runTestClearGcOpts(main, nmFlag, lpFlag, "-XX:+UseSerialGC"); + RunUtil.runTestClearGcOpts(main, nmFlag, lpFlag, "-XX:+UseParallelGC"); + RunUtil.runTestClearGcOpts(main, nmFlag, lpFlag, "-XX:+UseG1GC", g1Flag); + RunUtil.runTestClearGcOpts(main, nmFlag, lpFlag, "-XX:+UseConcMarkSweepGC"); } private static volatile boolean listenerInvoked = false; @@ -155,19 +160,23 @@ public class LowMemoryTest { Thread allocator = new AllocatorThread(); Thread sweeper = new SweeperThread(); - // Now set threshold + // The chunk size needs to be larger than YOUNG_GEN_SIZE, + // otherwise we will get intermittent failures when objects + // end up in the young gen instead of the old gen. + final long epsilon = 1024; + chunkSize = YOUNG_GEN_SIZE + epsilon; + MemoryUsage mu = mpool.getUsage(); - chunkSize = (mu.getMax() - mu.getUsed()) / 20; newThreshold = mu.getUsed() + (chunkSize * NUM_CHUNKS); - // Sanity check. Make sure the chunkSize is large than the YOUNG_GEN_SIZE - // If the chunkSize are lower than the YOUNG_GEN_SIZE, we will get intermittent - // failures when objects end up in the young gen instead of the old gen. + // Sanity check. Make sure the new threshold isn't too large. // Tweak the test if this fails. - if (chunkSize < YOUNG_GEN_SIZE) { - throw new RuntimeException("TEST FAILED: " + - " chunkSize: " + chunkSize + " is less than YOUNG_GEN_SIZE: " + YOUNG_GEN_SIZE + - " max: " + mu.getMax() + " used: " + mu.getUsed() + " newThreshold: " + newThreshold); + final long headRoom = chunkSize * 2; + final long max = mu.getMax(); + if (max != -1 && newThreshold > max - headRoom) { + throw new RuntimeException("TEST FAILED: newThreshold: " + newThreshold + + " is too near the maximum old gen size: " + max + + " used: " + mu.getUsed() + " headRoom: " + headRoom); } System.out.println("Setting threshold for " + mpool.getName() + diff --git a/jdk/test/java/lang/management/MemoryMXBean/MemoryManagement.java b/jdk/test/java/lang/management/MemoryMXBean/MemoryManagement.java index b9db049946f..4cd913d7bcd 100644 --- a/jdk/test/java/lang/management/MemoryMXBean/MemoryManagement.java +++ b/jdk/test/java/lang/management/MemoryMXBean/MemoryManagement.java @@ -31,7 +31,7 @@ * @author Mandy Chung * * @build MemoryManagement MemoryUtil - * @run main/othervm/timeout=600 -Xmn8m MemoryManagement + * @run main/othervm/timeout=600 -Xmn8m -XX:+IgnoreUnrecognizedVMOptions -XX:G1HeapRegionSize=1 -XX:-UseLargePages MemoryManagement */ import java.lang.management.*; @@ -103,25 +103,24 @@ public class MemoryManagement { Thread allocator = new AllocatorThread(); + // The chunk size needs to be larger than YOUNG_GEN_SIZE, + // otherwise we will get intermittent failures when objects + // end up in the young gen instead of the old gen. + final long epsilon = 1024; + chunkSize = YOUNG_GEN_SIZE + epsilon; + // Now set threshold MemoryUsage mu = mpool.getUsage(); - long max = mu.getMax(); - if (max != -1) { - chunkSize = (max - mu.getUsed()) / 20; - } else { // 6980984 - System.gc(); - chunkSize = Runtime.getRuntime().freeMemory()/20; - } newThreshold = mu.getUsed() + (chunkSize * NUM_CHUNKS); - // Sanity check. Make sure the chunkSize is large than the YOUNG_GEN_SIZE - // If the chunkSize are lower than the YOUNG_GEN_SIZE, we will get intermittent - // failures when objects end up in the young gen instead of the old gen. + // Sanity check. Make sure the new threshold isn't too large. // Tweak the test if this fails. - if (chunkSize < YOUNG_GEN_SIZE) { - throw new RuntimeException("TEST FAILED: " + - " chunkSize: " + chunkSize + " is less than YOUNG_GEN_SIZE: " + YOUNG_GEN_SIZE + - " max: " + mu.getMax() + " used: " + mu.getUsed() + " newThreshold: " + newThreshold); + final long headRoom = chunkSize * 2; + final long max = mu.getMax(); + if (max != -1 && newThreshold > max - headRoom) { + throw new RuntimeException("TEST FAILED: newThreshold: " + newThreshold + + " is too near the maximum old gen size: " + max + + " used: " + mu.getUsed() + " headRoom: " + headRoom); } System.out.println("Setting threshold for " + mpool.getName() + diff --git a/jdk/test/java/lang/reflect/Field/TestFieldReflectValueOf.java b/jdk/test/java/lang/reflect/Field/TestFieldReflectValueOf.java new file mode 100644 index 00000000000..bc80c49f03a --- /dev/null +++ b/jdk/test/java/lang/reflect/Field/TestFieldReflectValueOf.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @bug 5043030 + * @summary Verify that the method java.lang.reflect.Field.get(Object) makes + * use of the same caching mechanism as used for autoboxing + * when wrapping values of the primitive types. + * @author Andrej Golovnin + */ + +import java.lang.reflect.Field; + +public class TestFieldReflectValueOf { + + @SuppressWarnings("unused") + private static boolean booleanStaticField; + @SuppressWarnings("unused") + private static byte byteStaticField; + @SuppressWarnings("unused") + private static char charStaticField; + @SuppressWarnings("unused") + private static int intStaticField; + @SuppressWarnings("unused") + private static long longStaticField; + @SuppressWarnings("unused") + private static short shortStaticField; + + @SuppressWarnings("unused") + private static volatile boolean booleanStaticVolatileField; + @SuppressWarnings("unused") + private static volatile byte byteStaticVolatileField; + @SuppressWarnings("unused") + private static volatile char charStaticVolatileField; + @SuppressWarnings("unused") + private static volatile int intStaticVolatileField; + @SuppressWarnings("unused") + private static volatile long longStaticVolatileField; + @SuppressWarnings("unused") + private static volatile short shortStaticVolatileField; + + @SuppressWarnings("unused") + private boolean booleanField; + @SuppressWarnings("unused") + private byte byteField; + @SuppressWarnings("unused") + private char charField; + @SuppressWarnings("unused") + private int intField; + @SuppressWarnings("unused") + private long longField; + @SuppressWarnings("unused") + private short shortField; + + @SuppressWarnings("unused") + private volatile boolean booleanVolatileField; + @SuppressWarnings("unused") + private volatile byte byteVolatileField; + @SuppressWarnings("unused") + private volatile char charVolatileField; + @SuppressWarnings("unused") + private volatile int intVolatileField; + @SuppressWarnings("unused") + private volatile long longVolatileField; + @SuppressWarnings("unused") + private volatile short shortVolatileField; + + public static void main(String[] args) { + testUnsafeStaticFieldAccessors(); + testUnsafeQualifiedStaticFieldAccessors(); + testUnsafeFieldAccessors(); + testUnsafeQualifiedFieldAccessors(); + } + + private static void testUnsafeStaticFieldAccessors() { + testFieldAccessors(true, false); + } + + private static void testUnsafeQualifiedStaticFieldAccessors() { + testFieldAccessors(true, true); + } + + private static void testUnsafeFieldAccessors() { + testFieldAccessors(false, false); + } + + private static void testUnsafeQualifiedFieldAccessors() { + testFieldAccessors(false, true); + } + + private static void testFieldAccessors(boolean checkStatic, + boolean checkVolatile) + { + // Boolean#valueOf test + testField(Boolean.TYPE, Boolean.FALSE, checkStatic, checkVolatile); + testField(Boolean.TYPE, Boolean.TRUE, checkStatic, checkVolatile); + + // Byte#valueOf test + for (int b = Byte.MIN_VALUE; b < (Byte.MAX_VALUE + 1); b++) { + testField(Byte.TYPE, Byte.valueOf((byte) b), checkStatic, checkVolatile); + } + + // Character#valueOf test + for (char c = '\u0000'; c <= '\u007F'; c++) { + testField(Character.TYPE, Character.valueOf(c), checkStatic, checkVolatile); + } + + // Integer#valueOf test + for (int i = -128; i <= 127; i++) { + testField(Integer.TYPE, Integer.valueOf(i), checkStatic, checkVolatile); + } + + // Long#valueOf test + for (long l = -128L; l <= 127L; l++) { + testField(Long.TYPE, Long.valueOf(l), checkStatic, checkVolatile); + } + + // Short#valueOf test + for (short s = -128; s <= 127; s++) { + testField(Short.TYPE, Short.valueOf(s), checkStatic, checkVolatile); + } + } + + private static void testField(Class primType, Object wrappedValue, + boolean checkStatic, boolean checkVolatile) + { + String fieldName = primType.getName(); + if (checkStatic) { + fieldName += "Static"; + } + if (checkVolatile) { + fieldName += "Volatile"; + } + fieldName += "Field"; + try { + Field field = TestFieldReflectValueOf.class.getDeclaredField(fieldName); + field.setAccessible(true); + TestFieldReflectValueOf obj = new TestFieldReflectValueOf(); + field.set(obj, wrappedValue); + Object result = field.get(obj); + if (result != wrappedValue) { + throw new RuntimeException("The value " + wrappedValue + + " is not cached for the type " + primType); + } + } catch ( NoSuchFieldException | SecurityException + | IllegalAccessException | IllegalArgumentException e) + { + throw new RuntimeException(e); + } + } + +} diff --git a/jdk/test/java/lang/reflect/Method/invoke/TestMethodReflectValueOf.java b/jdk/test/java/lang/reflect/Method/invoke/TestMethodReflectValueOf.java new file mode 100644 index 00000000000..048eac55f45 --- /dev/null +++ b/jdk/test/java/lang/reflect/Method/invoke/TestMethodReflectValueOf.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @bug 5043030 + * @summary Verify that the method java.lang.reflect.Method.invoke(Object, Object...) + * makes use of the same caching mechanism as used for autoboxing + * when wrapping returned values of the primitive types. + * @author Andrej Golovnin + * @run main/othervm -Dsun.reflect.noInflation=true TestMethodReflectValueOf + * @run main/othervm -Dsun.reflect.noInflation=false -Dsun.reflect.inflationThreshold=500 TestMethodReflectValueOf + */ + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +public class TestMethodReflectValueOf { + + public static void main(String[] args) { + // When the inflation is disabled we compare values using "==" + // as the returned values of the primitive types should be cached + // by the same mechanism as used for autoboxing. When the inflation + // is enabled we use "equals()"-method to compare values as the native + // code still creates new instances to wrap values of the primitive + // types. + boolean checkIdentity = Boolean.getBoolean("sun.reflect.noInflation"); + + // Boolean#valueOf test + testMethod(Boolean.TYPE, Boolean.FALSE, checkIdentity); + testMethod(Boolean.TYPE, Boolean.TRUE, checkIdentity); + + // Byte#valueOf test + for (int b = Byte.MIN_VALUE; b < (Byte.MAX_VALUE + 1); b++) { + testMethod(Byte.TYPE, Byte.valueOf((byte) b), checkIdentity); + } + + // Character#valueOf test + for (char c = '\u0000'; c <= '\u007F'; c++) { + testMethod(Character.TYPE, Character.valueOf(c), checkIdentity); + } + + // Integer#valueOf test + for (int i = -128; i <= 127; i++) { + testMethod(Integer.TYPE, Integer.valueOf(i), checkIdentity); + } + + // Long#valueOf test + for (long l = -128L; l <= 127L; l++) { + testMethod(Long.TYPE, Long.valueOf(l), checkIdentity); + } + + // Short#valueOf test + for (short s = -128; s <= 127; s++) { + testMethod(Short.TYPE, Short.valueOf(s), checkIdentity); + } + } + + public static void testMethod(Class primType, Object wrappedValue, + boolean checkIdentity) + { + String methodName = primType.getName() + "Method"; + try { + Method method = TestMethodReflectValueOf.class.getMethod(methodName, primType); + Object result = method.invoke(new TestMethodReflectValueOf(), wrappedValue); + if (checkIdentity) { + if (result != wrappedValue) { + throw new RuntimeException("The value " + wrappedValue + + " is not cached for the type " + primType); + } + } else { + if (!result.equals(wrappedValue)) { + throw new RuntimeException("The result value " + result + + " is not equal to the expected value " + + wrappedValue + " for the type " + primType); + } + } + } catch ( NoSuchMethodException | SecurityException + | IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) + { + throw new RuntimeException(e); + } + } + + public int intMethod(int value) { + return value; + } + + public long longMethod(long value) { + return value; + } + + public short shortMethod(short value) { + return value; + } + + public byte byteMethod(byte value) { + return value; + } + + public char charMethod(char value) { + return value; + } + + public boolean booleanMethod(boolean value) { + return value; + } + +} diff --git a/jdk/test/java/lang/reflect/annotationSharing/AnnotationSharing.java b/jdk/test/java/lang/reflect/annotationSharing/AnnotationSharing.java new file mode 100644 index 00000000000..98a13d6416c --- /dev/null +++ b/jdk/test/java/lang/reflect/annotationSharing/AnnotationSharing.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @bug 8054987 + * @summary Test sharing of annotations between Executable/Field instances. + * Sharing should not be noticeable when performing mutating + * operations. + * @run testng AnnotationSharing + */ + +import java.lang.annotation.*; +import java.lang.reflect.*; + +import org.testng.annotations.Test; + +public class AnnotationSharing { + @Test + public void testMethodSharing() throws Exception { + Method[] m1 = AnnotationSharing.class.getMethods(); + Method[] m2 = AnnotationSharing.class.getMethods(); + validateSharingSafelyObservable(m1, m2); + } + + @Test + public void testDeclaredMethodSharing() throws Exception { + Method[] m3 = AnnotationSharing.class.getDeclaredMethods(); + Method[] m4 = AnnotationSharing.class.getDeclaredMethods(); + validateSharingSafelyObservable(m3, m4); + } + + @Test + public void testFieldSharing() throws Exception { + Field[] f1 = AnnotationSharing.class.getFields(); + Field[] f2 = AnnotationSharing.class.getFields(); + validateSharingSafelyObservable(f1, f2); + } + + @Test + public void testDeclaredFieldsSharing() throws Exception { + Field[] f3 = AnnotationSharing.class.getDeclaredFields(); + Field[] f4 = AnnotationSharing.class.getDeclaredFields(); + validateSharingSafelyObservable(f3, f4); + } + + @Test + public void testMethodSharingOccurs() throws Exception { + Method mm1 = AnnotationSharing.class.getDeclaredMethod("m", (Class[])null); + Method mm2 = AnnotationSharing.class.getDeclaredMethod("m", (Class[])null); + validateAnnotationSharing(mm1, mm2); + } + + @Test + public void testMethodSharingIsSafe() throws Exception { + Method mm1 = AnnotationSharing.class.getDeclaredMethod("m", (Class[])null); + Method mm2 = AnnotationSharing.class.getDeclaredMethod("m", (Class[])null); + validateAnnotationSharingIsSafe(mm1, mm2); + validateArrayValues(mm1.getAnnotation(Baz.class), mm2.getAnnotation(Baz.class)); + } + + @Test + public void testFieldSharingOccurs() throws Exception { + Field ff1 = AnnotationSharing.class.getDeclaredField("f"); + Field ff2 = AnnotationSharing.class.getDeclaredField("f"); + validateAnnotationSharing(ff1, ff2); + } + + @Test + public void testFieldSharingIsSafe() throws Exception { + Field ff1 = AnnotationSharing.class.getDeclaredField("f"); + Field ff2 = AnnotationSharing.class.getDeclaredField("f"); + validateAnnotationSharingIsSafe(ff1, ff2); + validateArrayValues(ff1.getAnnotation(Baz.class), ff2.getAnnotation(Baz.class)); + } + + // Validate that AccessibleObject instances are not shared + private static void validateSharingSafelyObservable(AccessibleObject[] m1, AccessibleObject[] m2) + throws Exception { + + // Validate that setAccessible works + for (AccessibleObject m : m1) + m.setAccessible(false); + + for (AccessibleObject m : m2) + m.setAccessible(true); + + for (AccessibleObject m : m1) + if (m.isAccessible()) + throw new RuntimeException(m + " should not be accessible"); + + for (AccessibleObject m : m2) + if (!m.isAccessible()) + throw new RuntimeException(m + " should be accessible"); + + // Validate that methods are still equal() + for (int i = 0; i < m1.length; i++) + if (!m1[i].equals(m2[i])) + throw new RuntimeException(m1[i] + " and " + m2[i] + " should be equal()"); + + // Validate that the arrays aren't shared + for (int i = 0; i < m1.length; i++) + m1[i] = null; + + for (int i = 0; i < m2.length; i++) + if (m2[i] == null) + throw new RuntimeException("Detected sharing of AccessibleObject arrays"); + } + + // Validate that annotations are shared + private static void validateAnnotationSharing(AccessibleObject m1, AccessibleObject m2) { + Bar b1 = m1.getAnnotation(Bar.class); + Bar b2 = m2.getAnnotation(Bar.class); + + if (b1 != b2) + throw new RuntimeException(b1 + " and " + b2 + " should be =="); + + } + + // Validate that Method instances representing the annotation elements + // behave as intended + private static void validateAnnotationSharingIsSafe(AccessibleObject m1, AccessibleObject m2) + throws Exception { + Bar b1 = m1.getAnnotation(Bar.class); + Bar b2 = m2.getAnnotation(Bar.class); + + Method mm1 = b1.annotationType().getMethod("value", (Class[]) null); + Method mm2 = b2.annotationType().getMethod("value", (Class[]) null); + inner(mm1, mm2); + + mm1 = b1.getClass().getMethod("value", (Class[]) null); + mm2 = b2.getClass().getMethod("value", (Class[]) null); + inner(mm1, mm2); + + } + private static void inner(Method mm1, Method mm2) + throws Exception { + if (!mm1.equals(mm2)) + throw new RuntimeException(mm1 + " and " + mm2 + " should be equal()"); + + mm1.setAccessible(false); + mm2.setAccessible(true); + + if (mm1.isAccessible()) + throw new RuntimeException(mm1 + " should not be accessible"); + + if (!mm2.isAccessible()) + throw new RuntimeException(mm2 + " should be accessible"); + } + + // Validate that array element values are not shared + private static void validateArrayValues(Baz a, Baz b) { + String[] s1 = a.value(); + String[] s2 = b.value(); + + s1[0] = "22"; + + if (!s2[0].equals("1")) + throw new RuntimeException("Mutation of array elements should not be detectable"); + } + + @Foo @Bar("val") @Baz({"1", "2"}) + public void m() { + return ; + } + + @Foo @Bar("someValue") @Baz({"1", "22", "33"}) + public Object f = new Object(); +} + +@Retention(RetentionPolicy.RUNTIME) +@interface Foo {} + +@Retention(RetentionPolicy.RUNTIME) +@interface Bar { + String value(); +} + +@Retention(RetentionPolicy.RUNTIME) +@interface Baz { + String [] value(); +} diff --git a/jdk/test/java/net/NetworkInterface/Test.java b/jdk/test/java/net/NetworkInterface/Test.java index 6db3079992d..de74eec5ca4 100644 --- a/jdk/test/java/net/NetworkInterface/Test.java +++ b/jdk/test/java/net/NetworkInterface/Test.java @@ -22,7 +22,9 @@ */ /* @test - * @bug 4405354 6594296 + * @bug 4405354 6594296 8058216 + * @run main Test + * @run main/othervm -Djava.net.preferIPv4Stack=true Test * @summary Basic tests for NetworkInterface */ import java.net.NetworkInterface; diff --git a/jdk/test/java/sql/test/sql/DateTests.java b/jdk/test/java/sql/test/sql/DateTests.java index 5a660507eee..ae6c276c4f6 100644 --- a/jdk/test/java/sql/test/sql/DateTests.java +++ b/jdk/test/java/sql/test/sql/DateTests.java @@ -26,261 +26,43 @@ import java.sql.Date; import java.time.Instant; import java.time.LocalDate; import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import util.BaseTest; -public class DateTests { +public class DateTests extends BaseTest { - public DateTests() { - } - - @BeforeClass - public static void setUpClass() throws Exception { - } - - @AfterClass - public static void tearDownClass() throws Exception { - } - - @BeforeMethod - public void setUpMethod() throws Exception { - } - - @AfterMethod - public void tearDownMethod() throws Exception { - } - - /** + /* * Validate an IllegalArgumentException is thrown for an invalid Date string */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_year() throws Exception { - String expResult = "20009-11-01"; - Date.valueOf(expResult); + @Test(dataProvider = "invalidDateValues", + expectedExceptions = IllegalArgumentException.class) + public void test(String d) throws Exception { + Date.valueOf(d); } - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_year2() throws Exception { - String expResult = "09-11-01"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_year3() throws Exception { - String expResult = "-11-01"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_month() throws Exception { - String expResult = "2009-111-01"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_month3() throws Exception { - String expResult = "2009--01"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_month4() throws Exception { - String expResult = "2009-13-01"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_day() throws Exception { - String expResult = "2009-11-011"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_day3() throws Exception { - String expResult = "2009-11-"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_day4() throws Exception { - String expResult = "2009-11-00"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_day5() throws Exception { - String expResult = "2009-11-33"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_valueOf() throws Exception { - String expResult = "--"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_valueOf2() throws Exception { - String expResult = ""; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_valueOf3() throws Exception { - String expResult = null; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_valueOf4() throws Exception { - String expResult = "-"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_valueOf5() throws Exception { - String expResult = "2009"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_valueOf6() throws Exception { - String expResult = "2009-01"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_valueOf7() throws Exception { - String expResult = "---"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_valueOf8() throws Exception { - String expResult = "2009-13--1"; - Date.valueOf(expResult); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Date string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_valueOf10() { - String expResult = "1900-1-0"; - Date.valueOf(expResult); - } - - /** + /* * Test that a date created from a date string is equal to the value * returned from toString() */ - @Test - public void test_valueOf() { - String expResult = "2009-08-30"; - Date d = Date.valueOf(expResult); - assertEquals(expResult, d.toString()); + @Test(dataProvider = "validDateValues") + public void test00(String d, String expectedD) { + Date d1 = Date.valueOf(d); + Date d2 = Date.valueOf(expectedD); + assertTrue(d1.equals(d2) && d2.equals(d1) + && d1.toString().equals(expectedD), "Error d1 != d2"); } - /** - * Test that two dates, one with lead 0s omitted for month are equal - */ - @Test - public void testValid_month_single_digit() { - String testDate = "2009-1-01"; - String expResult = "2009-01-01"; - Date d = Date.valueOf(testDate); - Date d2 = Date.valueOf(expResult); - assertEquals(d, d2); - } - - /** - * Test that two dates, one with lead 0s omitted for day are equal - */ - @Test - public void testValid_day_single_digit() { - String testDate = "2009-11-1"; - String expResult = "2009-11-01"; - Date d = Date.valueOf(testDate); - Date d2 = Date.valueOf(expResult); - assertEquals(d, d2); - } - - /** - * Test that two dates, one with lead 0s omitted for month and day are equal - */ - @Test - public void testValid_month_day_single_digit() { - String testDate = "2009-1-1"; - String expResult = "2009-01-01"; - Date d = Date.valueOf(testDate); - Date d2 = Date.valueOf(expResult); - assertEquals(d, d2); - } - - /** + /* * Validate that a Date.after() returns false when same date is compared */ @Test - public void test1() { + public void test01() { Date d = Date.valueOf("1961-08-30"); assertFalse(d.after(d), "Error d.after(d) = true"); } - /** + /* * Validate that a Date.after() returns true when later date is compared to * earlier date */ @@ -291,7 +73,7 @@ public class DateTests { assertTrue(d2.after(d), "Error d2.after(d) = false"); } - /** + /* * Validate that a Date.after() returns false when earlier date is compared * to later date */ @@ -302,7 +84,7 @@ public class DateTests { assertFalse(d.after(d2), "Error d.after(d2) = true"); } - /** + /* * Validate that a Date.after() returns false when date compared to another * date created from the original date */ @@ -314,7 +96,7 @@ public class DateTests { assertFalse(d2.after(d), "Error d2.after(d) = true"); } - /** + /* * Validate that a Date.before() returns false when same date is compared */ @Test @@ -323,7 +105,7 @@ public class DateTests { assertFalse(d.before(d), "Error d.before(d) = true"); } - /** + /* * Validate that a Date.before() returns true when earlier date is compared * to later date */ @@ -334,7 +116,7 @@ public class DateTests { assertTrue(d.before(d2), "Error d.before(d2) = false"); } - /** + /* * Validate that a Date.before() returns false when later date is compared * to earlier date */ @@ -345,7 +127,7 @@ public class DateTests { assertFalse(d2.before(d), "Error d2.before(d) = true"); } - /** + /* * Validate that a Date.before() returns false when date compared to another * date created from the original date */ @@ -357,7 +139,7 @@ public class DateTests { assertFalse(d2.before(d), "Error d2.before(d) = true"); } - /** + /* * Validate that a Date.compareTo returns 0 when both Date objects are the * same */ @@ -367,7 +149,7 @@ public class DateTests { assertTrue(d.compareTo(d) == 0, "Error d.compareTo(d) !=0"); } - /** + /* * Validate that a Date.compareTo returns 0 when both Date objects represent * the same date */ @@ -378,7 +160,7 @@ public class DateTests { assertTrue(d.compareTo(d2) == 0, "Error d.compareTo(d2) !=0"); } - /** + /* * Validate that a Date.compareTo returns -1 when comparing a date to a * later date */ @@ -389,7 +171,7 @@ public class DateTests { assertTrue(d.compareTo(d2) == -1, "Error d.compareTo(d2) != -1"); } - /** + /* * Validate that a Date.compareTo returns 1 when comparing a date to an * earlier date */ @@ -400,7 +182,7 @@ public class DateTests { assertTrue(d2.compareTo(d) == 1, "Error d.compareTo(d2) != 1"); } - /** + /* * Validate that a Date made from a LocalDate are equal */ @Test @@ -411,7 +193,7 @@ public class DateTests { assertTrue(d.equals(d2), "Error d != d2"); } - /** + /* * Validate that a Date LocalDate value, made from a LocalDate are equal */ @Test @@ -422,7 +204,7 @@ public class DateTests { "Error LocalDate values are not equal"); } - /** + /* * Validate an NPE occurs when a null LocalDate is passed to valueOf */ @Test(expectedExceptions = NullPointerException.class) @@ -431,7 +213,7 @@ public class DateTests { Date.valueOf(ld); } - /** + /* * Validate an UnsupportedOperationException occurs when toInstant() is * called */ @@ -441,7 +223,7 @@ public class DateTests { Instant instant = d.toInstant(); } - /** + /* * Validate that two Date objects are equal when one is created from the * toString() of the other */ @@ -452,7 +234,7 @@ public class DateTests { assertTrue(d.equals(d2) && d2.equals(d), "Error d != d2"); } - /** + /* * Validate that two Date values one created using valueOf and another via a * constructor are equal */ @@ -464,7 +246,7 @@ public class DateTests { assertTrue(d.equals(d2), "Error d != d2"); } - /** + /* * Validate that two Date values one created using getTime() of the other * are equal */ @@ -476,7 +258,7 @@ public class DateTests { assertTrue(d.equals(d2), "Error d != d2"); } - /** + /* * Validate that a Date value is equal to itself */ @Test @@ -486,7 +268,7 @@ public class DateTests { assertTrue(d.equals(d), "Error d != d"); } - /** + /* * Validate an IllegalArgumentException is thrown for calling getHours */ @Test(expectedExceptions = IllegalArgumentException.class) @@ -495,7 +277,7 @@ public class DateTests { d.getHours(); } - /** + /* * Validate an IllegalArgumentException is thrown for calling getMinutes */ @Test(expectedExceptions = IllegalArgumentException.class) @@ -504,7 +286,7 @@ public class DateTests { d.getMinutes(); } - /** + /* * Validate an IllegalArgumentException is thrown for calling getSeconds */ @Test(expectedExceptions = IllegalArgumentException.class) @@ -513,7 +295,7 @@ public class DateTests { d.getSeconds(); } - /** + /* * Validate an IllegalArgumentException is thrown for calling setHours */ @Test(expectedExceptions = IllegalArgumentException.class) @@ -522,7 +304,7 @@ public class DateTests { d.setHours(8); } - /** + /* * Validate an IllegalArgumentException is thrown for calling setMinutes */ @Test(expectedExceptions = IllegalArgumentException.class) @@ -531,7 +313,7 @@ public class DateTests { d.setMinutes(0); } - /** + /* * Validate an IllegalArgumentException is thrown for calling setSeconds */ @Test(expectedExceptions = IllegalArgumentException.class) @@ -539,4 +321,53 @@ public class DateTests { Date d = Date.valueOf("1961-08-30"); d.setSeconds(0); } + + /* + * DataProvider used to provide Date which are not valid and are used + * to validate that an IllegalArgumentException will be thrown from the + * valueOf method + */ + @DataProvider(name = "invalidDateValues") + private Object[][] invalidDateValues() { + return new Object[][]{ + {"20009-11-01"}, + {"09-11-01"}, + {"-11-01"}, + {"2009-111-01"}, + {"2009--01"}, + {"2009-13-01"}, + {"2009-11-011"}, + {"2009-11-"}, + {"2009-11-00"}, + {"2009-11-33"}, + {"--"}, + {""}, + {null}, + {"-"}, + {"2009"}, + {"2009-01"}, + {"---"}, + {"2009-13--1"}, + {"1900-1-0"}, + {"2009-01-01 10:50:01"}, + {"1996-12-10 12:26:19.1"}, + {"10:50:01"} + }; + } + + /* + * DataProvider used to provide Dates which are valid and are used + * to validate that an IllegalArgumentException will not be thrown from the + * valueOf method and the corect value from toString() is returned + */ + @DataProvider(name = "validDateValues") + private Object[][] validDateValues() { + return new Object[][]{ + {"2009-08-30", "2009-08-30"}, + {"2009-01-8", "2009-01-08"}, + {"2009-1-01", "2009-01-01"}, + {"2009-1-1", "2009-01-01"} + + }; + } } diff --git a/jdk/test/java/sql/test/sql/TimeTests.java b/jdk/test/java/sql/test/sql/TimeTests.java index 4064c37d18e..7b99679754b 100644 --- a/jdk/test/java/sql/test/sql/TimeTests.java +++ b/jdk/test/java/sql/test/sql/TimeTests.java @@ -25,56 +25,35 @@ package test.sql; import java.sql.Time; import java.time.LocalTime; import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import util.BaseTest; -public class TimeTests { +public class TimeTests extends BaseTest { - public TimeTests() { - } - - @BeforeClass - public static void setUpClass() throws Exception { - } - - @AfterClass - public static void tearDownClass() throws Exception { - } - - @BeforeMethod - public void setUpMethod() throws Exception { - } - - @AfterMethod - public void tearDownMethod() throws Exception { - } - - /** + /* * Validate an IllegalArgumentException is thrown for calling getYear */ @Test(expectedExceptions = IllegalArgumentException.class) - public void test1() { + public void test01() { Time t = Time.valueOf("08:30:59"); t.getYear(); } - /** + /* * Validate an IllegalArgumentException is thrown for calling getMonth */ @Test(expectedExceptions = IllegalArgumentException.class) - public void test2() { + public void test02() { Time t = Time.valueOf("08:30:59"); t.getMonth(); } - /** + /* * Validate an IllegalArgumentException is thrown for calling getDay */ @Test(expectedExceptions = IllegalArgumentException.class) - public void test3() { + public void test03() { Time t = Time.valueOf("08:30:59"); t.getDay(); } @@ -83,62 +62,62 @@ public class TimeTests { * Validate an IllegalArgumentException is thrown for calling getDate */ @Test(expectedExceptions = IllegalArgumentException.class) - public void test4() { + public void test04() { Time t = Time.valueOf("08:30:59"); t.getDate(); } - /** + /* * Validate an IllegalArgumentException is thrown for calling setYear */ @Test(expectedExceptions = IllegalArgumentException.class) - public void test5() { + public void test05() { Time t = Time.valueOf("08:30:59"); t.setYear(8); } - /** + /* * Validate an IllegalArgumentException is thrown for calling setMonth */ @Test(expectedExceptions = IllegalArgumentException.class) - public void test6() { + public void test06() { Time t = Time.valueOf("08:30:59"); t.setMonth(8); } - /** + /* * Validate an IllegalArgumentException is thrown for calling setDate */ @Test(expectedExceptions = IllegalArgumentException.class) - public void test7() { + public void test07() { Time t = Time.valueOf("08:30:59"); t.setDate(30); } - /** + /* * Validate an IllegalArgumentException is thrown for calling getDate */ @Test(expectedExceptions = IllegalArgumentException.class) - public void test8() { + public void test08() { Time t = Time.valueOf("08:30:59"); t.getDate(); } - /** + /* * Validate that a Time made from a toLocalTime() LocalTime are equal */ @Test - public void test13() { + public void test09() { Time t = Time.valueOf("08:30:59"); Time t2 = Time.valueOf(t.toLocalTime()); assertTrue(t.equals(t2), "Error t != t2"); } - /** + /* * Validate that a Time LocalTime value, made from a LocalTime are equal */ @Test - public void test14() { + public void test10() { LocalTime lt = LocalTime.of(8, 30, 59); Time t = Time.valueOf(lt); System.out.println("lt=" + lt + ",t=" + t.toLocalTime()); @@ -146,231 +125,224 @@ public class TimeTests { "Error LocalTime values are not equal"); } - /** + /* * Validate an NPE occurs when a null LocalDate is passed to valueOf */ @Test(expectedExceptions = NullPointerException.class) - public void test15() throws Exception { + public void test11() throws Exception { LocalTime ld = null; Time.valueOf(ld); } - /** + /* * Validate an UnsupportedOperationException occurs when toInstant() is * called */ @Test(expectedExceptions = UnsupportedOperationException.class) - public void test16() throws Exception { + public void test12() throws Exception { Time t = new Time(System.currentTimeMillis()); t.toInstant(); } - /** - * Validate that a Time made from valueOf(String) returns the same String - * from Time.toString(); - */ - @Test - public void test17() { - String time = "08:30:59"; - Time t = Time.valueOf(time); - assertTrue(time.equals(t.toString()), "Error t != t2"); - } - - /** + /* * Validate that two Time objects are equal when one is created from the - * toString() of the other + * toString() of the other and that the correct value is returned from + * toString() */ - @Test - public void test18() { - Time t = Time.valueOf("08:30:59"); - Time t2 = Time.valueOf(t.toString()); - assertTrue(t.equals(t2) && t2.equals(t), "Error t != t2"); + @Test(dataProvider = "validTimeValues") + public void test13(String time, String expected) { + Time t1 = Time.valueOf(time); + Time t2 = Time.valueOf(t1.toString()); + assertTrue(t1.equals(t2) && t2.equals(t1) + && t1.toString().equals(expected), "Error t1 != t2"); } - /** + /* * Validate that two Time values one created using valueOf and another via a * constructor are equal */ @Test - public void test19() { + public void test14() { Time t = Time.valueOf("08:30:59"); Time t2 = new Time(8, 30, 59); assertTrue(t.equals(t2) && t2.equals(t), "Error t != t2"); } - /** + /* * Validate that two Time values one created using valueOf and another via a * constructor are equal */ @Test - public void test20() { + public void test15() { Time t = Time.valueOf("08:30:59"); Time t2 = new Time(t.getTime()); assertTrue(t.equals(t2) && t2.equals(t), "Error t != t2"); } - /** - * Validate an IllegalArgumentException is thrown for calling valueOf with a - * null String - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void test21() { - String time = null; - Time t = Time.valueOf(time); - - } - - /** + /* * Validate an IllegalArgumentException is thrown for an invalid Time string */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void test22() throws Exception { - Time.valueOf("1961-08-30"); + @Test(dataProvider = "invalidTimeValues", + expectedExceptions = IllegalArgumentException.class) + public void test16(String time) throws Exception { + Time.valueOf(time); } - /** - * Validate an IllegalArgumentException is thrown for an invalid Time string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void test23() throws Exception { - Time.valueOf("8:"); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Time string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void test24() throws Exception { - Time.valueOf("a:b:c"); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Time string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void test25() throws Exception { - Time.valueOf("08:10"); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Time string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void test26() throws Exception { - Time.valueOf("08:10:10:10"); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Time string - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void test27() throws Exception { - Time.valueOf("08:10:Batman"); - } - - /** + /* * Validate that Time.after() returns false when same date is compared */ @Test - public void test28() { + public void test17() { Time t = Time.valueOf("08:30:59"); assertFalse(t.after(t), "Error t.after(t) = true"); } - /** + /* * Validate that Time.after() returns true when later date is compared to * earlier date */ @Test - public void test29() { + public void test18() { Time t = Time.valueOf("08:30:59"); Time t2 = new Time(System.currentTimeMillis()); assertTrue(t2.after(t), "Error t2.after(t) = false"); } - /** + /* * Validate that Time.after() returns false when earlier date is compared to * itself */ @Test - public void test30() { + public void test19() { Time t = Time.valueOf("08:30:59"); Time t2 = new Time(t.getTime()); assertFalse(t.after(t2), "Error t.after(t2) = true"); assertFalse(t2.after(t), "Error t2.after(t) = true"); } - /** + /* * Validate that Time.before() returns false when same date is compared */ @Test - public void test31() { + public void test20() { Time t = Time.valueOf("08:30:59"); assertFalse(t.before(t), "Error t.before(t) = true"); } - /** + /* * Validate that Time.before() returns true when earlier date is compared to * later date */ @Test - public void test32() { + public void test21() { Time t = Time.valueOf("08:30:59"); Time t2 = new Time(System.currentTimeMillis()); assertTrue(t.before(t2), "Error t.before(t2) = false"); } - /** + /* * Validate that Time.before() returns false when earlier date is compared * to itself */ @Test - public void test33() { + public void test22() { Time t = Time.valueOf("08:30:59"); Time t2 = new Time(t.getTime()); assertFalse(t.before(t2), "Error t.after(t2) = true"); assertFalse(t2.before(t), "Error t2.after(t) = true"); } - /** + /* * Validate that Time.compareTo returns 0 when both Date objects are the * same */ @Test - public void test34() { + public void test23() { Time t = Time.valueOf("08:30:59"); assertTrue(t.compareTo(t) == 0, "Error t.compareTo(t) !=0"); } - /** + /* * Validate thatTime.compareTo returns 0 when both Time objects are the same */ @Test - public void test35() { + public void test24() { Time t = Time.valueOf("08:30:59"); Time t2 = new Time(t.getTime()); assertTrue(t.compareTo(t2) == 0, "Error t.compareTo(t2) !=0"); } - /** + /* * Validate that Time.compareTo returns 1 when comparing a later Time to an * earlier Time */ @Test - public void test36() { + public void test25() { Time t = Time.valueOf("08:30:59"); Time t2 = new Time(t.getTime() + 1); assertTrue(t2.compareTo(t) == 1, "Error t2.compareTo(t) !=1"); } - /** + /* * Validate thatTime.compareTo returns 1 when comparing a later Time to an * earlier Time */ @Test - public void test37() { + public void test26() { Time t = Time.valueOf("08:30:59"); Time t2 = new Time(t.getTime() + 1); assertTrue(t.compareTo(t2) == -1, "Error t.compareTo(t2) != -1"); } + + /* + * DataProvider used to provide Time values which are not valid and are used + * to validate that an IllegalArgumentException will be thrown from the + * valueOf method + */ + @DataProvider(name = "invalidTimeValues") + private Object[][] invalidTimeValues() { + return new Object[][]{ + {"2009-11-01 10:50:01"}, + {"1961-08-30 10:50:01.1"}, + {"1961-08-30"}, + {"00:00:00."}, + {"10:50:0.1"}, + {":00:00"}, + {"00::00"}, + {"00:00:"}, + {"::"}, + {" : : "}, + {"0a:00:00"}, + {"00:bb:00"}, + {"00:01:cc"}, + {"08:10:Batman"}, + {"08:10:10:10"}, + {"08:10"}, + {"a:b:c"}, + {null}, + {"8:"} + }; + } + + /* + * DataProvider used to provide Time values which are valid and are used + * to validate that an IllegalArgumentException will not be thrown from the + * valueOf method. It also contains the expected return value from + * toString() + */ + @DataProvider(name = "validTimeValues") + private Object[][] validTimeValues() { + return new Object[][]{ + {"10:50:01", "10:50:01"}, + {"01:1:1", "01:01:01"}, + {"01:01:1", "01:01:01"}, + {"1:01:1", "01:01:01"}, + {"2:02:02", "02:02:02"}, + {"2:02:2", "02:02:02"}, + {"10:50:1", "10:50:01"}, + {"00:00:00", "00:00:00"}, + {"08:30:59", "08:30:59"}, + {"9:0:1", "09:00:01"} + }; + } } diff --git a/jdk/test/java/sql/test/sql/TimestampTests.java b/jdk/test/java/sql/test/sql/TimestampTests.java index ac87f16c013..a555b6ae0b5 100644 --- a/jdk/test/java/sql/test/sql/TimestampTests.java +++ b/jdk/test/java/sql/test/sql/TimestampTests.java @@ -29,66 +29,27 @@ import java.time.Instant; import java.time.LocalDateTime; import java.util.Calendar; import static org.testng.Assert.*; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import util.BaseTest; -public class TimestampTests { +public class TimestampTests extends BaseTest { - public TimestampTests() { - } - - @BeforeClass - public static void setUpClass() throws Exception { - } - - @AfterClass - public static void tearDownClass() throws Exception { - } - - @BeforeMethod - public void setUpMethod() throws Exception { - } - - @AfterMethod - public void tearDownMethod() throws Exception { - } - - /** + /* * Validate an IllegalArgumentException is thrown for an invalid Timestamp */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_timestamp() throws Exception { - String testTS = "2009-11-01-01 10:50"; - Timestamp.valueOf(testTS); + @Test(dataProvider = "invalidTimestampValues", + expectedExceptions = IllegalArgumentException.class) + public void test(String ts) throws Exception { + Timestamp.valueOf(ts); } - /** - * Validate an IllegalArgumentException is thrown for an invalid Timestamp - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_year2() throws Exception { - String testTS = "aaaa-11-01-01 10:50"; - Timestamp.valueOf(testTS); - } - - /** - * Validate an IllegalArgumentException is thrown for an invalid Timestamp - */ - @Test(expectedExceptions = IllegalArgumentException.class) - public void testInvalid_year3() throws Exception { - String testTS = "aaaa-11-01 10:50"; - Timestamp.valueOf(testTS); - } - - /** + /* * Validate that two Timestamp are equal when the leading 0 in seconds is * omitted */ @Test - public void test1() throws Exception { + public void test01() throws Exception { String testTS = "2009-01-01 10:50:00"; String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); @@ -96,23 +57,23 @@ public class TimestampTests { assertEquals(ts, ts2, "Error ts1 != ts2"); } - /** + /* * Validate two Timestamps created from the same string are equal */ @Test - public void test2() throws Exception { + public void test02() throws Exception { String testTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); Timestamp ts2 = Timestamp.valueOf(testTS); assertEquals(ts, ts2, "Error ts1 != ts2"); } - /** + /* * Validate that two Timestamp values one with leading 0s for month and day * equals same string without the leading 0s. */ @Test - public void test3() throws Exception { + public void test03() throws Exception { String testTS = "2009-1-1 10:50:0"; String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); @@ -120,12 +81,12 @@ public class TimestampTests { assertEquals(ts, ts2, "Error ts1 != ts2"); } - /** + /* * Validate that two Timestamp values one with leading 0s for day omitted * are equal */ @Test - public void test4() throws Exception { + public void test04() throws Exception { String testTS = "2009-01-1 10:50:0"; String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); @@ -133,12 +94,12 @@ public class TimestampTests { assertEquals(ts, ts2, "Error ts1 != ts2"); } - /** + /* * Validate that two Timestamp values one with leading 0s for month omitted * and both with leading 0s for seconds omitted are equal */ @Test - public void test5() throws Exception { + public void test05() throws Exception { String testTS = "2009-1-01 10:50:0"; String ExpectedTS = "2009-01-01 10:50:0"; Timestamp ts = Timestamp.valueOf(testTS); @@ -146,11 +107,11 @@ public class TimestampTests { assertEquals(ts, ts2, "Error ts1 != ts2"); } - /** + /* * Validate that two Timestamp values one with leading 0s for month omitted */ @Test - public void test6() throws Exception { + public void test06() throws Exception { String testTS = "2005-1-01 10:20:50.00"; String ExpectedTS = "2005-01-01 10:20:50.00"; Timestamp ts = Timestamp.valueOf(testTS); @@ -158,52 +119,53 @@ public class TimestampTests { assertEquals(ts, ts2, "Error ts1 != ts2"); } - /** + /* * Validate that two Timestamp values one created using valueOf and another * via a constructor are equal */ @Test - public void test7() { + public void test07() { Timestamp ts1 = Timestamp.valueOf("1996-12-13 14:15:25.001"); Timestamp ts2 = new Timestamp(96, 11, 13, 14, 15, 25, 1000000); assertTrue(ts1.equals(ts2), "Error ts1 != ts2"); } - /** + /* * Validate that two Timestamp values one created using valueOf and another * via a constructor are equal */ @Test - public void test8() { + public void test08() { Timestamp ts1 = Timestamp.valueOf("1996-12-13 14:15:25.001"); Timestamp ts2 = new Timestamp(ts1.getTime()); assertTrue(ts1.equals(ts2), "Error ts1 != ts2"); } - /** + /* * Validate that two Timestamp values one created using valueOf and another * via a constructor are equal */ @Test - public void test9() { + public void test09() { Timestamp ts1 = Timestamp.valueOf("1996-12-13 14:15:25.0"); Timestamp ts2 = new Timestamp(96, 11, 13, 14, 15, 25, 0); assertTrue(ts1.equals(ts2), "Error ts1 != ts2"); } - /** + /* * Validate that a Timestamp cannot be equal to null */ @Test public void test10() { Timestamp ts1 = Timestamp.valueOf("1961-08-30 14:15:25.745634"); - assertFalse(ts1.equals(null), "Error ts1 == null"); + Timestamp ts2 = null; + assertFalse(ts1.equals(ts2), "Error ts1 == null"); } - /** + /* * Validate that a Timestamp is equal to another timestamp created with the * using the same value but not equal to a Timestamp which is one day later */ @@ -218,7 +180,7 @@ public class TimestampTests { } - /** + /* * Validate that a Timestamp is equal to itself */ @Test @@ -227,19 +189,20 @@ public class TimestampTests { assertTrue(ts1.equals(ts1), "Error ts1 != ts1"); } - /** + /* * Validate that two Timestamps are equal when one is created from the * toString() of the other */ - @Test - public void test13() { - Timestamp ts1 = Timestamp.valueOf("1996-12-10 12:26:19.12"); + @Test(dataProvider = "validTimestampValues") + public void test13(String ts, String expectedTS) { + Timestamp ts1 = Timestamp.valueOf(ts); Timestamp ts2 = Timestamp.valueOf(ts1.toString()); - assertTrue(ts1.equals(ts2) && ts2.equals(ts1), "Error ts1 != ts2"); + assertTrue(ts1.equals(ts2) && ts2.equals(ts1) + && ts1.toString().equals(expectedTS), "Error ts1 != ts2"); } // Before Tests - /** + /* * Validate that Timestamp ts1 is before Timestamp ts2 */ @Test @@ -249,7 +212,7 @@ public class TimestampTests { assertTrue(ts1.before(ts2), "Error ts1 not before ts2"); } - /** + /* * Validate that Timestamp ts1 is before Timestamp ts2 */ @Test @@ -259,7 +222,7 @@ public class TimestampTests { assertTrue(ts1.before(ts2), "Error ts1 not before ts2"); } - /** + /* * Validate that Timestamp ts1 is before Timestamp ts2 */ @Test @@ -289,7 +252,7 @@ public class TimestampTests { assertFalse(ts1.before(ts1), "Error ts1 before ts1!"); } - /** + /* * Create 3 Timestamps and make sure the 1st is before the other two * Timestamps which are each greater than the one before it */ @@ -302,7 +265,7 @@ public class TimestampTests { assertTrue(ts1.before(ts2) && ts2.before(ts3) && ts1.before(ts3)); } - /** + /* * Validate that Timestamp ts1 is not after Timestamp ts2 */ @Test @@ -313,7 +276,7 @@ public class TimestampTests { } - /** + /* * Validate that Timestamp ts1 is after Timestamp ts2 */ @Test @@ -323,7 +286,7 @@ public class TimestampTests { assertTrue(ts1.after(ts2), "Error ts1 not after ts2"); } - /** + /* * Validate that a NullPointerException is thrown if a null is passed to the * after method */ @@ -333,7 +296,7 @@ public class TimestampTests { ts1.after(null); } - /** + /* * Validate that a Timestamp cannot be after itself */ @Test @@ -341,9 +304,10 @@ public class TimestampTests { Timestamp ts1 = Timestamp.valueOf("1999-11-10 12:26:19.3456543"); assertFalse(ts1.after(ts1), "Error ts1 is after itself"); } - /** - * Validate that a Timestamp after() works correctly with Timestamp - * created using milliseconds + + /* + * Validate that a Timestamp after() works correctly with Timestamp created + * using milliseconds */ @Test public void test24() { @@ -354,7 +318,7 @@ public class TimestampTests { assertTrue(ts1.after(ts2) && ts2.after(ts3) && ts1.after(ts3)); } - /** + /* * Validate compareTo returns 0 for Timestamps that are the same */ @Test @@ -364,7 +328,7 @@ public class TimestampTests { assertTrue(ts1.compareTo(ts2) == 0, "Error ts1 != ts2"); } - /** + /* * Validate compareTo returns -1 for when the 1st Timestamp is earlier than * the 2nd Timestamp */ @@ -376,7 +340,7 @@ public class TimestampTests { assertTrue(ts2.compareTo(ts1) == 1, "Error ts1 is not before ts2"); } - /** + /* * Validate compareTo returns 1 for when the 1st Timestamp is later than the * 2nd Timestamp */ @@ -388,7 +352,7 @@ public class TimestampTests { assertTrue(ts2.compareTo(ts1) == -1, "Error ts1 not after ts2"); } - /** + /* * Validate compareTo returns 0 for Timestamps that are the same */ @Test @@ -398,7 +362,7 @@ public class TimestampTests { assertTrue(ts1.compareTo(ts2) == 0, "Error ts1 != ts2"); } - /** + /* * Validate compareTo returns 0 for Timestamps that are the same */ @Test @@ -408,7 +372,7 @@ public class TimestampTests { assertFalse(ts1.equals(d), "Error ts1 == d"); } - /** + /* * Validate compareTo returns 0 for Timestamps that are the same */ @Test @@ -418,7 +382,7 @@ public class TimestampTests { assertTrue(ts1.equals(d), "Error ts1 != d"); } - /** + /* * Validate equals returns false when a Date object is passed to equals */ @Test @@ -428,7 +392,7 @@ public class TimestampTests { assertFalse(ts1.equals(d), "Error ts1 != d"); } - /** + /* * Validate equals returns false when a Date object is passed to equals */ @Test @@ -438,7 +402,7 @@ public class TimestampTests { assertFalse(ts1.equals(d), "Error ts1 != d"); } - /** + /* * Validate equals returns false when a Time object is passed to equals */ @Test @@ -448,7 +412,7 @@ public class TimestampTests { assertFalse(ts1.equals(t1), "Error ts1 == t1"); } - /** + /* * Validate equals returns false when a String object is passed to equals */ @Test @@ -457,7 +421,7 @@ public class TimestampTests { assertFalse(ts1.equals("1966-08-30 08:08:08"), "Error ts1 == a String"); } - /** + /* * Validate getTime() returns the same value from 2 timeStamps created by */ @Test @@ -469,7 +433,7 @@ public class TimestampTests { assertTrue(ts1.equals(ts2), "Error ts1 != ts2"); } - /** + /* * Validate getTime() returns the same value from 2 timeStamps when * setTime() is used to specify the same value for both Timestamps */ @@ -483,7 +447,7 @@ public class TimestampTests { assertTrue(ts1.equals(ts2), "Error ts1 != ts2"); } - /** + /* * Validate an IllegalArgumentException is thrown for an invalid nanos value */ @Test(expectedExceptions = IllegalArgumentException.class) @@ -493,7 +457,7 @@ public class TimestampTests { } - /** + /* * Validate an IllegalArgumentException is thrown for an invalid nanos value */ @Test(expectedExceptions = IllegalArgumentException.class) @@ -501,10 +465,9 @@ public class TimestampTests { int nanos = 999999999; Timestamp ts1 = Timestamp.valueOf("1961-08-30 00:00:00"); ts1.setNanos(nanos + 1); - } - /** + /* * Validate you can set nanos to 999999999 */ @Test @@ -513,10 +476,9 @@ public class TimestampTests { Timestamp ts1 = Timestamp.valueOf("1961-08-30 00:00:00"); ts1.setNanos(nanos); assertTrue(ts1.getNanos() == nanos, "Error Invalid Nanos value"); - } - /** + /* * Validate you can set nanos to 0 */ @Test @@ -525,10 +487,9 @@ public class TimestampTests { Timestamp ts1 = Timestamp.valueOf("1961-08-30 00:00:00"); ts1.setNanos(nanos); assertTrue(ts1.getNanos() == nanos, "Error Invalid Nanos value"); - } - /** + /* * Validate that a Timestamp made from a LocalDateTime are equal */ @Test @@ -539,7 +500,7 @@ public class TimestampTests { assertTrue(ts1.equals(ts2), "Error ts1 != ts2"); } - /** + /* * Validate that a Timestamp LocalDateTime value, made from a LocalDateTime * are equal */ @@ -551,7 +512,7 @@ public class TimestampTests { "Error LocalDateTime values are not equal"); } - /** + /* * Validate an NPE occurs when a null LocalDateTime is passed to valueOF */ @Test(expectedExceptions = NullPointerException.class) @@ -560,7 +521,7 @@ public class TimestampTests { Timestamp.valueOf(ldt); } - /** + /* * Validate that a Timestamp made from a Instant are equal */ @Test @@ -571,7 +532,7 @@ public class TimestampTests { assertTrue(ts1.equals(ts2), "Error ts1 != ts2"); } - /** + /* * Validate that a Timestamp made from a Instant are equal */ @Test @@ -582,7 +543,7 @@ public class TimestampTests { "Error Instant values do not match"); } - /** + /* * Validate an NPE occurs when a null instant is passed to from */ @Test(expectedExceptions = NullPointerException.class) @@ -592,7 +553,7 @@ public class TimestampTests { } // Added SQE tests - /** + /* * Create a Timestamp and a 2nd Timestamp that is 1 month earlier and * validate that it is not before or after the original Timestamp */ @@ -607,7 +568,7 @@ public class TimestampTests { assertFalse(ts1.before(ts2) || ts2.after(ts1)); } - /** + /* * Create two Timestamps and validate that compareTo returns 1 to indicate * the 1st Timestamp is greater than the 2nd Timestamp */ @@ -622,7 +583,7 @@ public class TimestampTests { assertTrue(ts1.compareTo(ts2) == 1); } - /** + /* * Create two Timestamps and validate that the 1st Timestamp is not equal to * the 2nd Timestamp but equal to itself */ @@ -637,4 +598,114 @@ public class TimestampTests { assertTrue(!ts1.equals(ts2) && ts1.equals(ts1)); } + /* + * Validate that two Timestamps are equal when one is created from the + * toString() of the other + */ + @Test(dataProvider = "validateNanos") + public void test51(String ts, int nanos) { + Timestamp ts1 = Timestamp.valueOf(ts); + Timestamp ts2 = Timestamp.valueOf(ts1.toString()); + assertTrue(ts1.getNanos() == nanos && ts1.equals(ts2), + "Error with Nanos"); + } + + /* + * DataProvider used to provide Timestamps which are not valid and are used + * to validate that an IllegalArgumentException will be thrown from the + * valueOf method + */ + @DataProvider(name = "invalidTimestampValues") + private Object[][] invalidTimestampValues() { + return new Object[][]{ + {"2009-11-01-01 10:50:01"}, + {"aaaa-11-01-01 10:50"}, + {"aaaa-11-01 10:50"}, + {"1961--30 00:00:00"}, + {"--30 00:00:00"}, + {"-- 00:00:00"}, + {"1961-1- 00:00:00"}, + {"2009-11-01"}, + {"10:50:01"}, + {"1961-a-30 00:00:00"}, + {"1961-01-bb 00:00:00"}, + {"1961-08-30 00:00:00."}, + {"1961-08-30 :00:00"}, + {"1961-08-30 00::00"}, + {"1961-08-30 00:00:"}, + {"1961-08-30 ::"}, + {"1961-08-30 0a:00:00"}, + {"1961-08-30 00:bb:00"}, + {"1961-08-30 00:01:cc"}, + {"1961-08-30 00:00:00.01a"}, + {"1961-08-30 00:00:00.a"}, + {"1996-12-10 12:26:19.1234567890"}, + {null} + }; + } + + /* + * DataProvider used to provide Timestamps which are valid and are used + * to validate that an IllegalArgumentException will not be thrown from the + * valueOf method and the corect value from toString() is returned + */ + @DataProvider(name = "validTimestampValues") + private Object[][] validTimestampValues() { + return new Object[][]{ + {"1961-08-30 00:00:00", "1961-08-30 00:00:00.0"}, + {"1961-08-30 11:22:33", "1961-08-30 11:22:33.0"}, + {"1961-8-30 00:00:00", "1961-08-30 00:00:00.0"}, + {"1966-08-1 00:00:00", "1966-08-01 00:00:00.0"}, + {"1996-12-10 12:26:19.1", "1996-12-10 12:26:19.1"}, + {"1996-12-10 12:26:19.12", "1996-12-10 12:26:19.12"}, + {"1996-12-10 12:26:19.123", "1996-12-10 12:26:19.123"}, + {"1996-12-10 12:26:19.1234", "1996-12-10 12:26:19.1234"}, + {"1996-12-10 12:26:19.12345", "1996-12-10 12:26:19.12345"}, + {"1996-12-10 12:26:19.123456", "1996-12-10 12:26:19.123456"}, + {"1996-12-10 12:26:19.1234567", "1996-12-10 12:26:19.1234567"}, + {"1996-12-10 12:26:19.12345678", "1996-12-10 12:26:19.12345678"}, + {"1996-12-10 12:26:19.123456789", "1996-12-10 12:26:19.123456789"}, + {"1996-12-10 12:26:19.000000001", "1996-12-10 12:26:19.000000001"}, + {"1996-12-10 12:26:19.000000012", "1996-12-10 12:26:19.000000012"}, + {"1996-12-10 12:26:19.000000123", "1996-12-10 12:26:19.000000123"}, + {"1996-12-10 12:26:19.000001234", "1996-12-10 12:26:19.000001234"}, + {"1996-12-10 12:26:19.000012345", "1996-12-10 12:26:19.000012345"}, + {"1996-12-10 12:26:19.000123456", "1996-12-10 12:26:19.000123456"}, + {"1996-12-10 12:26:19.001234567", "1996-12-10 12:26:19.001234567"}, + {"1996-12-10 12:26:19.12345678", "1996-12-10 12:26:19.12345678"}, + {"1996-12-10 12:26:19.0", "1996-12-10 12:26:19.0"}, + {"1996-12-10 12:26:19.01230", "1996-12-10 12:26:19.0123"} + }; + } + + /* + * DataProvider used to provide Timestamp and Nanos values in order to + * validate that the correct Nanos value is generated from the specified + * Timestamp + */ + @DataProvider(name = "validateNanos") + private Object[][] validateNanos() { + return new Object[][]{ + {"1961-08-30 00:00:00", 0}, + {"1996-12-10 12:26:19.1", 100000000}, + {"1996-12-10 12:26:19.12", 120000000}, + {"1996-12-10 12:26:19.123", 123000000}, + {"1996-12-10 12:26:19.1234", 123400000}, + {"1996-12-10 12:26:19.12345", 123450000}, + {"1996-12-10 12:26:19.123456", 123456000}, + {"1996-12-10 12:26:19.1234567", 123456700}, + {"1996-12-10 12:26:19.12345678", 123456780}, + {"1996-12-10 12:26:19.123456789", 123456789}, + {"1996-12-10 12:26:19.000000001", 1}, + {"1996-12-10 12:26:19.000000012", 12}, + {"1996-12-10 12:26:19.000000123", 123}, + {"1996-12-10 12:26:19.000001234", 1234}, + {"1996-12-10 12:26:19.000012345", 12345}, + {"1996-12-10 12:26:19.000123456", 123456}, + {"1996-12-10 12:26:19.001234567", 1234567}, + {"1996-12-10 12:26:19.012345678", 12345678}, + {"1996-12-10 12:26:19.0", 0}, + {"1996-12-10 12:26:19.01230", 12300000} + }; + } } diff --git a/jdk/test/javax/management/loading/MLetInternalsTest.java b/jdk/test/javax/management/loading/MLetInternalsTest.java new file mode 100644 index 00000000000..a359697b0b3 --- /dev/null +++ b/jdk/test/javax/management/loading/MLetInternalsTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014, 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. + */ + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import javax.management.loading.MLet; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeTest; + +import static org.testng.Assert.*; + +/* + * @test + * @bug 8058089 + * @summary Tests various internal functions provided by MLet for correctness + * @author Jaroslav Bachorik + * @run testng MLetInternalsTest + */ +public class MLetInternalsTest { + private final static String CONSTRUCT_PARAMETER = "constructParameter"; + + private final static Map testedMethods = new HashMap<>(); + + @BeforeClass + public static void setupClass() { + testedMethods.clear(); + try { + Method m = MLet.class.getDeclaredMethod( + CONSTRUCT_PARAMETER, + String.class, String.class + ); + m.setAccessible(true); + + testedMethods.put(CONSTRUCT_PARAMETER, m); + } catch (Exception ex) { + throw new Error(ex); + } + } + + private MLet mlet; + + @BeforeTest + public void setupTest() { + mlet = new MLet(); + } + + @Test + public void testConstructParameter() throws Exception { + assertEquals(constructParameter("120", "int"), 120); + assertEquals(constructParameter("120", "java.lang.Integer"), Integer.valueOf(120)); + assertEquals(constructParameter("120", "long"), 120L); + assertEquals(constructParameter("120", "java.lang.Long"), Long.valueOf(120)); + assertEquals(constructParameter("120.0", "float"), 120.0f); + assertEquals(constructParameter("120.0", "java.lang.Float"), Float.valueOf(120.0f)); + assertEquals(constructParameter("120.0", "double"), 120.0d); + assertEquals(constructParameter("120", "java.lang.Double"), Double.valueOf(120d)); + assertEquals(constructParameter("120", "java.lang.String"), "120"); + assertEquals(constructParameter("120", "byte"), (byte)120); + assertEquals(constructParameter("120", "java.lang.Byte"), (byte)120); + assertEquals(constructParameter("120", "short"), (short)120); + assertEquals(constructParameter("120", "java.lang.Short"), (short)120); + assertEquals(constructParameter("true", "boolean"), true); + assertEquals(constructParameter("true", "java.lang.Boolean"), Boolean.valueOf(true)); + } + + private Object constructParameter(String param, String type) throws Exception { + return testedMethods.get(CONSTRUCT_PARAMETER).invoke(mlet, param, type); + } +} diff --git a/jdk/test/sun/invoke/util/ValueConversionsTest.java b/jdk/test/sun/invoke/util/ValueConversionsTest.java index 726e90d9dea..483afb7625c 100644 --- a/jdk/test/sun/invoke/util/ValueConversionsTest.java +++ b/jdk/test/sun/invoke/util/ValueConversionsTest.java @@ -25,11 +25,11 @@ package test.sun.invoke.util; import sun.invoke.util.ValueConversions; import sun.invoke.util.Wrapper; +import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.MethodHandle; import java.io.Serializable; import java.util.Arrays; -import java.util.Collections; import org.junit.Test; import static org.junit.Assert.*; @@ -37,24 +37,13 @@ import static org.junit.Assert.*; * @summary unit tests for value-type conversion utilities * @compile -XDignore.symbol.file ValueConversionsTest.java * @run junit/othervm test.sun.invoke.util.ValueConversionsTest - * @run junit/othervm - * -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.START_ARITY=250 - * test.sun.invoke.util.ValueConversionsTest */ -// This might take a while and burn lots of metadata: -// @run junit/othervm -DValueConversionsTest.MAX_ARITY=255 -DValueConversionsTest.EXHAUSTIVE=true test.sun.invoke.util.ValueConversionsTest - /** * * @author jrose */ public class ValueConversionsTest { - private static final Class CLASS = ValueConversionsTest.class; - private static final int MAX_ARITY = Integer.getInteger(CLASS.getSimpleName()+".MAX_ARITY", 40); - private static final int START_ARITY = Integer.getInteger(CLASS.getSimpleName()+".START_ARITY", 0); - private static final boolean EXHAUSTIVE = Boolean.getBoolean(CLASS.getSimpleName()+".EXHAUSTIVE"); - @Test public void testUnbox() throws Throwable { testUnbox(false); @@ -66,9 +55,7 @@ public class ValueConversionsTest { } private void testUnbox(boolean doCast) throws Throwable { - //System.out.println("unbox"); for (Wrapper dst : Wrapper.values()) { - //System.out.println(dst); for (Wrapper src : Wrapper.values()) { testUnbox(doCast, dst, src); } @@ -78,6 +65,7 @@ public class ValueConversionsTest { private void testUnbox(boolean doCast, Wrapper dst, Wrapper src) throws Throwable { boolean expectThrow = !doCast && !dst.isConvertibleFrom(src); if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT) return; // must have prims + if (dst == Wrapper.VOID || src == Wrapper.VOID ) return; // must have values if (dst == Wrapper.OBJECT) expectThrow = false; // everything (even VOID==null here) converts to OBJECT try { @@ -91,9 +79,9 @@ public class ValueConversionsTest { } MethodHandle unboxer; if (doCast) - unboxer = ValueConversions.unboxCast(dst.primitiveType()); + unboxer = ValueConversions.unboxCast(dst); else - unboxer = ValueConversions.unbox(dst.primitiveType()); + unboxer = ValueConversions.unboxWiden(dst); Object expResult = (box == null) ? dst.zero() : dst.wrap(box); Object result = null; switch (dst) { @@ -104,9 +92,7 @@ public class ValueConversionsTest { case CHAR: result = (char) unboxer.invokeExact(box); break; case BYTE: result = (byte) unboxer.invokeExact(box); break; case SHORT: result = (short) unboxer.invokeExact(box); break; - case OBJECT: result = (Object) unboxer.invokeExact(box); break; case BOOLEAN: result = (boolean) unboxer.invokeExact(box); break; - case VOID: result = null; unboxer.invokeExact(box); break; } if (expectThrow) { expResult = "(need an exception)"; @@ -123,25 +109,23 @@ public class ValueConversionsTest { @Test public void testBox() throws Throwable { - //System.out.println("box"); for (Wrapper w : Wrapper.values()) { - if (w == Wrapper.VOID) continue; // skip this; no unboxed form - //System.out.println(w); + if (w == Wrapper.VOID) continue; // skip this; no unboxed form + if (w == Wrapper.OBJECT) continue; // skip this; already unboxed for (int n = -5; n < 10; n++) { Object box = w.wrap(n); - MethodHandle boxer = ValueConversions.box(w.primitiveType()); + MethodHandle boxer = ValueConversions.boxExact(w); Object expResult = box; Object result = null; switch (w) { - case INT: result = boxer.invokeExact(/*int*/n); break; - case LONG: result = boxer.invokeExact((long)n); break; - case FLOAT: result = boxer.invokeExact((float)n); break; - case DOUBLE: result = boxer.invokeExact((double)n); break; - case CHAR: result = boxer.invokeExact((char)n); break; - case BYTE: result = boxer.invokeExact((byte)n); break; - case SHORT: result = boxer.invokeExact((short)n); break; - case OBJECT: result = boxer.invokeExact((Object)n); break; - case BOOLEAN: result = boxer.invokeExact((n & 1) != 0); break; + case INT: result = (Integer) boxer.invokeExact(/*int*/n); break; + case LONG: result = (Long) boxer.invokeExact((long)n); break; + case FLOAT: result = (Float) boxer.invokeExact((float)n); break; + case DOUBLE: result = (Double) boxer.invokeExact((double)n); break; + case CHAR: result = (Character) boxer.invokeExact((char)n); break; + case BYTE: result = (Byte) boxer.invokeExact((byte)n); break; + case SHORT: result = (Short) boxer.invokeExact((short)n); break; + case BOOLEAN: result = (Boolean) boxer.invokeExact((n & 1) != 0); break; } assertEquals("(dst,src,n,box)="+Arrays.asList(w,w,n,box), expResult, result); @@ -151,16 +135,14 @@ public class ValueConversionsTest { @Test public void testCast() throws Throwable { - //System.out.println("cast"); Class[] types = { Object.class, Serializable.class, String.class, Number.class, Integer.class }; Object[] objects = { new Object(), Boolean.FALSE, "hello", (Long)12L, (Integer)6 }; for (Class dst : types) { - MethodHandle caster = ValueConversions.cast(dst); - assertEquals(caster.type(), ValueConversions.identity().type()); + MethodHandle caster = ValueConversions.cast().bindTo(dst); + assertEquals(caster.type(), MethodHandles.identity(Object.class).type()); for (Object obj : objects) { Class src = obj.getClass(); boolean canCast = dst.isAssignableFrom(src); - //System.out.println("obj="+obj+" <: dst="+dst+(canCast ? " (OK)" : " (will fail)")); try { Object result = caster.invokeExact(obj); if (canCast) @@ -175,26 +157,13 @@ public class ValueConversionsTest { } } - @Test - public void testIdentity() throws Throwable { - //System.out.println("identity"); - MethodHandle id = ValueConversions.identity(); - Object expResult = "foo"; - Object result = id.invokeExact(expResult); - // compiler bug: ValueConversions.identity().invokeExact("bar"); - assertEquals(expResult, result); - } - @Test public void testConvert() throws Throwable { - //System.out.println("convert"); for (long tval = 0, ctr = 0;;) { if (++ctr > 99999) throw new AssertionError("too many test values"); - // next test value: - //System.out.println(Long.toHexString(tval)); // prints 3776 test patterns + // prints 3776 test patterns (3776 = 8*59*8) tval = nextTestValue(tval); if (tval == 0) { - //System.out.println("test value count = "+ctr); // 3776 = 8*59*8 break; // repeat } } @@ -205,15 +174,12 @@ public class ValueConversionsTest { } } static void testConvert(Wrapper src, Wrapper dst, long tval) throws Throwable { - //System.out.println(src+" => "+dst); + if (dst == Wrapper.OBJECT || src == Wrapper.OBJECT) return; // must have prims + if (dst == Wrapper.VOID || src == Wrapper.VOID ) return; // must have values boolean testSingleCase = (tval != 0); final long tvalInit = tval; MethodHandle conv = ValueConversions.convertPrimitive(src, dst); - MethodType convType; - if (src == Wrapper.VOID) - convType = MethodType.methodType(dst.primitiveType() /* , void */); - else - convType = MethodType.methodType(dst.primitiveType(), src.primitiveType()); + MethodType convType = MethodType.methodType(dst.primitiveType(), src.primitiveType()); assertEquals(convType, conv.type()); MethodHandle converter = conv.asType(conv.type().changeReturnType(Object.class)); for (;;) { @@ -229,9 +195,7 @@ public class ValueConversionsTest { case CHAR: result = converter.invokeExact((char)n); break; case BYTE: result = converter.invokeExact((byte)n); break; case SHORT: result = converter.invokeExact((short)n); break; - case OBJECT: result = converter.invokeExact((Object)n); break; case BOOLEAN: result = converter.invokeExact((n & 1) != 0); break; - case VOID: result = converter.invokeExact(); break; default: throw new AssertionError(); } assertEquals("(src,dst,n,testValue)="+Arrays.asList(src,dst,"0x"+Long.toHexString(n),testValue), @@ -269,169 +233,4 @@ public class ValueConversionsTest { } return tweakSign(ux); } - - @Test - public void testVarargsArray() throws Throwable { - //System.out.println("varargsArray"); - final int MIN = START_ARITY; - final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added - for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 17, MAX)) { - MethodHandle target = ValueConversions.varargsArray(nargs); - Object[] args = new Object[nargs]; - for (int i = 0; i < nargs; i++) - args[i] = "#"+i; - Object res = target.invokeWithArguments(args); - assertArrayEquals(args, (Object[])res); - } - } - - @Test - public void testVarargsReferenceArray() throws Throwable { - //System.out.println("varargsReferenceArray"); - testTypedVarargsArray(Object[].class); - testTypedVarargsArray(String[].class); - testTypedVarargsArray(Number[].class); - } - - @Test - public void testVarargsPrimitiveArray() throws Throwable { - //System.out.println("varargsPrimitiveArray"); - testTypedVarargsArray(int[].class); - testTypedVarargsArray(long[].class); - testTypedVarargsArray(byte[].class); - testTypedVarargsArray(boolean[].class); - testTypedVarargsArray(short[].class); - testTypedVarargsArray(char[].class); - testTypedVarargsArray(float[].class); - testTypedVarargsArray(double[].class); - } - - private static int nextArgCount(int nargs, int density, int MAX) { - if (EXHAUSTIVE) return nargs + 1; - if (nargs >= MAX) return Integer.MAX_VALUE; - int BOT = 20, TOP = MAX-5; - if (density < 10) { BOT = 10; MAX = TOP-2; } - if (nargs <= BOT || nargs >= TOP) { - ++nargs; - } else { - int bump = Math.max(1, 100 / density); - nargs += bump; - if (nargs > TOP) nargs = TOP; - } - return nargs; - } - - private void testTypedVarargsArray(Class arrayType) throws Throwable { - //System.out.println(arrayType.getSimpleName()); - Class elemType = arrayType.getComponentType(); - int MIN = START_ARITY; - int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added - int density = 3; - if (elemType == int.class || elemType == long.class) density = 7; - if (elemType == long.class || elemType == double.class) { MAX /= 2; MIN /= 2; } - for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, density, MAX)) { - Object[] args = makeTestArray(elemType, nargs); - MethodHandle varargsArray = ValueConversions.varargsArray(arrayType, nargs); - MethodType vaType = varargsArray.type(); - assertEquals(arrayType, vaType.returnType()); - if (nargs != 0) { - assertEquals(elemType, vaType.parameterType(0)); - assertEquals(elemType, vaType.parameterType(vaType.parameterCount()-1)); - } - assertEquals(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType)), - vaType); - Object res = varargsArray.invokeWithArguments(args); - String resString = toArrayString(res); - assertEquals(Arrays.toString(args), resString); - - MethodHandle spreader = varargsArray.asSpreader(arrayType, nargs); - MethodType stype = spreader.type(); - assert(stype == MethodType.methodType(arrayType, arrayType)); - if (nargs <= 5) { - // invoke target as a spreader also: - @SuppressWarnings("cast") - Object res2 = spreader.invokeWithArguments((Object)res); - String res2String = toArrayString(res2); - assertEquals(Arrays.toString(args), res2String); - // invoke the spreader on a generic Object[] array; check for error - try { - Object res3 = spreader.invokeWithArguments((Object)args); - String res3String = toArrayString(res3); - assertTrue(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); - assertEquals(Arrays.toString(args), res3String); - } catch (ClassCastException ex) { - assertFalse(arrayType.getName(), arrayType.isAssignableFrom(Object[].class)); - } - } - if (nargs == 0) { - // invoke spreader on null arglist - Object res3 = spreader.invokeWithArguments((Object)null); - String res3String = toArrayString(res3); - assertEquals(Arrays.toString(args), res3String); - } - } - } - - private static Object[] makeTestArray(Class elemType, int len) { - Wrapper elem = null; - if (elemType.isPrimitive()) - elem = Wrapper.forPrimitiveType(elemType); - else if (Wrapper.isWrapperType(elemType)) - elem = Wrapper.forWrapperType(elemType); - Object[] args = new Object[len]; - for (int i = 0; i < len; i++) { - Object arg = i * 100; - if (elem == null) { - if (elemType == String.class) - arg = "#"+arg; - arg = elemType.cast(arg); // just to make sure - } else { - switch (elem) { - case BOOLEAN: arg = (i % 3 == 0); break; - case CHAR: arg = 'a' + i; break; - case LONG: arg = (long)i * 1000_000_000; break; - case FLOAT: arg = (float)i / 100; break; - case DOUBLE: arg = (double)i / 1000_000; break; - } - arg = elem.cast(arg, elemType); - } - args[i] = arg; - } - //System.out.println(elemType.getName()+Arrays.toString(args)); - return args; - } - - private static String toArrayString(Object a) { - if (a == null) return "null"; - Class elemType = a.getClass().getComponentType(); - if (elemType == null) return a.toString(); - if (elemType.isPrimitive()) { - switch (Wrapper.forPrimitiveType(elemType)) { - case INT: return Arrays.toString((int[])a); - case BYTE: return Arrays.toString((byte[])a); - case BOOLEAN: return Arrays.toString((boolean[])a); - case SHORT: return Arrays.toString((short[])a); - case CHAR: return Arrays.toString((char[])a); - case FLOAT: return Arrays.toString((float[])a); - case LONG: return Arrays.toString((long[])a); - case DOUBLE: return Arrays.toString((double[])a); - } - } - return Arrays.toString((Object[])a); - } - - @Test - public void testVarargsList() throws Throwable { - //System.out.println("varargsList"); - final int MIN = START_ARITY; - final int MAX = MAX_ARITY-2; // 253+1 would cause parameter overflow with 'this' added - for (int nargs = MIN; nargs <= MAX; nargs = nextArgCount(nargs, 7, MAX)) { - MethodHandle target = ValueConversions.varargsList(nargs); - Object[] args = new Object[nargs]; - for (int i = 0; i < nargs; i++) - args[i] = "#"+i; - Object res = target.invokeWithArguments(args); - assertEquals(Arrays.asList(args), res); - } - } } diff --git a/langtools/.hgignore b/langtools/.hgignore index e742c856114..3e5642a05a5 100644 --- a/langtools/.hgignore +++ b/langtools/.hgignore @@ -1,5 +1,6 @@ ^build/ ^dist/ +^webrev ^.idea /nbproject/private/ ^.hgtip diff --git a/langtools/.hgtags b/langtools/.hgtags index 2b07047382b..7000df2d0bb 100644 --- a/langtools/.hgtags +++ b/langtools/.hgtags @@ -269,3 +269,7 @@ aca19c52b24627a6b085cfeb433051a3e420cd81 jdk9-b22 980b18627fd3f4936be2d9e2b013abb461cb2ddb jdk9-b24 d60b572d759449913d02478219ad87e0574a3909 jdk9-b25 5b20a93f8db0b87b902bde48bb7cbb05e88e6e9c jdk9-b26 +10fc81ac75b445528851c9e46c6eeb4438d4e363 jdk9-b27 +016786f793149135ab6b23c71087c1ca12691d77 jdk9-b28 +13705e2ddeb20a78e066595a1709e61f257189e9 jdk9-b29 +ef5427c13e1e741c457a2ed868e3b6d6bf717754 jdk9-b30 diff --git a/langtools/make/CompileInterim.gmk b/langtools/make/CompileInterim.gmk index 2d19f28e362..1a5efd2aedf 100644 --- a/langtools/make/CompileInterim.gmk +++ b/langtools/make/CompileInterim.gmk @@ -41,12 +41,14 @@ $(eval $(call SetupJavaCompilation,BUILD_INTERIM_LANGTOOLS, \ DISABLE_SJAVAC := true, \ SRC := $(LANGTOOLS_TOPDIR)/src/java.compiler/share/classes \ $(LANGTOOLS_TOPDIR)/src/jdk.compiler/share/classes \ + $(LANGTOOLS_TOPDIR)/src/jdk.dev/share/classes \ $(LANGTOOLS_TOPDIR)/src/jdk.javadoc/share/classes \ $(LANGTOOLS_TOPDIR)/src/java.base/share/classes \ $(LANGTOOLS_OUTPUTDIR)/gensrc/jdk.compiler \ + $(LANGTOOLS_OUTPUTDIR)/gensrc/jdk.dev \ $(LANGTOOLS_OUTPUTDIR)/gensrc/jdk.javadoc, \ EXCLUDES := com/sun/tools/javac/nio compileproperties anttasks crules, \ - COPY := $(RESOURCE_SUFFIXES), \ + COPY := $(RESOURCE_SUFFIXES) jdeps.properties jdkinternals.properties version.properties, \ BIN := $(LANGTOOLS_OUTPUTDIR)/interim_classes, \ JAR := $(INTERIM_LANGTOOLS_JAR))) diff --git a/langtools/make/build.properties b/langtools/make/build.properties index ab5f02e4ac9..32e67f2f00c 100644 --- a/langtools/make/build.properties +++ b/langtools/make/build.properties @@ -44,7 +44,7 @@ boot.javac.target = 7 target.java = ${target.java.home}/bin/java # Version info -- override as needed -jdk.version = 1.8.0 +jdk.version = 1.9.0 build.number = b00 milestone = internal @@ -68,7 +68,7 @@ javac.no.jdk.warnings = -XDignore.symbol.file=true # set the following to -version to verify the versions of javac being used javac.version.opt = # in time, there should be no exceptions to -Xlint:all -javac.lint.opts = -Xlint:all -Werror +javac.lint.opts = -Xlint:all,-deprecation -Werror # options for the task for javac #javadoc.jls3.url=http://java.sun.com/docs/books/jls/ @@ -128,17 +128,11 @@ javac.tests = \ javadoc.includes = \ com/sun/javadoc/ \ - com/sun/tools/javadoc/ - -javadoc.tests = \ - tools/javadoc/ - -# - -doclets.includes = \ + com/sun/tools/javadoc/ \ com/sun/tools/doclets/ -doclets.tests = \ +javadoc.tests = \ + tools/javadoc/ \ com/sun/javadoc/ # diff --git a/langtools/make/build.xml b/langtools/make/build.xml index 17894c8c076..2d4488d4f3f 100644 --- a/langtools/make/build.xml +++ b/langtools/make/build.xml @@ -83,7 +83,7 @@ - global property definitions - general top level targets - general diagnostic/debugging targets - - groups of targets for each tool: javac, javadoc, doclets, javah, javap + - groups of targets for each tool: javac, javadoc, javah, javap Within each group, the following targets are provided, where applicable build-bootstrap-TOOL build the bootstrap version of the tool build-classes-TOOL build the classes for the tool @@ -252,15 +252,15 @@ - + @@ -615,7 +615,7 @@ + jarclasspath="javac.jar"/> @@ -625,7 +625,7 @@ + jarclasspath="javac.jar"/> @@ -643,40 +643,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -684,7 +650,7 @@ + jarclasspath="javadoc.jar javac.jar"/> diff --git a/langtools/make/intellij/codeStyleSettings.xml b/langtools/make/intellij/codeStyleSettings.xml index 54942ddbb48..ae9f490e60c 100644 --- a/langtools/make/intellij/codeStyleSettings.xml +++ b/langtools/make/intellij/codeStyleSettings.xml @@ -3,6 +3,7 @@ diff --git a/langtools/make/intellij/langtools.iml b/langtools/make/intellij/langtools.iml index fd7d1b4d9ac..57af163ce76 100644 --- a/langtools/make/intellij/langtools.iml +++ b/langtools/make/intellij/langtools.iml @@ -6,10 +6,14 @@ - - + + + + + + - + diff --git a/langtools/make/intellij/workspace.xml b/langtools/make/intellij/workspace.xml index ad456effa7d..eb5e3adb181 100644 --- a/langtools/make/intellij/workspace.xml +++ b/langtools/make/intellij/workspace.xml @@ -142,6 +142,9 @@ + +

TODO: this method is useless here

- * - * @param file a file - * @return a JavaFileObject from the standard file manager. - */ - public JavaFileObject asJavaFileObject(File file) { - JavacFileManager fm = (JavacFileManager)context.get(JavaFileManager.class); - return fm.getRegularFile(file); + @Override @DefinedBy(Api.COMPILER_TREE) + public Iterable parse() { + return handleExceptions(new Callable>() { + @Override + public Iterable call() { + return parseInternal(); + } + }, List.nil(), List.nil()); } - /** - * Parse the specified files returning a list of abstract syntax trees. - * - * @throws java.io.IOException TODO - * @return a list of abstract syntax trees - */ - public Iterable parse() throws IOException { + private Iterable parseInternal() { try { - prepareCompiler(); - List units = compiler.parseFiles(fileObjects); + prepareCompiler(true); + List units = compiler.parseFiles(args.getFileObjects()); for (JCCompilationUnit unit: units) { JavaFileObject file = unit.getSourceFile(); if (notYetEntered.containsKey(file)) @@ -263,11 +240,10 @@ public class JavacTaskImpl extends BasicJavacTask { /** * Translate all the abstract syntax trees to elements. * - * @throws IOException TODO * @return a list of elements corresponding to the top level * classes in the abstract syntax trees */ - public Iterable enter() throws IOException { + public Iterable enter() { return enter(null); } @@ -275,17 +251,15 @@ public class JavacTaskImpl extends BasicJavacTask { * Translate the given abstract syntax trees to elements. * * @param trees a list of abstract syntax trees. - * @throws java.io.IOException TODO * @return a list of elements corresponding to the top level * classes in the abstract syntax trees */ public Iterable enter(Iterable trees) - throws IOException { if (trees == null && notYetEntered != null && notYetEntered.isEmpty()) return List.nil(); - prepareCompiler(); + prepareCompiler(true); ListBuffer roots = null; @@ -296,8 +270,8 @@ public class JavacTaskImpl extends BasicJavacTask { // list to be entered. if (notYetEntered.size() > 0) { if (!parsed) - parse(); // TODO would be nice to specify files needed to be parsed - for (JavaFileObject file: fileObjects) { + parseInternal(); // TODO would be nice to specify files needed to be parsed + for (JavaFileObject file: args.getFileObjects()) { JCCompilationUnit unit = notYetEntered.remove(file); if (unit != null) { if (roots == null) @@ -347,13 +321,14 @@ public class JavacTaskImpl extends BasicJavacTask { } } - /** - * Complete all analysis. - * @throws IOException TODO - */ - @Override - public Iterable analyze() throws IOException { - return analyze(null); + @Override @DefinedBy(Api.COMPILER_TREE) + public Iterable analyze() { + return handleExceptions(new Callable>() { + @Override + public Iterable call() { + return analyze(null); + } + }, List.nil(), List.nil()); } /** @@ -363,11 +338,12 @@ public class JavacTaskImpl extends BasicJavacTask { * If null is specified, all outstanding classes will be analyzed. * * @param classes a list of class elements + * @return the elements that were analyzed */ // This implementation requires that we open up privileges on JavaCompiler. // An alternative implementation would be to move this code to JavaCompiler and // wrap it here - public Iterable analyze(Iterable classes) throws IOException { + public Iterable analyze(Iterable classes) { enter(null); // ensure all classes have been entered final ListBuffer results = new ListBuffer<>(); @@ -376,6 +352,7 @@ public class JavacTaskImpl extends BasicJavacTask { handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results); } else { Filter f = new Filter() { + @Override public void process(Env env) { handleFlowResults(compiler.flow(compiler.attribute(env)), results); } @@ -406,14 +383,14 @@ public class JavacTaskImpl extends BasicJavacTask { genList.addAll(queue); } - - /** - * Generate code. - * @throws IOException TODO - */ - @Override - public Iterable generate() throws IOException { - return generate(null); + @Override @DefinedBy(Api.COMPILER_TREE) + public Iterable generate() { + return handleExceptions(new Callable>() { + @Override + public Iterable call() { + return generate(null); + } + }, List.nil(), List.nil()); } /** @@ -424,8 +401,9 @@ public class JavacTaskImpl extends BasicJavacTask { * If null is specified, code will be generated for all outstanding classes. * * @param classes a list of class elements + * @return the files that were generated */ - public Iterable generate(Iterable classes) throws IOException { + public Iterable generate(Iterable classes) { final ListBuffer results = new ListBuffer<>(); try { analyze(null); // ensure all classes have been parsed, entered, and analyzed @@ -436,6 +414,7 @@ public class JavacTaskImpl extends BasicJavacTask { } else { Filter f = new Filter() { + @Override public void process(Env env) { compiler.generate(compiler.desugar(ListBuffer.of(env)), results); } @@ -454,26 +433,6 @@ public class JavacTaskImpl extends BasicJavacTask { return results; } - public TypeMirror getTypeMirror(Iterable path) { - // TODO: Should complete attribution if necessary - Tree last = null; - for (Tree node : path) - last = node; - return ((JCTree)last).type; - } - - public JavacElements getElements() { - if (context == null) - throw new IllegalStateException(); - return JavacElements.instance(context); - } - - public JavacTypes getTypes() { - if (context == null) - throw new IllegalStateException(); - return JavacTypes.instance(context); - } - public Iterable pathFor(CompilationUnitTree unit, Tree node) { return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse(); } @@ -503,6 +462,10 @@ public class JavacTaskImpl extends BasicJavacTask { /** * For internal use only. This method will be * removed without warning. + * @param expr the type expression to be analyzed + * @param scope the scope in which to analyze the type expression + * @return the type + * @throws IllegalArgumentException if the type expression of null or empty */ public Type parseType(String expr, TypeElement scope) { if (expr == null || expr.equals("")) diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java index 1b9521c05ec..b3826331f71 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java @@ -33,23 +33,22 @@ import java.io.Writer; import java.nio.charset.Charset; import java.util.Collections; import java.util.EnumSet; -import java.util.Iterator; import java.util.Locale; import java.util.Set; + import javax.lang.model.SourceVersion; import javax.tools.*; import com.sun.source.util.JavacTask; import com.sun.tools.javac.file.JavacFileManager; -import com.sun.tools.javac.main.Main; +import com.sun.tools.javac.main.Arguments; import com.sun.tools.javac.main.Option; -import com.sun.tools.javac.main.OptionHelper; -import com.sun.tools.javac.main.OptionHelper.GrumpyHelper; import com.sun.tools.javac.util.ClientCodeException; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.DefinedBy; +import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.Log.PrefixKind; -import com.sun.tools.javac.util.Options; +import com.sun.tools.javac.util.PropagatedException; /** * TODO: describe com.sun.tools.javac.api.Tool @@ -81,6 +80,7 @@ public final class JavacTool implements JavaCompiler { return new JavacTool(); } + @Override @DefinedBy(Api.COMPILER) public JavacFileManager getStandardFileManager( DiagnosticListener diagnosticListener, Locale locale, @@ -96,7 +96,7 @@ public final class JavacTool implements JavaCompiler { return new JavacFileManager(context, true, charset); } - @Override + @Override @DefinedBy(Api.COMPILER) public JavacTask getTask(Writer out, JavaFileManager fileManager, DiagnosticListener diagnosticListener, @@ -109,6 +109,7 @@ public final class JavacTool implements JavaCompiler { context); } + /* Internal version of getTask, allowing context to be provided. */ public JavacTask getTask(Writer out, JavaFileManager fileManager, DiagnosticListener diagnosticListener, @@ -120,14 +121,17 @@ public final class JavacTool implements JavaCompiler { try { ClientCodeWrapper ccw = ClientCodeWrapper.instance(context); - if (options != null) + if (options != null) { for (String option : options) option.getClass(); // null check + } + if (classes != null) { for (String cls : classes) if (!SourceVersion.isName(cls)) // implicit null check throw new IllegalArgumentException("Not a valid class name: " + cls); } + if (compilationUnits != null) { compilationUnits = ccw.wrapJavaFileObjects(compilationUnits); // implicit null check for (JavaFileObject cu : compilationUnits) { @@ -153,82 +157,17 @@ public final class JavacTool implements JavaCompiler { context.put(JavaFileManager.class, fileManager); - processOptions(context, fileManager, options); - Main compiler = new Main("javacTask", context.get(Log.outKey)); - return new JavacTaskImpl(compiler, options, context, classes, compilationUnits); + Arguments args = Arguments.instance(context); + args.init("javac", options, classes, compilationUnits); + return new JavacTaskImpl(context); + } catch (PropagatedException ex) { + throw ex.getCause(); } catch (ClientCodeException ex) { throw new RuntimeException(ex.getCause()); } } - private void processOptions(Context context, - JavaFileManager fileManager, - Iterable options) - { - if (options == null) - return; - - final Options optionTable = Options.instance(context); - Log log = Log.instance(context); - - Option[] recognizedOptions = - Option.getJavacToolOptions().toArray(new Option[0]); - OptionHelper optionHelper = new GrumpyHelper(log) { - @Override - public String get(Option option) { - return optionTable.get(option.getText()); - } - - @Override - public void put(String name, String value) { - optionTable.put(name, value); - } - - @Override - public void remove(String name) { - optionTable.remove(name); - } - }; - - Iterator flags = options.iterator(); - while (flags.hasNext()) { - String flag = flags.next(); - int j; - for (j=0; j getSourceVersions() { return Collections.unmodifiableSet(EnumSet.range(SourceVersion.RELEASE_3, SourceVersion.latest())); } + @Override @DefinedBy(Api.COMPILER) public int isSupportedOption(String option) { Set