diff --git a/.github/scripts/gen-build-failure-report.sh b/.github/scripts/gen-build-failure-report.sh index fd3215fc7fe..c8336a5f7a1 100644 --- a/.github/scripts/gen-build-failure-report.sh +++ b/.github/scripts/gen-build-failure-report.sh @@ -24,12 +24,19 @@ # questions. # +# Import common utils +. report-utils.sh + GITHUB_STEP_SUMMARY="$1" BUILD_DIR="$(ls -d build/*)" # Send signal to the do-build action that we failed touch "$BUILD_DIR/build-failure" +# Collect hs_errs for build-time crashes, e.g. javac, jmod, jlink, CDS. +# These usually land in make/ +hs_err_files=$(ls make/hs_err*.log 2> /dev/null || true) + ( echo '### :boom: Build failure summary' echo '' @@ -46,6 +53,20 @@ touch "$BUILD_DIR/build-failure" echo '' echo '' + for hs_err in $hs_err_files; do + echo "
View HotSpot error log: "$hs_err"" + echo '' + echo '```' + echo "$hs_err:" + echo '' + cat "$hs_err" + echo '```' + echo '
' + echo '' + done + echo '' echo ':arrow_right: To see the entire test log, click the job in the list to the left. To download logs, see the `failure-logs` [artifact above](#artifacts).' ) >> $GITHUB_STEP_SUMMARY + +truncate_summary diff --git a/.github/scripts/gen-test-results.sh b/.github/scripts/gen-test-results.sh index 9e85eef4dc0..6c6cbaa3740 100644 --- a/.github/scripts/gen-test-results.sh +++ b/.github/scripts/gen-test-results.sh @@ -24,6 +24,9 @@ # questions. # +# Import common utils +. report-utils.sh + GITHUB_STEP_SUMMARY="$1" test_suite_name=$(cat build/run-test-prebuilt/test-support/test-last-ids.txt) @@ -89,18 +92,6 @@ for test in $failures $errors; do fi done >> $GITHUB_STEP_SUMMARY -# With many failures, the summary can easily exceed 1024 kB, the limit set by Github -# Trim it down if so. -summary_size=$(wc -c < $GITHUB_STEP_SUMMARY) -if [[ $summary_size -gt 1000000 ]]; then - # Trim to below 1024 kB, and cut off after the last detail group - head -c 1000000 $GITHUB_STEP_SUMMARY | tac | sed -n -e '/<\/details>/,$ p' | tac > $GITHUB_STEP_SUMMARY.tmp - mv $GITHUB_STEP_SUMMARY.tmp $GITHUB_STEP_SUMMARY - ( - echo '' - echo ':x: **WARNING: Summary is too large and has been truncated.**' - echo '' - ) >> $GITHUB_STEP_SUMMARY -fi - echo ':arrow_right: To see the entire test log, click the job in the list to the left.' >> $GITHUB_STEP_SUMMARY + +truncate_summary diff --git a/.github/scripts/report-utils.sh b/.github/scripts/report-utils.sh new file mode 100644 index 00000000000..da5b6c04b3c --- /dev/null +++ b/.github/scripts/report-utils.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# +# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# 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. +# + +function truncate_summary() { + # With large hs_errs, the summary can easily exceed 1024 kB, the limit set by Github + # Trim it down if so. + summary_size=$(wc -c < $GITHUB_STEP_SUMMARY) + if [[ $summary_size -gt 1000000 ]]; then + # Trim to below 1024 kB, and cut off after the last detail group + head -c 1000000 $GITHUB_STEP_SUMMARY | tac | sed -n -e '/<\/details>/,$ p' | tac > $GITHUB_STEP_SUMMARY.tmp + mv $GITHUB_STEP_SUMMARY.tmp $GITHUB_STEP_SUMMARY + ( + echo '' + echo ':x: **WARNING: Summary is too large and has been truncated.**' + echo '' + ) >> $GITHUB_STEP_SUMMARY + fi +} diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index 439d56543c7..62478dc5f95 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -29,21 +29,21 @@ GTEST_VERSION=1.14.0 JTREG_VERSION=7.4+1 LINUX_X64_BOOT_JDK_EXT=tar.gz -LINUX_X64_BOOT_JDK_URL=https://github.com/SAP/SapMachine/releases/download/sapmachine-22.0.2/sapmachine-jdk-22.0.2_linux-x64_bin.tar.gz -LINUX_X64_BOOT_JDK_SHA256=aac2bbbd41c40b9c185a26b2ec8f72d78987b48d06855d76e14f633cc823ff4a +LINUX_X64_BOOT_JDK_URL=https://github.com/SAP/SapMachine/releases/download/sapmachine-23/sapmachine-jdk-23_linux-x64_bin.tar.gz +LINUX_X64_BOOT_JDK_SHA256=d0211ad74b6b25aa9f2ed6a9aa77da2d81e48aa7036c0e7a5bac904ecd1c8225 ALPINE_LINUX_X64_BOOT_JDK_EXT=tar.gz -ALPINE_LINUX_X64_BOOT_JDK_URL=https://github.com/adoptium/temurin22-binaries/releases/download/jdk-22.0.2%2B9/OpenJDK22U-jdk_x64_alpine-linux_hotspot_22.0.2_9.tar.gz -ALPINE_LINUX_X64_BOOT_JDK_SHA256=49f73414824b1a7c268a611225fa4d7ce5e25600201e0f1cd59f94d1040b5264 +ALPINE_LINUX_X64_BOOT_JDK_URL=https://github.com/SAP/SapMachine/releases/download/sapmachine-23/sapmachine-jdk-23_linux-x64-musl_bin.tar.gz +ALPINE_LINUX_X64_BOOT_JDK_SHA256=39987d950d36b997295737c671c5f104be1dd6dee7a1611244d50a4df57213e0 MACOS_AARCH64_BOOT_JDK_EXT=tar.gz -MACOS_AARCH64_BOOT_JDK_URL=https://github.com/SAP/SapMachine/releases/download/sapmachine-22.0.2/sapmachine-jdk-22.0.2_macos-aarch64_bin.tar.gz -MACOS_AARCH64_BOOT_JDK_SHA256=5ecf2ea143bd130a1748533d1352bea8c0857fef6050f66ed33379ca15ba82ad +MACOS_AARCH64_BOOT_JDK_URL=https://github.com/SAP/SapMachine/releases/download/sapmachine-23/sapmachine-jdk-23_macos-aarch64_bin.tar.gz +MACOS_AARCH64_BOOT_JDK_SHA256=8c5414b627e9c2616a77f614363b276dca9ac814bffd769eb4aebd9e9fddbafe MACOS_X64_BOOT_JDK_EXT=tar.gz -MACOS_X64_BOOT_JDK_URL=https://github.com/SAP/SapMachine/releases/download/sapmachine-22.0.2/sapmachine-jdk-22.0.2_macos-x64_bin.tar.gz -MACOS_X64_BOOT_JDK_SHA256=a036ed70758880e88ababb05cae2954bf01974ddaee3cac3c51348e05c47d2a5 +MACOS_X64_BOOT_JDK_URL=https://github.com/SAP/SapMachine/releases/download/sapmachine-23/sapmachine-jdk-23_macos-x64_bin.tar.gz +MACOS_X64_BOOT_JDK_SHA256=2125eb7250d331fa18521375d954fdd9efed611308eb72cf732c8c8fd5a877af WINDOWS_X64_BOOT_JDK_EXT=zip -WINDOWS_X64_BOOT_JDK_URL=https://github.com/SAP/SapMachine/releases/download/sapmachine-22.0.2/sapmachine-jdk-22.0.2_windows-x64_bin.zip -WINDOWS_X64_BOOT_JDK_SHA256=0c456f7b0827f061220ed661ec108965ac7860a9bc189ceb048ad1ab2c0e2e06 +WINDOWS_X64_BOOT_JDK_URL=https://github.com/SAP/SapMachine/releases/download/sapmachine-23/sapmachine-jdk-23_windows-x64_bin.zip +WINDOWS_X64_BOOT_JDK_SHA256=d4c86b3374f1c82615c1c92eb760d4c7153395a9856df34bb93ede9d1c077de4 diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 30c45d4cde1..a85c20b2098 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -390,8 +390,8 @@ var getJibProfilesCommon = function (input, data) { }; }; - common.boot_jdk_version = "22"; - common.boot_jdk_build_number = "36"; + common.boot_jdk_version = "23"; + common.boot_jdk_build_number = "37"; common.boot_jdk_home = input.get("boot_jdk", "install_path") + "/jdk-" + common.boot_jdk_version + (input.build_os == "macosx" ? ".jdk/Contents/Home" : ""); diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index 1d47c2cddd0..055f9ca8866 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -37,6 +37,6 @@ DEFAULT_VERSION_DATE=2025-03-18 DEFAULT_VERSION_CLASSFILE_MAJOR=68 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 -DEFAULT_ACCEPTABLE_BOOT_VERSIONS="22 23 24" +DEFAULT_ACCEPTABLE_BOOT_VERSIONS="23 24" DEFAULT_JDK_SOURCE_TARGET_VERSION=24 DEFAULT_PROMOTED_VERSION_PRE=ea diff --git a/make/modules/jdk.hotspot.agent/Lib.gmk b/make/modules/jdk.hotspot.agent/Lib.gmk index f0ede594d0c..12f1c1f2a90 100644 --- a/make/modules/jdk.hotspot.agent/Lib.gmk +++ b/make/modules/jdk.hotspot.agent/Lib.gmk @@ -59,9 +59,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBSAPROC, \ OPTIMIZATION := HIGH, \ EXTRA_HEADER_DIRS := java.base:libjvm, \ DISABLED_WARNINGS_gcc := sign-compare, \ - DISABLED_WARNINGS_gcc_LinuxDebuggerLocal.cpp := unused-variable, \ DISABLED_WARNINGS_gcc_ps_core.c := pointer-arith, \ - DISABLED_WARNINGS_gcc_symtab.c := unused-but-set-variable, \ DISABLED_WARNINGS_clang := sign-compare, \ DISABLED_WARNINGS_clang_libproc_impl.c := format-nonliteral, \ DISABLED_WARNINGS_clang_MacosxDebuggerLocal.m := unused-variable, \ diff --git a/make/modules/jdk.incubator.vector/Lib.gmk b/make/modules/jdk.incubator.vector/Lib.gmk index 0620549f05c..bf6ace6f97f 100644 --- a/make/modules/jdk.incubator.vector/Lib.gmk +++ b/make/modules/jdk.incubator.vector/Lib.gmk @@ -37,3 +37,21 @@ ifeq ($(call isTargetOs, linux windows)+$(call isTargetCpu, x86_64)+$(INCLUDE_CO TARGETS += $(BUILD_LIBJSVML) endif + +################################################################################ +## Build libsleef +################################################################################ + +ifeq ($(call isTargetOs, linux)+$(call isTargetCpu, riscv64)+$(INCLUDE_COMPILER2), true+true+true) + $(eval $(call SetupJdkLibrary, BUILD_LIBSLEEF, \ + NAME := sleef, \ + OPTIMIZATION := HIGH, \ + SRC := libsleef/lib, \ + EXTRA_SRC := libsleef/generated, \ + DISABLED_WARNINGS_gcc := unused-function sign-compare tautological-compare ignored-qualifiers, \ + DISABLED_WARNINGS_clang := unused-function sign-compare tautological-compare ignored-qualifiers, \ + CFLAGS := -march=rv64gcv, \ + )) + + TARGETS += $(BUILD_LIBSLEEF) +endif diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 7d2a35cefd8..c7cae54d14c 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2721,7 +2721,7 @@ typedef void (MacroAssembler::* mem_vector_insn)(FloatRegister Rt, { Address addr = mem2address(opcode, base, index, scale, disp); if (addr.getMode() == Address::base_plus_offset) { - // Fix up any out-of-range offsets. + /* Fix up any out-of-range offsets. */ assert_different_registers(rscratch1, base); assert_different_registers(rscratch1, reg); addr = __ legitimize_address(addr, size_in_memory, rscratch1); @@ -2762,11 +2762,7 @@ typedef void (MacroAssembler::* mem_vector_insn)(FloatRegister Rt, int opcode, Register base, int index, int size, int disp) { if (index == -1) { - // Fix up any out-of-range offsets. - assert_different_registers(rscratch1, base); - Address addr = Address(base, disp); - addr = __ legitimize_address(addr, (1 << T), rscratch1); - (masm->*insn)(reg, T, addr); + (masm->*insn)(reg, T, Address(base, disp)); } else { assert(disp == 0, "unsupported address mode"); (masm->*insn)(reg, T, Address(base, as_Register(index), Address::lsl(size))); @@ -2821,7 +2817,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrsbw(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrsbw(iRegI dst, memory1 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrsbw, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -2829,7 +2825,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrsb(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrsb(iRegI dst, memory1 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrsb, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -2837,7 +2833,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrb(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrb(iRegI dst, memory1 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrb, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -2845,7 +2841,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrb(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldrb(iRegL dst, memory1 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrb, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -2853,7 +2849,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrshw(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrshw(iRegI dst, memory2 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrshw, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); @@ -2861,7 +2857,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrsh(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrsh(iRegI dst, memory2 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrsh, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); @@ -2869,7 +2865,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrh(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrh(iRegI dst, memory2 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrh, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); @@ -2877,7 +2873,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrh(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldrh(iRegL dst, memory2 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrh, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); @@ -2885,7 +2881,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrw(iRegI dst, memory mem) %{ + enc_class aarch64_enc_ldrw(iRegI dst, memory4 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrw, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -2893,7 +2889,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrw(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldrw(iRegL dst, memory4 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrw, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -2901,7 +2897,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrsw(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldrsw(iRegL dst, memory4 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldrsw, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -2909,7 +2905,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldr(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldr(iRegL dst, memory8 mem) %{ Register dst_reg = as_Register($dst$$reg); loadStore(masm, &MacroAssembler::ldr, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 8); @@ -2917,7 +2913,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrs(vRegF dst, memory mem) %{ + enc_class aarch64_enc_ldrs(vRegF dst, memory4 mem) %{ FloatRegister dst_reg = as_FloatRegister($dst$$reg); loadStore(masm, &MacroAssembler::ldrs, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -2925,7 +2921,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_ldrd(vRegD dst, memory mem) %{ + enc_class aarch64_enc_ldrd(vRegD dst, memory8 mem) %{ FloatRegister dst_reg = as_FloatRegister($dst$$reg); loadStore(masm, &MacroAssembler::ldrd, dst_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 8); @@ -2933,7 +2929,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strb(iRegI src, memory mem) %{ + enc_class aarch64_enc_strb(iRegI src, memory1 mem) %{ Register src_reg = as_Register($src$$reg); loadStore(masm, &MacroAssembler::strb, src_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -2941,14 +2937,14 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strb0(memory mem) %{ + enc_class aarch64_enc_strb0(memory1 mem) %{ loadStore(masm, &MacroAssembler::strb, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); %} // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strh(iRegI src, memory mem) %{ + enc_class aarch64_enc_strh(iRegI src, memory2 mem) %{ Register src_reg = as_Register($src$$reg); loadStore(masm, &MacroAssembler::strh, src_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); @@ -2956,14 +2952,14 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strh0(memory mem) %{ + enc_class aarch64_enc_strh0(memory2 mem) %{ loadStore(masm, &MacroAssembler::strh, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 2); %} // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strw(iRegI src, memory mem) %{ + enc_class aarch64_enc_strw(iRegI src, memory4 mem) %{ Register src_reg = as_Register($src$$reg); loadStore(masm, &MacroAssembler::strw, src_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -2971,14 +2967,14 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strw0(memory mem) %{ + enc_class aarch64_enc_strw0(memory4 mem) %{ loadStore(masm, &MacroAssembler::strw, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); %} // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_str(iRegL src, memory mem) %{ + enc_class aarch64_enc_str(iRegL src, memory8 mem) %{ Register src_reg = as_Register($src$$reg); // we sometimes get asked to store the stack pointer into the // current thread -- we cannot do that directly on AArch64 @@ -2993,14 +2989,14 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_str0(memory mem) %{ + enc_class aarch64_enc_str0(memory8 mem) %{ loadStore(masm, &MacroAssembler::str, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 8); %} // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strs(vRegF src, memory mem) %{ + enc_class aarch64_enc_strs(vRegF src, memory4 mem) %{ FloatRegister src_reg = as_FloatRegister($src$$reg); loadStore(masm, &MacroAssembler::strs, src_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 4); @@ -3008,7 +3004,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strd(vRegD src, memory mem) %{ + enc_class aarch64_enc_strd(vRegD src, memory8 mem) %{ FloatRegister src_reg = as_FloatRegister($src$$reg); loadStore(masm, &MacroAssembler::strd, src_reg, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 8); @@ -3016,7 +3012,7 @@ encode %{ // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strb0_ordered(memory mem) %{ + enc_class aarch64_enc_strb0_ordered(memory4 mem) %{ __ membar(Assembler::StoreStore); loadStore(masm, &MacroAssembler::strb, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); @@ -3218,7 +3214,7 @@ encode %{ // synchronized read/update encodings - enc_class aarch64_enc_ldaxr(iRegL dst, memory mem) %{ + enc_class aarch64_enc_ldaxr(iRegL dst, memory8 mem) %{ Register dst_reg = as_Register($dst$$reg); Register base = as_Register($mem$$base); int index = $mem$$index; @@ -3246,7 +3242,7 @@ encode %{ } %} - enc_class aarch64_enc_stlxr(iRegLNoSp src, memory mem) %{ + enc_class aarch64_enc_stlxr(iRegLNoSp src, memory8 mem) %{ Register src_reg = as_Register($src$$reg); Register base = as_Register($mem$$base); int index = $mem$$index; @@ -4174,10 +4170,60 @@ operand immIU7() interface(CONST_INTER); %} -// Offset for immediate loads and stores +// Offset for scaled or unscaled immediate loads and stores operand immIOffset() %{ - predicate(n->get_int() >= -256 && n->get_int() <= 65520); + predicate(Address::offset_ok_for_immed(n->get_int(), 0)); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIOffset1() +%{ + predicate(Address::offset_ok_for_immed(n->get_int(), 0)); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIOffset2() +%{ + predicate(Address::offset_ok_for_immed(n->get_int(), 1)); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIOffset4() +%{ + predicate(Address::offset_ok_for_immed(n->get_int(), 2)); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIOffset8() +%{ + predicate(Address::offset_ok_for_immed(n->get_int(), 3)); + match(ConI); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immIOffset16() +%{ + predicate(Address::offset_ok_for_immed(n->get_int(), 4)); match(ConI); op_cost(0); @@ -4195,6 +4241,56 @@ operand immLOffset() interface(CONST_INTER); %} +operand immLoffset1() +%{ + predicate(Address::offset_ok_for_immed(n->get_long(), 0)); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immLoffset2() +%{ + predicate(Address::offset_ok_for_immed(n->get_long(), 1)); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immLoffset4() +%{ + predicate(Address::offset_ok_for_immed(n->get_long(), 2)); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immLoffset8() +%{ + predicate(Address::offset_ok_for_immed(n->get_long(), 3)); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + +operand immLoffset16() +%{ + predicate(Address::offset_ok_for_immed(n->get_long(), 4)); + match(ConL); + + op_cost(0); + format %{ %} + interface(CONST_INTER); +%} + // 5 bit signed long integer operand immL5() %{ @@ -5107,7 +5203,21 @@ operand indIndex(iRegP reg, iRegL lreg) %} %} -operand indOffI(iRegP reg, immIOffset off) +operand indOffI1(iRegP reg, immIOffset1 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffI2(iRegP reg, immIOffset2 off) %{ constraint(ALLOC_IN_RC(ptr_reg)); match(AddP reg off); @@ -5121,7 +5231,105 @@ operand indOffI(iRegP reg, immIOffset off) %} %} -operand indOffL(iRegP reg, immLOffset off) +operand indOffI4(iRegP reg, immIOffset4 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffI8(iRegP reg, immIOffset8 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffI16(iRegP reg, immIOffset16 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffL1(iRegP reg, immLoffset1 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffL2(iRegP reg, immLoffset2 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffL4(iRegP reg, immLoffset4 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffL8(iRegP reg, immLoffset8 off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP reg off); + op_cost(0); + format %{ "[$reg, $off]" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + +operand indOffL16(iRegP reg, immLoffset16 off) %{ constraint(ALLOC_IN_RC(ptr_reg)); match(AddP reg off); @@ -5497,7 +5705,10 @@ operand iRegL2P(iRegL reg) %{ interface(REG_INTER) %} -opclass vmem(indirect, indIndex, indOffI, indOffL, indOffIN, indOffLN); +opclass vmem2(indirect, indIndex, indOffI2, indOffL2); +opclass vmem4(indirect, indIndex, indOffI4, indOffL4); +opclass vmem8(indirect, indIndex, indOffI8, indOffL8); +opclass vmem16(indirect, indIndex, indOffI16, indOffL16); //----------OPERAND CLASSES---------------------------------------------------- // Operand Classes are groups of operands that are used as to simplify @@ -5509,9 +5720,23 @@ opclass vmem(indirect, indIndex, indOffI, indOffL, indOffIN, indOffLN); // memory is used to define read/write location for load/store // instruction defs. we can turn a memory op into an Address -opclass memory(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI, indOffL, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, - indOffLN, indirectX2P, indOffX2P); +opclass memory1(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI1, indOffL1, + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indirectX2P, indOffX2P); + +opclass memory2(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI2, indOffL2, + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indirectX2P, indOffX2P); + +opclass memory4(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI4, indOffL4, + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); + +opclass memory8(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI8, indOffL8, + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); + +// All of the memory operands. For the pipeline description. +opclass memory(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, + indOffI1, indOffL1, indOffI2, indOffL2, indOffI4, indOffL4, indOffI8, indOffL8, + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); + // iRegIorL2I is used for src inputs in rules for 32 bit int (I) // operations. it allows the src to be either an iRegI or a (ConvL2I @@ -6213,7 +6438,7 @@ define %{ // Load Instructions // Load Byte (8 bit signed) -instruct loadB(iRegINoSp dst, memory mem) +instruct loadB(iRegINoSp dst, memory1 mem) %{ match(Set dst (LoadB mem)); predicate(!needs_acquiring_load(n)); @@ -6227,7 +6452,7 @@ instruct loadB(iRegINoSp dst, memory mem) %} // Load Byte (8 bit signed) into long -instruct loadB2L(iRegLNoSp dst, memory mem) +instruct loadB2L(iRegLNoSp dst, memory1 mem) %{ match(Set dst (ConvI2L (LoadB mem))); predicate(!needs_acquiring_load(n->in(1))); @@ -6241,7 +6466,7 @@ instruct loadB2L(iRegLNoSp dst, memory mem) %} // Load Byte (8 bit unsigned) -instruct loadUB(iRegINoSp dst, memory mem) +instruct loadUB(iRegINoSp dst, memory1 mem) %{ match(Set dst (LoadUB mem)); predicate(!needs_acquiring_load(n)); @@ -6255,7 +6480,7 @@ instruct loadUB(iRegINoSp dst, memory mem) %} // Load Byte (8 bit unsigned) into long -instruct loadUB2L(iRegLNoSp dst, memory mem) +instruct loadUB2L(iRegLNoSp dst, memory1 mem) %{ match(Set dst (ConvI2L (LoadUB mem))); predicate(!needs_acquiring_load(n->in(1))); @@ -6269,7 +6494,7 @@ instruct loadUB2L(iRegLNoSp dst, memory mem) %} // Load Short (16 bit signed) -instruct loadS(iRegINoSp dst, memory mem) +instruct loadS(iRegINoSp dst, memory2 mem) %{ match(Set dst (LoadS mem)); predicate(!needs_acquiring_load(n)); @@ -6283,7 +6508,7 @@ instruct loadS(iRegINoSp dst, memory mem) %} // Load Short (16 bit signed) into long -instruct loadS2L(iRegLNoSp dst, memory mem) +instruct loadS2L(iRegLNoSp dst, memory2 mem) %{ match(Set dst (ConvI2L (LoadS mem))); predicate(!needs_acquiring_load(n->in(1))); @@ -6297,7 +6522,7 @@ instruct loadS2L(iRegLNoSp dst, memory mem) %} // Load Char (16 bit unsigned) -instruct loadUS(iRegINoSp dst, memory mem) +instruct loadUS(iRegINoSp dst, memory2 mem) %{ match(Set dst (LoadUS mem)); predicate(!needs_acquiring_load(n)); @@ -6311,7 +6536,7 @@ instruct loadUS(iRegINoSp dst, memory mem) %} // Load Short/Char (16 bit unsigned) into long -instruct loadUS2L(iRegLNoSp dst, memory mem) +instruct loadUS2L(iRegLNoSp dst, memory2 mem) %{ match(Set dst (ConvI2L (LoadUS mem))); predicate(!needs_acquiring_load(n->in(1))); @@ -6325,7 +6550,7 @@ instruct loadUS2L(iRegLNoSp dst, memory mem) %} // Load Integer (32 bit signed) -instruct loadI(iRegINoSp dst, memory mem) +instruct loadI(iRegINoSp dst, memory4 mem) %{ match(Set dst (LoadI mem)); predicate(!needs_acquiring_load(n)); @@ -6339,7 +6564,7 @@ instruct loadI(iRegINoSp dst, memory mem) %} // Load Integer (32 bit signed) into long -instruct loadI2L(iRegLNoSp dst, memory mem) +instruct loadI2L(iRegLNoSp dst, memory4 mem) %{ match(Set dst (ConvI2L (LoadI mem))); predicate(!needs_acquiring_load(n->in(1))); @@ -6353,7 +6578,7 @@ instruct loadI2L(iRegLNoSp dst, memory mem) %} // Load Integer (32 bit unsigned) into long -instruct loadUI2L(iRegLNoSp dst, memory mem, immL_32bits mask) +instruct loadUI2L(iRegLNoSp dst, memory4 mem, immL_32bits mask) %{ match(Set dst (AndL (ConvI2L (LoadI mem)) mask)); predicate(!needs_acquiring_load(n->in(1)->in(1)->as_Load())); @@ -6367,7 +6592,7 @@ instruct loadUI2L(iRegLNoSp dst, memory mem, immL_32bits mask) %} // Load Long (64 bit signed) -instruct loadL(iRegLNoSp dst, memory mem) +instruct loadL(iRegLNoSp dst, memory8 mem) %{ match(Set dst (LoadL mem)); predicate(!needs_acquiring_load(n)); @@ -6381,7 +6606,7 @@ instruct loadL(iRegLNoSp dst, memory mem) %} // Load Range -instruct loadRange(iRegINoSp dst, memory mem) +instruct loadRange(iRegINoSp dst, memory4 mem) %{ match(Set dst (LoadRange mem)); @@ -6394,7 +6619,7 @@ instruct loadRange(iRegINoSp dst, memory mem) %} // Load Pointer -instruct loadP(iRegPNoSp dst, memory mem) +instruct loadP(iRegPNoSp dst, memory8 mem) %{ match(Set dst (LoadP mem)); predicate(!needs_acquiring_load(n) && (n->as_Load()->barrier_data() == 0)); @@ -6408,7 +6633,7 @@ instruct loadP(iRegPNoSp dst, memory mem) %} // Load Compressed Pointer -instruct loadN(iRegNNoSp dst, memory mem) +instruct loadN(iRegNNoSp dst, memory4 mem) %{ match(Set dst (LoadN mem)); predicate(!needs_acquiring_load(n) && n->as_Load()->barrier_data() == 0); @@ -6422,7 +6647,7 @@ instruct loadN(iRegNNoSp dst, memory mem) %} // Load Klass Pointer -instruct loadKlass(iRegPNoSp dst, memory mem) +instruct loadKlass(iRegPNoSp dst, memory8 mem) %{ match(Set dst (LoadKlass mem)); predicate(!needs_acquiring_load(n)); @@ -6436,7 +6661,7 @@ instruct loadKlass(iRegPNoSp dst, memory mem) %} // Load Narrow Klass Pointer -instruct loadNKlass(iRegNNoSp dst, memory mem) +instruct loadNKlass(iRegNNoSp dst, memory4 mem) %{ match(Set dst (LoadNKlass mem)); predicate(!needs_acquiring_load(n)); @@ -6450,7 +6675,7 @@ instruct loadNKlass(iRegNNoSp dst, memory mem) %} // Load Float -instruct loadF(vRegF dst, memory mem) +instruct loadF(vRegF dst, memory4 mem) %{ match(Set dst (LoadF mem)); predicate(!needs_acquiring_load(n)); @@ -6464,7 +6689,7 @@ instruct loadF(vRegF dst, memory mem) %} // Load Double -instruct loadD(vRegD dst, memory mem) +instruct loadD(vRegD dst, memory8 mem) %{ match(Set dst (LoadD mem)); predicate(!needs_acquiring_load(n)); @@ -6668,7 +6893,7 @@ instruct loadConD(vRegD dst, immD con) %{ // Store Instructions // Store CMS card-mark Immediate -instruct storeimmCM0(immI0 zero, memory mem) +instruct storeimmCM0(immI0 zero, memory1 mem) %{ match(Set mem (StoreCM mem zero)); @@ -6683,7 +6908,7 @@ instruct storeimmCM0(immI0 zero, memory mem) // Store CMS card-mark Immediate with intervening StoreStore // needed when using CMS with no conditional card marking -instruct storeimmCM0_ordered(immI0 zero, memory mem) +instruct storeimmCM0_ordered(immI0 zero, memory1 mem) %{ match(Set mem (StoreCM mem zero)); @@ -6698,7 +6923,7 @@ instruct storeimmCM0_ordered(immI0 zero, memory mem) %} // Store Byte -instruct storeB(iRegIorL2I src, memory mem) +instruct storeB(iRegIorL2I src, memory1 mem) %{ match(Set mem (StoreB mem src)); predicate(!needs_releasing_store(n)); @@ -6712,7 +6937,7 @@ instruct storeB(iRegIorL2I src, memory mem) %} -instruct storeimmB0(immI0 zero, memory mem) +instruct storeimmB0(immI0 zero, memory1 mem) %{ match(Set mem (StoreB mem zero)); predicate(!needs_releasing_store(n)); @@ -6726,7 +6951,7 @@ instruct storeimmB0(immI0 zero, memory mem) %} // Store Char/Short -instruct storeC(iRegIorL2I src, memory mem) +instruct storeC(iRegIorL2I src, memory2 mem) %{ match(Set mem (StoreC mem src)); predicate(!needs_releasing_store(n)); @@ -6739,7 +6964,7 @@ instruct storeC(iRegIorL2I src, memory mem) ins_pipe(istore_reg_mem); %} -instruct storeimmC0(immI0 zero, memory mem) +instruct storeimmC0(immI0 zero, memory2 mem) %{ match(Set mem (StoreC mem zero)); predicate(!needs_releasing_store(n)); @@ -6754,7 +6979,7 @@ instruct storeimmC0(immI0 zero, memory mem) // Store Integer -instruct storeI(iRegIorL2I src, memory mem) +instruct storeI(iRegIorL2I src, memory4 mem) %{ match(Set mem(StoreI mem src)); predicate(!needs_releasing_store(n)); @@ -6767,7 +6992,7 @@ instruct storeI(iRegIorL2I src, memory mem) ins_pipe(istore_reg_mem); %} -instruct storeimmI0(immI0 zero, memory mem) +instruct storeimmI0(immI0 zero, memory4 mem) %{ match(Set mem(StoreI mem zero)); predicate(!needs_releasing_store(n)); @@ -6781,7 +7006,7 @@ instruct storeimmI0(immI0 zero, memory mem) %} // Store Long (64 bit signed) -instruct storeL(iRegL src, memory mem) +instruct storeL(iRegL src, memory8 mem) %{ match(Set mem (StoreL mem src)); predicate(!needs_releasing_store(n)); @@ -6795,7 +7020,7 @@ instruct storeL(iRegL src, memory mem) %} // Store Long (64 bit signed) -instruct storeimmL0(immL0 zero, memory mem) +instruct storeimmL0(immL0 zero, memory8 mem) %{ match(Set mem (StoreL mem zero)); predicate(!needs_releasing_store(n)); @@ -6809,7 +7034,7 @@ instruct storeimmL0(immL0 zero, memory mem) %} // Store Pointer -instruct storeP(iRegP src, memory mem) +instruct storeP(iRegP src, memory8 mem) %{ match(Set mem (StoreP mem src)); predicate(!needs_releasing_store(n) && n->as_Store()->barrier_data() == 0); @@ -6823,7 +7048,7 @@ instruct storeP(iRegP src, memory mem) %} // Store Pointer -instruct storeimmP0(immP0 zero, memory mem) +instruct storeimmP0(immP0 zero, memory8 mem) %{ match(Set mem (StoreP mem zero)); predicate(!needs_releasing_store(n) && n->as_Store()->barrier_data() == 0); @@ -6837,7 +7062,7 @@ instruct storeimmP0(immP0 zero, memory mem) %} // Store Compressed Pointer -instruct storeN(iRegN src, memory mem) +instruct storeN(iRegN src, memory4 mem) %{ match(Set mem (StoreN mem src)); predicate(!needs_releasing_store(n) && n->as_Store()->barrier_data() == 0); @@ -6850,7 +7075,7 @@ instruct storeN(iRegN src, memory mem) ins_pipe(istore_reg_mem); %} -instruct storeImmN0(immN0 zero, memory mem) +instruct storeImmN0(immN0 zero, memory4 mem) %{ match(Set mem (StoreN mem zero)); predicate(!needs_releasing_store(n) && n->as_Store()->barrier_data() == 0); @@ -6864,7 +7089,7 @@ instruct storeImmN0(immN0 zero, memory mem) %} // Store Float -instruct storeF(vRegF src, memory mem) +instruct storeF(vRegF src, memory4 mem) %{ match(Set mem (StoreF mem src)); predicate(!needs_releasing_store(n)); @@ -6881,7 +7106,7 @@ instruct storeF(vRegF src, memory mem) // implement storeImmF0 and storeFImmPacked // Store Double -instruct storeD(vRegD src, memory mem) +instruct storeD(vRegD src, memory8 mem) %{ match(Set mem (StoreD mem src)); predicate(!needs_releasing_store(n)); @@ -6895,7 +7120,7 @@ instruct storeD(vRegD src, memory mem) %} // Store Compressed Klass Pointer -instruct storeNKlass(iRegN src, memory mem) +instruct storeNKlass(iRegN src, memory4 mem) %{ predicate(!needs_releasing_store(n)); match(Set mem (StoreNKlass mem src)); @@ -6914,7 +7139,7 @@ instruct storeNKlass(iRegN src, memory mem) // prefetch instructions // Must be safe to execute with invalid address (cannot fault). -instruct prefetchalloc( memory mem ) %{ +instruct prefetchalloc( memory8 mem ) %{ match(PrefetchAllocation mem); ins_cost(INSN_COST); @@ -7486,7 +7711,7 @@ instruct popCountI(iRegINoSp dst, iRegIorL2I src, vRegF tmp) %{ ins_pipe(pipe_class_default); %} -instruct popCountI_mem(iRegINoSp dst, memory mem, vRegF tmp) %{ +instruct popCountI_mem(iRegINoSp dst, memory4 mem, vRegF tmp) %{ match(Set dst (PopCountI (LoadI mem))); effect(TEMP tmp); ins_cost(INSN_COST * 13); @@ -7527,7 +7752,7 @@ instruct popCountL(iRegINoSp dst, iRegL src, vRegD tmp) %{ ins_pipe(pipe_class_default); %} -instruct popCountL_mem(iRegINoSp dst, memory mem, vRegD tmp) %{ +instruct popCountL_mem(iRegINoSp dst, memory8 mem, vRegD tmp) %{ match(Set dst (PopCountL (LoadL mem))); effect(TEMP tmp); ins_cost(INSN_COST * 13); @@ -16680,7 +16905,7 @@ instruct compressBitsI_reg(iRegINoSp dst, iRegIorL2I src, iRegIorL2I mask, ins_pipe(pipe_slow); %} -instruct compressBitsI_memcon(iRegINoSp dst, memory mem, immI mask, +instruct compressBitsI_memcon(iRegINoSp dst, memory4 mem, immI mask, vRegF tdst, vRegF tsrc, vRegF tmask) %{ match(Set dst (CompressBits (LoadI mem) mask)); effect(TEMP tdst, TEMP tsrc, TEMP tmask); @@ -16717,7 +16942,7 @@ instruct compressBitsL_reg(iRegLNoSp dst, iRegL src, iRegL mask, ins_pipe(pipe_slow); %} -instruct compressBitsL_memcon(iRegLNoSp dst, memory mem, immL mask, +instruct compressBitsL_memcon(iRegLNoSp dst, memory8 mem, immL mask, vRegF tdst, vRegF tsrc, vRegF tmask) %{ match(Set dst (CompressBits (LoadL mem) mask)); effect(TEMP tdst, TEMP tsrc, TEMP tmask); @@ -16754,7 +16979,7 @@ instruct expandBitsI_reg(iRegINoSp dst, iRegIorL2I src, iRegIorL2I mask, ins_pipe(pipe_slow); %} -instruct expandBitsI_memcon(iRegINoSp dst, memory mem, immI mask, +instruct expandBitsI_memcon(iRegINoSp dst, memory4 mem, immI mask, vRegF tdst, vRegF tsrc, vRegF tmask) %{ match(Set dst (ExpandBits (LoadI mem) mask)); effect(TEMP tdst, TEMP tsrc, TEMP tmask); @@ -16792,7 +17017,7 @@ instruct expandBitsL_reg(iRegLNoSp dst, iRegL src, iRegL mask, %} -instruct expandBitsL_memcon(iRegINoSp dst, memory mem, immL mask, +instruct expandBitsL_memcon(iRegINoSp dst, memory8 mem, immL mask, vRegF tdst, vRegF tsrc, vRegF tmask) %{ match(Set dst (ExpandBits (LoadL mem) mask)); effect(TEMP tdst, TEMP tsrc, TEMP tmask); diff --git a/src/hotspot/cpu/aarch64/aarch64_vector.ad b/src/hotspot/cpu/aarch64/aarch64_vector.ad index cdbc4103df8..0d3a240cecf 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector.ad +++ b/src/hotspot/cpu/aarch64/aarch64_vector.ad @@ -345,7 +345,7 @@ source %{ // ------------------------------ Vector load/store ---------------------------- // Load Vector (16 bits) -instruct loadV2(vReg dst, vmem mem) %{ +instruct loadV2(vReg dst, vmem2 mem) %{ predicate(n->as_LoadVector()->memory_size() == 2); match(Set dst (LoadVector mem)); format %{ "loadV2 $dst, $mem\t# vector (16 bits)" %} @@ -354,7 +354,7 @@ instruct loadV2(vReg dst, vmem mem) %{ %} // Store Vector (16 bits) -instruct storeV2(vReg src, vmem mem) %{ +instruct storeV2(vReg src, vmem2 mem) %{ predicate(n->as_StoreVector()->memory_size() == 2); match(Set mem (StoreVector mem src)); format %{ "storeV2 $mem, $src\t# vector (16 bits)" %} @@ -363,7 +363,7 @@ instruct storeV2(vReg src, vmem mem) %{ %} // Load Vector (32 bits) -instruct loadV4(vReg dst, vmem mem) %{ +instruct loadV4(vReg dst, vmem4 mem) %{ predicate(n->as_LoadVector()->memory_size() == 4); match(Set dst (LoadVector mem)); format %{ "loadV4 $dst, $mem\t# vector (32 bits)" %} @@ -372,7 +372,7 @@ instruct loadV4(vReg dst, vmem mem) %{ %} // Store Vector (32 bits) -instruct storeV4(vReg src, vmem mem) %{ +instruct storeV4(vReg src, vmem4 mem) %{ predicate(n->as_StoreVector()->memory_size() == 4); match(Set mem (StoreVector mem src)); format %{ "storeV4 $mem, $src\t# vector (32 bits)" %} @@ -381,7 +381,7 @@ instruct storeV4(vReg src, vmem mem) %{ %} // Load Vector (64 bits) -instruct loadV8(vReg dst, vmem mem) %{ +instruct loadV8(vReg dst, vmem8 mem) %{ predicate(n->as_LoadVector()->memory_size() == 8); match(Set dst (LoadVector mem)); format %{ "loadV8 $dst, $mem\t# vector (64 bits)" %} @@ -390,7 +390,7 @@ instruct loadV8(vReg dst, vmem mem) %{ %} // Store Vector (64 bits) -instruct storeV8(vReg src, vmem mem) %{ +instruct storeV8(vReg src, vmem8 mem) %{ predicate(n->as_StoreVector()->memory_size() == 8); match(Set mem (StoreVector mem src)); format %{ "storeV8 $mem, $src\t# vector (64 bits)" %} @@ -399,7 +399,7 @@ instruct storeV8(vReg src, vmem mem) %{ %} // Load Vector (128 bits) -instruct loadV16(vReg dst, vmem mem) %{ +instruct loadV16(vReg dst, vmem16 mem) %{ predicate(n->as_LoadVector()->memory_size() == 16); match(Set dst (LoadVector mem)); format %{ "loadV16 $dst, $mem\t# vector (128 bits)" %} @@ -408,7 +408,7 @@ instruct loadV16(vReg dst, vmem mem) %{ %} // Store Vector (128 bits) -instruct storeV16(vReg src, vmem mem) %{ +instruct storeV16(vReg src, vmem16 mem) %{ predicate(n->as_StoreVector()->memory_size() == 16); match(Set mem (StoreVector mem src)); format %{ "storeV16 $mem, $src\t# vector (128 bits)" %} diff --git a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 index 020a75b51fa..99708e9ef31 100644 --- a/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_vector_ad.m4 @@ -338,7 +338,7 @@ dnl VECTOR_LOAD_STORE($1, $2, $3, $4, $5 ) dnl VECTOR_LOAD_STORE(type, nbytes, arg_name, nbits, size) define(`VECTOR_LOAD_STORE', ` // ifelse(load, $1, Load, Store) Vector ($4 bits) -instruct $1V$2(vReg $3, vmem mem) %{ +instruct $1V$2(vReg $3, vmem$2 mem) %{ predicate(`n->as_'ifelse(load, $1, Load, Store)Vector()->memory_size() == $2); match(Set ifelse(load, $1, dst (LoadVector mem), mem (StoreVector mem src))); format %{ "$1V$2 ifelse(load, $1, `$dst, $mem', `$mem, $src')\t# vector ($4 bits)" %} diff --git a/src/hotspot/cpu/aarch64/ad_encode.m4 b/src/hotspot/cpu/aarch64/ad_encode.m4 index e3d8ea661b6..008dbd2c936 100644 --- a/src/hotspot/cpu/aarch64/ad_encode.m4 +++ b/src/hotspot/cpu/aarch64/ad_encode.m4 @@ -34,7 +34,7 @@ define(access, ` define(load,` // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_$2($1 dst, memory mem) %{dnl + enc_class aarch64_enc_$2($1 dst, memory$5 mem) %{dnl access(dst,$2,$3,$4,$5)')dnl load(iRegI,ldrsbw,,,1) load(iRegI,ldrsb,,,1) @@ -53,12 +53,12 @@ load(vRegD,ldrd,Float,,8) define(STORE,` // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_$2($1 src, memory mem) %{dnl + enc_class aarch64_enc_$2($1 src, memory$5 mem) %{dnl access(src,$2,$3,$4,$5)')dnl define(STORE0,` // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_$2`'0(memory mem) %{ + enc_class aarch64_enc_$2`'0(memory$4 mem) %{ choose(masm,zr,$2,$mem->opcode(), as_$3Register($mem$$base),$mem$$index,$mem$$scale,$mem$$disp,$4)')dnl STORE(iRegI,strb,,,1) @@ -82,7 +82,7 @@ STORE(vRegD,strd,Float,,8) // This encoding class is generated automatically from ad_encode.m4. // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE - enc_class aarch64_enc_strb0_ordered(memory mem) %{ + enc_class aarch64_enc_strb0_ordered(memory4 mem) %{ __ membar(Assembler::StoreStore); loadStore(masm, &MacroAssembler::strb, zr, $mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp, 1); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 5e116d82761..1385366d879 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1168,8 +1168,8 @@ void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { if (op->init_check()) { - __ ldrb(rscratch1, Address(op->klass()->as_register(), - InstanceKlass::init_state_offset())); + __ lea(rscratch1, Address(op->klass()->as_register(), InstanceKlass::init_state_offset())); + __ ldarb(rscratch1, rscratch1); __ cmpw(rscratch1, InstanceKlass::fully_initialized); add_debug_info_for_null_check_here(op->stub()->info()); __ br(Assembler::NE, *op->stub()->entry()); diff --git a/src/hotspot/cpu/aarch64/c2_CodeStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_CodeStubs_aarch64.cpp index dabafb9288b..4bd509880f2 100644 --- a/src/hotspot/cpu/aarch64/c2_CodeStubs_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_CodeStubs_aarch64.cpp @@ -64,31 +64,4 @@ void C2EntryBarrierStub::emit(C2_MacroAssembler& masm) { __ emit_int32(0); // nmethod guard value } -int C2HandleAnonOMOwnerStub::max_size() const { - // Max size of stub has been determined by testing with 0, in which case - // C2CodeStubList::emit() will throw an assertion and report the actual size that - // is needed. - return 24; -} - -void C2HandleAnonOMOwnerStub::emit(C2_MacroAssembler& masm) { - __ bind(entry()); - Register mon = monitor(); - Register t = tmp(); - assert(t != noreg, "need tmp register"); - - // Fix owner to be the current thread. - __ str(rthread, Address(mon, ObjectMonitor::owner_offset())); - - // Pop owner object from lock-stack. - __ ldrw(t, Address(rthread, JavaThread::lock_stack_top_offset())); - __ subw(t, t, oopSize); -#ifdef ASSERT - __ str(zr, Address(rthread, t)); -#endif - __ strw(t, Address(rthread, JavaThread::lock_stack_top_offset())); - - __ b(continuation()); -} - #undef __ diff --git a/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad b/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad index 5e690a8e47b..6e401724baa 100644 --- a/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad @@ -51,7 +51,7 @@ static void x_load_barrier_slow_path(MacroAssembler* masm, const MachNode* node, %} // Load Pointer -instruct xLoadP(iRegPNoSp dst, memory mem, rFlagsReg cr) +instruct xLoadP(iRegPNoSp dst, memory8 mem, rFlagsReg cr) %{ match(Set dst (LoadP mem)); predicate(UseZGC && !ZGenerational && !needs_acquiring_load(n) && (n->as_Load()->barrier_data() != 0)); diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index 1510b42bfe9..56d45384779 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -100,7 +100,7 @@ static void z_store_barrier(MacroAssembler* masm, const MachNode* node, Address %} // Load Pointer -instruct zLoadP(iRegPNoSp dst, memory mem, rFlagsReg cr) +instruct zLoadP(iRegPNoSp dst, memory8 mem, rFlagsReg cr) %{ match(Set dst (LoadP mem)); predicate(UseZGC && ZGenerational && !needs_acquiring_load(n) && n->as_Load()->barrier_data() != 0); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index c5c02619d44..16473b09fff 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -1838,7 +1838,8 @@ void MacroAssembler::clinit_barrier(Register klass, Register scratch, Label* L_f L_slow_path = &L_fallthrough; } // Fast path check: class is fully initialized - ldrb(scratch, Address(klass, InstanceKlass::init_state_offset())); + lea(scratch, Address(klass, InstanceKlass::init_state_offset())); + ldarb(scratch, scratch); subs(zr, scratch, InstanceKlass::fully_initialized); br(Assembler::EQ, *L_fast_path); diff --git a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp index 68800d04d69..aa6a9d14ff1 100644 --- a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,6 +27,7 @@ #include "asm/macroAssembler.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/vmClasses.hpp" +#include "compiler/disassembler.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "memory/allocation.inline.hpp" @@ -36,7 +37,7 @@ #include "runtime/frame.inline.hpp" #include "runtime/stubRoutines.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index eb235f8472c..31116e006f0 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -7320,6 +7320,28 @@ class StubGenerator: public StubCodeGenerator { return start; } + // load Method* target of MethodHandle + // j_rarg0 = jobject receiver + // rmethod = result + address generate_upcall_stub_load_target() { + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(j_rarg0, rscratch1, rscratch2); + // Load target method from receiver + __ load_heap_oop(rmethod, Address(j_rarg0, java_lang_invoke_MethodHandle::form_offset()), rscratch1, rscratch2); + __ load_heap_oop(rmethod, Address(rmethod, java_lang_invoke_LambdaForm::vmentry_offset()), rscratch1, rscratch2); + __ load_heap_oop(rmethod, Address(rmethod, java_lang_invoke_MemberName::method_offset()), rscratch1, rscratch2); + __ access_load_at(T_ADDRESS, IN_HEAP, rmethod, + Address(rmethod, java_lang_invoke_ResolvedMethodName::vmtarget_offset()), + noreg, noreg); + __ str(rmethod, Address(rthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + + __ ret(lr); + + return start; + } + #undef __ #define __ masm-> @@ -8241,6 +8263,7 @@ class StubGenerator: public StubCodeGenerator { #endif StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); StubRoutines::aarch64::set_completed(); // Inidicate that arraycopy and zero_blocks stubs are generated } diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp index 3210789bbbd..9894841e933 100644 --- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "classfile/javaClasses.hpp" +#include "compiler/disassembler.hpp" #include "compiler/compiler_globals.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "interpreter/bytecodeHistogram.hpp" @@ -67,7 +68,7 @@ // Max size with JVMTI int TemplateInterpreter::InterpreterCodeSize = 200 * 1024; -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> address TemplateInterpreterGenerator::generate_slow_signature_handler() { address entry = __ pc(); @@ -1998,13 +1999,21 @@ void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, address& vep) { assert(t->is_valid() && t->tos_in() == vtos, "illegal template"); Label L; - aep = __ pc(); __ push_ptr(); __ b(L); - fep = __ pc(); __ push_f(); __ b(L); - dep = __ pc(); __ push_d(); __ b(L); - lep = __ pc(); __ push_l(); __ b(L); - bep = cep = sep = - iep = __ pc(); __ push_i(); - vep = __ pc(); + aep = __ pc(); // atos entry point + __ push_ptr(); + __ b(L); + fep = __ pc(); // ftos entry point + __ push_f(); + __ b(L); + dep = __ pc(); // dtos entry point + __ push_d(); + __ b(L); + lep = __ pc(); // ltos entry point + __ push_l(); + __ b(L); + bep = cep = sep = iep = __ pc(); // [bcsi]tos entry point + __ push_i(); + vep = __ pc(); // vtos entry point __ bind(L); generate_and_dispatch(t); } diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 25eb339bfce..48ff356f9a5 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "compiler/disassembler.hpp" #include "compiler/compilerDefinitions.inline.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/collectedHeap.hpp" @@ -49,7 +50,7 @@ #include "runtime/synchronizer.hpp" #include "utilities/powerOfTwo.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> // Address computation: local variables diff --git a/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp b/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp index 28ec07815be..517fccb2d1a 100644 --- a/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/upcallLinker_aarch64.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -117,7 +118,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -222,7 +223,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ lea(c_rarg0, Address(sp, frame_data_offset)); - __ movptr(c_rarg1, (intptr_t)receiver); __ movptr(rscratch1, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry)); __ blr(rscratch1); __ mov(rthread, r0); @@ -238,12 +238,10 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, as_VMStorage(shuffle_reg), abi._shadow_space_bytes, 0); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(j_rarg0, rthread); - __ block_comment("} receiver "); - - __ mov_metadata(rmethod, entry); - __ str(rmethod, Address(rthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + __ block_comment("{ load target "); + __ movptr(j_rarg0, (intptr_t)receiver); + __ far_call(RuntimeAddress(StubRoutines::upcall_stub_load_target()), rscratch1); // puts target Method* in rmethod + __ block_comment("} load target "); __ push_cont_fastpath(rthread); @@ -318,7 +316,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp index bb6a93e6f8d..b14e6f0b4ca 100644 --- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp +++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp @@ -948,6 +948,7 @@ void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { if (op->init_check()) { Register tmp = op->tmp1()->as_register(); __ ldrb(tmp, Address(op->klass()->as_register(), InstanceKlass::init_state_offset())); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtemp); add_debug_info_for_null_check_here(op->stub()->info()); __ cmp(tmp, InstanceKlass::fully_initialized); __ b(*op->stub()->entry(), ne); diff --git a/src/hotspot/cpu/arm/templateTable_arm.cpp b/src/hotspot/cpu/arm/templateTable_arm.cpp index 80519fd89f4..0974ff1f9a9 100644 --- a/src/hotspot/cpu/arm/templateTable_arm.cpp +++ b/src/hotspot/cpu/arm/templateTable_arm.cpp @@ -3974,6 +3974,7 @@ void TemplateTable::_new() { // make sure klass is initialized // make sure klass is fully initialized __ ldrb(Rtemp, Address(Rklass, InstanceKlass::init_state_offset())); + __ membar(MacroAssembler::Membar_mask_bits(MacroAssembler::LoadLoad | MacroAssembler::LoadStore), Rtemp); __ cmp(Rtemp, InstanceKlass::fully_initialized); __ b(slow_case, ne); diff --git a/src/hotspot/cpu/arm/upcallLinker_arm.cpp b/src/hotspot/cpu/arm/upcallLinker_arm.cpp index c7645f4a033..696b2001e6b 100644 --- a/src/hotspot/cpu/arm/upcallLinker_arm.cpp +++ b/src/hotspot/cpu/arm/upcallLinker_arm.cpp @@ -25,7 +25,7 @@ #include "prims/upcallLinker.hpp" #include "utilities/debug.hpp" -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 42934dc7c31..684c06614a9 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -2274,6 +2274,7 @@ void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { } __ lbz(op->tmp1()->as_register(), in_bytes(InstanceKlass::init_state_offset()), op->klass()->as_register()); + // acquire barrier included in membar_storestore() which follows the allocation immediately. __ cmpwi(CCR0, op->tmp1()->as_register(), InstanceKlass::fully_initialized); __ bc_far_optimized(Assembler::bcondCRbiIs0, __ bi0(CCR0, Assembler::equal), *op->stub()->entry()); } diff --git a/src/hotspot/cpu/ppc/frame_ppc.cpp b/src/hotspot/cpu/ppc/frame_ppc.cpp index 4c1ffeb0d76..eb16af5e9db 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.cpp +++ b/src/hotspot/cpu/ppc/frame_ppc.cpp @@ -117,9 +117,9 @@ bool frame::safe_for_sender(JavaThread *thread) { return false; } - common_abi* sender_abi = (common_abi*) fp; + volatile common_abi* sender_abi = (common_abi*) fp; // May get updated concurrently by deoptimization! intptr_t* sender_sp = (intptr_t*) fp; - address sender_pc = (address) sender_abi->lr;; + address sender_pc = (address) sender_abi->lr; if (Continuation::is_return_barrier_entry(sender_pc)) { // If our sender_pc is the return barrier, then our "real" sender is the continuation entry @@ -134,9 +134,18 @@ bool frame::safe_for_sender(JavaThread *thread) { return false; } + intptr_t* unextended_sender_sp = is_interpreted_frame() ? interpreter_frame_sender_sp() : sender_sp; + + // If the sender is a deoptimized nmethod we need to check if the original pc is valid. + nmethod* sender_nm = sender_blob->as_nmethod_or_null(); + if (sender_nm != nullptr && sender_nm->is_deopt_pc(sender_pc)) { + address orig_pc = *(address*)((address)unextended_sender_sp + sender_nm->orig_pc_offset()); + if (!sender_nm->insts_contains_inclusive(orig_pc)) return false; + } + // It should be safe to construct the sender though it might not be valid. - frame sender(sender_sp, sender_pc, nullptr /* unextended_sp */, nullptr /* fp */, sender_blob); + frame sender(sender_sp, sender_pc, unextended_sender_sp, nullptr /* fp */, sender_blob); // Do we have a valid fp? address sender_fp = (address) sender.fp(); diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 8d8e39b8bbc..a194c030a61 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -2410,7 +2410,7 @@ void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass, void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fast_path, Label* L_slow_path) { assert(L_fast_path != nullptr || L_slow_path != nullptr, "at least one is required"); - Label L_fallthrough; + Label L_check_thread, L_fallthrough; if (L_fast_path == nullptr) { L_fast_path = &L_fallthrough; } else if (L_slow_path == nullptr) { @@ -2419,10 +2419,14 @@ void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fa // Fast path check: class is fully initialized lbz(R0, in_bytes(InstanceKlass::init_state_offset()), klass); + // acquire by cmp-branch-isync if fully_initialized cmpwi(CCR0, R0, InstanceKlass::fully_initialized); - beq(CCR0, *L_fast_path); + bne(CCR0, L_check_thread); + isync(); + b(*L_fast_path); // Fast path check: current thread is initializer thread + bind(L_check_thread); ld(R0, in_bytes(InstanceKlass::init_thread_offset()), klass); cmpd(CCR0, thread, R0); if (L_slow_path == &L_fallthrough) { diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index ee3f1911e20..206c161287f 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -4587,6 +4587,30 @@ address generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { return start; } + // load Method* target of MethodHandle + // R3_ARG1 = jobject receiver + // R19_method = result Method* + address generate_upcall_stub_load_target() { + + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(R3_ARG1, R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS); + // Load target method from receiver + __ load_heap_oop(R19_method, java_lang_invoke_MethodHandle::form_offset(), R3_ARG1, + R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); + __ load_heap_oop(R19_method, java_lang_invoke_LambdaForm::vmentry_offset(), R19_method, + R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); + __ load_heap_oop(R19_method, java_lang_invoke_MemberName::method_offset(), R19_method, + R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); + __ ld(R19_method, java_lang_invoke_ResolvedMethodName::vmtarget_offset(), R19_method); + __ std(R19_method, in_bytes(JavaThread::callee_target_offset()), R16_thread); // just in case callee is deoptimized + + __ blr(); + + return start; + } + // Initialization void generate_initial_stubs() { // Generates all stubs and initializes the entry points @@ -4651,6 +4675,7 @@ address generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { } StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); } void generate_compiler_stubs() { diff --git a/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp index b60fd4f16d1..635bab900d1 100644 --- a/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -118,7 +119,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; // arg save & restore + move -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -221,7 +222,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry), R0); __ addi(R3_ARG1, R1_SP, frame_data_offset); - __ load_const_optimized(R4_ARG2, (intptr_t)receiver, R0); __ call_c(call_target_address); __ mr(R16_thread, R3_RET); __ block_comment("} on_entry"); @@ -236,12 +236,12 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, as_VMStorage(callerSP), frame::native_abi_minframe_size, frame::jit_out_preserve_size); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(R3_ARG1); - __ block_comment("} receiver "); - - __ load_const_optimized(R19_method, (intptr_t)entry); - __ std(R19_method, in_bytes(JavaThread::callee_target_offset()), R16_thread); + __ block_comment("{ load target "); + __ load_const_optimized(call_target_address, StubRoutines::upcall_stub_load_target(), R0); + __ load_const_optimized(R3_ARG1, (intptr_t)receiver, R0); + __ mtctr(call_target_address); + __ bctrl(); // loads target Method* into R19_method + __ block_comment("} load target "); __ push_cont_fastpath(); @@ -326,7 +326,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/riscv/assembler_riscv.hpp b/src/hotspot/cpu/riscv/assembler_riscv.hpp index d1021d9e283..ad3d18fa392 100644 --- a/src/hotspot/cpu/riscv/assembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/assembler_riscv.hpp @@ -46,8 +46,10 @@ class Argument { public: enum { - n_int_register_parameters_c = 8, // x10, x11, ... x17 (c_rarg0, c_rarg1, ...) - n_float_register_parameters_c = 8, // f10, f11, ... f17 (c_farg0, c_farg1, ... ) + // check more info at https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc + n_int_register_parameters_c = 8, // x10, x11, ... x17 (c_rarg0, c_rarg1, ...) + n_float_register_parameters_c = 8, // f10, f11, ... f17 (c_farg0, c_farg1, ... ) + n_vector_register_parameters_c = 16, // v8, v9, ... v23 n_int_register_parameters_j = 8, // x11, ... x17, x10 (j_rarg0, j_rarg1, ...) n_float_register_parameters_j = 8 // f10, f11, ... f17 (j_farg0, j_farg1, ...) @@ -143,6 +145,10 @@ constexpr Register x19_sender_sp = x19; // Sender's SP while in interpreter constexpr Register t0 = x5; constexpr Register t1 = x6; constexpr Register t2 = x7; +constexpr Register t3 = x28; +constexpr Register t4 = x29; +constexpr Register t5 = x30; +constexpr Register t6 = x31; const Register g_INTArgReg[Argument::n_int_register_parameters_c] = { c_rarg0, c_rarg1, c_rarg2, c_rarg3, c_rarg4, c_rarg5, c_rarg6, c_rarg7 diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp index 940706b0a73..828f70e4dec 100644 --- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp @@ -980,6 +980,7 @@ void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { if (op->init_check()) { __ lbu(t0, Address(op->klass()->as_register(), InstanceKlass::init_state_offset())); + __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); __ mv(t1, (u1)InstanceKlass::fully_initialized); add_debug_info_for_null_check_here(op->stub()->info()); __ bne(t0, t1, *op->stub()->entry(), /* is_far */ true); diff --git a/src/hotspot/cpu/riscv/c2_CodeStubs_riscv.cpp b/src/hotspot/cpu/riscv/c2_CodeStubs_riscv.cpp index 7995750aba9..db18525b89c 100644 --- a/src/hotspot/cpu/riscv/c2_CodeStubs_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_CodeStubs_riscv.cpp @@ -71,32 +71,4 @@ void C2EntryBarrierStub::emit(C2_MacroAssembler& masm) { __ emit_int32(0); // nmethod guard value } -int C2HandleAnonOMOwnerStub::max_size() const { - // Max size of stub has been determined by testing with 0 without using RISC-V compressed - // instruction-set extension, in which case C2CodeStubList::emit() will throw an assertion - // and report the actual size that is needed. - return 20 DEBUG_ONLY(+8); -} - -void C2HandleAnonOMOwnerStub::emit(C2_MacroAssembler& masm) { - __ bind(entry()); - Register mon = monitor(); - Register t = tmp(); - assert(t != noreg, "need tmp register"); - - // Fix owner to be the current thread. - __ sd(xthread, Address(mon, ObjectMonitor::owner_offset())); - - // Pop owner object from lock-stack. - __ lwu(t, Address(xthread, JavaThread::lock_stack_top_offset())); - __ subw(t, t, oopSize); -#ifdef ASSERT - __ add(t0, xthread, t); - __ sd(zr, Address(t0, 0)); -#endif - __ sw(t, Address(xthread, JavaThread::lock_stack_top_offset())); - - __ j(continuation()); -} - #undef __ diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index b99ba542423..46701b6ede3 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -493,6 +493,7 @@ void MacroAssembler::clinit_barrier(Register klass, Register tmp, Label* L_fast_ // Fast path check: class is fully initialized lbu(tmp, Address(klass, InstanceKlass::init_state_offset())); + membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); sub(tmp, tmp, InstanceKlass::fully_initialized); beqz(tmp, *L_fast_path); @@ -2947,7 +2948,7 @@ int MacroAssembler::corrected_idivq(Register result, Register rs1, Register rs2, return idivq_offset; } -// Look up the method for a megamorpic invkkeinterface call. +// Look up the method for a megamorphic invokeinterface call. // The target method is determined by . // The receiver klass is in recv_klass. // On success, the result will be in method_result, and execution falls through. @@ -2962,9 +2963,9 @@ void MacroAssembler::lookup_interface_method(Register recv_klass, assert_different_registers(recv_klass, intf_klass, scan_tmp); assert_different_registers(method_result, intf_klass, scan_tmp); assert(recv_klass != method_result || !return_method, - "recv_klass can be destroyed when mehtid isn't needed"); + "recv_klass can be destroyed when method isn't needed"); assert(itable_index.is_constant() || itable_index.as_register() == method_result, - "caller must be same register for non-constant itable index as for method"); + "caller must use same register for non-constant itable index as for method"); // Compute start of first itableOffsetEntry (which is at the end of the vtable). int vtable_base = in_bytes(Klass::vtable_start_offset()); diff --git a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp index deeb771d83b..f638db9f0bf 100644 --- a/src/hotspot/cpu/riscv/methodHandles_riscv.cpp +++ b/src/hotspot/cpu/riscv/methodHandles_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -28,6 +28,7 @@ #include "asm/macroAssembler.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/vmClasses.hpp" +#include "compiler/disassembler.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "memory/allocation.inline.hpp" @@ -37,7 +38,7 @@ #include "runtime/frame.inline.hpp" #include "runtime/stubRoutines.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> #ifdef PRODUCT #define BLOCK_COMMENT(str) /* nothing */ @@ -444,7 +445,6 @@ void MethodHandles::generate_method_handle_dispatch(MacroAssembler* _masm, __ far_jump(RuntimeAddress(SharedRuntime::throw_IncompatibleClassChangeError_entry())); } } - } #ifndef PRODUCT diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 563dfd4cde9..a76d1722670 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1972,12 +1972,16 @@ const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) // Vector calling convention not yet implemented. bool Matcher::supports_vector_calling_convention(void) { - return false; + return EnableVectorSupport && UseVectorStubs; } OptoRegPair Matcher::vector_return_value(uint ideal_reg) { - Unimplemented(); - return OptoRegPair(0, 0); + assert(EnableVectorSupport && UseVectorStubs, "sanity"); + assert(ideal_reg == Op_VecA, "sanity"); + // check more info at https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc + int lo = V8_num; + int hi = V8_K_num; + return OptoRegPair(hi, lo); } // Is this branch offset short enough that a short branch can be used? @@ -10075,6 +10079,23 @@ instruct CallLeafDirect(method meth, rFlagsReg cr) ins_pipe(pipe_class_call); %} +// Call Runtime Instruction without safepoint and with vector arguments + +instruct CallLeafDirectVector(method meth, rFlagsReg cr) +%{ + match(CallLeafVector); + + effect(USE meth, KILL cr); + + ins_cost(BRANCH_COST); + + format %{ "CALL, runtime leaf vector $meth" %} + + ins_encode(riscv_enc_java_to_runtime(meth)); + + ins_pipe(pipe_class_call); +%} + // Call Runtime Instruction instruct CallLeafNoFPDirect(method meth, rFlagsReg cr) diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index 27da26d404c..2b629fcfcb2 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -666,7 +666,20 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm int SharedRuntime::vector_calling_convention(VMRegPair *regs, uint num_bits, uint total_args_passed) { - Unimplemented(); + assert(total_args_passed <= Argument::n_vector_register_parameters_c, "unsupported"); + assert(num_bits >= 64 && num_bits <= 2048 && is_power_of_2(num_bits), "unsupported"); + + // check more info at https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc + static const VectorRegister VEC_ArgReg[Argument::n_vector_register_parameters_c] = { + v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23 + }; + + const int next_reg_val = 3; + for (uint i = 0; i < total_args_passed; i++) { + VMReg vmreg = VEC_ArgReg[i]->as_VMReg(); + regs[i].set_pair(vmreg->next(next_reg_val), vmreg); + } return 0; } diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 5970111088f..bdb92e0b835 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -4482,7 +4482,7 @@ class StubGenerator: public StubCodeGenerator { RegSet reg_cache_saved_regs = RegSet::of(x24, x25, x26, x27); // s8, s9, s10, s11 RegSet reg_cache_regs; reg_cache_regs += reg_cache_saved_regs; - reg_cache_regs += RegSet::of(x28, x29, x30, x31); // t3, t4, t5, t6 + reg_cache_regs += RegSet::of(t3, t4, t5, t6); BufRegCache reg_cache(_masm, reg_cache_regs); RegSet saved_regs; @@ -5462,8 +5462,8 @@ class StubGenerator: public StubCodeGenerator { Register isMIME = c_rarg6; Register codec = c_rarg7; - Register dstBackup = x31; - Register length = x28; // t3, total length of src data in bytes + Register dstBackup = t6; + Register length = t3; // total length of src data in bytes Label ProcessData, Exit; Label ProcessScalar, ScalarLoop; @@ -5498,7 +5498,7 @@ class StubGenerator: public StubCodeGenerator { Register stepSrcM1 = send; Register stepSrcM2 = doff; Register stepDst = isURL; - Register size = x29; // t4 + Register size = t4; __ mv(size, MaxVectorSize * 2); __ mv(stepSrcM1, MaxVectorSize * 4); @@ -5550,7 +5550,7 @@ class StubGenerator: public StubCodeGenerator { // scalar version { Register byte0 = soff, byte1 = send, byte2 = doff, byte3 = isURL; - Register combined32Bits = x29; // t5 + Register combined32Bits = t4; // encoded: [byte0[5:0] : byte1[5:0] : byte2[5:0]] : byte3[5:0]] => // plain: [byte0[5:0]+byte1[5:4] : byte1[3:0]+byte2[5:2] : byte2[1:0]+byte3[5:0]] @@ -5708,10 +5708,10 @@ class StubGenerator: public StubCodeGenerator { Register nmax = c_rarg4; Register base = c_rarg5; Register count = c_rarg6; - Register temp0 = x28; // t3 - Register temp1 = x29; // t4 - Register temp2 = x30; // t5 - Register temp3 = x31; // t6 + Register temp0 = t3; + Register temp1 = t4; + Register temp2 = t5; + Register temp3 = t6; VectorRegister vzero = v31; VectorRegister vbytes = v8; // group: v8, v9, v10, v11 @@ -6071,6 +6071,58 @@ static const int64_t right_3_bits = right_n_bits(3); return start; } + void generate_vector_math_stubs() { + if (!UseRVV) { + log_info(library)("vector is not supported, skip loading vector math (sleef) library!"); + return; + } + + // Get native vector math stub routine addresses + void* libsleef = nullptr; + char ebuf[1024]; + char dll_name[JVM_MAXPATHLEN]; + if (os::dll_locate_lib(dll_name, sizeof(dll_name), Arguments::get_dll_dir(), "sleef")) { + libsleef = os::dll_load(dll_name, ebuf, sizeof ebuf); + } + if (libsleef == nullptr) { + log_info(library)("Failed to load native vector math (sleef) library, %s!", ebuf); + return; + } + + // Method naming convention + // All the methods are named as _ + // + // Where: + // is the operation name, e.g. sin, cos + // is to indicate float/double + // "fx/dx" for vector float/double operation + // is the precision level + // "u10/u05" represents 1.0/0.5 ULP error bounds + // We use "u10" for all operations by default + // But for those functions do not have u10 support, we use "u05" instead + // rvv, indicates riscv vector extension + // + // e.g. sinfx_u10rvv is the method for computing vector float sin using rvv instructions + // + log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, JNI_LIB_PREFIX "sleef" JNI_LIB_SUFFIX, p2i(libsleef)); + + for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { + int vop = VectorSupport::VECTOR_OP_MATH_START + op; + if (vop == VectorSupport::VECTOR_OP_TANH) { // skip tanh because of performance regression + continue; + } + + // The native library does not support u10 level of "hypot". + const char* ulf = (vop == VectorSupport::VECTOR_OP_HYPOT) ? "u05" : "u10"; + + snprintf(ebuf, sizeof(ebuf), "%sfx_%srvv", VectorSupport::mathname[op], ulf); + StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_SCALABLE][op] = (address)os::dll_lookup(libsleef, ebuf); + + snprintf(ebuf, sizeof(ebuf), "%sdx_%srvv", VectorSupport::mathname[op], ulf); + StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_SCALABLE][op] = (address)os::dll_lookup(libsleef, ebuf); + } + } + #endif // COMPILER2 /** @@ -6102,7 +6154,7 @@ static const int64_t right_3_bits = right_n_bits(3); __ kernel_crc32(crc, buf, len, c_rarg3, c_rarg4, c_rarg5, c_rarg6, // tmp's for tables - c_rarg7, t2, x28, x29, x30, x31); // misc tmps + c_rarg7, t2, t3, t4, t5, t6); // misc tmps __ leave(); // required for proper stackwalking of RuntimeStub frame __ ret(); @@ -6124,6 +6176,29 @@ static const int64_t right_3_bits = right_n_bits(3); return start; } + // load Method* target of MethodHandle + // j_rarg0 = jobject receiver + // xmethod = Method* result + address generate_upcall_stub_load_target() { + + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(j_rarg0, t0, t1); + // Load target method from receiver + __ load_heap_oop(xmethod, Address(j_rarg0, java_lang_invoke_MethodHandle::form_offset()), t0, t1); + __ load_heap_oop(xmethod, Address(xmethod, java_lang_invoke_LambdaForm::vmentry_offset()), t0, t1); + __ load_heap_oop(xmethod, Address(xmethod, java_lang_invoke_MemberName::method_offset()), t0, t1); + __ access_load_at(T_ADDRESS, IN_HEAP, xmethod, + Address(xmethod, java_lang_invoke_ResolvedMethodName::vmtarget_offset()), + noreg, noreg); + __ sd(xmethod, Address(xthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + + __ ret(); + + return start; + } + #undef __ // Initialization @@ -6189,6 +6264,7 @@ static const int64_t right_3_bits = right_n_bits(3); #endif // COMPILER2 StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); StubRoutines::riscv::set_completed(); } @@ -6267,6 +6343,8 @@ static const int64_t right_3_bits = right_n_bits(3); generate_string_indexof_stubs(); + generate_vector_math_stubs(); + #endif // COMPILER2 } diff --git a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp index 1f32488777d..7c811aa3a0c 100644 --- a/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateInterpreterGenerator_riscv.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -27,6 +27,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "classfile/javaClasses.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "interpreter/bytecodeHistogram.hpp" #include "interpreter/bytecodeTracer.hpp" @@ -70,7 +71,7 @@ // Max size with JVMTI int TemplateInterpreter::InterpreterCodeSize = 256 * 1024; -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> //----------------------------------------------------------------------------- @@ -1748,13 +1749,21 @@ void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t, address& vep) { assert(t != nullptr && t->is_valid() && t->tos_in() == vtos, "illegal template"); Label L; - aep = __ pc(); __ push_ptr(); __ j(L); - fep = __ pc(); __ push_f(); __ j(L); - dep = __ pc(); __ push_d(); __ j(L); - lep = __ pc(); __ push_l(); __ j(L); - bep = cep = sep = - iep = __ pc(); __ push_i(); - vep = __ pc(); + aep = __ pc(); // atos entry point + __ push_ptr(); + __ j(L); + fep = __ pc(); // ftos entry point + __ push_f(); + __ j(L); + dep = __ pc(); // dtos entry point + __ push_d(); + __ j(L); + lep = __ pc(); // ltos entry point + __ push_l(); + __ j(L); + bep = cep = sep = iep = __ pc(); // [bcsi]tos entry point + __ push_i(); + vep = __ pc(); // vtos entry point __ bind(L); generate_and_dispatch(t); } diff --git a/src/hotspot/cpu/riscv/templateTable_riscv.cpp b/src/hotspot/cpu/riscv/templateTable_riscv.cpp index 2e6902180a8..2fede262057 100644 --- a/src/hotspot/cpu/riscv/templateTable_riscv.cpp +++ b/src/hotspot/cpu/riscv/templateTable_riscv.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "compiler/disassembler.hpp" #include "gc/shared/barrierSetAssembler.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/tlab_globals.hpp" @@ -49,7 +50,7 @@ #include "runtime/synchronizer.hpp" #include "utilities/powerOfTwo.hpp" -#define __ _masm-> +#define __ Disassembler::hook(__FILE__, __LINE__, _masm)-> // Address computation: local variables diff --git a/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp b/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp index 383f332f8fd..55160be99d0 100644 --- a/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp +++ b/src/hotspot/cpu/riscv/upcallLinker_riscv.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -117,7 +118,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -223,7 +224,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ la(c_rarg0, Address(sp, frame_data_offset)); - __ movptr(c_rarg1, (address) receiver); __ rt_call(CAST_FROM_FN_PTR(address, UpcallLinker::on_entry)); __ mv(xthread, x10); __ reinit_heapbase(); @@ -260,12 +260,10 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, as_VMStorage(shuffle_reg), abi._shadow_space_bytes, 0); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(j_rarg0, xthread); - __ block_comment("} receiver "); - - __ mov_metadata(xmethod, entry); - __ sd(xmethod, Address(xthread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + __ block_comment("{ load target "); + __ movptr(j_rarg0, (address) receiver); + __ far_call(RuntimeAddress(StubRoutines::upcall_stub_load_target())); // loads Method* into xmethod + __ block_comment("} load target "); __ push_cont_fastpath(xthread); @@ -338,7 +336,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char *name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp index d288f4a893d..8990cf1663d 100644 --- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp @@ -2350,6 +2350,7 @@ void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr de void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { if (op->init_check()) { // Make sure klass is initialized & doesn't have finalizer. + // init_state needs acquire, but S390 is TSO, and so we are already good. const int state_offset = in_bytes(InstanceKlass::init_state_offset()); Register iklass = op->klass()->as_register(); add_debug_info_for_null_check_here(op->stub()->info()); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index e192bbab0de..6bfe5125959 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -3459,7 +3459,8 @@ void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fa L_slow_path = &L_fallthrough; } - // Fast path check: class is fully initialized + // Fast path check: class is fully initialized. + // init_state needs acquire, but S390 is TSO, and so we are already good. z_cli(Address(klass, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized); z_bre(*L_fast_path); diff --git a/src/hotspot/cpu/s390/matcher_s390.hpp b/src/hotspot/cpu/s390/matcher_s390.hpp index 6c6cae3c58f..d8b1ae68f6f 100644 --- a/src/hotspot/cpu/s390/matcher_s390.hpp +++ b/src/hotspot/cpu/s390/matcher_s390.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2017, 2022 SAP SE. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,11 +63,16 @@ return true; } - // Suppress CMOVL. Conditional move available on z/Architecture only from z196 onwards. Not exploited yet. - static int long_cmove_cost() { return ConditionalMoveLimit; } + // Use conditional move (CMOVL) + static int long_cmove_cost() { + // z196/z11 or later hardware support conditional moves + return VM_Version::has_LoadStoreConditional() ? 0 : ConditionalMoveLimit; + } - // Suppress CMOVF. Conditional move available on z/Architecture only from z196 onwards. Not exploited yet. - static int float_cmove_cost() { return ConditionalMoveLimit; } + static int float_cmove_cost() { + // z196/z11 or later hardware support conditional moves + return VM_Version::has_LoadStoreConditional() ? 0 : ConditionalMoveLimit; + } // Set this as clone_shift_expressions. static bool narrow_oop_use_complex_address() { diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index d878731cca5..dd9ed4c9546 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -3053,6 +3053,29 @@ class StubGenerator: public StubCodeGenerator { return start; } + // load Method* target of MethodHandle + // Z_ARG1 = jobject receiver + // Z_method = Method* result + address generate_upcall_stub_load_target() { + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(Z_ARG1, Z_tmp_1, Z_tmp_2); + // Load target method from receiver + __ load_heap_oop(Z_method, Address(Z_ARG1, java_lang_invoke_MethodHandle::form_offset()), + noreg, noreg, IS_NOT_NULL); + __ load_heap_oop(Z_method, Address(Z_method, java_lang_invoke_LambdaForm::vmentry_offset()), + noreg, noreg, IS_NOT_NULL); + __ load_heap_oop(Z_method, Address(Z_method, java_lang_invoke_MemberName::method_offset()), + noreg, noreg, IS_NOT_NULL); + __ z_lg(Z_method, Address(Z_method, java_lang_invoke_ResolvedMethodName::vmtarget_offset())); + __ z_stg(Z_method, Address(Z_thread, JavaThread::callee_target_offset())); // just in case callee is deoptimized + + __ z_br(Z_R14); + + return start; + } + void generate_initial_stubs() { // Generates all stubs and initializes the entry points. @@ -3110,6 +3133,7 @@ class StubGenerator: public StubCodeGenerator { } StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); } void generate_compiler_stubs() { diff --git a/src/hotspot/cpu/s390/upcallLinker_s390.cpp b/src/hotspot/cpu/s390/upcallLinker_s390.cpp index 734b4e89c7c..8baad40a519 100644 --- a/src/hotspot/cpu/s390/upcallLinker_s390.cpp +++ b/src/hotspot/cpu/s390/upcallLinker_s390.cpp @@ -23,6 +23,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" +#include "classfile/javaClasses.hpp" #include "logging/logStream.hpp" #include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" @@ -116,7 +117,7 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr static const int upcall_stub_code_base_size = 1024; static const int upcall_stub_size_per_arg = 16; // arg save & restore + move -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -206,7 +207,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("on_entry {"); __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry)); __ z_aghik(Z_ARG1, Z_SP, frame_data_offset); - __ load_const_optimized(Z_ARG2, (intptr_t)receiver); __ call(call_target_address); __ z_lgr(Z_thread, Z_RET); __ block_comment("} on_entry"); @@ -216,12 +216,11 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, shuffle_reg, abi._shadow_space_bytes, frame::z_jit_out_preserve_size); __ block_comment("} argument_shuffle"); - __ block_comment("receiver {"); - __ get_vm_result(Z_ARG1); - __ block_comment("} receiver"); - - __ load_const_optimized(Z_method, (intptr_t)entry); - __ z_stg(Z_method, Address(Z_thread, in_bytes(JavaThread::callee_target_offset()))); + __ block_comment("load_target {"); + __ load_const_optimized(Z_ARG1, (intptr_t)receiver); + __ load_const_optimized(call_target_address, StubRoutines::upcall_stub_load_target()); + __ call(call_target_address); // load taget Method* into Z_method + __ block_comment("} load_target"); __ z_lg(call_target_address, Address(Z_method, in_bytes(Method::from_compiled_offset()))); __ call(call_target_address); @@ -274,7 +273,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.as_string()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp index c3444d5a5ab..6d9812c11ae 100644 --- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp @@ -1578,6 +1578,7 @@ void LIR_Assembler::emit_opConvert(LIR_OpConvert* op) { void LIR_Assembler::emit_alloc_obj(LIR_OpAllocObj* op) { if (op->init_check()) { add_debug_info_for_null_check_here(op->stub()->info()); + // init_state needs acquire, but x86 is TSO, and so we are already good. __ cmpb(Address(op->klass()->as_register(), InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized); diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index 839745f76ec..aba5344b7e4 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -822,7 +822,7 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, } movptr(Address(thread, JavaThread::unlocked_inflated_monitor_offset()), monitor); - testl(monitor, monitor); // Fast Unlock ZF = 0 + orl(t, 1); // Fast Unlock ZF = 0 jmpb(slow_path); // Recursive unlock. diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp index 893ae4e844b..018258a012e 100644 --- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp @@ -5084,7 +5084,8 @@ void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fa L_slow_path = &L_fallthrough; } - // Fast path check: class is fully initialized + // Fast path check: class is fully initialized. + // init_state needs acquire, but x86 is TSO, and so we are already good. cmpb(Address(klass, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized); jcc(Assembler::equal, *L_fast_path); diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp index 4f37dc31d03..ee6311c25f6 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" +#include "classfile/javaClasses.hpp" #include "classfile/vmIntrinsics.hpp" #include "compiler/oopMap.hpp" #include "gc/shared/barrierSet.hpp" @@ -3796,6 +3797,28 @@ address StubGenerator::generate_upcall_stub_exception_handler() { return start; } +// load Method* target of MethodHandle +// j_rarg0 = jobject receiver +// rbx = result +address StubGenerator::generate_upcall_stub_load_target() { + StubCodeMark mark(this, "StubRoutines", "upcall_stub_load_target"); + address start = __ pc(); + + __ resolve_global_jobject(j_rarg0, r15_thread, rscratch1); + // Load target method from receiver + __ load_heap_oop(rbx, Address(j_rarg0, java_lang_invoke_MethodHandle::form_offset()), rscratch1); + __ load_heap_oop(rbx, Address(rbx, java_lang_invoke_LambdaForm::vmentry_offset()), rscratch1); + __ load_heap_oop(rbx, Address(rbx, java_lang_invoke_MemberName::method_offset()), rscratch1); + __ access_load_at(T_ADDRESS, IN_HEAP, rbx, + Address(rbx, java_lang_invoke_ResolvedMethodName::vmtarget_offset()), + noreg, noreg); + __ movptr(Address(r15_thread, JavaThread::callee_target_offset()), rbx); // just in case callee is deoptimized + + __ ret(0); + + return start; +} + address StubGenerator::generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { StubCodeMark mark(this, "StubRoutines", "lookup_secondary_supers_table"); @@ -3955,6 +3978,7 @@ void StubGenerator::generate_final_stubs() { } StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); + StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); } void StubGenerator::generate_compiler_stubs() { @@ -4160,41 +4184,41 @@ void StubGenerator::generate_compiler_stubs() { log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, JNI_LIB_PREFIX "jsvml" JNI_LIB_SUFFIX, p2i(libjsvml)); if (UseAVX > 2) { - for (int op = 0; op < VectorSupport::NUM_SVML_OP; op++) { - int vop = VectorSupport::VECTOR_OP_SVML_START + op; + for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { + int vop = VectorSupport::VECTOR_OP_MATH_START + op; if ((!VM_Version::supports_avx512dq()) && (vop == VectorSupport::VECTOR_OP_LOG || vop == VectorSupport::VECTOR_OP_LOG10 || vop == VectorSupport::VECTOR_OP_POW)) { continue; } - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf16_ha_z0", VectorSupport::svmlname[op]); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf16_ha_z0", VectorSupport::mathname[op]); StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_512][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s8_ha_z0", VectorSupport::svmlname[op]); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%s8_ha_z0", VectorSupport::mathname[op]); StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_512][op] = (address)os::dll_lookup(libjsvml, ebuf); } } const char* avx_sse_str = (UseAVX >= 2) ? "l9" : ((UseAVX == 1) ? "e9" : "ex"); - for (int op = 0; op < VectorSupport::NUM_SVML_OP; op++) { - int vop = VectorSupport::VECTOR_OP_SVML_START + op; + for (int op = 0; op < VectorSupport::NUM_VECTOR_OP_MATH; op++) { + int vop = VectorSupport::VECTOR_OP_MATH_START + op; if (vop == VectorSupport::VECTOR_OP_POW) { continue; } - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf4_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf4_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_64][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf4_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf4_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_128][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf8_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%sf8_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_256][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s1_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%s1_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_64][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s2_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%s2_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_128][op] = (address)os::dll_lookup(libjsvml, ebuf); - snprintf(ebuf, sizeof(ebuf), "__jsvml_%s4_ha_%s", VectorSupport::svmlname[op], avx_sse_str); + snprintf(ebuf, sizeof(ebuf), "__jsvml_%s4_ha_%s", VectorSupport::mathname[op], avx_sse_str); StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_256][op] = (address)os::dll_lookup(libjsvml, ebuf); } } diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp index 71777fbfffe..7280e9fbe95 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.hpp @@ -620,6 +620,7 @@ class StubGenerator: public StubCodeGenerator { // shared exception handler for FFM upcall stubs address generate_upcall_stub_exception_handler(); + address generate_upcall_stub_load_target(); // Specialized stub implementations for UseSecondarySupersTable. address generate_lookup_secondary_supers_table_stub(u1 super_klass_index); diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index 5e783225fcb..527d961259e 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -4048,6 +4048,7 @@ void TemplateTable::_new() { __ push(rcx); // save the contexts of klass for initializing the header // make sure klass is initialized + // init_state needs acquire, but x86 is TSO, and so we are already good. #ifdef _LP64 assert(VM_Version::supports_fast_class_init_checks(), "must support fast class initialization checks"); __ clinit_barrier(rcx, r15_thread, nullptr /*L_fast_path*/, &slow_case); diff --git a/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp b/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp index e5075e180d9..d795c751d02 100644 --- a/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp +++ b/src/hotspot/cpu/x86/upcallLinker_x86_32.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "prims/upcallLinker.hpp" -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp b/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp index 82179f9022e..bc261bfd93f 100644 --- a/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp +++ b/src/hotspot/cpu/x86/upcallLinker_x86_64.cpp @@ -23,7 +23,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" -#include "code/codeBlob.hpp" +#include "classfile/javaClasses.hpp" #include "code/codeBlob.hpp" #include "code/vmreg.inline.hpp" #include "compiler/disassembler.hpp" @@ -169,10 +169,10 @@ static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescr __ block_comment("} restore_callee_saved_regs "); } -static const int upcall_stub_code_base_size = 1024; +static const int upcall_stub_code_base_size = 1200; static const int upcall_stub_size_per_arg = 16; -address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, +address UpcallLinker::make_upcall_stub(jobject receiver, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, @@ -281,7 +281,6 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, __ block_comment("{ on_entry"); __ vzeroupper(); __ lea(c_rarg0, Address(rsp, frame_data_offset)); - __ movptr(c_rarg1, (intptr_t)receiver); // stack already aligned __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, UpcallLinker::on_entry))); __ movptr(r15_thread, rax); @@ -297,12 +296,10 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, arg_shuffle.generate(_masm, shuffle_reg, abi._shadow_space_bytes, 0); __ block_comment("} argument shuffle"); - __ block_comment("{ receiver "); - __ get_vm_result(j_rarg0, r15_thread); - __ block_comment("} receiver "); - - __ mov_metadata(rbx, entry); - __ movptr(Address(r15_thread, JavaThread::callee_target_offset()), rbx); // just in case callee is deoptimized + __ block_comment("{ load target "); + __ movptr(j_rarg0, (intptr_t)receiver); + __ call(RuntimeAddress(StubRoutines::upcall_stub_load_target())); // puts target Method* in rbx + __ block_comment("} load target "); __ push_cont_fastpath(); @@ -377,7 +374,7 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, #ifndef PRODUCT stringStream ss; - ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + ss.print("upcall_stub_%s", signature->as_C_string()); const char* name = _masm->code_string(ss.freeze()); #else // PRODUCT const char* name = "upcall_stub"; diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 2549feb8a40..038797924a9 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -437,6 +437,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ cmpl(rax, 0x80000); __ jcc(Assembler::notEqual, vector_save_restore); +#ifndef PRODUCT bool save_apx = UseAPX; VM_Version::set_apx_cpuFeatures(); UseAPX = true; @@ -453,6 +454,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ movq(Address(rsi, 8), r31); UseAPX = save_apx; +#endif #endif __ bind(vector_save_restore); // diff --git a/src/hotspot/cpu/zero/upcallLinker_zero.cpp b/src/hotspot/cpu/zero/upcallLinker_zero.cpp index 6447dac86c9..408ebc32820 100644 --- a/src/hotspot/cpu/zero/upcallLinker_zero.cpp +++ b/src/hotspot/cpu/zero/upcallLinker_zero.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "prims/upcallLinker.hpp" -address UpcallLinker::make_upcall_stub(jobject mh, Method* entry, +address UpcallLinker::make_upcall_stub(jobject mh, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/os/aix/osThread_aix.cpp b/src/hotspot/os/aix/osThread_aix.cpp index 4049d6b58b7..ab08a766156 100644 --- a/src/hotspot/os/aix/osThread_aix.cpp +++ b/src/hotspot/os/aix/osThread_aix.cpp @@ -23,32 +23,27 @@ * */ -// no precompiled headers - -#include "memory/allocation.inline.hpp" -#include "runtime/handles.inline.hpp" -#include "runtime/mutexLocker.hpp" -#include "runtime/os.hpp" +#include "precompiled.hpp" +#include "memory/allocation.hpp" +#include "runtime/mutex.hpp" #include "runtime/osThread.hpp" -#include "runtime/safepoint.hpp" -#include "runtime/vmThread.hpp" - -void OSThread::pd_initialize() { - _thread_id = 0; - _kernel_thread_id = 0; - _siginfo = nullptr; - _ucontext = nullptr; - _expanding_stack = 0; - _alt_sig_stack = nullptr; - _last_cpu_times.sys = _last_cpu_times.user = 0L; +#include +OSThread::OSThread() + : _thread_id(0), + _thread_type(), + _kernel_thread_id(0), + _caller_sigmask(), + sr(), + _siginfo(nullptr), + _ucontext(nullptr), + _expanding_stack(0), + _alt_sig_stack(nullptr), + _startThread_lock(new Monitor(Mutex::event, "startThread_lock")) { sigemptyset(&_caller_sigmask); - - _startThread_lock = new Monitor(Mutex::event, "startThread_lock"); - assert(_startThread_lock != nullptr, "check"); } -void OSThread::pd_destroy() { +OSThread::~OSThread() { delete _startThread_lock; } diff --git a/src/hotspot/os/aix/osThread_aix.hpp b/src/hotspot/os/aix/osThread_aix.hpp index 5feb3c5799a..8f3799d0701 100644 --- a/src/hotspot/os/aix/osThread_aix.hpp +++ b/src/hotspot/os/aix/osThread_aix.hpp @@ -26,22 +26,17 @@ #ifndef OS_AIX_OSTHREAD_AIX_HPP #define OS_AIX_OSTHREAD_AIX_HPP - public: - typedef pthread_t thread_id_t; +#include "runtime/osThreadBase.hpp" +#include "suspendResume_posix.hpp" +#include "utilities/globalDefinitions.hpp" - private: - int _thread_type; +class OSThread : public OSThreadBase { + friend class VMStructs; - public: - - int thread_type() const { - return _thread_type; - } - void set_thread_type(int type) { - _thread_type = type; - } + typedef pthread_t thread_id_t; - private: + thread_id_t _thread_id; + int _thread_type; // On AIX, we use the pthread id as OSThread::thread_id and keep the kernel thread id // separately for diagnostic purposes. @@ -54,15 +49,27 @@ sigset_t _caller_sigmask; // Caller's signal mask public: + OSThread(); + ~OSThread(); + + int thread_type() const { + return _thread_type; + } + void set_thread_type(int type) { + _thread_type = type; + } // Methods to save/restore caller's signal mask sigset_t caller_sigmask() const { return _caller_sigmask; } void set_caller_sigmask(sigset_t sigmask) { _caller_sigmask = sigmask; } -#ifndef PRODUCT - // Used for debugging, return a unique integer for each thread. - int thread_identifier() const { return _thread_id; } -#endif + thread_id_t thread_id() const { + return _thread_id; + } + void set_thread_id(thread_id_t id) { + _thread_id = id; + } + tid_t kernel_thread_id() const { return _kernel_thread_id; } @@ -71,7 +78,7 @@ } pthread_t pthread_id() const { - // Here: same as OSThread::thread_id() + // Here: same as thread_id() return _thread_id; } @@ -79,7 +86,6 @@ // suspension support. // *************************************************************** - public: // flags that support signal based suspend/resume on Aix are in a // separate class to avoid confusion with many flags in OSThread that // are used by VM level suspend/resume. @@ -125,22 +131,10 @@ return _startThread_lock; } - // *************************************************************** - // Platform dependent initialization and cleanup - // *************************************************************** - - private: - - void pd_initialize(); - void pd_destroy(); - - public: - - // The last measured values of cpu timing to prevent the "stale - // value return" bug in thread_cpu_time. - volatile struct { - jlong sys; - jlong user; - } _last_cpu_times; + // Printing + uintx thread_id_for_printing() const override { + return (uintx)_thread_id; + } +}; #endif // OS_AIX_OSTHREAD_AIX_HPP diff --git a/src/hotspot/os/aix/vmStructs_aix.hpp b/src/hotspot/os/aix/vmStructs_aix.hpp index 1a2f4c4bf6e..f3bbc80e62c 100644 --- a/src/hotspot/os/aix/vmStructs_aix.hpp +++ b/src/hotspot/os/aix/vmStructs_aix.hpp @@ -29,9 +29,20 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) +#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, pthread_t) \ + +#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ + \ + /**********************/ \ + /* Posix Thread IDs */ \ + /**********************/ \ + \ + declare_unsigned_integer_type(pthread_t) #define VM_INT_CONSTANTS_OS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os/bsd/osThread_bsd.cpp b/src/hotspot/os/bsd/osThread_bsd.cpp index 7b9ad1f76a8..4080ea1bf29 100644 --- a/src/hotspot/os/bsd/osThread_bsd.cpp +++ b/src/hotspot/os/bsd/osThread_bsd.cpp @@ -22,30 +22,32 @@ * */ -// no precompiled headers -#include "memory/allocation.inline.hpp" -#include "runtime/mutexLocker.hpp" +#include "precompiled.hpp" +#include "memory/allocation.hpp" +#include "runtime/mutex.hpp" #include "runtime/osThread.hpp" #include -void OSThread::pd_initialize() { +OSThread::OSThread() + : _thread_id( #ifdef __APPLE__ - _thread_id = 0; + 0 #else - _thread_id = nullptr; + nullptr #endif - _unique_thread_id = 0; - _pthread_id = nullptr; - _siginfo = nullptr; - _ucontext = nullptr; - _expanding_stack = 0; - _alt_sig_stack = nullptr; - + ), + _thread_type(), + _pthread_id(nullptr), + _unique_thread_id(0), + _caller_sigmask(), + sr(), + _siginfo(nullptr), + _ucontext(nullptr), + _expanding_stack(0), + _alt_sig_stack(nullptr), + _startThread_lock(new Monitor(Mutex::event, "startThread_lock")) { sigemptyset(&_caller_sigmask); - - _startThread_lock = new Monitor(Mutex::event, "startThread_lock"); - assert(_startThread_lock !=nullptr, "check"); } // Additional thread_id used to correlate threads in SA @@ -64,6 +66,6 @@ void OSThread::set_unique_thread_id() { #endif } -void OSThread::pd_destroy() { +OSThread::~OSThread() { delete _startThread_lock; } diff --git a/src/hotspot/os/bsd/osThread_bsd.hpp b/src/hotspot/os/bsd/osThread_bsd.hpp index 11376835063..e54e7195f98 100644 --- a/src/hotspot/os/bsd/osThread_bsd.hpp +++ b/src/hotspot/os/bsd/osThread_bsd.hpp @@ -25,19 +25,12 @@ #ifndef OS_BSD_OSTHREAD_BSD_HPP #define OS_BSD_OSTHREAD_BSD_HPP - private: - int _thread_type; +#include "runtime/osThreadBase.hpp" +#include "suspendResume_posix.hpp" +#include "utilities/globalDefinitions.hpp" - public: - - int thread_type() const { - return _thread_type; - } - void set_thread_type(int type) { - _thread_type = type; - } - - private: +class OSThread : public OSThreadBase { + friend class VMStructs; #ifdef __APPLE__ typedef thread_t thread_id_t; @@ -45,6 +38,9 @@ typedef pid_t thread_id_t; #endif + thread_id_t _thread_id; + int _thread_type; + // _pthread_id is the pthread id, which is used by library calls // (e.g. pthread_kill). pthread_t _pthread_id; @@ -57,15 +53,26 @@ sigset_t _caller_sigmask; // Caller's signal mask public: + OSThread(); + ~OSThread(); + + int thread_type() const { + return _thread_type; + } + void set_thread_type(int type) { + _thread_type = type; + } // Methods to save/restore caller's signal mask sigset_t caller_sigmask() const { return _caller_sigmask; } void set_caller_sigmask(sigset_t sigmask) { _caller_sigmask = sigmask; } -#ifndef PRODUCT - // Used for debugging, return a unique integer for each thread. - intptr_t thread_identifier() const { return (intptr_t)_pthread_id; } -#endif + thread_id_t thread_id() const { + return _thread_id; + } + void set_thread_id(thread_id_t id) { + _thread_id = id; + } pthread_t pthread_id() const { return _pthread_id; @@ -80,7 +87,6 @@ // suspension support. // *************************************************************** -public: // flags that support signal based suspend/resume on Bsd are in a // separate class to avoid confusion with many flags in OSThread that // are used by VM level suspend/resume. @@ -126,17 +132,9 @@ return _startThread_lock; } - // *************************************************************** - // Platform dependent initialization and cleanup - // *************************************************************** - -private: - - void pd_initialize(); - void pd_destroy(); - -// Reconciliation History -// osThread_solaris.hpp 1.24 99/08/27 13:11:54 -// End + uintx thread_id_for_printing() const override { + return (uintx)_thread_id; + } +}; #endif // OS_BSD_OSTHREAD_BSD_HPP diff --git a/src/hotspot/os/bsd/vmStructs_bsd.hpp b/src/hotspot/os/bsd/vmStructs_bsd.hpp index 84c1be77374..8c9c132e1c2 100644 --- a/src/hotspot/os/bsd/vmStructs_bsd.hpp +++ b/src/hotspot/os/bsd/vmStructs_bsd.hpp @@ -31,9 +31,21 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) +#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ + nonstatic_field(OSThread, _unique_thread_id, uint64_t) + +#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ + \ + /**********************/ \ + /* Thread IDs */ \ + /**********************/ \ + \ + declare_unsigned_integer_type(OSThread::thread_id_t) #define VM_INT_CONSTANTS_OS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 527573644a8..56dcadd670f 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -64,16 +64,16 @@ class CgroupV2CpuController: public CgroupCpuController { bool is_read_only() override { return reader()->is_read_only(); } - const char* subsystem_path() { + const char* subsystem_path() override { return reader()->subsystem_path(); } bool needs_hierarchy_adjustment() override { return reader()->needs_hierarchy_adjustment(); } - void set_subsystem_path(const char* cgroup_path) { + void set_subsystem_path(const char* cgroup_path) override { reader()->set_subsystem_path(cgroup_path); } - const char* mount_point() { return reader()->mount_point(); } + const char* mount_point() override { return reader()->mount_point(); } const char* cgroup_path() override { return reader()->cgroup_path(); } }; @@ -97,16 +97,16 @@ class CgroupV2MemoryController final: public CgroupMemoryController { bool is_read_only() override { return reader()->is_read_only(); } - const char* subsystem_path() { + const char* subsystem_path() override { return reader()->subsystem_path(); } bool needs_hierarchy_adjustment() override { return reader()->needs_hierarchy_adjustment(); } - void set_subsystem_path(const char* cgroup_path) { + void set_subsystem_path(const char* cgroup_path) override { reader()->set_subsystem_path(cgroup_path); } - const char* mount_point() { return reader()->mount_point(); } + const char* mount_point() override { return reader()->mount_point(); } const char* cgroup_path() override { return reader()->cgroup_path(); } }; diff --git a/src/hotspot/os/linux/osThread_linux.cpp b/src/hotspot/os/linux/osThread_linux.cpp index 9c77cb32f6d..3dd6e3bbcd1 100644 --- a/src/hotspot/os/linux/osThread_linux.cpp +++ b/src/hotspot/os/linux/osThread_linux.cpp @@ -22,27 +22,27 @@ * */ -// no precompiled headers -#include "memory/allocation.inline.hpp" +#include "precompiled.hpp" +#include "memory/allocation.hpp" #include "runtime/mutex.hpp" #include "runtime/osThread.hpp" #include -void OSThread::pd_initialize() { - _thread_id = 0; - _pthread_id = 0; - _siginfo = nullptr; - _ucontext = nullptr; - _expanding_stack = 0; - _alt_sig_stack = nullptr; - +OSThread::OSThread() + : _thread_id(0), + _thread_type(), + _pthread_id(0), + _caller_sigmask(), + sr(), + _siginfo(nullptr), + _ucontext(nullptr), + _expanding_stack(0), + _alt_sig_stack(nullptr), + _startThread_lock(new Monitor(Mutex::event, "startThread_lock")) { sigemptyset(&_caller_sigmask); - - _startThread_lock = new Monitor(Mutex::event, "startThread_lock"); - assert(_startThread_lock !=nullptr, "check"); } -void OSThread::pd_destroy() { +OSThread::~OSThread() { delete _startThread_lock; } diff --git a/src/hotspot/os/linux/osThread_linux.hpp b/src/hotspot/os/linux/osThread_linux.hpp index a849673af62..f8dfd5a213b 100644 --- a/src/hotspot/os/linux/osThread_linux.hpp +++ b/src/hotspot/os/linux/osThread_linux.hpp @@ -24,13 +24,28 @@ #ifndef OS_LINUX_OSTHREAD_LINUX_HPP #define OS_LINUX_OSTHREAD_LINUX_HPP - public: + +#include "runtime/osThreadBase.hpp" +#include "suspendResume_posix.hpp" +#include "utilities/globalDefinitions.hpp" + +class OSThread : public OSThreadBase { + friend class VMStructs; + typedef pid_t thread_id_t; - private: + thread_id_t _thread_id; int _thread_type; + // _pthread_id is the pthread id, which is used by library calls + // (e.g. pthread_kill). + pthread_t _pthread_id; + + sigset_t _caller_sigmask; // Caller's signal mask + public: + OSThread(); + ~OSThread(); int thread_type() const { return _thread_type; @@ -39,22 +54,16 @@ _thread_type = type; } - // _pthread_id is the pthread id, which is used by library calls - // (e.g. pthread_kill). - pthread_t _pthread_id; - - sigset_t _caller_sigmask; // Caller's signal mask - - public: - // Methods to save/restore caller's signal mask sigset_t caller_sigmask() const { return _caller_sigmask; } void set_caller_sigmask(sigset_t sigmask) { _caller_sigmask = sigmask; } -#ifndef PRODUCT - // Used for debugging, return a unique integer for each thread. - int thread_identifier() const { return _thread_id; } -#endif + thread_id_t thread_id() const { + return _thread_id; + } + void set_thread_id(thread_id_t id) { + _thread_id = id; + } pthread_t pthread_id() const { return _pthread_id; @@ -67,7 +76,6 @@ // suspension support. // *************************************************************** -public: // flags that support signal based suspend/resume on Linux are in a // separate class to avoid confusion with many flags in OSThread that // are used by VM level suspend/resume. @@ -113,17 +121,10 @@ return _startThread_lock; } - // *************************************************************** - // Platform dependent initialization and cleanup - // *************************************************************** - -private: - - void pd_initialize(); - void pd_destroy(); - -// Reconciliation History -// osThread_solaris.hpp 1.24 99/08/27 13:11:54 -// End + // Printing + uintx thread_id_for_printing() const override { + return (uintx)_thread_id; + } +}; #endif // OS_LINUX_OSTHREAD_LINUX_HPP diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index e08e0b72132..cace6e348e9 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -820,7 +820,7 @@ static void *thread_native_entry(Thread *thread) { OSThread* osthread = thread->osthread(); Monitor* sync = osthread->startThread_lock(); - osthread->set_thread_id(checked_cast(os::current_thread_id())); + osthread->set_thread_id(checked_cast(os::current_thread_id())); if (UseNUMA) { int lgrp_id = os::numa_get_group_id(); diff --git a/src/hotspot/os/linux/vmStructs_linux.hpp b/src/hotspot/os/linux/vmStructs_linux.hpp index 818f6bb188f..3b82ac58ac6 100644 --- a/src/hotspot/os/linux/vmStructs_linux.hpp +++ b/src/hotspot/os/linux/vmStructs_linux.hpp @@ -31,9 +31,22 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) +#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + nonstatic_field(OSThread, _thread_id, pid_t) \ + nonstatic_field(OSThread, _pthread_id, pthread_t) + +#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ + \ + /**********************/ \ + /* Posix Thread IDs */ \ + /**********************/ \ + \ + declare_integer_type(pid_t) \ + declare_unsigned_integer_type(pthread_t) #define VM_INT_CONSTANTS_OS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os/windows/globals_windows.hpp b/src/hotspot/os/windows/globals_windows.hpp index 78cbac6e9cc..8f0a6261cc0 100644 --- a/src/hotspot/os/windows/globals_windows.hpp +++ b/src/hotspot/os/windows/globals_windows.hpp @@ -38,6 +38,10 @@ product(bool, UseAllWindowsProcessorGroups, false, \ "Use all processor groups on supported Windows versions") \ \ +product(bool, EnableAllLargePageSizesForWindows, false, \ + "Enable support for multiple large page sizes on " \ + "Windows Server") \ + \ product(bool, UseOSErrorReporting, false, \ "Let VM fatal error propagate to the OS (ie. WER on Windows)") diff --git a/src/hotspot/os/windows/osThread_windows.cpp b/src/hotspot/os/windows/osThread_windows.cpp index 5f369bb7aa0..922b4b0104b 100644 --- a/src/hotspot/os/windows/osThread_windows.cpp +++ b/src/hotspot/os/windows/osThread_windows.cpp @@ -22,17 +22,17 @@ * */ -// no precompiled headers -#include "runtime/os.hpp" +#include "precompiled.hpp" #include "runtime/osThread.hpp" -void OSThread::pd_initialize() { - set_thread_handle(nullptr); - set_thread_id(0); - set_interrupt_event(nullptr); -} +#include + +OSThread::OSThread() + : _thread_id(0), + _thread_handle(nullptr), + _interrupt_event(nullptr) {} -void OSThread::pd_destroy() { +OSThread::~OSThread() { if (_interrupt_event != nullptr) { CloseHandle(_interrupt_event); } diff --git a/src/hotspot/os/windows/osThread_windows.hpp b/src/hotspot/os/windows/osThread_windows.hpp index 5bd07646b17..e54783aef1c 100644 --- a/src/hotspot/os/windows/osThread_windows.hpp +++ b/src/hotspot/os/windows/osThread_windows.hpp @@ -25,17 +25,29 @@ #ifndef OS_WINDOWS_OSTHREAD_WINDOWS_HPP #define OS_WINDOWS_OSTHREAD_WINDOWS_HPP - typedef void* HANDLE; - public: +#include "runtime/osThreadBase.hpp" +#include "utilities/globalDefinitions.hpp" + +class OSThread : public OSThreadBase { + friend class VMStructs; + typedef unsigned long thread_id_t; + typedef void* HANDLE; + + thread_id_t _thread_id; - private: // Win32-specific thread information HANDLE _thread_handle; // Win32 thread handle HANDLE _interrupt_event; // Event signalled on thread interrupt for use by // Process.waitFor(). public: + OSThread(); + ~OSThread(); + + thread_id_t thread_id() const { return _thread_id; } + void set_thread_id(thread_id_t id) { _thread_id = id; } + // The following will only apply in the Win32 implementation, and should only // be visible in the concrete class, not this which should be an abstract base class HANDLE thread_handle() const { return _thread_handle; } @@ -45,13 +57,9 @@ // This is specialized on Windows to interact with the _interrupt_event. void set_interrupted(bool z); -#ifndef PRODUCT - // Used for debugging, return a unique integer for each thread. - int thread_identifier() const { return _thread_id; } -#endif - - private: - void pd_initialize(); - void pd_destroy(); + uintx thread_id_for_printing() const override { + return (uintx)_thread_id; + } +}; #endif // OS_WINDOWS_OSTHREAD_WINDOWS_HPP diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 817757f1ac6..0d5727b98f4 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -63,6 +63,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/statSampler.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/suspendedThreadTask.hpp" #include "runtime/threadCritical.hpp" #include "runtime/threads.hpp" #include "runtime/timer.hpp" @@ -3126,7 +3127,7 @@ class NUMANodeListHolder { static size_t _large_page_size = 0; -static bool request_lock_memory_privilege() { +bool os::win32::request_lock_memory_privilege() { HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, os::current_process_id()); @@ -3310,14 +3311,14 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, return p_buf; } -static size_t large_page_init_decide_size() { +size_t os::win32::large_page_init_decide_size() { // print a warning if any large page related flag is specified on command line bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) || !FLAG_IS_DEFAULT(LargePageSizeInBytes); -#define WARN(msg) if (warn_on_failure) { warning(msg); } +#define WARN(...) if (warn_on_failure) { warning(__VA_ARGS__); } - if (!request_lock_memory_privilege()) { + if (!os::win32::request_lock_memory_privilege()) { WARN("JVM cannot use large page memory because it does not have enough privilege to lock pages in memory."); return 0; } @@ -3328,15 +3329,26 @@ static size_t large_page_init_decide_size() { return 0; } -#if defined(IA32) || defined(AMD64) - if (size > 4*M || LargePageSizeInBytes > 4*M) { +#if defined(IA32) + if (size > 4 * M || LargePageSizeInBytes > 4 * M) { WARN("JVM cannot use large pages bigger than 4mb."); return 0; } +#elif defined(AMD64) + if (!EnableAllLargePageSizesForWindows) { + if (size > 4 * M || LargePageSizeInBytes > 4 * M) { + WARN("JVM cannot use large pages bigger than 4mb."); + return 0; + } + } #endif - if (LargePageSizeInBytes > 0 && LargePageSizeInBytes % size == 0) { - size = LargePageSizeInBytes; + if (LargePageSizeInBytes > 0) { + if (LargePageSizeInBytes % size == 0) { + size = LargePageSizeInBytes; + } else { + WARN("The specified large page size (%d) is not a multiple of the minimum large page size (%d), defaulting to minimum page size.", LargePageSizeInBytes, size); + } } #undef WARN @@ -3349,12 +3361,23 @@ void os::large_page_init() { return; } - _large_page_size = large_page_init_decide_size(); + _large_page_size = os::win32::large_page_init_decide_size(); const size_t default_page_size = os::vm_page_size(); if (_large_page_size > default_page_size) { +#if !defined(IA32) + if (EnableAllLargePageSizesForWindows) { + size_t min_size = GetLargePageMinimum(); + + // Populate _page_sizes with large page sizes less than or equal to _large_page_size, ensuring each page size is double the size of the previous one. + for (size_t page_size = min_size; page_size < _large_page_size; page_size *= 2) { + _page_sizes.add(page_size); + } + } +#endif + _page_sizes.add(_large_page_size); } - + // Set UseLargePages based on whether a large page size was successfully determined UseLargePages = _large_page_size != 0; } @@ -3618,7 +3641,6 @@ static char* reserve_large_pages_aligned(size_t size, size_t alignment, bool exe char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_size, char* addr, bool exec) { assert(UseLargePages, "only for large pages"); - assert(page_size == os::large_page_size(), "Currently only support one large page size on Windows"); assert(is_aligned(addr, alignment), "Must be"); assert(is_aligned(addr, page_size), "Must be"); @@ -3627,11 +3649,17 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_ return nullptr; } + // Ensure GetLargePageMinimum() returns a valid positive value + size_t large_page_min = GetLargePageMinimum(); + if (large_page_min <= 0) { + return nullptr; + } + // The requested alignment can be larger than the page size, for example with G1 // the alignment is bound to the heap region size. So this reservation needs to // ensure that the requested alignment is met. When there is a requested address // this solves it self, since it must be properly aligned already. - if (addr == nullptr && alignment > page_size) { + if (addr == nullptr && alignment > large_page_min) { return reserve_large_pages_aligned(bytes, alignment, exec); } @@ -5965,7 +5993,7 @@ static void do_resume(HANDLE* h) { // retrieve a suspend/resume context capable handle // from the tid. Caller validates handle return value. void get_thread_handle_for_extended_context(HANDLE* h, - OSThread::thread_id_t tid) { + DWORD tid) { if (h != nullptr) { *h = OpenThread(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE, tid); } diff --git a/src/hotspot/os/windows/os_windows.hpp b/src/hotspot/os/windows/os_windows.hpp index 3bc5ab9eef1..1d523724300 100644 --- a/src/hotspot/os/windows/os_windows.hpp +++ b/src/hotspot/os/windows/os_windows.hpp @@ -65,6 +65,8 @@ class os::win32 { static void setmode_streams(); static bool is_windows_11_or_greater(); static bool is_windows_server_2022_or_greater(); + static bool request_lock_memory_privilege(); + static size_t large_page_init_decide_size(); static int windows_major_version() { assert(_major_version > 0, "windows version not initialized."); return _major_version; diff --git a/src/hotspot/os/windows/vmStructs_windows.hpp b/src/hotspot/os/windows/vmStructs_windows.hpp index 2550e685f16..93f4ea7c811 100644 --- a/src/hotspot/os/windows/vmStructs_windows.hpp +++ b/src/hotspot/os/windows/vmStructs_windows.hpp @@ -29,9 +29,18 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) +#define VM_STRUCTS_OS(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ + \ + /******************************/ \ + /* Threads (NOTE: incomplete) */ \ + /******************************/ \ + \ + nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ + unchecked_nonstatic_field(OSThread, _thread_handle, sizeof(HANDLE)) /* NOTE: no type */ + +#define VM_TYPES_OS(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ + \ + declare_unsigned_integer_type(OSThread::thread_id_t) #define VM_INT_CONSTANTS_OS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/aix_ppc/vmStructs_aix_ppc.hpp b/src/hotspot/os_cpu/aix_ppc/vmStructs_aix_ppc.hpp index 157d57f8e0f..123cd67248f 100644 --- a/src/hotspot/os_cpu/aix_ppc/vmStructs_aix_ppc.hpp +++ b/src/hotspot/os_cpu/aix_ppc/vmStructs_aix_ppc.hpp @@ -30,21 +30,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, pthread_t) \ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/bsd_aarch64/vmStructs_bsd_aarch64.hpp b/src/hotspot/os_cpu/bsd_aarch64/vmStructs_bsd_aarch64.hpp index 07b878106cf..c384afac7ec 100644 --- a/src/hotspot/os_cpu/bsd_aarch64/vmStructs_bsd_aarch64.hpp +++ b/src/hotspot/os_cpu/bsd_aarch64/vmStructs_bsd_aarch64.hpp @@ -31,22 +31,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _unique_thread_id, uint64_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Thread IDs */ \ - /**********************/ \ - \ - declare_unsigned_integer_type(OSThread::thread_id_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/bsd_x86/vmStructs_bsd_x86.hpp b/src/hotspot/os_cpu/bsd_x86/vmStructs_bsd_x86.hpp index fb43541fa77..b48ea82712e 100644 --- a/src/hotspot/os_cpu/bsd_x86/vmStructs_bsd_x86.hpp +++ b/src/hotspot/os_cpu/bsd_x86/vmStructs_bsd_x86.hpp @@ -29,22 +29,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _unique_thread_id, uint64_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Thread IDs */ \ - /**********************/ \ - \ - declare_unsigned_integer_type(OSThread::thread_id_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_aarch64/vmStructs_linux_aarch64.hpp b/src/hotspot/os_cpu/linux_aarch64/vmStructs_linux_aarch64.hpp index f2ad002996b..3c8e9c44414 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vmStructs_linux_aarch64.hpp +++ b/src/hotspot/os_cpu/linux_aarch64/vmStructs_linux_aarch64.hpp @@ -30,23 +30,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_arm/vmStructs_linux_arm.hpp b/src/hotspot/os_cpu/linux_arm/vmStructs_linux_arm.hpp index 9b4bd0faf0a..120726bf55f 100644 --- a/src/hotspot/os_cpu/linux_arm/vmStructs_linux_arm.hpp +++ b/src/hotspot/os_cpu/linux_arm/vmStructs_linux_arm.hpp @@ -29,22 +29,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_ppc/vmStructs_linux_ppc.hpp b/src/hotspot/os_cpu/linux_ppc/vmStructs_linux_ppc.hpp index 9464c359770..ae948c73031 100644 --- a/src/hotspot/os_cpu/linux_ppc/vmStructs_linux_ppc.hpp +++ b/src/hotspot/os_cpu/linux_ppc/vmStructs_linux_ppc.hpp @@ -30,23 +30,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, pid_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(pid_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_riscv/vmStructs_linux_riscv.hpp b/src/hotspot/os_cpu/linux_riscv/vmStructs_linux_riscv.hpp index 6cf7683a586..3946394c19b 100644 --- a/src/hotspot/os_cpu/linux_riscv/vmStructs_linux_riscv.hpp +++ b/src/hotspot/os_cpu/linux_riscv/vmStructs_linux_riscv.hpp @@ -30,23 +30,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_s390/vmStructs_linux_s390.hpp b/src/hotspot/os_cpu/linux_s390/vmStructs_linux_s390.hpp index 0442510fa24..a0fb5eb1a6a 100644 --- a/src/hotspot/os_cpu/linux_s390/vmStructs_linux_s390.hpp +++ b/src/hotspot/os_cpu/linux_s390/vmStructs_linux_s390.hpp @@ -30,23 +30,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, pid_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(pid_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/linux_x86/vmStructs_linux_x86.hpp b/src/hotspot/os_cpu/linux_x86/vmStructs_linux_x86.hpp index 277486549c0..8f6d3657237 100644 --- a/src/hotspot/os_cpu/linux_x86/vmStructs_linux_x86.hpp +++ b/src/hotspot/os_cpu/linux_x86/vmStructs_linux_x86.hpp @@ -29,23 +29,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - /**********************/ \ - /* Posix Thread IDs */ \ - /**********************/ \ - \ - declare_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/windows_aarch64/vmStructs_windows_aarch64.hpp b/src/hotspot/os_cpu/windows_aarch64/vmStructs_windows_aarch64.hpp index 220787823dc..18a5588b743 100644 --- a/src/hotspot/os_cpu/windows_aarch64/vmStructs_windows_aarch64.hpp +++ b/src/hotspot/os_cpu/windows_aarch64/vmStructs_windows_aarch64.hpp @@ -29,18 +29,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - unchecked_nonstatic_field(OSThread, _thread_handle, sizeof(HANDLE)) /* NOTE: no type */ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - declare_unsigned_integer_type(OSThread::thread_id_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/os_cpu/windows_x86/vmStructs_windows_x86.hpp b/src/hotspot/os_cpu/windows_x86/vmStructs_windows_x86.hpp index 9f50a7ed9ae..985a6a331da 100644 --- a/src/hotspot/os_cpu/windows_x86/vmStructs_windows_x86.hpp +++ b/src/hotspot/os_cpu/windows_x86/vmStructs_windows_x86.hpp @@ -29,18 +29,9 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ - \ - /******************************/ \ - /* Threads (NOTE: incomplete) */ \ - /******************************/ \ - \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - unchecked_nonstatic_field(OSThread, _thread_handle, sizeof(HANDLE)) /* NOTE: no type */ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ - \ - declare_unsigned_integer_type(OSThread::thread_id_t) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 81c4d001078..23f621ffec8 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -41,7 +41,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaFrameAnchor.hpp" -#include "runtime/jniHandles.hpp" +#include "runtime/jniHandles.inline.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/safepoint.hpp" #include "runtime/sharedRuntime.hpp" @@ -623,7 +623,7 @@ UpcallStub* UpcallStub::create(const char* name, CodeBuffer* cb, jobject receive // Track memory usage statistic after releasing CodeCache_lock MemoryService::track_code_cache_memory_usage(); - trace_new_stub(blob, "UpcallStub"); + trace_new_stub(blob, "UpcallStub - ", name); return blob; } @@ -772,6 +772,10 @@ void UpcallStub::verify() { void UpcallStub::print_on(outputStream* st) const { RuntimeBlob::print_on(st); print_value_on(st); + st->print_cr("Frame data offset: %d", (int) _frame_data_offset); + oop recv = JNIHandles::resolve(_receiver); + st->print("Receiver MH="); + recv->print_on(st); Disassembler::decode((RuntimeBlob*)this, st); } diff --git a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp index 8e17d1d2a7a..4ec7e10cd9a 100644 --- a/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp +++ b/src/hotspot/share/gc/g1/c2/g1BarrierSetC2.cpp @@ -332,7 +332,8 @@ Node* G1BarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& val) co if (tightly_coupled_alloc) { assert(!use_ReduceInitialCardMarks(), "post-barriers are only needed for tightly-coupled initialization stores when ReduceInitialCardMarks is disabled"); - access.set_barrier_data(access.barrier_data() ^ G1C2BarrierPre); + // Pre-barriers are unnecessary for tightly-coupled initialization stores. + access.set_barrier_data(access.barrier_data() & ~G1C2BarrierPre); } } return BarrierSetC2::store_at_resolved(access, val); diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index d315497268f..ec90fd37750 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -26,7 +26,7 @@ #include "gc/g1/g1Analytics.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectionSet.hpp" -#include "gc/g1/g1CollectionSetCandidates.hpp" +#include "gc/g1/g1CollectionSetCandidates.inline.hpp" #include "gc/g1/g1CollectorState.hpp" #include "gc/g1/g1HeapRegion.inline.hpp" #include "gc/g1/g1HeapRegionRemSet.inline.hpp" @@ -346,20 +346,16 @@ void G1CollectionSet::finalize_old_part(double time_remaining_ms) { G1CollectionCandidateRegionList pinned_retained_regions; if (collector_state()->in_mixed_phase()) { - time_remaining_ms = _policy->select_candidates_from_marking(&candidates()->marking_regions(), - time_remaining_ms, - &initial_old_regions, - &_optional_old_regions, - &pinned_marking_regions); + time_remaining_ms = select_candidates_from_marking(time_remaining_ms, + &initial_old_regions, + &pinned_marking_regions); } else { log_debug(gc, ergo, cset)("Do not add marking candidates to collection set due to pause type."); } - _policy->select_candidates_from_retained(&candidates()->retained_regions(), - time_remaining_ms, - &initial_old_regions, - &_optional_old_regions, - &pinned_retained_regions); + select_candidates_from_retained(time_remaining_ms, + &initial_old_regions, + &pinned_retained_regions); // Move initially selected old regions to collection set directly. move_candidates_to_collection_set(&initial_old_regions); @@ -394,6 +390,215 @@ void G1CollectionSet::move_candidates_to_collection_set(G1CollectionCandidateReg candidates()->remove(regions); } +static void print_finish_message(const char* reason, bool from_marking) { + log_debug(gc, ergo, cset)("Finish adding %s candidates to collection set (%s).", + from_marking ? "marking" : "retained", reason); +} + +double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms, + G1CollectionCandidateRegionList* initial_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions) { + uint num_expensive_regions = 0; + + uint num_initial_regions_selected = 0; + uint num_optional_regions_selected = 0; + uint num_pinned_regions = 0; + + double predicted_initial_time_ms = 0.0; + double predicted_optional_time_ms = 0.0; + + double optional_threshold_ms = time_remaining_ms * _policy->optional_prediction_fraction(); + + const uint min_old_cset_length = _policy->calc_min_old_cset_length(candidates()->last_marking_candidates_length()); + const uint max_old_cset_length = MAX2(min_old_cset_length, _policy->calc_max_old_cset_length()); + const uint max_optional_regions = max_old_cset_length - min_old_cset_length; + bool check_time_remaining = _policy->use_adaptive_young_list_length(); + + G1CollectionCandidateList* marking_list = &candidates()->marking_regions(); + assert(marking_list != nullptr, "must be"); + + log_debug(gc, ergo, cset)("Start adding marking candidates to collection set. " + "Min %u regions, max %u regions, available %u regions" + "time remaining %1.2fms, optional threshold %1.2fms", + min_old_cset_length, max_old_cset_length, marking_list->length(), time_remaining_ms, optional_threshold_ms); + + G1CollectionCandidateListIterator iter = marking_list->begin(); + for (; iter != marking_list->end(); ++iter) { + if (num_initial_regions_selected + num_optional_regions_selected >= max_old_cset_length) { + // Added maximum number of old regions to the CSet. + print_finish_message("Maximum number of regions reached", true); + break; + } + G1HeapRegion* hr = (*iter)->_r; + // Skip evacuating pinned marking regions because we are not getting any free + // space from them (and we expect to get free space from marking candidates). + // Also prepare to move them to retained regions to be evacuated optionally later + // to not impact the mixed phase too much. + if (hr->has_pinned_objects()) { + num_pinned_regions++; + (*iter)->update_num_unreclaimed(); + log_trace(gc, ergo, cset)("Marking candidate %u can not be reclaimed currently. Skipping.", hr->hrm_index()); + pinned_old_regions->append(hr); + continue; + } + double predicted_time_ms = _policy->predict_region_total_time_ms(hr, false); + time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0); + // Add regions to old set until we reach the minimum amount + if (initial_old_regions->length() < min_old_cset_length) { + initial_old_regions->append(hr); + num_initial_regions_selected++; + predicted_initial_time_ms += predicted_time_ms; + // Record the number of regions added with no time remaining + if (time_remaining_ms == 0.0) { + num_expensive_regions++; + } + } else if (!check_time_remaining) { + // In the non-auto-tuning case, we'll finish adding regions + // to the CSet if we reach the minimum. + print_finish_message("Region amount reached min", true); + break; + } else { + // Keep adding regions to old set until we reach the optional threshold + if (time_remaining_ms > optional_threshold_ms) { + predicted_initial_time_ms += predicted_time_ms; + initial_old_regions->append(hr); + num_initial_regions_selected++; + } else if (time_remaining_ms > 0) { + // Keep adding optional regions until time is up. + assert(_optional_old_regions.length() < max_optional_regions, "Should not be possible."); + predicted_optional_time_ms += predicted_time_ms; + _optional_old_regions.append(hr); + num_optional_regions_selected++; + } else { + print_finish_message("Predicted time too high", true); + break; + } + } + } + if (iter == marking_list->end()) { + log_debug(gc, ergo, cset)("Marking candidates exhausted."); + } + + if (num_expensive_regions > 0) { + log_debug(gc, ergo, cset)("Added %u marking candidates to collection set although the predicted time was too high.", + num_expensive_regions); + } + + log_debug(gc, ergo, cset)("Finish adding marking candidates to collection set. Initial: %u, optional: %u, pinned: %u, " + "predicted initial time: %1.2fms, predicted optional time: %1.2fms, time remaining: %1.2fms", + num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, + predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms); + + assert(initial_old_regions->length() == num_initial_regions_selected, "must be"); + assert(_optional_old_regions.length() == num_optional_regions_selected, "must be"); + return time_remaining_ms; +} + +void G1CollectionSet::select_candidates_from_retained(double time_remaining_ms, + G1CollectionCandidateRegionList* initial_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions) { + uint num_initial_regions_selected = 0; + uint num_optional_regions_selected = 0; + uint num_expensive_regions_selected = 0; + uint num_pinned_regions = 0; + + double predicted_initial_time_ms = 0.0; + double predicted_optional_time_ms = 0.0; + + uint const min_regions = _policy->min_retained_old_cset_length(); + // We want to make sure that on the one hand we process the retained regions asap, + // but on the other hand do not take too many of them as optional regions. + // So we split the time budget into budget we will unconditionally take into the + // initial old regions, and budget for taking optional regions from the retained + // list. + double optional_time_remaining_ms = _policy->max_time_for_retaining(); + time_remaining_ms = MIN2(time_remaining_ms, optional_time_remaining_ms); + + G1CollectionCandidateList* retained_list = &candidates()->retained_regions(); + + log_debug(gc, ergo, cset)("Start adding retained candidates to collection set. " + "Min %u regions, available %u, " + "time remaining %1.2fms, optional remaining %1.2fms", + min_regions, retained_list->length(), time_remaining_ms, optional_time_remaining_ms); + + for (G1CollectionSetCandidateInfo* ci : *retained_list) { + G1HeapRegion* r = ci->_r; + double predicted_time_ms = _policy->predict_region_total_time_ms(r, collector_state()->in_young_only_phase()); + bool fits_in_remaining_time = predicted_time_ms <= time_remaining_ms; + // If we can't reclaim that region ignore it for now. + if (r->has_pinned_objects()) { + num_pinned_regions++; + if (ci->update_num_unreclaimed()) { + log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Skipping.", r->hrm_index()); + } else { + log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Dropping.", r->hrm_index()); + pinned_old_regions->append(r); + } + continue; + } + + if (fits_in_remaining_time || (num_expensive_regions_selected < min_regions)) { + predicted_initial_time_ms += predicted_time_ms; + if (!fits_in_remaining_time) { + num_expensive_regions_selected++; + } + initial_old_regions->append(r); + num_initial_regions_selected++; + } else if (predicted_time_ms <= optional_time_remaining_ms) { + predicted_optional_time_ms += predicted_time_ms; + _optional_old_regions.append(r); + num_optional_regions_selected++; + } else { + // Fits neither initial nor optional time limit. Exit. + break; + } + time_remaining_ms = MAX2(0.0, time_remaining_ms - predicted_time_ms); + optional_time_remaining_ms = MAX2(0.0, optional_time_remaining_ms - predicted_time_ms); + } + + uint num_regions_selected = num_initial_regions_selected + num_optional_regions_selected; + if (num_regions_selected == retained_list->length()) { + log_debug(gc, ergo, cset)("Retained candidates exhausted."); + } + if (num_expensive_regions_selected > 0) { + log_debug(gc, ergo, cset)("Added %u retained candidates to collection set although the predicted time was too high.", + num_expensive_regions_selected); + } + + log_debug(gc, ergo, cset)("Finish adding retained candidates to collection set. Initial: %u, optional: %u, pinned: %u, " + "predicted initial time: %1.2fms, predicted optional time: %1.2fms, " + "time remaining: %1.2fms optional time remaining %1.2fms", + num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, + predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms, optional_time_remaining_ms); +} + +void G1CollectionSet::select_candidates_from_optional_regions(double time_remaining_ms, + G1CollectionCandidateRegionList* selected_regions) { + assert(optional_region_length() > 0, + "Should only be called when there are optional regions"); + + double total_prediction_ms = 0.0; + + for (G1HeapRegion* r : _optional_old_regions) { + double prediction_ms = _policy->predict_region_total_time_ms(r, false); + + if (prediction_ms > time_remaining_ms) { + log_debug(gc, ergo, cset)("Prediction %.3fms for region %u does not fit remaining time: %.3fms.", + prediction_ms, r->hrm_index(), time_remaining_ms); + break; + } + // This region will be included in the next optional evacuation. + + total_prediction_ms += prediction_ms; + time_remaining_ms -= prediction_ms; + + selected_regions->append(r); + } + + log_debug(gc, ergo, cset)("Prepared %u regions out of %u for optional evacuation. Total predicted time: %.3fms", + selected_regions->length(), _optional_old_regions.length(), total_prediction_ms); +} + void G1CollectionSet::prepare_optional_regions(G1CollectionCandidateRegionList* regions){ uint cur_index = 0; for (G1HeapRegion* r : *regions) { @@ -441,9 +646,8 @@ bool G1CollectionSet::finalize_optional_for_evacuation(double remaining_pause_ti update_incremental_marker(); G1CollectionCandidateRegionList selected_regions; - _policy->calculate_optional_collection_set_regions(&_optional_old_regions, - remaining_pause_time, - &selected_regions); + select_candidates_from_optional_regions(remaining_pause_time, + &selected_regions); move_candidates_to_collection_set(&selected_regions); diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.hpp index e569d3ee966..5280ba7d0fd 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp @@ -196,6 +196,22 @@ class G1CollectionSet { // and retained collection set candidates. void finalize_old_part(double time_remaining_ms); + // Calculate and fill in the initial, optional and pinned old gen candidate regions from + // the given candidate list and the remaining time. + // Returns the remaining time. + double select_candidates_from_marking(double time_remaining_ms, + G1CollectionCandidateRegionList* initial_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions); + + void select_candidates_from_retained(double time_remaining_ms, + G1CollectionCandidateRegionList* initial_old_regions, + G1CollectionCandidateRegionList* pinned_old_regions); + + // Calculate the number of optional regions from the given collection set candidates, + // the remaining time and the maximum number of these regions. + void select_candidates_from_optional_regions(double time_remaining_ms, + G1CollectionCandidateRegionList* selected); + // Iterate the part of the collection set given by the offset and length applying the given // G1HeapRegionClosure. The worker_id will determine where in the part to start the iteration // to allow for more efficient parallel iteration. diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index e7e57c962c7..6d0864f032c 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -1467,219 +1467,6 @@ uint G1Policy::calc_max_old_cset_length() const { return (uint)ceil(result); } -static void print_finish_message(const char* reason, bool from_marking) { - log_debug(gc, ergo, cset)("Finish adding %s candidates to collection set (%s).", - from_marking ? "marking" : "retained", reason); -} - -double G1Policy::select_candidates_from_marking(G1CollectionCandidateList* marking_list, - double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions) { - assert(marking_list != nullptr, "must be"); - - uint num_expensive_regions = 0; - - uint num_initial_regions_selected = 0; - uint num_optional_regions_selected = 0; - uint num_pinned_regions = 0; - - double predicted_initial_time_ms = 0.0; - double predicted_optional_time_ms = 0.0; - - double optional_threshold_ms = time_remaining_ms * optional_prediction_fraction(); - - const uint min_old_cset_length = calc_min_old_cset_length(candidates()->last_marking_candidates_length()); - const uint max_old_cset_length = MAX2(min_old_cset_length, calc_max_old_cset_length()); - const uint max_optional_regions = max_old_cset_length - min_old_cset_length; - bool check_time_remaining = use_adaptive_young_list_length(); - - log_debug(gc, ergo, cset)("Start adding marking candidates to collection set. " - "Min %u regions, max %u regions, available %u regions" - "time remaining %1.2fms, optional threshold %1.2fms", - min_old_cset_length, max_old_cset_length, marking_list->length(), time_remaining_ms, optional_threshold_ms); - - G1CollectionCandidateListIterator iter = marking_list->begin(); - for (; iter != marking_list->end(); ++iter) { - if (num_initial_regions_selected + num_optional_regions_selected >= max_old_cset_length) { - // Added maximum number of old regions to the CSet. - print_finish_message("Maximum number of regions reached", true); - break; - } - G1HeapRegion* hr = (*iter)->_r; - // Skip evacuating pinned marking regions because we are not getting any free - // space from them (and we expect to get free space from marking candidates). - // Also prepare to move them to retained regions to be evacuated optionally later - // to not impact the mixed phase too much. - if (hr->has_pinned_objects()) { - num_pinned_regions++; - (*iter)->update_num_unreclaimed(); - log_trace(gc, ergo, cset)("Marking candidate %u can not be reclaimed currently. Skipping.", hr->hrm_index()); - pinned_old_regions->append(hr); - continue; - } - double predicted_time_ms = predict_region_total_time_ms(hr, false); - time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0); - // Add regions to old set until we reach the minimum amount - if (initial_old_regions->length() < min_old_cset_length) { - initial_old_regions->append(hr); - num_initial_regions_selected++; - predicted_initial_time_ms += predicted_time_ms; - // Record the number of regions added with no time remaining - if (time_remaining_ms == 0.0) { - num_expensive_regions++; - } - } else if (!check_time_remaining) { - // In the non-auto-tuning case, we'll finish adding regions - // to the CSet if we reach the minimum. - print_finish_message("Region amount reached min", true); - break; - } else { - // Keep adding regions to old set until we reach the optional threshold - if (time_remaining_ms > optional_threshold_ms) { - predicted_initial_time_ms += predicted_time_ms; - initial_old_regions->append(hr); - num_initial_regions_selected++; - } else if (time_remaining_ms > 0) { - // Keep adding optional regions until time is up. - assert(optional_old_regions->length() < max_optional_regions, "Should not be possible."); - predicted_optional_time_ms += predicted_time_ms; - optional_old_regions->append(hr); - num_optional_regions_selected++; - } else { - print_finish_message("Predicted time too high", true); - break; - } - } - } - if (iter == marking_list->end()) { - log_debug(gc, ergo, cset)("Marking candidates exhausted."); - } - - if (num_expensive_regions > 0) { - log_debug(gc, ergo, cset)("Added %u marking candidates to collection set although the predicted time was too high.", - num_expensive_regions); - } - - log_debug(gc, ergo, cset)("Finish adding marking candidates to collection set. Initial: %u, optional: %u, pinned: %u, " - "predicted initial time: %1.2fms, predicted optional time: %1.2fms, time remaining: %1.2fms", - num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, - predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms); - - assert(initial_old_regions->length() == num_initial_regions_selected, "must be"); - assert(optional_old_regions->length() == num_optional_regions_selected, "must be"); - return time_remaining_ms; -} - -void G1Policy::select_candidates_from_retained(G1CollectionCandidateList* retained_list, - double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions) { - - uint const min_regions = min_retained_old_cset_length(); - - uint num_initial_regions_selected = 0; - uint num_optional_regions_selected = 0; - uint num_expensive_regions_selected = 0; - uint num_pinned_regions = 0; - - double predicted_initial_time_ms = 0.0; - double predicted_optional_time_ms = 0.0; - - // We want to make sure that on the one hand we process the retained regions asap, - // but on the other hand do not take too many of them as optional regions. - // So we split the time budget into budget we will unconditionally take into the - // initial old regions, and budget for taking optional regions from the retained - // list. - double optional_time_remaining_ms = max_time_for_retaining(); - time_remaining_ms = MIN2(time_remaining_ms, optional_time_remaining_ms); - - log_debug(gc, ergo, cset)("Start adding retained candidates to collection set. " - "Min %u regions, available %u, " - "time remaining %1.2fms, optional remaining %1.2fms", - min_regions, retained_list->length(), time_remaining_ms, optional_time_remaining_ms); - - for (G1CollectionSetCandidateInfo* ci : *retained_list) { - G1HeapRegion* r = ci->_r; - double predicted_time_ms = predict_region_total_time_ms(r, collector_state()->in_young_only_phase()); - bool fits_in_remaining_time = predicted_time_ms <= time_remaining_ms; - // If we can't reclaim that region ignore it for now. - if (r->has_pinned_objects()) { - num_pinned_regions++; - if (ci->update_num_unreclaimed()) { - log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Skipping.", r->hrm_index()); - } else { - log_trace(gc, ergo, cset)("Retained candidate %u can not be reclaimed currently. Dropping.", r->hrm_index()); - pinned_old_regions->append(r); - } - continue; - } - - if (fits_in_remaining_time || (num_expensive_regions_selected < min_regions)) { - predicted_initial_time_ms += predicted_time_ms; - if (!fits_in_remaining_time) { - num_expensive_regions_selected++; - } - initial_old_regions->append(r); - num_initial_regions_selected++; - } else if (predicted_time_ms <= optional_time_remaining_ms) { - predicted_optional_time_ms += predicted_time_ms; - optional_old_regions->append(r); - num_optional_regions_selected++; - } else { - // Fits neither initial nor optional time limit. Exit. - break; - } - time_remaining_ms = MAX2(0.0, time_remaining_ms - predicted_time_ms); - optional_time_remaining_ms = MAX2(0.0, optional_time_remaining_ms - predicted_time_ms); - } - - uint num_regions_selected = num_initial_regions_selected + num_optional_regions_selected; - if (num_regions_selected == retained_list->length()) { - log_debug(gc, ergo, cset)("Retained candidates exhausted."); - } - if (num_expensive_regions_selected > 0) { - log_debug(gc, ergo, cset)("Added %u retained candidates to collection set although the predicted time was too high.", - num_expensive_regions_selected); - } - - log_debug(gc, ergo, cset)("Finish adding retained candidates to collection set. Initial: %u, optional: %u, pinned: %u, " - "predicted initial time: %1.2fms, predicted optional time: %1.2fms, " - "time remaining: %1.2fms optional time remaining %1.2fms", - num_initial_regions_selected, num_optional_regions_selected, num_pinned_regions, - predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms, optional_time_remaining_ms); -} - -void G1Policy::calculate_optional_collection_set_regions(G1CollectionCandidateRegionList* optional_regions, - double time_remaining_ms, - G1CollectionCandidateRegionList* selected_regions) { - assert(_collection_set->optional_region_length() > 0, - "Should only be called when there are optional regions"); - - double total_prediction_ms = 0.0; - - for (G1HeapRegion* r : *optional_regions) { - double prediction_ms = predict_region_total_time_ms(r, false); - - if (prediction_ms > time_remaining_ms) { - log_debug(gc, ergo, cset)("Prediction %.3fms for region %u does not fit remaining time: %.3fms.", - prediction_ms, r->hrm_index(), time_remaining_ms); - break; - } - // This region will be included in the next optional evacuation. - - total_prediction_ms += prediction_ms; - time_remaining_ms -= prediction_ms; - - selected_regions->append(r); - } - - log_debug(gc, ergo, cset)("Prepared %u regions out of %u for optional evacuation. Total predicted time: %.3fms", - selected_regions->length(), optional_regions->length(), total_prediction_ms); -} - void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) { start_adding_survivor_regions(); diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 98d44408467..9a6ffb570be 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -335,27 +335,7 @@ class G1Policy: public CHeapObj { // Amount of allowed waste in bytes in the collection set. size_t allowed_waste_in_collection_set() const; - // Calculate and fill in the initial, optional and pinned old gen candidate regions from - // the given candidate list and the remaining time. - // Returns the remaining time. - double select_candidates_from_marking(G1CollectionCandidateList* marking_list, - double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions); - - void select_candidates_from_retained(G1CollectionCandidateList* retained_list, - double time_remaining_ms, - G1CollectionCandidateRegionList* initial_old_regions, - G1CollectionCandidateRegionList* optional_old_regions, - G1CollectionCandidateRegionList* pinned_old_regions); - - // Calculate the number of optional regions from the given collection set candidates, - // the remaining time and the maximum number of these regions and return the number - // of actually selected regions in num_optional_regions. - void calculate_optional_collection_set_regions(G1CollectionCandidateRegionList* optional_old_regions, - double time_remaining_ms, - G1CollectionCandidateRegionList* selected); + private: @@ -423,12 +403,12 @@ class G1Policy: public CHeapObj { size_t desired_survivor_size(uint max_regions) const; +public: // Fraction used when predicting how many optional regions to include in // the CSet. This fraction of the available time is used for optional regions, // the rest is used to add old regions to the normal CSet. double optional_prediction_fraction() const { return 0.2; } -public: // Fraction used when evacuating the optional regions. This fraction of the // remaining time is used to choose what regions to include in the evacuation. double optional_evacuation_fraction() const { return 0.75; } diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp index f5f65cf1c48..bb5ac5036fe 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.cpp +++ b/src/hotspot/share/gc/g1/g1RemSet.cpp @@ -967,6 +967,10 @@ class G1MergeHeapRootsTask : public WorkerTask { _merged[G1GCPhaseTimes::MergeRSCards] += increment; } + void dec_remset_cards(size_t decrement) { + _merged[G1GCPhaseTimes::MergeRSCards] -= decrement; + } + size_t merged(uint i) const { return _merged[i]; } }; @@ -1091,6 +1095,11 @@ class G1MergeHeapRootsTask : public WorkerTask { G1MergeCardSetStats stats() { _merge_card_set_cache.flush(); + // Compensation for the dummy cards that were initially pushed into the + // card cache. + // We do not need to compensate for the other counters because the dummy + // card mark will never update another counter because it is initally "dirty". + _stats.dec_remset_cards(G1MergeCardSetCache::CacheSize); return _stats; } }; diff --git a/src/hotspot/share/gc/shared/oopStorage.cpp b/src/hotspot/share/gc/shared/oopStorage.cpp index 568888ac7d9..2373d6b1d93 100644 --- a/src/hotspot/share/gc/shared/oopStorage.cpp +++ b/src/hotspot/share/gc/shared/oopStorage.cpp @@ -300,7 +300,12 @@ void OopStorage::Block::set_active_index(size_t index) { size_t OopStorage::Block::active_index_safe(const Block* block) { STATIC_ASSERT(sizeof(intptr_t) == sizeof(block->_active_index)); - return SafeFetchN((intptr_t*)&block->_active_index, 0); + // Be careful, because block could be a false positive from block_for_ptr. + assert(block != nullptr, "precondition"); + uintptr_t block_addr = reinterpret_cast(block); + uintptr_t index_loc = block_addr + offset_of(Block, _active_index); + static_assert(sizeof(size_t) == sizeof(intptr_t), "assumption"); + return static_cast(SafeFetchN(reinterpret_cast(index_loc), 0)); } unsigned OopStorage::Block::get_index(const oop* ptr) const { @@ -366,21 +371,23 @@ void OopStorage::Block::delete_block(const Block& block) { OopStorage::Block* OopStorage::Block::block_for_ptr(const OopStorage* owner, const oop* ptr) { STATIC_ASSERT(_data_pos == 0); - // Const-ness of ptr is not related to const-ness of containing block. + assert(ptr != nullptr, "precondition"); // Blocks are allocated section-aligned, so get the containing section. - oop* section_start = align_down(const_cast(ptr), block_alignment); + uintptr_t section_start = align_down(reinterpret_cast(ptr), block_alignment); // Start with a guess that the containing section is the last section, // so the block starts section_count-1 sections earlier. - oop* section = section_start - (section_size * (section_count - 1)); + size_t section_size_in_bytes = sizeof(oop) * section_size; + uintptr_t section = section_start - (section_size_in_bytes * (section_count - 1)); // Walk up through the potential block start positions, looking for // the owner in the expected location. If we're below the actual block // start position, the value at the owner position will be some oop // (possibly null), which can never match the owner. intptr_t owner_addr = reinterpret_cast(owner); - for (unsigned i = 0; i < section_count; ++i, section += section_size) { - Block* candidate = reinterpret_cast(section); - if (SafeFetchN(&candidate->_owner_address, 0) == owner_addr) { - return candidate; + for (unsigned i = 0; i < section_count; ++i, section += section_size_in_bytes) { + uintptr_t owner_loc = section + offset_of(Block, _owner_address); + static_assert(sizeof(OopStorage*) == sizeof(intptr_t), "assumption"); + if (SafeFetchN(reinterpret_cast(owner_loc), 0) == owner_addr) { + return reinterpret_cast(section); } } return nullptr; @@ -643,8 +650,7 @@ class OopStorage::WithActiveArray : public StackObj { } }; -OopStorage::Block* OopStorage::find_block_or_null(const oop* ptr) const { - assert(ptr != nullptr, "precondition"); +OopStorage::Block* OopStorage::block_for_ptr(const oop* ptr) const { return Block::block_for_ptr(this, ptr); } @@ -771,7 +777,7 @@ static inline void check_release_entry(const oop* entry) { void OopStorage::release(const oop* ptr) { check_release_entry(ptr); - Block* block = find_block_or_null(ptr); + Block* block = block_for_ptr(ptr); assert(block != nullptr, "%s: invalid release " PTR_FORMAT, name(), p2i(ptr)); log_trace(oopstorage, ref)("%s: releasing " PTR_FORMAT, name(), p2i(ptr)); block->release_entries(block->bitmask_for_entry(ptr), this); @@ -782,7 +788,7 @@ void OopStorage::release(const oop* const* ptrs, size_t size) { size_t i = 0; while (i < size) { check_release_entry(ptrs[i]); - Block* block = find_block_or_null(ptrs[i]); + Block* block = block_for_ptr(ptrs[i]); assert(block != nullptr, "%s: invalid release " PTR_FORMAT, name(), p2i(ptrs[i])); size_t count = 0; uintx releasing = 0; @@ -989,7 +995,8 @@ bool OopStorage::delete_empty_blocks() { } OopStorage::EntryStatus OopStorage::allocation_status(const oop* ptr) const { - const Block* block = find_block_or_null(ptr); + if (ptr == nullptr) return INVALID_ENTRY; + const Block* block = block_for_ptr(ptr); if (block != nullptr) { // Prevent block deletion and _active_array modification. MutexLocker ml(_allocation_mutex, Mutex::_no_safepoint_check_flag); @@ -1137,7 +1144,7 @@ const char* OopStorage::name() const { return _name; } bool OopStorage::print_containing(const oop* addr, outputStream* st) { if (addr != nullptr) { - Block* block = find_block_or_null(addr); + Block* block = block_for_ptr(addr); if (block != nullptr && block->print_containing(addr, st)) { st->print(" in oop storage \"%s\"", name()); return true; diff --git a/src/hotspot/share/gc/shared/oopStorage.hpp b/src/hotspot/share/gc/shared/oopStorage.hpp index 96cc5a23d6a..34c980a0586 100644 --- a/src/hotspot/share/gc/shared/oopStorage.hpp +++ b/src/hotspot/share/gc/shared/oopStorage.hpp @@ -288,7 +288,7 @@ class OopStorage : public CHeapObjBase { Block* block_for_allocation(); void log_block_transition(Block* block, const char* new_state) const; - Block* find_block_or_null(const oop* ptr) const; + Block* block_for_ptr(const oop* ptr) const; void delete_empty_block(const Block& block); bool reduce_deferred_updates(); void record_needs_cleanup(); diff --git a/src/hotspot/share/gc/shared/oopStorage.inline.hpp b/src/hotspot/share/gc/shared/oopStorage.inline.hpp index 545da0be0a7..da0926a20b6 100644 --- a/src/hotspot/share/gc/shared/oopStorage.inline.hpp +++ b/src/hotspot/share/gc/shared/oopStorage.inline.hpp @@ -184,7 +184,10 @@ class OopStorage::Block /* No base class, to avoid messing up alignment. */ { void set_active_index(size_t index); static size_t active_index_safe(const Block* block); // Returns 0 if access fails. - // Returns null if ptr is not in a block or not allocated in that block. + // Return block of owner containing ptr, if ptr is a valid entry of owner. + // If ptr is not a valid entry of owner then returns either null or a "false + // positive" pointer; see allocation_status. + // precondition: ptr != nullptr static Block* block_for_ptr(const OopStorage* owner, const oop* ptr); oop* allocate(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp index 8a4ef63b8e3..df2d6d092e6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahControlThread.cpp @@ -177,10 +177,13 @@ void ShenandoahControlThread::run_service() { // it is a normal completion, or the abort. heap->free_set()->log_status_under_lock(); - // Notify Universe about new heap usage. This has implications for - // global soft refs policy, and we better report it every time heap - // usage goes down. - heap->update_capacity_and_used_at_gc(); + { + // Notify Universe about new heap usage. This has implications for + // global soft refs policy, and we better report it every time heap + // usage goes down. + ShenandoahHeapLocker locker(heap->lock()); + heap->update_capacity_and_used_at_gc(); + } // Signal that we have completed a visit to all live objects. heap->record_whole_heap_examined_timestamp(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 03e19a3af5e..310cd5b8061 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -910,7 +910,6 @@ void ShenandoahFreeSet::try_recycle_trashed(ShenandoahHeapRegion* r) { void ShenandoahFreeSet::recycle_trash() { // lock is not reentrable, check we don't have it shenandoah_assert_not_heaplocked(); - size_t count = 0; for (size_t i = 0; i < _heap->num_regions(); i++) { ShenandoahHeapRegion* r = _heap->get_region(i); @@ -919,16 +918,45 @@ void ShenandoahFreeSet::recycle_trash() { } } - // Relinquish the lock after this much time passed. - static constexpr jlong deadline_ns = 30000; // 30 us + size_t total_batches = 0; + jlong batch_start_time = 0; + jlong recycle_trash_start_time = os::javaTimeNanos(); // This value will be treated as the initial batch_start_time + jlong batch_end_time = recycle_trash_start_time; + // Process as many batches as can be processed within 10 us. + static constexpr jlong deadline_ns = 10000; // 10 us size_t idx = 0; + jlong predicted_next_batch_end_time; + jlong batch_process_time_estimate = 0; while (idx < count) { - os::naked_yield(); // Yield to allow allocators to take the lock - ShenandoahHeapLocker locker(_heap->lock()); - const jlong deadline = os::javaTimeNanos() + deadline_ns; - while (idx < count && os::javaTimeNanos() < deadline) { - try_recycle_trashed(_trash_regions[idx++]); + if (idx > 0) { + os::naked_yield(); // Yield to allow allocators to take the lock, except on the first iteration } + // Avoid another call to javaTimeNanos() if we already know time at which last batch ended + batch_start_time = batch_end_time; + const jlong deadline = batch_start_time + deadline_ns; + + ShenandoahHeapLocker locker(_heap->lock()); + do { + // Measurements on typical 2024 hardware suggest it typically requires between 1400 and 2000 ns to process a batch of + // 32 regions, assuming low contention with other threads. Sometimes this goes higher, when mutator threads + // are contending for CPU cores and/or the heap lock. On this hardware with a 10 us deadline, we expect 3-6 batches + // to be processed between yields most of the time. + // + // Note that deadline is enforced since the end of previous batch. In the case that yield() or acquisition of heap lock + // takes a "long time", we will have less time to process regions, but we will always process at least one batch between + // yields. Yielding more frequently when there is heavy contention for the heap lock or for CPU cores is considered the + // right thing to do. + const size_t REGIONS_PER_BATCH = 32; + size_t max_idx = MIN2(count, idx + REGIONS_PER_BATCH); + while (idx < max_idx) { + try_recycle_trashed(_trash_regions[idx++]); + } + total_batches++; + batch_end_time = os::javaTimeNanos(); + // Estimate includes historic combination of yield times and heap lock acquisition times. + batch_process_time_estimate = (batch_end_time - recycle_trash_start_time) / total_batches; + predicted_next_batch_end_time = batch_end_time + batch_process_time_estimate; + } while ((idx < count) && (predicted_next_batch_end_time < deadline)); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp index 6b95303fb33..55d21b06e4b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.hpp @@ -80,6 +80,8 @@ class ShenandoahSimpleBitMap { bool is_forward_consecutive_ones(idx_t start_idx, idx_t count) const; bool is_backward_consecutive_ones(idx_t last_idx, idx_t count) const; + static inline uintx tail_mask(uintx bit_number); + public: inline idx_t aligned_index(idx_t idx) const { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.inline.hpp index c047e1ed663..4582ab9a781 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahSimpleBitMap.inline.hpp @@ -27,7 +27,7 @@ #include "gc/shenandoah/shenandoahSimpleBitMap.hpp" -inline uintx tail_mask(uintx bit_number) { +inline uintx ShenandoahSimpleBitMap::tail_mask(uintx bit_number) { if (bit_number >= BitsPerWord) { return -1; } diff --git a/src/hotspot/share/gc/z/zHeap.cpp b/src/hotspot/share/gc/z/zHeap.cpp index 40aa76867f4..1e917bb5ee3 100644 --- a/src/hotspot/share/gc/z/zHeap.cpp +++ b/src/hotspot/share/gc/z/zHeap.cpp @@ -241,10 +241,10 @@ void ZHeap::undo_alloc_page(ZPage* page) { log_trace(gc)("Undo page allocation, thread: " PTR_FORMAT " (%s), page: " PTR_FORMAT ", size: " SIZE_FORMAT, p2i(Thread::current()), ZUtils::thread_name(), p2i(page), page->size()); - free_page(page); + free_page(page, false /* allow_defragment */); } -void ZHeap::free_page(ZPage* page) { +void ZHeap::free_page(ZPage* page, bool allow_defragment) { // Remove page table entry _page_table.remove(page); @@ -253,7 +253,7 @@ void ZHeap::free_page(ZPage* page) { } // Free page - _page_allocator.free_page(page); + _page_allocator.free_page(page, allow_defragment); } size_t ZHeap::free_empty_pages(const ZArray* pages) { diff --git a/src/hotspot/share/gc/z/zHeap.hpp b/src/hotspot/share/gc/z/zHeap.hpp index 18fa0d6349b..7b75c63cf8c 100644 --- a/src/hotspot/share/gc/z/zHeap.hpp +++ b/src/hotspot/share/gc/z/zHeap.hpp @@ -104,7 +104,7 @@ class ZHeap { // Page allocation ZPage* alloc_page(ZPageType type, size_t size, ZAllocationFlags flags, ZPageAge age); void undo_alloc_page(ZPage* page); - void free_page(ZPage* page); + void free_page(ZPage* page, bool allow_defragment); size_t free_empty_pages(const ZArray* pages); // Object allocation diff --git a/src/hotspot/share/gc/z/zPageAllocator.cpp b/src/hotspot/share/gc/z/zPageAllocator.cpp index 01200f76b51..010241294a7 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.cpp +++ b/src/hotspot/share/gc/z/zPageAllocator.cpp @@ -275,7 +275,7 @@ bool ZPageAllocator::prime_cache(ZWorkers* workers, size_t size) { workers->run_all(&task); } - free_page(page); + free_page(page, false /* allow_defragment */); return true; } @@ -462,6 +462,38 @@ void ZPageAllocator::destroy_page(ZPage* page) { safe_destroy_page(page); } +bool ZPageAllocator::should_defragment(const ZPage* page) const { + // A small page can end up at a high address (second half of the address space) + // if we've split a larger page or we have a constrained address space. To help + // fight address space fragmentation we remap such pages to a lower address, if + // a lower address is available. + return page->type() == ZPageType::small && + page->start() >= to_zoffset(_virtual.reserved() / 2) && + page->start() > _virtual.lowest_available_address(); +} + +ZPage* ZPageAllocator::defragment_page(ZPage* page) { + // Harvest the physical memory (which is committed) + ZPhysicalMemory pmem; + ZPhysicalMemory& old_pmem = page->physical_memory(); + pmem.add_segments(old_pmem); + old_pmem.remove_segments(); + + _unmapper->unmap_and_destroy_page(page); + + // Allocate new virtual memory at a low address + const ZVirtualMemory vmem = _virtual.alloc(pmem.size(), true /* force_low_address */); + + // Create the new page and map it + ZPage* new_page = new ZPage(ZPageType::small, vmem, pmem); + map_page(new_page); + + // Update statistics + ZStatInc(ZCounterDefragment); + + return new_page; +} + bool ZPageAllocator::is_alloc_allowed(size_t size) const { const size_t available = _current_max_capacity - _used - _claimed; return available >= size; @@ -623,16 +655,6 @@ ZPage* ZPageAllocator::alloc_page_create(ZPageAllocation* allocation) { return new ZPage(allocation->type(), vmem, pmem); } -bool ZPageAllocator::should_defragment(const ZPage* page) const { - // A small page can end up at a high address (second half of the address space) - // if we've split a larger page or we have a constrained address space. To help - // fight address space fragmentation we remap such pages to a lower address, if - // a lower address is available. - return page->type() == ZPageType::small && - page->start() >= to_zoffset(_virtual.reserved() / 2) && - page->start() > _virtual.lowest_available_address(); -} - bool ZPageAllocator::is_alloc_satisfied(ZPageAllocation* allocation) const { // The allocation is immediately satisfied if the list of pages contains // exactly one page, with the type and size that was requested. However, @@ -652,12 +674,6 @@ bool ZPageAllocator::is_alloc_satisfied(ZPageAllocation* allocation) const { return false; } - if (should_defragment(page)) { - // Defragment address space - ZStatInc(ZCounterDefragment); - return false; - } - // Allocation immediately satisfied return true; } @@ -773,6 +789,18 @@ void ZPageAllocator::satisfy_stalled() { } } +ZPage* ZPageAllocator::prepare_to_recycle(ZPage* page, bool allow_defragment) { + // Make sure we have a page that is safe to recycle + ZPage* const to_recycle = _safe_recycle.register_and_clone_if_activated(page); + + // Defragment the page before recycle if allowed and needed + if (allow_defragment && should_defragment(to_recycle)) { + return defragment_page(to_recycle); + } + + return to_recycle; +} + void ZPageAllocator::recycle_page(ZPage* page) { // Set time when last used page->set_last_used(); @@ -781,9 +809,11 @@ void ZPageAllocator::recycle_page(ZPage* page) { _cache.free_page(page); } -void ZPageAllocator::free_page(ZPage* page) { +void ZPageAllocator::free_page(ZPage* page, bool allow_defragment) { const ZGenerationId generation_id = page->generation_id(); - ZPage* const to_recycle = _safe_recycle.register_and_clone_if_activated(page); + + // Prepare page for recycling before taking the lock + ZPage* const to_recycle = prepare_to_recycle(page, allow_defragment); ZLocker locker(&_lock); @@ -800,11 +830,12 @@ void ZPageAllocator::free_page(ZPage* page) { } void ZPageAllocator::free_pages(const ZArray* pages) { - ZArray to_recycle; + ZArray to_recycle_pages; size_t young_size = 0; size_t old_size = 0; + // Prepare pages for recycling before taking the lock ZArrayIterator pages_iter(pages); for (ZPage* page; pages_iter.next(&page);) { if (page->is_young()) { @@ -812,7 +843,12 @@ void ZPageAllocator::free_pages(const ZArray* pages) { } else { old_size += page->size(); } - to_recycle.push(_safe_recycle.register_and_clone_if_activated(page)); + + // Prepare to recycle + ZPage* const to_recycle = prepare_to_recycle(page, true /* allow_defragment */); + + // Register for recycling + to_recycle_pages.push(to_recycle); } ZLocker locker(&_lock); @@ -823,7 +859,7 @@ void ZPageAllocator::free_pages(const ZArray* pages) { decrease_used_generation(ZGenerationId::old, old_size); // Free pages - ZArrayIterator iter(&to_recycle); + ZArrayIterator iter(&to_recycle_pages); for (ZPage* page; iter.next(&page);) { recycle_page(page); } @@ -833,11 +869,16 @@ void ZPageAllocator::free_pages(const ZArray* pages) { } void ZPageAllocator::free_pages_alloc_failed(ZPageAllocation* allocation) { - ZArray to_recycle; + ZArray to_recycle_pages; + // Prepare pages for recycling before taking the lock ZListRemoveIterator allocation_pages_iter(allocation->pages()); for (ZPage* page; allocation_pages_iter.next(&page);) { - to_recycle.push(_safe_recycle.register_and_clone_if_activated(page)); + // Prepare to recycle + ZPage* const to_recycle = prepare_to_recycle(page, false /* allow_defragment */); + + // Register for recycling + to_recycle_pages.push(to_recycle); } ZLocker locker(&_lock); @@ -849,7 +890,7 @@ void ZPageAllocator::free_pages_alloc_failed(ZPageAllocation* allocation) { size_t freed = 0; // Free any allocated/flushed pages - ZArrayIterator iter(&to_recycle); + ZArrayIterator iter(&to_recycle_pages); for (ZPage* page; iter.next(&page);) { freed += page->size(); recycle_page(page); diff --git a/src/hotspot/share/gc/z/zPageAllocator.hpp b/src/hotspot/share/gc/z/zPageAllocator.hpp index 5d3d59a4163..7df83a10eaf 100644 --- a/src/hotspot/share/gc/z/zPageAllocator.hpp +++ b/src/hotspot/share/gc/z/zPageAllocator.hpp @@ -104,13 +104,15 @@ class ZPageAllocator { void destroy_page(ZPage* page); + bool should_defragment(const ZPage* page) const; + ZPage* defragment_page(ZPage* page); + bool is_alloc_allowed(size_t size) const; bool alloc_page_common_inner(ZPageType type, size_t size, ZList* pages); bool alloc_page_common(ZPageAllocation* allocation); bool alloc_page_stall(ZPageAllocation* allocation); bool alloc_page_or_stall(ZPageAllocation* allocation); - bool should_defragment(const ZPage* page) const; bool is_alloc_satisfied(ZPageAllocation* allocation) const; ZPage* alloc_page_create(ZPageAllocation* allocation); ZPage* alloc_page_finalize(ZPageAllocation* allocation); @@ -149,9 +151,10 @@ class ZPageAllocator { void reset_statistics(ZGenerationId id); ZPage* alloc_page(ZPageType type, size_t size, ZAllocationFlags flags, ZPageAge age); + ZPage* prepare_to_recycle(ZPage* page, bool allow_defragment); void recycle_page(ZPage* page); void safe_destroy_page(ZPage* page); - void free_page(ZPage* page); + void free_page(ZPage* page, bool allow_defragment); void free_pages(const ZArray* pages); void enable_safe_destroy() const; diff --git a/src/hotspot/share/gc/z/zRelocate.cpp b/src/hotspot/share/gc/z/zRelocate.cpp index 33304bcefd3..7f69c0752bc 100644 --- a/src/hotspot/share/gc/z/zRelocate.cpp +++ b/src/hotspot/share/gc/z/zRelocate.cpp @@ -411,7 +411,7 @@ static void retire_target_page(ZGeneration* generation, ZPage* page) { // relocate the remaining objects, leaving the target page empty when // relocation completed. if (page->used() == 0) { - ZHeap::heap()->free_page(page); + ZHeap::heap()->free_page(page, true /* allow_defragment */); } } @@ -1012,7 +1012,7 @@ class ZRelocateWork : public StackObj { page->log_msg(" (relocate page done normal)"); // Free page - ZHeap::heap()->free_page(page); + ZHeap::heap()->free_page(page, true /* allow_defragment */); } } }; diff --git a/src/hotspot/share/interpreter/abstractInterpreter.cpp b/src/hotspot/share/interpreter/abstractInterpreter.cpp index 9d72f9ba0ed..616ba29c62b 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.cpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.cpp @@ -74,7 +74,9 @@ void AbstractInterpreter::print() { tty->print_cr("avg codelet size = %6d bytes", _code->used_space() / _code->number_of_stubs()); tty->cr(); } + _should_print_instructions = PrintInterpreter; _code->print(); + _should_print_instructions = false; tty->print_cr("----------------------------------------------------------------------"); tty->cr(); } @@ -91,6 +93,8 @@ address AbstractInterpreter::_slow_signature_handler; address AbstractInterpreter::_entry_table [AbstractInterpreter::number_of_method_entries]; address AbstractInterpreter::_native_abi_to_tosca [AbstractInterpreter::number_of_result_handlers]; +bool AbstractInterpreter::_should_print_instructions = false; + //------------------------------------------------------------------------------------------------------------------------ // Generation of complete interpreter diff --git a/src/hotspot/share/interpreter/abstractInterpreter.hpp b/src/hotspot/share/interpreter/abstractInterpreter.hpp index 790706c2de3..55fb58021a0 100644 --- a/src/hotspot/share/interpreter/abstractInterpreter.hpp +++ b/src/hotspot/share/interpreter/abstractInterpreter.hpp @@ -126,6 +126,8 @@ class AbstractInterpreter: AllStatic { static address _rethrow_exception_entry; // rethrows an activation in previous frame + static bool _should_print_instructions; // only with PrintInterpreter and when printing all InterpreterCodelet + friend class AbstractInterpreterGenerator; friend class InterpreterMacroAssembler; @@ -133,6 +135,7 @@ class AbstractInterpreter: AllStatic { // Initialization/debugging static void initialize(); static StubQueue* code() { return _code; } + static bool should_print_instructions() { return _should_print_instructions; } // Method activation diff --git a/src/hotspot/share/interpreter/bytecodeTracer.cpp b/src/hotspot/share/interpreter/bytecodeTracer.cpp index e5a3e9c16f4..cdb53b62f8c 100644 --- a/src/hotspot/share/interpreter/bytecodeTracer.cpp +++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp @@ -105,7 +105,7 @@ class BytecodePrinter { // the incoming method. We could lose a line of trace output. // This is acceptable in a debug-only feature. st->cr(); - st->print("[%ld] ", (long) Thread::current()->osthread()->thread_id()); + st->print("[" UINTX_FORMAT "] ", Thread::current()->osthread()->thread_id_for_printing()); method->print_name(st); st->cr(); _current_method = method(); @@ -128,7 +128,7 @@ class BytecodePrinter { code == Bytecodes::_return_register_finalizer || (code >= Bytecodes::_ireturn && code <= Bytecodes::_return)) { int bci = (int)(bcp - method->code_base()); - st->print("[%ld] ", (long) Thread::current()->osthread()->thread_id()); + st->print("[" UINTX_FORMAT "] ", Thread::current()->osthread()->thread_id_for_printing()); if (Verbose) { st->print("%8d %4d " INTPTR_FORMAT " " INTPTR_FORMAT " %s", BytecodeCounter::counter_value(), bci, tos, tos2, Bytecodes::name(code)); diff --git a/src/hotspot/share/interpreter/interpreter.cpp b/src/hotspot/share/interpreter/interpreter.cpp index 3c4ff4c1749..cba26f5aa6a 100644 --- a/src/hotspot/share/interpreter/interpreter.cpp +++ b/src/hotspot/share/interpreter/interpreter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. * 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,7 +66,7 @@ void InterpreterCodelet::verify() {} void InterpreterCodelet::print_on(outputStream* st) const { ttyLocker ttyl; - if (PrintInterpreter) { + if (AbstractInterpreter::should_print_instructions()) { st->cr(); st->print_cr("----------------------------------------------------------------------"); } @@ -76,7 +76,7 @@ void InterpreterCodelet::print_on(outputStream* st) const { st->print_cr("[" INTPTR_FORMAT ", " INTPTR_FORMAT "] %d bytes", p2i(code_begin()), p2i(code_end()), code_size()); - if (PrintInterpreter) { + if (AbstractInterpreter::should_print_instructions()) { st->cr(); Disassembler::decode(code_begin(), code_end(), st NOT_PRODUCT(COMMA &_asm_remarks)); } diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 13b20893219..5452cca96b8 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -38,6 +38,7 @@ #include "runtime/continuationEntry.hpp" #include "runtime/deoptimization.hpp" #include "runtime/flags/jvmFlag.hpp" +#include "runtime/objectMonitor.hpp" #include "runtime/osThread.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 5e226a90764..6b6d35ee026 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -4103,7 +4103,7 @@ void InstanceKlass::set_init_state(ClassState state) { assert(good_state || state == allocated, "illegal state transition"); #endif assert(_init_thread == nullptr, "should be cleared before state change"); - _init_state = state; + Atomic::release_store(&_init_state, state); } #if INCLUDE_JVMTI diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp index eaffa0250d1..45d65f273c8 100644 --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -507,14 +507,14 @@ class InstanceKlass: public Klass { public: // initialization state - bool is_loaded() const { return _init_state >= loaded; } - bool is_linked() const { return _init_state >= linked; } - bool is_initialized() const { return _init_state == fully_initialized; } - bool is_not_initialized() const { return _init_state < being_initialized; } - bool is_being_initialized() const { return _init_state == being_initialized; } - bool is_in_error_state() const { return _init_state == initialization_error; } + bool is_loaded() const { return init_state() >= loaded; } + bool is_linked() const { return init_state() >= linked; } + bool is_initialized() const { return init_state() == fully_initialized; } + bool is_not_initialized() const { return init_state() < being_initialized; } + bool is_being_initialized() const { return init_state() == being_initialized; } + bool is_in_error_state() const { return init_state() == initialization_error; } bool is_reentrant_initialization(Thread *thread) { return thread == _init_thread; } - ClassState init_state() const { return _init_state; } + ClassState init_state() const { return Atomic::load_acquire(&_init_state); } const char* init_state_name() const; bool is_rewritten() const { return _misc_flags.rewritten(); } diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 9a7d93dc469..802af20adae 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -395,9 +395,159 @@ Node* AddNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) { } } + // Convert a + a + ... + a into a*n + Node* serial_additions = convert_serial_additions(phase, bt); + if (serial_additions != nullptr) { + return serial_additions; + } + return AddNode::Ideal(phase, can_reshape); } +// Try to convert a serial of additions into a single multiplication. Also convert `(a * CON) + a` to `(CON + 1) * a` as +// a side effect. On success, a new MulNode is returned. +Node* AddNode::convert_serial_additions(PhaseGVN* phase, BasicType bt) { + // We need to make sure that the current AddNode is not part of a MulNode that has already been optimized to a + // power-of-2 addition (e.g., 3 * a => (a << 2) + a). Without this check, GVN would keep trying to optimize the same + // node and can't progress. For example, 3 * a => (a << 2) + a => 3 * a => (a << 2) + a => ... + if (find_power_of_two_addition_pattern(this, bt, nullptr) != nullptr) { + return nullptr; + } + + Node* in1 = in(1); + Node* in2 = in(2); + jlong multiplier; + + // While multiplications can be potentially optimized to power-of-2 subtractions (e.g., a * 7 => (a << 3) - a), + // (x - y) + y => x is already handled by the Identity() methods. So, we don't need to check for that pattern here. + if (find_simple_addition_pattern(in1, bt, &multiplier) == in2 + || find_simple_lshift_pattern(in1, bt, &multiplier) == in2 + || find_simple_multiplication_pattern(in1, bt, &multiplier) == in2 + || find_power_of_two_addition_pattern(in1, bt, &multiplier) == in2) { + multiplier++; // +1 for the in2 term + + Node* con = (bt == T_INT) + ? (Node*) phase->intcon((jint) multiplier) // intentional type narrowing to allow overflow at max_jint + : (Node*) phase->longcon(multiplier); + return MulNode::make(con, in2, bt); + } + + return nullptr; +} + +// Try to match `a + a`. On success, return `a` and set `2` as `multiplier`. +// The method matches `n` for pattern: AddNode(a, a). +Node* AddNode::find_simple_addition_pattern(Node* n, BasicType bt, jlong* multiplier) { + if (n->Opcode() == Op_Add(bt) && n->in(1) == n->in(2)) { + *multiplier = 2; + return n->in(1); + } + + return nullptr; +} + +// Try to match `a << CON`. On success, return `a` and set `1 << CON` as `multiplier`. +// Match `n` for pattern: LShiftNode(a, CON). +// Note that the power-of-2 multiplication optimization could potentially convert a MulNode to this pattern. +Node* AddNode::find_simple_lshift_pattern(Node* n, BasicType bt, jlong* multiplier) { + // Note that power-of-2 multiplication optimization could potentially convert a MulNode to this pattern + if (n->Opcode() == Op_LShift(bt) && n->in(2)->is_Con()) { + Node* con = n->in(2); + if (con->is_top()) { + return nullptr; + } + + *multiplier = ((jlong) 1 << con->get_int()); + return n->in(1); + } + + return nullptr; +} + +// Try to match `CON * a`. On success, return `a` and set `CON` as `multiplier`. +// Match `n` for patterns: +// - MulNode(CON, a) +// - MulNode(a, CON) +Node* AddNode::find_simple_multiplication_pattern(Node* n, BasicType bt, jlong* multiplier) { + // This optimization technically only produces MulNode(CON, a), but we might as match MulNode(a, CON), too. + if (n->Opcode() == Op_Mul(bt) && (n->in(1)->is_Con() || n->in(2)->is_Con())) { + Node* con = n->in(1); + Node* base = n->in(2); + + // swap ConNode to lhs for easier matching + if (!con->is_Con()) { + swap(con, base); + } + + if (con->is_top()) { + return nullptr; + } + + *multiplier = con->get_integer_as_long(bt); + return base; + } + + return nullptr; +} + +// Try to match `(a << CON1) + (a << CON2)`. On success, return `a` and set `(1 << CON1) + (1 << CON2)` as `multiplier`. +// Match `n` for patterns: +// - AddNode(LShiftNode(a, CON), LShiftNode(a, CON)/a) +// - AddNode(LShiftNode(a, CON)/a, LShiftNode(a, CON)) +// given that lhs is different from rhs. +// Note that one of the term of the addition could simply be `a` (i.e., a << 0). Calling this function with `multiplier` +// being null is safe. +Node* AddNode::find_power_of_two_addition_pattern(Node* n, BasicType bt, jlong* multiplier) { + if (n->Opcode() == Op_Add(bt) && n->in(1) != n->in(2)) { + Node* lhs = n->in(1); + Node* rhs = n->in(2); + + // swap LShiftNode to lhs for easier matching + if (lhs->Opcode() != Op_LShift(bt)) { + swap(lhs, rhs); + } + + // AddNode(LShiftNode(a, CON), *)? + if (lhs->Opcode() != Op_LShift(bt) || !lhs->in(2)->is_Con()) { + return nullptr; + } + + jlong lhs_multiplier = 0; + if (multiplier != nullptr) { + Node* con = lhs->in(2); + if (con->is_top()) { + return nullptr; + } + + lhs_multiplier = (jlong) 1 << con->get_int(); + } + + // AddNode(LShiftNode(a, CON), a)? + if (lhs->in(1) == rhs) { + if (multiplier != nullptr) { + *multiplier = lhs_multiplier + 1; + } + + return rhs; + } + + // AddNode(LShiftNode(a, CON), LShiftNode(a, CON2))? + if (rhs->Opcode() == Op_LShift(bt) && lhs->in(1) == rhs->in(1) && rhs->in(2)->is_Con()) { + if (multiplier != nullptr) { + Node* con = rhs->in(2); + if (con->is_top()) { + return nullptr; + } + + *multiplier = lhs_multiplier + ((jlong) 1 << con->get_int()); + } + + return lhs->in(1); + } + return nullptr; + } + return nullptr; +} Node* AddINode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* in1 = in(1); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 8879606954a..8afbb440572 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -42,6 +42,13 @@ typedef const Pair ConstAddOperands; // by virtual functions. class AddNode : public Node { virtual uint hash() const; + + Node* convert_serial_additions(PhaseGVN* phase, BasicType bt); + static Node* find_simple_addition_pattern(Node* n, BasicType bt, jlong* multiplier); + static Node* find_simple_lshift_pattern(Node* n, BasicType bt, jlong* multiplier); + static Node* find_simple_multiplication_pattern(Node* n, BasicType bt, jlong* multiplier); + static Node* find_power_of_two_addition_pattern(Node* n, BasicType bt, jlong* multiplier); + public: AddNode( Node *in1, Node *in2 ) : Node(nullptr,in1,in2) { init_class_id(Class_Add); diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 1af085cd128..b39db528691 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -398,7 +398,10 @@ PhaseCFG::PhaseCFG(Arena* arena, RootNode* root, Matcher& matcher) Node *x = new GotoNode(nullptr); x->init_req(0, x); _goto = matcher.match_tree(x); - assert(_goto != nullptr, ""); + assert(_goto != nullptr || C->failure_is_artificial(), ""); + if (C->failing()) { + return; + } _goto->set_req(0,_goto); // Build the CFG in Reverse Post Order diff --git a/src/hotspot/share/opto/c2_CodeStubs.hpp b/src/hotspot/share/opto/c2_CodeStubs.hpp index 318bc2f45fc..e778cfcde47 100644 --- a/src/hotspot/share/opto/c2_CodeStubs.hpp +++ b/src/hotspot/share/opto/c2_CodeStubs.hpp @@ -117,21 +117,6 @@ class C2FastUnlockLightweightStub : public C2CodeStub { Label& slow_path_continuation() { return continuation(); } }; -#ifdef _LP64 -class C2HandleAnonOMOwnerStub : public C2CodeStub { -private: - Register _monitor; - Register _tmp; -public: - C2HandleAnonOMOwnerStub(Register monitor, Register tmp = noreg) : C2CodeStub(), - _monitor(monitor), _tmp(tmp) {} - Register monitor() { return _monitor; } - Register tmp() { return _tmp; } - int max_size() const; - void emit(C2_MacroAssembler& masm); -}; -#endif - //-----------------------------C2GeneralStub----------------------------------- // A generalized stub that can be used to implement an arbitrary stub in a // type-safe manner. An example: diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 42de77acca9..c14162ddf6e 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -70,6 +70,14 @@ develop(bool, StressMethodHandleLinkerInlining, false, \ "Stress inlining through method handle linkers") \ \ + develop(bool, StressBailout, false, \ + "Perform bailouts randomly at C2 failing() checks") \ + \ + develop(uint, StressBailoutMean, 100000, \ + "The expected number of failing() checks made until " \ + "a random bailout.") \ + range(1, max_juint) \ + \ develop(intx, OptoPrologueNops, 0, \ "Insert this many extra nop instructions " \ "in the prologue of every nmethod") \ diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index d715e653343..e800b3c736b 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -755,7 +755,7 @@ Node *CallNode::match( const ProjNode *proj, const Matcher *match ) { if (Opcode() == Op_CallLeafVector) { // If the return is in vector, compute appropriate regmask taking into account the whole range - if(ideal_reg >= Op_VecS && ideal_reg <= Op_VecZ) { + if(ideal_reg >= Op_VecA && ideal_reg <= Op_VecZ) { if(OptoReg::is_valid(regs.second())) { for (OptoReg::Name r = regs.first(); r <= regs.second(); r = OptoReg::add(r, 1)) { rm.Insert(r); diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index 220b916436e..be0aadacbc2 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -479,6 +479,9 @@ void PhaseChaitin::Register_Allocate() { } uint new_max_lrg_id = Split(_lrg_map.max_lrg_id(), &split_arena); // Split spilling LRG everywhere + if (C->failing()) { + return; + } _lrg_map.set_max_lrg_id(new_max_lrg_id); // Bail out if unique gets too large (ie - unique > MaxNodeLimit - 2*NodeLimitFudgeFactor) // or we failed to split @@ -551,6 +554,9 @@ void PhaseChaitin::Register_Allocate() { return; } uint new_max_lrg_id = Split(_lrg_map.max_lrg_id(), &split_arena); // Split spilling LRG everywhere + if (C->failing()) { + return; + } _lrg_map.set_max_lrg_id(new_max_lrg_id); // Bail out if unique gets too large (ie - unique > MaxNodeLimit - 2*NodeLimitFudgeFactor) C->check_node_count(2 * NodeLimitFudgeFactor, "out of nodes after split"); diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 5abc398cb8c..fa0d39057cb 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -720,7 +720,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, } if (StressLCM || StressGCM || StressIGVN || StressCCP || - StressIncrementalInlining || StressMacroExpansion || StressUnstableIfTraps) { + StressIncrementalInlining || StressMacroExpansion || StressUnstableIfTraps || StressBailout) { initialize_stress_seed(directive); } @@ -798,7 +798,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, assert(failure_reason() != nullptr, "expect reason for parse failure"); stringStream ss; ss.print("method parse failed: %s", failure_reason()); - record_method_not_compilable(ss.as_string()); + record_method_not_compilable(ss.as_string() DEBUG_ONLY(COMMA true)); return; } GraphKit kit(jvms); @@ -973,7 +973,7 @@ Compile::Compile( ciEnv* ci_env, _types = new (comp_arena()) Type_Array(comp_arena()); _node_hash = new (comp_arena()) NodeHash(comp_arena(), 255); - if (StressLCM || StressGCM) { + if (StressLCM || StressGCM || StressBailout) { initialize_stress_seed(directive); } @@ -1018,6 +1018,7 @@ void Compile::Init(bool aliasing) { #ifdef ASSERT _phase_optimize_finished = false; + _phase_verify_ideal_loop = false; _exception_backedge = false; _type_verify = nullptr; #endif @@ -1108,7 +1109,7 @@ void Compile::Init(bool aliasing) { #ifdef ASSERT // Verify that the current StartNode is valid. void Compile::verify_start(StartNode* s) const { - assert(failing() || s == start(), "should be StartNode"); + assert(failing_internal() || s == start(), "should be StartNode"); } #endif @@ -1118,7 +1119,7 @@ void Compile::verify_start(StartNode* s) const { * the ideal graph. */ StartNode* Compile::start() const { - assert (!failing(), "Must not have pending failure. Reason is: %s", failure_reason()); + assert (!failing_internal() || C->failure_is_artificial(), "Must not have pending failure. Reason is: %s", failure_reason()); for (DUIterator_Fast imax, i = root()->fast_outs(imax); i < imax; i++) { Node* start = root()->fast_out(i); if (start->is_Start()) { @@ -1465,12 +1466,18 @@ const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const { } else { ciInstanceKlass *canonical_holder = ik->get_canonical_holder(offset); assert(offset < canonical_holder->layout_helper_size_in_bytes(), ""); - if (!ik->equals(canonical_holder) || tj->offset() != offset) { - if( is_known_inst ) { - tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, true, nullptr, offset, to->instance_id()); - } else { - tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, false, nullptr, offset); - } + assert(tj->offset() == offset, "no change to offset expected"); + bool xk = to->klass_is_exact(); + int instance_id = to->instance_id(); + + // If the input type's class is the holder: if exact, the type only includes interfaces implemented by the holder + // but if not exact, it may include extra interfaces: build new type from the holder class to make sure only + // its interfaces are included. + if (xk && ik->equals(canonical_holder)) { + assert(tj == TypeInstPtr::make(to->ptr(), canonical_holder, is_known_inst, nullptr, offset, instance_id), "exact type should be canonical type"); + } else { + assert(xk || !is_known_inst, "Known instance should be exact type"); + tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, is_known_inst, nullptr, offset, instance_id); } } } @@ -2114,7 +2121,7 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) { igvn_worklist()->ensure_empty(); // should be done with igvn while (inline_incrementally_one()) { - assert(!failing(), "inconsistent"); + assert(!failing_internal() || failure_is_artificial(), "inconsistent"); } if (failing()) return; @@ -2157,7 +2164,7 @@ void Compile::process_late_inline_calls_no_inline(PhaseIterGVN& igvn) { igvn_worklist()->ensure_empty(); // should be done with igvn while (inline_incrementally_one()) { - assert(!failing(), "inconsistent"); + assert(!failing_internal() || failure_is_artificial(), "inconsistent"); } if (failing()) return; @@ -2944,6 +2951,9 @@ void Compile::Code_Gen() { // Build a proper-looking CFG PhaseCFG cfg(node_arena(), root(), matcher); + if (failing()) { + return; + } _cfg = &cfg; { TracePhase tp("scheduler", &timers[_t_scheduler]); @@ -4329,7 +4339,7 @@ void Compile::verify_graph_edges(bool no_dead_code) { // to backtrack and retry without subsuming loads. Other than this backtracking // behavior, the Compile's failure reason is quietly copied up to the ciEnv // by the logic in C2Compiler. -void Compile::record_failure(const char* reason) { +void Compile::record_failure(const char* reason DEBUG_ONLY(COMMA bool allow_multiple_failures)) { if (log() != nullptr) { log()->elem("failure reason='%s' phase='compile'", reason); } @@ -4339,6 +4349,8 @@ void Compile::record_failure(const char* reason) { if (CaptureBailoutInformation) { _first_failure_details = new CompilationFailureInfo(reason); } + } else { + assert(!StressBailout || allow_multiple_failures, "should have handled previous failure."); } if (!C->failure_reason_is(C2Compiler::retry_no_subsuming_loads())) { @@ -4366,7 +4378,9 @@ Compile::TracePhase::TracePhase(const char* name, elapsedTimer* accumulator) } Compile::TracePhase::~TracePhase() { - if (_compile->failing()) return; + if (_compile->failing_internal()) { + return; // timing code, not stressing bailouts. + } #ifdef ASSERT if (PrintIdealNodeCount) { tty->print_cr("phase name='%s' nodes='%d' live='%d' live_graph_walk='%d'", @@ -5057,6 +5071,22 @@ bool Compile::randomized_select(int count) { return (random() & RANDOMIZED_DOMAIN_MASK) < (RANDOMIZED_DOMAIN / count); } +#ifdef ASSERT +// Failures are geometrically distributed with probability 1/StressBailoutMean. +bool Compile::fail_randomly() { + if ((random() % StressBailoutMean) != 0) { + return false; + } + record_failure("StressBailout"); + return true; +} + +bool Compile::failure_is_artificial() { + assert(failing_internal(), "should be failing"); + return C->failure_reason_is("StressBailout"); +} +#endif + CloneMap& Compile::clone_map() { return _clone_map; } void Compile::set_clone_map(Dict* d) { _clone_map._dict = d; } @@ -5144,7 +5174,7 @@ void Compile::sort_macro_nodes() { } void Compile::print_method(CompilerPhaseType cpt, int level, Node* n) { - if (failing()) { return; } + if (failing_internal()) { return; } // failing_internal to not stress bailouts from printing code. EventCompilerPhase event(UNTIMED); if (event.should_commit()) { CompilerEvent::PhaseEvent::post(event, C->_latest_stage_start_counter, cpt, C->_compile_id, level); diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 13ad980434b..1ccfedd0d46 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -391,6 +391,8 @@ class Compile : public Phase { DEBUG_ONLY(Unique_Node_List* _modified_nodes;) // List of nodes which inputs were modified DEBUG_ONLY(bool _phase_optimize_finished;) // Used for live node verification while creating new nodes + DEBUG_ONLY(bool _phase_verify_ideal_loop;) // Are we in PhaseIdealLoop verification? + // Arenas for new-space and old-space nodes. // Swapped between using _node_arena. // The lifetime of the old-space nodes is during xform. @@ -786,6 +788,12 @@ class Compile : public Phase { void set_post_loop_opts_phase() { _post_loop_opts_phase = true; } void reset_post_loop_opts_phase() { _post_loop_opts_phase = false; } +#ifdef ASSERT + bool phase_verify_ideal_loop() const { return _phase_verify_ideal_loop; } + void set_phase_verify_ideal_loop() { _phase_verify_ideal_loop = true; } + void reset_phase_verify_ideal_loop() { _phase_verify_ideal_loop = false; } +#endif + bool allow_macro_nodes() { return _allow_macro_nodes; } void reset_allow_macro_nodes() { _allow_macro_nodes = false; } @@ -815,7 +823,7 @@ class Compile : public Phase { ciEnv* env() const { return _env; } CompileLog* log() const { return _log; } - bool failing() const { + bool failing_internal() const { return _env->failing() || _failure_reason.get() != nullptr; } @@ -827,6 +835,27 @@ class Compile : public Phase { const CompilationFailureInfo* first_failure_details() const { return _first_failure_details; } + bool failing() { + if (failing_internal()) { + return true; + } +#ifdef ASSERT + // Disable stress code for PhaseIdealLoop verification (would have cascading effects). + if (phase_verify_ideal_loop()) { + return false; + } + if (StressBailout) { + return fail_randomly(); + } +#endif + return false; + } + +#ifdef ASSERT + bool fail_randomly(); + bool failure_is_artificial(); +#endif + bool failure_reason_is(const char* r) const { return (r == _failure_reason.get()) || (r != nullptr && @@ -834,11 +863,11 @@ class Compile : public Phase { strcmp(r, _failure_reason.get()) == 0); } - void record_failure(const char* reason); - void record_method_not_compilable(const char* reason) { + void record_failure(const char* reason DEBUG_ONLY(COMMA bool allow_multiple_failures = false)); + void record_method_not_compilable(const char* reason DEBUG_ONLY(COMMA bool allow_multiple_failures = false)) { env()->record_method_not_compilable(reason); // Record failure reason. - record_failure(reason); + record_failure(reason DEBUG_ONLY(COMMA allow_multiple_failures)); } bool check_node_count(uint margin, const char* reason) { if (oom()) { diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index 4646e1bb9c7..9ba571b926d 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -746,6 +746,21 @@ Block* PhaseCFG::insert_anti_dependences(Block* LCA, Node* load, bool verify) { // The anti-dependence constraints apply only to the fringe of this tree. Node* initial_mem = load->in(MemNode::Memory); + + // We don't optimize the memory graph for pinned loads, so we may need to raise the + // root of our search tree through the corresponding slices of MergeMem nodes to + // get to the node that really creates the memory state for this slice. + if (load_alias_idx >= Compile::AliasIdxRaw) { + while (initial_mem->is_MergeMem()) { + MergeMemNode* mm = initial_mem->as_MergeMem(); + Node* p = mm->memory_at(load_alias_idx); + if (p != mm->base_memory()) { + initial_mem = p; + } else { + break; + } + } + } worklist_def_use_mem_states.push(nullptr, initial_mem); while (worklist_def_use_mem_states.is_nonempty()) { // Examine a nearby store to see if it might interfere with our load. @@ -1512,8 +1527,8 @@ void PhaseCFG::schedule_late(VectorSet &visited, Node_Stack &stack) { C->record_failure(C2Compiler::retry_no_subsuming_loads()); } else { // Bailout without retry when (early->_dom_depth > LCA->_dom_depth) - assert(false, "graph should be schedulable"); - C->record_method_not_compilable("late schedule failed: incorrect graph"); + assert(C->failure_is_artificial(), "graph should be schedulable"); + C->record_method_not_compilable("late schedule failed: incorrect graph" DEBUG_ONLY(COMMA true)); } return; } @@ -1693,8 +1708,8 @@ void PhaseCFG::global_code_motion() { Block* block = get_block(i); if (!schedule_local(block, ready_cnt, visited, recalc_pressure_nodes)) { if (!C->failure_reason_is(C2Compiler::retry_no_subsuming_loads())) { - assert(false, "local schedule failed"); - C->record_method_not_compilable("local schedule failed"); + assert(C->failure_is_artificial(), "local schedule failed"); + C->record_method_not_compilable("local schedule failed" DEBUG_ONLY(COMMA true)); } _regalloc = nullptr; return; diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 3bc5b9a8b2a..27120c5ea1e 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -340,7 +340,9 @@ static inline void add_one_req(Node* dstphi, Node* src) { // having a control input of its exception map, rather than null. Such // regions do not appear except in this function, and in use_exception_state. void GraphKit::combine_exception_states(SafePointNode* ex_map, SafePointNode* phi_map) { - if (failing()) return; // dying anyway... + if (failing_internal()) { + return; // dying anyway... + } JVMState* ex_jvms = ex_map->_jvms; assert(ex_jvms->same_calls_as(phi_map->_jvms), "consistent call chains"); assert(ex_jvms->stkoff() == phi_map->_jvms->stkoff(), "matching locals"); @@ -446,7 +448,7 @@ void GraphKit::combine_exception_states(SafePointNode* ex_map, SafePointNode* ph //--------------------------use_exception_state-------------------------------- Node* GraphKit::use_exception_state(SafePointNode* phi_map) { - if (failing()) { stop(); return top(); } + if (failing_internal()) { stop(); return top(); } Node* region = phi_map->control(); Node* hidden_merge_mark = root(); assert(phi_map->jvms()->map() == phi_map, "sanity: 1-1 relation"); @@ -1556,6 +1558,7 @@ Node* GraphKit::make_load(Node* ctl, Node* adr, const Type* t, BasicType bt, bool mismatched, bool unsafe, uint8_t barrier_data) { + assert(adr_idx == C->get_alias_index(_gvn.type(adr)->isa_ptr()), "slice of address and input slice don't match"); assert(adr_idx != Compile::AliasIdxTop, "use other make_load factory" ); const TypePtr* adr_type = nullptr; // debug-mode-only argument debug_only(adr_type = C->get_adr_type(adr_idx)); @@ -1585,6 +1588,7 @@ Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt, bool unsafe, int barrier_data) { assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); + assert(adr_idx == C->get_alias_index(_gvn.type(adr)->isa_ptr()), "slice of address and input slice don't match"); const TypePtr* adr_type = nullptr; debug_only(adr_type = C->get_adr_type(adr_idx)); Node *mem = memory(adr_idx); @@ -2056,7 +2060,9 @@ Node* GraphKit::uncommon_trap(int trap_request, ciKlass* klass, const char* comment, bool must_throw, bool keep_exact_action) { - if (failing()) stop(); + if (failing_internal()) { + stop(); + } if (stopped()) return nullptr; // trap reachable? // Note: If ProfileTraps is true, and if a deopt. actually @@ -3008,7 +3014,7 @@ void GraphKit::guard_klass_being_initialized(Node* klass) { Node* adr = basic_plus_adr(top(), klass, init_state_off); Node* init_state = LoadNode::make(_gvn, nullptr, immutable_memory(), adr, adr->bottom_type()->is_ptr(), TypeInt::BYTE, - T_BYTE, MemNode::unordered); + T_BYTE, MemNode::acquire); init_state = _gvn.transform(init_state); Node* being_initialized_state = makecon(TypeInt::make(InstanceKlass::being_initialized)); diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index e7f17c72a1b..421ce933ed1 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -82,7 +82,7 @@ class GraphKit : public Phase { #ifdef ASSERT ~GraphKit() { - assert(failing() || !has_exceptions(), + assert(failing_internal() || !has_exceptions(), "unless compilation failed, user must call transfer_exceptions_into_jvms"); } #endif @@ -182,6 +182,7 @@ class GraphKit : public Phase { // Tell if the compilation is failing. bool failing() const { return C->failing(); } + bool failing_internal() const { return C->failing_internal(); } // Set _map to null, signalling a stop to further bytecode execution. // Preserve the map intact for future use, and return it back to the caller. diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 3c6de96074a..87be6a76eb2 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -1204,7 +1204,7 @@ bool PhaseCFG::schedule_local(Block* block, GrowableArray& ready_cnt, Vecto // to the Compile object, and the C2Compiler will see it and retry. C->record_failure(C2Compiler::retry_no_subsuming_loads()); } else { - assert(false, "graph should be schedulable"); + assert(C->failure_is_artificial(), "graph should be schedulable"); } // assert( phi_cnt == end_idx(), "did not schedule all" ); return false; diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 4d182e06a56..4ab4eea6f8f 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -2904,7 +2904,7 @@ bool LibraryCallKit::inline_unsafe_allocate() { Node* insp = basic_plus_adr(kls, in_bytes(InstanceKlass::init_state_offset())); // Use T_BOOLEAN for InstanceKlass::_init_state so the compiler // can generate code to load it as unsigned byte. - Node* inst = make_load(nullptr, insp, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered); + Node* inst = make_load(nullptr, insp, TypeInt::UBYTE, T_BOOLEAN, MemNode::acquire); Node* bits = intcon(InstanceKlass::fully_initialized); test = _gvn.transform(new SubINode(inst, bits)); // The 'test' is non-zero if we need to take a slow path. @@ -2959,11 +2959,10 @@ bool LibraryCallKit::inline_native_notify_jvmti_funcs(address funcAddr, const ch Node* thread = ideal.thread(); Node* jt_addr = basic_plus_adr(thread, in_bytes(JavaThread::is_in_VTMS_transition_offset())); Node* vt_addr = basic_plus_adr(vt_oop, java_lang_Thread::is_in_VTMS_transition_offset()); - const TypePtr *addr_type = _gvn.type(addr)->isa_ptr(); sync_kit(ideal); - access_store_at(nullptr, jt_addr, addr_type, hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED); - access_store_at(nullptr, vt_addr, addr_type, hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED); + access_store_at(nullptr, jt_addr, _gvn.type(jt_addr)->is_ptr(), hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED); + access_store_at(nullptr, vt_addr, _gvn.type(vt_addr)->is_ptr(), hide, _gvn.type(hide), T_BOOLEAN, IN_NATIVE | MO_UNORDERED); ideal.sync_kit(this); } ideal.end_if(); @@ -3325,7 +3324,9 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Load the raw epoch value from the threadObj. Node* threadObj_epoch_offset = basic_plus_adr(threadObj, java_lang_Thread::jfr_epoch_offset()); - Node* threadObj_epoch_raw = access_load_at(threadObj, threadObj_epoch_offset, TypeRawPtr::BOTTOM, TypeInt::CHAR, T_CHAR, + Node* threadObj_epoch_raw = access_load_at(threadObj, threadObj_epoch_offset, + _gvn.type(threadObj_epoch_offset)->isa_ptr(), + TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. @@ -3344,7 +3345,8 @@ bool LibraryCallKit::inline_native_getEventWriter() { // Load the raw epoch value from the vthread. Node* vthread_epoch_offset = basic_plus_adr(vthread, java_lang_Thread::jfr_epoch_offset()); - Node* vthread_epoch_raw = access_load_at(vthread, vthread_epoch_offset, TypeRawPtr::BOTTOM, TypeInt::CHAR, T_CHAR, + Node* vthread_epoch_raw = access_load_at(vthread, vthread_epoch_offset, _gvn.type(vthread_epoch_offset)->is_ptr(), + TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. @@ -3590,7 +3592,7 @@ void LibraryCallKit::extend_setCurrentThread(Node* jt, Node* thread) { // Load the raw epoch value from the vthread. Node* epoch_offset = basic_plus_adr(thread, java_lang_Thread::jfr_epoch_offset()); - Node* epoch_raw = access_load_at(thread, epoch_offset, TypeRawPtr::BOTTOM, TypeInt::CHAR, T_CHAR, + Node* epoch_raw = access_load_at(thread, epoch_offset, _gvn.type(epoch_offset)->is_ptr(), TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); // Mask off the excluded information from the epoch. diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 10375fc23f6..4a853045174 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -374,7 +374,7 @@ class LibraryCallKit : public GraphKit { bool inline_index_vector(); bool inline_index_partially_in_upper_range(); - Node* gen_call_to_svml(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2); + Node* gen_call_to_vector_math(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2); enum VectorMaskUseType { VecMaskUseLoad = 1 << 0, diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 59662ad53fe..0bed38e5fb0 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1464,7 +1464,8 @@ IfTrueNode* PhaseIdealLoop::create_initialized_assertion_predicate(IfNode* templ Node* new_stride, Node* control) { assert(assertion_predicate_has_loop_opaque_node(template_assertion_predicate), "must find OpaqueLoop* nodes for Template Assertion Predicate"); - InitializedAssertionPredicate initialized_assertion_predicate(template_assertion_predicate, new_init, new_stride, this); + InitializedAssertionPredicateCreator initialized_assertion_predicate(template_assertion_predicate, new_init, + new_stride, this); IfTrueNode* success_proj = initialized_assertion_predicate.create(control); assert(!assertion_predicate_has_loop_opaque_node(success_proj->in(0)->as_If()), "Initialized Assertion Predicates do not have OpaqueLoop* nodes in the bool expression anymore"); diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index b2a1a9d5e21..6cb50b3dee2 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -692,14 +692,24 @@ SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, Ideal // We can only use that safepoint if there's no side effect between the backedge and the safepoint. - // mm is used for book keeping + // mm is the memory state at the safepoint (when it's a MergeMem) + // no_side_effect_since_safepoint() goes over the memory state at the backedge. It resets the mm input for each + // component of the memory state it encounters so it points to the base memory. Once no_side_effect_since_safepoint() + // is done, if no side effect after the safepoint was found, mm should transform to the base memory: the states at + // the backedge and safepoint are the same so all components of the memory state at the safepoint should have been + // reset. MergeMemNode* mm = nullptr; #ifdef ASSERT if (mem->is_MergeMem()) { mm = mem->clone()->as_MergeMem(); _igvn._worklist.push(mm); for (MergeMemStream mms(mem->as_MergeMem()); mms.next_non_empty(); ) { - if (mms.alias_idx() != Compile::AliasIdxBot && loop != get_loop(ctrl_or_self(mms.memory()))) { + // Loop invariant memory state won't be reset by no_side_effect_since_safepoint(). Do it here. + // Escape Analysis can add state to mm that it doesn't add to the backedge memory Phis, breaking verification + // code that relies on mm. Clear that extra state here. + if (mms.alias_idx() != Compile::AliasIdxBot && + (loop != get_loop(ctrl_or_self(mms.memory())) || + (mms.adr_type()->isa_oop_ptr() && mms.adr_type()->is_known_instance()))) { mm->set_memory_at(mms.alias_idx(), mem->as_MergeMem()->base_memory()); } } @@ -4339,13 +4349,21 @@ void PhaseIdealLoop::mark_loop_associated_parse_predicates_useful() { } } +// This visitor marks all visited Parse Predicates useful. +class ParsePredicateUsefulMarker : public PredicateVisitor { + public: + using PredicateVisitor::visit; + + void visit(const ParsePredicate& parse_predicate) override { + parse_predicate.head()->mark_useful(); + } +}; + void PhaseIdealLoop::mark_useful_parse_predicates_for_loop(IdealLoopTree* loop) { Node* entry = loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl); - const Predicates predicates(entry); - ParsePredicateIterator iterator(predicates); - while (iterator.has_next()) { - iterator.next()->mark_useful(); - } + const PredicateIterator predicate_iterator(entry); + ParsePredicateUsefulMarker useful_marker; + predicate_iterator.for_each(useful_marker); } void PhaseIdealLoop::add_useless_parse_predicates_to_igvn_worklist() { @@ -4935,7 +4953,9 @@ void PhaseIdealLoop::verify() const { bool success = true; PhaseIdealLoop phase_verify(_igvn, this); - if (C->failing()) return; + if (C->failing_internal()) { + return; + } // Verify ctrl and idom of every node. success &= verify_idom_and_nodes(C->root(), &phase_verify); @@ -6287,6 +6307,43 @@ void PhaseIdealLoop::build_loop_late_post(Node *n) { build_loop_late_post_work(n, true); } +// Class to visit all predicates in a predicate chain to find out which are dominated by a given node. Keeps track of +// the entry to the earliest predicate that is still dominated by the given dominator. This class is used when trying to +// legally skip all predicates when figuring out the latest placement such that a node does not interfere with Loop +// Predication or creating a Loop Limit Check Predicate later. +class DominatedPredicates : public UnifiedPredicateVisitor { + Node* const _dominator; + Node* _earliest_dominated_predicate_entry; + bool _should_continue; + PhaseIdealLoop* const _phase; + + public: + DominatedPredicates(Node* dominator, Node* start_node, PhaseIdealLoop* phase) + : _dominator(dominator), + _earliest_dominated_predicate_entry(start_node), + _should_continue(true), + _phase(phase) {} + NONCOPYABLE(DominatedPredicates); + + bool should_continue() const override { + return _should_continue; + } + + // Returns the entry to the earliest predicate that is still dominated by the given dominator (all could be dominated). + Node* earliest_dominated_predicate_entry() const { + return _earliest_dominated_predicate_entry; + } + + void visit_predicate(const Predicate& predicate) override { + Node* entry = predicate.entry(); + if (_phase->is_strict_dominator(entry, _dominator)) { + _should_continue = false; + } else { + _earliest_dominated_predicate_entry = entry; + } + } +}; + void PhaseIdealLoop::build_loop_late_post_work(Node *n, bool pinned) { if (n->req() == 2 && (n->Opcode() == Op_ConvI2L || n->Opcode() == Op_CastII) && !C->major_progress() && !_verify_only) { @@ -6398,14 +6455,10 @@ void PhaseIdealLoop::build_loop_late_post_work(Node *n, bool pinned) { // Move the node above predicates as far up as possible so a // following pass of Loop Predication doesn't hoist a predicate // that depends on it above that node. - PredicateEntryIterator predicate_iterator(least); - while (predicate_iterator.has_next()) { - Node* next_predicate_entry = predicate_iterator.next_entry(); - if (is_strict_dominator(next_predicate_entry, early)) { - break; - } - least = next_predicate_entry; - } + const PredicateIterator predicate_iterator(least); + DominatedPredicates dominated_predicates(early, least, this); + predicate_iterator.for_each(dominated_predicates); + least = dominated_predicates.earliest_dominated_predicate_entry(); } // Try not to place code on a loop entry projection // which can inhibit range check elimination. diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 2d169a6459b..3aa67bcb5cb 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1128,7 +1128,9 @@ class PhaseIdealLoop : public PhaseTransform { _verify_only(verify_me == nullptr), _mode(LoopOptsVerify), _nodes_required(UINT_MAX) { + DEBUG_ONLY(C->set_phase_verify_ideal_loop();) build_and_optimize(); + DEBUG_ONLY(C->reset_phase_verify_ideal_loop();) } #endif diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 6d96bff1c1c..2031b09ca9d 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -194,6 +194,9 @@ void Matcher::match( ) { } // One-time initialization of some register masks. init_spill_mask( C->root()->in(1) ); + if (C->failing()) { + return; + } _return_addr_mask = return_addr(); #ifdef _LP64 // Pointers take 2 slots in 64-bit land @@ -287,10 +290,16 @@ void Matcher::match( ) { // preserve area, locks & pad2. OptoReg::Name reg1 = warp_incoming_stk_arg(vm_parm_regs[i].first()); + if (C->failing()) { + return; + } if( OptoReg::is_valid(reg1)) _calling_convention_mask[i].Insert(reg1); OptoReg::Name reg2 = warp_incoming_stk_arg(vm_parm_regs[i].second()); + if (C->failing()) { + return; + } if( OptoReg::is_valid(reg2)) _calling_convention_mask[i].Insert(reg2); @@ -386,7 +395,7 @@ void Matcher::match( ) { // Don't set control, it will confuse GCM since there are no uses. // The control will be set when this node is used first time // in find_base_for_derived(). - assert(_mach_null != nullptr, ""); + assert(_mach_null != nullptr || C->failure_is_artificial(), ""); // bailouts are handled below. C->set_root(xroot->is_Root() ? xroot->as_Root() : nullptr); @@ -404,7 +413,7 @@ void Matcher::match( ) { assert(C->failure_reason() != nullptr, "graph lost: reason unknown"); ss.print("graph lost: reason unknown"); } - C->record_method_not_compilable(ss.as_string()); + C->record_method_not_compilable(ss.as_string() DEBUG_ONLY(COMMA true)); } if (C->failing()) { // delete old; @@ -1439,10 +1448,16 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { } // Grab first register, adjust stack slots and insert in mask. OptoReg::Name reg1 = warp_outgoing_stk_arg(first, begin_out_arg_area, out_arg_limit_per_call ); + if (C->failing()) { + return nullptr; + } if (OptoReg::is_valid(reg1)) rm->Insert( reg1 ); // Grab second register (if any), adjust stack slots and insert in mask. OptoReg::Name reg2 = warp_outgoing_stk_arg(second, begin_out_arg_area, out_arg_limit_per_call ); + if (C->failing()) { + return nullptr; + } if (OptoReg::is_valid(reg2)) rm->Insert( reg2 ); } // End of for all arguments @@ -2679,6 +2694,10 @@ bool Matcher::gen_narrow_oop_implicit_null_checks() { // Compute RegMask for an ideal register. const RegMask* Matcher::regmask_for_ideal_register(uint ideal_reg, Node* ret) { + assert(!C->failing_internal() || C->failure_is_artificial(), "already failing."); + if (C->failing()) { + return nullptr; + } const Type* t = Type::mreg2type[ideal_reg]; if (t == nullptr) { assert(ideal_reg >= Op_VecA && ideal_reg <= Op_VecZ, "not a vector: %d", ideal_reg); @@ -2709,7 +2728,10 @@ const RegMask* Matcher::regmask_for_ideal_register(uint ideal_reg, Node* ret) { default: ShouldNotReachHere(); } MachNode* mspill = match_tree(spill); - assert(mspill != nullptr, "matching failed: %d", ideal_reg); + assert(mspill != nullptr || C->failure_is_artificial(), "matching failed: %d", ideal_reg); + if (C->failing()) { + return nullptr; + } // Handle generic vector operand case if (Matcher::supports_generic_vector_operands && t->isa_vect()) { specialize_mach_node(mspill); @@ -2855,7 +2877,7 @@ bool Matcher::is_encode_and_store_pattern(const Node* n, const Node* m) { #ifdef ASSERT bool Matcher::verify_after_postselect_cleanup() { - assert(!C->failing(), "sanity"); + assert(!C->failing_internal() || C->failure_is_artificial(), "sanity"); if (supports_generic_vector_operands) { Unique_Node_List useful; C->identify_useful_nodes(useful); diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 260f887347f..eda0f65d6bc 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -1715,7 +1715,7 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) { node_offsets[n->_idx] = masm->offset(); } #endif - assert(!C->failing(), "Should not reach here if failing."); + assert(!C->failing_internal() || C->failure_is_artificial(), "Should not reach here if failing."); // "Normal" instruction case DEBUG_ONLY(uint instr_offset = masm->offset()); @@ -3393,7 +3393,7 @@ uint PhaseOutput::scratch_emit_size(const Node* n) { n->emit(&masm, C->regalloc()); // Emitting into the scratch buffer should not fail - assert (!C->failing(), "Must not have pending failure. Reason is: %s", C->failure_reason()); + assert(!C->failing_internal() || C->failure_is_artificial(), "Must not have pending failure. Reason is: %s", C->failure_reason()); if (is_branch) // Restore label. n->as_MachBranch()->label_set(saveL, save_bnum); diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index 484c49367cc..039283bc863 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -426,7 +426,7 @@ class Parse : public GraphKit { void set_parse_bci(int bci); // Must this parse be aborted? - bool failing() { return C->failing(); } + bool failing() const { return C->failing_internal(); } // might have cascading effects, not stressing bailouts for now. Block* rpo_at(int rpo) { assert(0 <= rpo && rpo < _block_count, "oob"); diff --git a/src/hotspot/share/opto/predicates.cpp b/src/hotspot/share/opto/predicates.cpp index 3887e8a5f6c..18eea3a10bc 100644 --- a/src/hotspot/share/opto/predicates.cpp +++ b/src/hotspot/share/opto/predicates.cpp @@ -73,14 +73,6 @@ ParsePredicateNode* ParsePredicate::init_parse_predicate(Node* parse_predicate_p return nullptr; } -bool ParsePredicate::is_predicate(Node* maybe_success_proj) { - if (!maybe_success_proj->is_IfProj()) { - return false; - } - IfNode* if_node = maybe_success_proj->in(0)->as_If(); - return if_node->is_ParsePredicate(); -} - Deoptimization::DeoptReason RegularPredicateWithUCT::uncommon_trap_reason(IfProjNode* if_proj) { CallStaticJavaNode* uct_call = if_proj->is_uncommon_trap_if_pattern(); if (uct_call == nullptr) { @@ -90,27 +82,31 @@ Deoptimization::DeoptReason RegularPredicateWithUCT::uncommon_trap_reason(IfProj } bool RegularPredicateWithUCT::is_predicate(Node* maybe_success_proj) { - if (may_be_predicate_if(maybe_success_proj)) { - IfProjNode* success_proj = maybe_success_proj->as_IfProj(); - const Deoptimization::DeoptReason deopt_reason = uncommon_trap_reason(success_proj); - return (deopt_reason == Deoptimization::Reason_loop_limit_check || - deopt_reason == Deoptimization::Reason_predicate || - deopt_reason == Deoptimization::Reason_profile_predicate); + if (RegularPredicate::may_be_predicate_if(maybe_success_proj)) { + return has_valid_uncommon_trap(maybe_success_proj); } else { return false; } } -bool RegularPredicateWithUCT::is_predicate(Node* node, Deoptimization::DeoptReason deopt_reason) { - if (may_be_predicate_if(node)) { +bool RegularPredicateWithUCT::has_valid_uncommon_trap(const Node* success_proj) { + assert(RegularPredicate::may_be_predicate_if(success_proj), "must have been checked before"); + const Deoptimization::DeoptReason deopt_reason = uncommon_trap_reason(success_proj->as_IfProj()); + return (deopt_reason == Deoptimization::Reason_loop_limit_check || + deopt_reason == Deoptimization::Reason_predicate || + deopt_reason == Deoptimization::Reason_profile_predicate); +} + +bool RegularPredicateWithUCT::is_predicate(const Node* node, Deoptimization::DeoptReason deopt_reason) { + if (RegularPredicate::may_be_predicate_if(node)) { return deopt_reason == uncommon_trap_reason(node->as_IfProj()); } else { return false; } } -// A Runtime Predicate must have an If or a RangeCheck node, while the If should not be a zero trip guard check. -bool RegularPredicateWithUCT::may_be_predicate_if(Node* node) { +// A Regular Predicate must have an If or a RangeCheck node, while the If should not be a zero trip guard check. +bool RegularPredicate::may_be_predicate_if(const Node* node) { if (node->is_IfProj()) { const IfNode* if_node = node->in(0)->as_If(); const int opcode_if = if_node->Opcode(); @@ -122,39 +118,43 @@ bool RegularPredicateWithUCT::may_be_predicate_if(Node* node) { return false; } -bool RuntimePredicate::is_success_proj(Node* node, Deoptimization::DeoptReason deopt_reason) { +// Runtime Predicates always have an UCT since they could normally fail at runtime. In this case we execute the trap +// on the failing path. +bool RuntimePredicate::is_predicate(Node* node) { + return RegularPredicateWithUCT::is_predicate(node); +} + +bool RuntimePredicate::is_predicate(Node* node, Deoptimization::DeoptReason deopt_reason) { return RegularPredicateWithUCT::is_predicate(node, deopt_reason); } -ParsePredicateIterator::ParsePredicateIterator(const Predicates& predicates) : _current_index(0) { - const PredicateBlock* loop_limit_check_predicate_block = predicates.loop_limit_check_predicate_block(); - if (loop_limit_check_predicate_block->has_parse_predicate()) { - _parse_predicates.push(loop_limit_check_predicate_block->parse_predicate()); - } - if (UseProfiledLoopPredicate) { - const PredicateBlock* profiled_loop_predicate_block = predicates.profiled_loop_predicate_block(); - if (profiled_loop_predicate_block->has_parse_predicate()) { - _parse_predicates.push(profiled_loop_predicate_block->parse_predicate()); - } +// A Template Assertion Predicate has an If/RangeCheckNode and either an UCT or a halt node depending on where it +// was created. +bool TemplateAssertionPredicate::is_predicate(Node* node) { + if (!RegularPredicate::may_be_predicate_if(node)) { + return false; } - if (UseLoopPredicate) { - const PredicateBlock* loop_predicate_block = predicates.loop_predicate_block(); - if (loop_predicate_block->has_parse_predicate()) { - _parse_predicates.push(loop_predicate_block->parse_predicate()); - } + IfNode* if_node = node->in(0)->as_If(); + if (if_node->in(1)->is_Opaque4()) { + return RegularPredicateWithUCT::has_valid_uncommon_trap(node) || AssertionPredicateWithHalt::has_halt(node); } + return false; } -ParsePredicateNode* ParsePredicateIterator::next() { - assert(has_next(), "always check has_next() first"); - return _parse_predicates.at(_current_index++); +// Initialized Assertion Predicates always have the dedicated opaque node and a halt node. +bool InitializedAssertionPredicate::is_predicate(Node* node) { + if (!AssertionPredicateWithHalt::is_predicate(node)) { + return false; + } + IfNode* if_node = node->in(0)->as_If(); + return if_node->in(1)->is_OpaqueInitializedAssertionPredicate(); } #ifdef ASSERT // Check that the block has at most one Parse Predicate and that we only find Regular Predicate nodes (i.e. IfProj, // If, or RangeCheck nodes). -void PredicateBlock::verify_block() { - Node* next = _parse_predicate.entry(); // Skip unique Parse Predicate of this block if present +void RegularPredicateBlock::verify_block(Node* tail) { + Node* next = tail; while (next != _entry) { assert(!next->is_ParsePredicate(), "can only have one Parse Predicate in a block"); const int opcode = next->Opcode(); @@ -166,17 +166,6 @@ void PredicateBlock::verify_block() { } #endif // ASSERT -// Walk over all Regular Predicates of this block (if any) and return the first node not belonging to the block -// anymore (i.e. entry to the first Regular Predicate in this block if any or `regular_predicate_proj` otherwise). -Node* PredicateBlock::skip_regular_predicates(Node* regular_predicate_proj, Deoptimization::DeoptReason deopt_reason) { - Node* entry = regular_predicate_proj; - while (RuntimePredicate::is_success_proj(entry, deopt_reason)) { - assert(entry->in(0)->as_If(), "must be If node"); - entry = entry->in(0)->in(0); - } - return entry; -} - // This strategy clones the OpaqueLoopInit and OpaqueLoopStride nodes. class CloneStrategy : public TransformStrategyForOpaqueLoopNodes { PhaseIdealLoop* const _phase; @@ -381,8 +370,8 @@ bool TemplateAssertionExpressionNode::is_template_assertion_predicate(Node* node return node->is_If() && node->in(1)->is_Opaque4(); } -InitializedAssertionPredicate::InitializedAssertionPredicate(IfNode* template_assertion_predicate, Node* new_init, - Node* new_stride, PhaseIdealLoop* phase) +InitializedAssertionPredicateCreator::InitializedAssertionPredicateCreator(IfNode* template_assertion_predicate, Node* new_init, + Node* new_stride, PhaseIdealLoop* phase) : _template_assertion_predicate(template_assertion_predicate), _new_init(new_init), _new_stride(new_stride), @@ -408,7 +397,7 @@ InitializedAssertionPredicate::InitializedAssertionPredicate(IfNode* template_as // success fail path new success new Halt // proj (Halt or UCT) proj // -IfTrueNode* InitializedAssertionPredicate::create(Node* control) { +IfTrueNode* InitializedAssertionPredicateCreator::create(Node* control) { IdealLoopTree* loop = _phase->get_loop(control); OpaqueInitializedAssertionPredicateNode* assertion_expression = create_assertion_expression(control); IfNode* if_node = create_if_node(control, assertion_expression, loop); @@ -417,7 +406,7 @@ IfTrueNode* InitializedAssertionPredicate::create(Node* control) { } // Create a new Assertion Expression to be used as bool input for the Initialized Assertion Predicate IfNode. -OpaqueInitializedAssertionPredicateNode* InitializedAssertionPredicate::create_assertion_expression(Node* control) { +OpaqueInitializedAssertionPredicateNode* InitializedAssertionPredicateCreator::create_assertion_expression(Node* control) { Opaque4Node* template_opaque = _template_assertion_predicate->in(1)->as_Opaque4(); TemplateAssertionExpression template_assertion_expression(template_opaque); Opaque4Node* tmp_opaque = template_assertion_expression.clone_and_replace_init_and_stride(_new_init, _new_stride, @@ -428,9 +417,9 @@ OpaqueInitializedAssertionPredicateNode* InitializedAssertionPredicate::create_a return assertion_expression; } -IfNode* InitializedAssertionPredicate::create_if_node(Node* control, - OpaqueInitializedAssertionPredicateNode* assertion_expression, - IdealLoopTree* loop) { +IfNode* InitializedAssertionPredicateCreator::create_if_node(Node* control, + OpaqueInitializedAssertionPredicateNode* assertion_expression, + IdealLoopTree* loop) { const int if_opcode = _template_assertion_predicate->Opcode(); NOT_PRODUCT(const AssertionPredicateType assertion_predicate_type = _template_assertion_predicate->assertion_predicate_type();) IfNode* if_node = if_opcode == Op_If ? @@ -440,19 +429,19 @@ IfNode* InitializedAssertionPredicate::create_if_node(Node* control, return if_node; } -IfTrueNode* InitializedAssertionPredicate::create_success_path(IfNode* if_node, IdealLoopTree* loop) { +IfTrueNode* InitializedAssertionPredicateCreator::create_success_path(IfNode* if_node, IdealLoopTree* loop) { IfTrueNode* success_proj = new IfTrueNode(if_node); _phase->register_control(success_proj, loop, if_node); return success_proj; } -void InitializedAssertionPredicate::create_fail_path(IfNode* if_node, IdealLoopTree* loop) { +void InitializedAssertionPredicateCreator::create_fail_path(IfNode* if_node, IdealLoopTree* loop) { IfFalseNode* fail_proj = new IfFalseNode(if_node); _phase->register_control(fail_proj, loop, if_node); create_halt_node(fail_proj, loop); } -void InitializedAssertionPredicate::create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop) { +void InitializedAssertionPredicateCreator::create_halt_node(IfFalseNode* fail_proj, IdealLoopTree* loop) { StartNode* start_node = _phase->C->start(); Node* frame = new ParmNode(start_node, TypeFunc::FramePtr); _phase->register_new_node(frame, start_node); @@ -461,17 +450,45 @@ void InitializedAssertionPredicate::create_halt_node(IfFalseNode* fail_proj, Ide _phase->register_control(halt, loop, fail_proj); } -// Is current node pointed to by iterator a predicate? -bool PredicateEntryIterator::has_next() const { - return ParsePredicate::is_predicate(_current) || - RegularPredicateWithUCT::is_predicate(_current) || - AssertionPredicateWithHalt::is_predicate(_current); +#ifndef PRODUCT +void PredicateBlock::dump() const { + dump(""); +} + +void PredicateBlock::dump(const char* prefix) const { + if (is_non_empty()) { + PredicatePrinter printer(prefix); + PredicateBlockIterator iterator(_tail, _deopt_reason); + iterator.for_each(printer); + } else { + tty->print_cr("%s- ", prefix); + } +} + +// Dumps all predicates from the loop to the earliest predicate in a pretty format. +void Predicates::dump() const { + if (has_any()) { + Node* loop_head = _tail->unique_ctrl_out(); + tty->print_cr("%d %s:", loop_head->_idx, loop_head->Name()); + tty->print_cr("- Loop Limit Check Predicate Block:"); + _loop_limit_check_predicate_block.dump(" "); + tty->print_cr("- Profiled Loop Predicate Block:"); + _profiled_loop_predicate_block.dump(" "); + tty->print_cr("- Loop Predicate Block:"); + _loop_predicate_block.dump(" "); + tty->cr(); + } else { + tty->print_cr(""); + } +} + +void Predicates::dump_at(Node* node) { + Predicates predicates(node); + predicates.dump(); } -// Skip the current predicate pointed to by iterator by returning the input into the predicate. This could possibly be -// a non-predicate node. -Node* PredicateEntryIterator::next_entry() { - assert(has_next(), "current must be predicate"); - _current = _current->in(0)->in(0); - return _current; +// Debug method to dump all predicates that are found above 'loop_node'. +void Predicates::dump_for_loop(LoopNode* loop_node) { + dump_at(loop_node->skip_strip_mined()->in(LoopNode::EntryControl)); } +#endif // NOT PRODUCT diff --git a/src/hotspot/share/opto/predicates.hpp b/src/hotspot/share/opto/predicates.hpp index 96f5c438b80..b38b888cc3d 100644 --- a/src/hotspot/share/opto/predicates.hpp +++ b/src/hotspot/share/opto/predicates.hpp @@ -30,6 +30,11 @@ #include "opto/opaquenode.hpp" class IdealLoopTree; +class InitializedAssertionPredicate; +class ParsePredicate; +class PredicateVisitor; +class RuntimePredicate; +class TemplateAssertionPredicate; /* * There are different kinds of predicates throughout the code. We differentiate between the following predicates: @@ -152,7 +157,8 @@ class IdealLoopTree; * together. * - Loop Limit Check Groups the Loop Limit Check Predicate (if created) and the Loop Limit * Predicate Block: Check Parse Predicate (if not removed, yet) together. - * + * - Regular Predicate Block: A block that only contains the Regular Predicates of a Predicate Block without the + * Parse Predicate. * * Initially, before applying any loop-splitting optimizations, we find the following structure after Loop Predication * (predicates inside square brackets [] do not need to exist if there are no checks to hoist): @@ -205,6 +211,41 @@ enum class AssertionPredicateType { }; #endif // NOT PRODUCT +// Interface to represent a C2 predicate. A predicate is always represented by two CFG nodes: +// - An If node (head) +// - An IfProj node representing the success projection of the If node (tail). +class Predicate : public StackObj { + public: + // Return the unique entry CFG node into the predicate. + virtual Node* entry() const = 0; + + // Return the head node of the predicate which is either: + // - A ParsePredicateNode if the predicate is a Parse Predicate + // - An IfNode or RangeCheckNode, otherwise. + virtual IfNode* head() const = 0; + + // Return the tail node of the predicate. Runtime Predicates can either have a true of false projection as success + // projection while Parse Predicates and Assertion Predicates always have a true projection as success projection. + virtual IfProjNode* tail() const = 0; +}; + +// Generic predicate visitor that does nothing. Subclass this visitor to add customized actions for each predicate. +// The visit methods of this visitor are called from the predicate iterator classes which walk the predicate chain. +// Use the UnifiedPredicateVisitor if the type of the predicate does not matter. +class PredicateVisitor : StackObj { + public: + virtual void visit(const ParsePredicate& parse_predicate) {} + virtual void visit(const RuntimePredicate& runtime_predicate) {} + virtual void visit(const TemplateAssertionPredicate& template_assertion_predicate) {} + virtual void visit(const InitializedAssertionPredicate& initialized_assertion_predicate) {} + + // This method can be overridden to stop the predicate iterators from visiting more predicates further up in the + // predicate chain. + virtual bool should_continue() const { + return true; + } +}; + // Class to represent Assertion Predicates with a HaltNode instead of an UCT (i.e. either an Initialized Assertion // Predicate or a Template Assertion Predicate created after the initial one at Loop Predication). class AssertionPredicatesWithHalt : public StackObj { @@ -228,9 +269,15 @@ class AssertionPredicatesWithHalt : public StackObj { // Note that all other Regular Predicates have an UCT node. class AssertionPredicateWithHalt : public StackObj { static bool has_assertion_predicate_opaque(const Node* predicate_proj); - static bool has_halt(const Node* success_proj); public: static bool is_predicate(const Node* maybe_success_proj); + static bool has_halt(const Node* success_proj); +}; + +// Utility class representing a Regular Predicate which is either a Runtime Predicate or an Assertion Predicate. +class RegularPredicate : public StackObj { + public: + static bool may_be_predicate_if(const Node* node); }; // Class to represent a single Regular Predicate with an UCT. This could either be: @@ -239,15 +286,15 @@ class AssertionPredicateWithHalt : public StackObj { // Note that all other Regular Predicates have a Halt node. class RegularPredicateWithUCT : public StackObj { static Deoptimization::DeoptReason uncommon_trap_reason(IfProjNode* if_proj); - static bool may_be_predicate_if(Node* node); public: static bool is_predicate(Node* maybe_success_proj); - static bool is_predicate(Node* node, Deoptimization::DeoptReason deopt_reason); + static bool is_predicate(const Node* node, Deoptimization::DeoptReason deopt_reason); + static bool has_valid_uncommon_trap(const Node* success_proj); }; // Class to represent a Parse Predicate. -class ParsePredicate : public StackObj { +class ParsePredicate : public Predicate { ParsePredicateSuccessProj* _success_proj; ParsePredicateNode* _parse_predicate_node; Node* _entry; @@ -267,7 +314,7 @@ class ParsePredicate : public StackObj { // Returns the control input node into this Parse Predicate if it is valid. Otherwise, it returns the passed node // into the constructor of this class. - Node* entry() const { + Node* entry() const override { return _entry; } @@ -277,23 +324,102 @@ class ParsePredicate : public StackObj { return _parse_predicate_node != nullptr; } - ParsePredicateNode* node() const { + ParsePredicateNode* head() const override { assert(is_valid(), "must be valid"); return _parse_predicate_node; } - ParsePredicateSuccessProj* success_proj() const { + ParsePredicateSuccessProj* tail() const override { assert(is_valid(), "must be valid"); return _success_proj; } +}; + +// Class to represent a Runtime Predicate which always has an associated UCT on the failing path. +class RuntimePredicate : public Predicate { + IfProjNode* _success_proj; + IfNode* _if_node; + + public: + explicit RuntimePredicate(IfProjNode* success_proj) + : _success_proj(success_proj), + _if_node(success_proj->in(0)->as_If()) { + assert(is_predicate(success_proj), "must be valid"); + } + NONCOPYABLE(RuntimePredicate); + private: static bool is_predicate(Node* maybe_success_proj); + + public: + Node* entry() const override { + return _if_node->in(0); + } + + IfNode* head() const override { + return _if_node; + } + + IfProjNode* tail() const override { + return _success_proj; + } + + static bool is_predicate(Node* node, Deoptimization::DeoptReason deopt_reason); }; -// Utility class for queries on Runtime Predicates. -class RuntimePredicate : public StackObj { +// Class to represent a Template Assertion Predicate. +class TemplateAssertionPredicate : public Predicate { + IfTrueNode* _success_proj; + IfNode* _if_node; + public: - static bool is_success_proj(Node* node, Deoptimization::DeoptReason deopt_reason); + explicit TemplateAssertionPredicate(IfTrueNode* success_proj) + : _success_proj(success_proj), + _if_node(success_proj->in(0)->as_If()) { + assert(is_predicate(success_proj), "must be valid"); + } + + Node* entry() const override { + return _if_node->in(0); + } + + IfNode* head() const override { + return _if_node; + } + + IfTrueNode* tail() const override { + return _success_proj; + } + + static bool is_predicate(Node* node); +}; + +// Class to represent an Initialized Assertion Predicate which always has a halt node on the failing path. +// This predicate should never fail at runtime by design. +class InitializedAssertionPredicate : public Predicate { + IfTrueNode* _success_proj; + IfNode* _if_node; + + public: + explicit InitializedAssertionPredicate(IfTrueNode* success_proj) + : _success_proj(success_proj), + _if_node(success_proj->in(0)->as_If()) { + assert(is_predicate(success_proj), "must be valid"); + } + + Node* entry() const override { + return _if_node->in(0); + } + + IfNode* head() const override { + return _if_node; + } + + IfTrueNode* tail() const override { + return _success_proj; + } + + static bool is_predicate(Node* node); }; // Interface to transform OpaqueLoopInit and OpaqueLoopStride nodes of a Template Assertion Expression. @@ -395,16 +521,16 @@ class TemplateAssertionExpressionNode : public StackObj { }; // This class creates a new Initialized Assertion Predicate. -class InitializedAssertionPredicate : public StackObj { +class InitializedAssertionPredicateCreator : public StackObj { IfNode* const _template_assertion_predicate; Node* const _new_init; Node* const _new_stride; PhaseIdealLoop* const _phase; public: - InitializedAssertionPredicate(IfNode* template_assertion_predicate, Node* new_init, Node* new_stride, - PhaseIdealLoop* phase); - NONCOPYABLE(InitializedAssertionPredicate); + InitializedAssertionPredicateCreator(IfNode* template_assertion_predicate, Node* new_init, Node* new_stride, + PhaseIdealLoop* phase); + NONCOPYABLE(InitializedAssertionPredicateCreator); IfTrueNode* create(Node* control); @@ -416,23 +542,208 @@ class InitializedAssertionPredicate : public StackObj { IfTrueNode* create_success_path(IfNode* if_node, IdealLoopTree* loop); }; +// This class iterates through all predicates of a Regular Predicate Block and applies the given visitor to each. +class RegularPredicateBlockIterator : public StackObj { + Node* const _start_node; + const Deoptimization::DeoptReason _deopt_reason; + + public: + RegularPredicateBlockIterator(Node* start_node, Deoptimization::DeoptReason deopt_reason) + : _start_node(start_node), + _deopt_reason(deopt_reason) {} + NONCOPYABLE(RegularPredicateBlockIterator); + + // Skip all predicates by just following the inputs. We do not call any user provided visitor. + Node* skip_all() const { + PredicateVisitor do_nothing; // No real visits, just do nothing. + return for_each(do_nothing); + } + + // Walk over all predicates of this block (if any) and apply the given 'predicate_visitor' to each predicate. + // Returns the entry to the earliest predicate. + Node* for_each(PredicateVisitor& predicate_visitor) const { + Node* current = _start_node; + while (predicate_visitor.should_continue()) { + if (TemplateAssertionPredicate::is_predicate(current)) { + TemplateAssertionPredicate template_assertion_predicate(current->as_IfTrue()); + predicate_visitor.visit(template_assertion_predicate); + current = template_assertion_predicate.entry(); + } else if (RuntimePredicate::is_predicate(current, _deopt_reason)) { + RuntimePredicate runtime_predicate(current->as_IfProj()); + predicate_visitor.visit(runtime_predicate); + current = runtime_predicate.entry(); + } else if (InitializedAssertionPredicate::is_predicate(current)) { + InitializedAssertionPredicate initialized_assertion_predicate(current->as_IfTrue()); + predicate_visitor.visit(initialized_assertion_predicate); + current = initialized_assertion_predicate.entry(); + } else { + // Either a Parse Predicate or not a Regular Predicate. In both cases, the node does not belong to this block. + break; + } + } + return current; + } +}; + +// This class iterates through all predicates of a Predicate Block and applies the given visitor to each. +class PredicateBlockIterator : public StackObj { + Node* const _start_node; + const ParsePredicate _parse_predicate; // Could be missing. + const RegularPredicateBlockIterator _regular_predicate_block_iterator; + + public: + PredicateBlockIterator(Node* start_node, Deoptimization::DeoptReason deopt_reason) + : _start_node(start_node), + _parse_predicate(start_node, deopt_reason), + _regular_predicate_block_iterator(_parse_predicate.entry(), deopt_reason) {} + + // Walk over all predicates of this block (if any) and apply the given 'predicate_visitor' to each predicate. + // Returns the entry to the earliest predicate. + Node* for_each(PredicateVisitor& predicate_visitor) const { + if (!predicate_visitor.should_continue()) { + return _start_node; + } + if (_parse_predicate.is_valid()) { + predicate_visitor.visit(_parse_predicate); + } + return _regular_predicate_block_iterator.for_each(predicate_visitor); + } +}; + +// Class to walk over all predicates starting at a node, which usually is the loop entry node, and following the inputs. +// At each predicate, a PredicateVisitor is applied which the user can implement freely. +class PredicateIterator : public StackObj { + Node* _start_node; + + public: + explicit PredicateIterator(Node* start_node) + : _start_node(start_node) {} + NONCOPYABLE(PredicateIterator); + + // Apply the 'predicate_visitor' for each predicate found in the predicate chain started at the provided node. + // Returns the entry to the earliest predicate. + Node* for_each(PredicateVisitor& predicate_visitor) const { + Node* current = _start_node; + PredicateBlockIterator loop_limit_check_predicate_iterator(current, Deoptimization::Reason_loop_limit_check); + current = loop_limit_check_predicate_iterator.for_each(predicate_visitor); + PredicateBlockIterator profiled_loop_predicate_iterator(current, Deoptimization::Reason_profile_predicate); + current = profiled_loop_predicate_iterator.for_each(predicate_visitor); + PredicateBlockIterator loop_predicate_iterator(current, Deoptimization::Reason_predicate); + return loop_predicate_iterator.for_each(predicate_visitor); + } +}; + +// Unified PredicateVisitor which only provides a single visit method for a generic Predicate. This visitor can be used +// when it does not matter what kind of predicate is visited. Note that we override all normal visit methods from +// PredicateVisitor by calling the unified method. These visit methods are marked final such that they cannot be +// overridden by implementors of this class. +class UnifiedPredicateVisitor : public PredicateVisitor { + public: + virtual void visit(const TemplateAssertionPredicate& template_assertion_predicate) override final { + visit_predicate(template_assertion_predicate); + } + + virtual void visit(const ParsePredicate& parse_predicate) override final { + visit_predicate(parse_predicate); + } + + virtual void visit(const RuntimePredicate& runtime_predicate) override final { + visit_predicate(runtime_predicate); + } + + virtual void visit(const InitializedAssertionPredicate& initialized_assertion_predicate) override final { + visit_predicate(initialized_assertion_predicate); + } + + virtual void visit_predicate(const Predicate& predicate) = 0; +}; + +// A block of Regular Predicates inside a Predicate Block without its Parse Predicate. +class RegularPredicateBlock : public StackObj { + const Deoptimization::DeoptReason _deopt_reason; + Node* const _entry; + + public: + RegularPredicateBlock(Node* tail, Deoptimization::DeoptReason deopt_reason) + : _deopt_reason(deopt_reason), + _entry(skip_all(tail)) { + DEBUG_ONLY(verify_block(tail);) + } + NONCOPYABLE(RegularPredicateBlock); + + private: + // Walk over all Regular Predicates of this block (if any) and return the first node not belonging to the block + // anymore (i.e. entry to the first Regular Predicate in this block if any or `tail` otherwise). + Node* skip_all(Node* tail) const { + RegularPredicateBlockIterator iterator(tail, _deopt_reason); + return iterator.skip_all(); + } + + DEBUG_ONLY(void verify_block(Node* tail);) + + public: + Node* entry() const { + return _entry; + } +}; + +#ifndef PRODUCT +// Visitor class to print all the visited predicates. Used by the Predicates class which does the printing starting +// at the loop node and then following the inputs to the earliest predicate. +class PredicatePrinter : public PredicateVisitor { + const char* _prefix; // Prefix added to each dumped string. + + public: + explicit PredicatePrinter(const char* prefix) : _prefix(prefix) {} + NONCOPYABLE(PredicatePrinter); + + void visit(const ParsePredicate& parse_predicate) override { + print_predicate_node("Parse Predicate", parse_predicate); + } + + void visit(const RuntimePredicate& runtime_predicate) override { + print_predicate_node("Runtime Predicate", runtime_predicate); + } + + void visit(const TemplateAssertionPredicate& template_assertion_predicate) override { + print_predicate_node("Template Assertion Predicate", template_assertion_predicate); + } + + void visit(const InitializedAssertionPredicate& initialized_assertion_predicate) override { + print_predicate_node("Initialized Assertion Predicate", initialized_assertion_predicate); + } + + private: + void print_predicate_node(const char* predicate_name, const Predicate& predicate) const { + tty->print_cr("%s- %s: %d %s", _prefix, predicate_name, predicate.head()->_idx, predicate.head()->Name()); + } +}; +#endif // NOT PRODUCT // This class represents a Predicate Block (i.e. either a Loop Predicate Block, a Profiled Loop Predicate Block, // or a Loop Limit Check Predicate Block). It contains zero or more Regular Predicates followed by a Parse Predicate // which, however, does not need to exist (we could already have decided to remove Parse Predicates for this loop). class PredicateBlock : public StackObj { - ParsePredicate _parse_predicate; // Could be missing. - Node* _entry; - - static Node* skip_regular_predicates(Node* regular_predicate_proj, Deoptimization::DeoptReason deopt_reason); - DEBUG_ONLY(void verify_block();) + const ParsePredicate _parse_predicate; // Could be missing. + const RegularPredicateBlock _regular_predicate_block; + Node* const _entry; +#ifndef PRODUCT + // Used for dumping. + Node* const _tail; + const Deoptimization::DeoptReason _deopt_reason; +#endif // NOT PRODUCT public: - PredicateBlock(Node* predicate_proj, Deoptimization::DeoptReason deopt_reason) - : _parse_predicate(predicate_proj, deopt_reason), - _entry(skip_regular_predicates(_parse_predicate.entry(), deopt_reason)) { - DEBUG_ONLY(verify_block();) - } + PredicateBlock(Node* tail, Deoptimization::DeoptReason deopt_reason) + : _parse_predicate(tail, deopt_reason), + _regular_predicate_block(_parse_predicate.entry(), deopt_reason), + _entry(_regular_predicate_block.entry()) +#ifndef PRODUCT + , _tail(tail) + , _deopt_reason(deopt_reason) +#endif // NOT PRODUCT + {} + NONCOPYABLE(PredicateBlock); // Returns the control input node into this Regular Predicate block. This is either: // - The control input to the first If node in the block representing a Runtime Predicate if there is at least one @@ -453,11 +764,11 @@ class PredicateBlock : public StackObj { } ParsePredicateNode* parse_predicate() const { - return _parse_predicate.node(); + return _parse_predicate.head(); } ParsePredicateSuccessProj* parse_predicate_success_proj() const { - return _parse_predicate.success_proj(); + return _parse_predicate.tail(); } bool has_runtime_predicates() const { @@ -471,25 +782,31 @@ class PredicateBlock : public StackObj { Node* skip_parse_predicate() const { return _parse_predicate.entry(); } + +#ifndef PRODUCT + void dump() const; + void dump(const char* prefix) const; +#endif // NOT PRODUCT }; // This class takes a loop entry node and finds all the available predicates for the loop. class Predicates : public StackObj { - Node* _loop_entry; - PredicateBlock _loop_limit_check_predicate_block; - PredicateBlock _profiled_loop_predicate_block; - PredicateBlock _loop_predicate_block; - Node* _entry; + Node* const _tail; + const PredicateBlock _loop_limit_check_predicate_block; + const PredicateBlock _profiled_loop_predicate_block; + const PredicateBlock _loop_predicate_block; + Node* const _entry; public: - Predicates(Node* loop_entry) - : _loop_entry(loop_entry), + explicit Predicates(Node* loop_entry) + : _tail(loop_entry), _loop_limit_check_predicate_block(loop_entry, Deoptimization::Reason_loop_limit_check), _profiled_loop_predicate_block(_loop_limit_check_predicate_block.entry(), Deoptimization::Reason_profile_predicate), _loop_predicate_block(_profiled_loop_predicate_block.entry(), Deoptimization::Reason_predicate), _entry(_loop_predicate_block.entry()) {} + NONCOPYABLE(Predicates); // Returns the control input the first predicate if there are any predicates. If there are no predicates, the same // node initially passed to the constructor is returned. @@ -510,35 +827,17 @@ class Predicates : public StackObj { } bool has_any() const { - return _entry != _loop_entry; + return _entry != _tail; } -}; - -// This class iterates over the Parse Predicates of a loop. -class ParsePredicateIterator : public StackObj { - GrowableArray _parse_predicates; - int _current_index; - - public: - ParsePredicateIterator(const Predicates& predicates); - bool has_next() const { - return _current_index < _parse_predicates.length(); - } - - ParsePredicateNode* next(); +#ifndef PRODUCT + /* + * Debug printing functions. + */ + void dump() const; + static void dump_at(Node* node); + static void dump_for_loop(LoopNode* loop_node); +#endif // NOT PRODUCT }; -// Special predicate iterator that can be used to walk through predicate entries, regardless of whether the predicate -// belongs to the same loop or not (i.e. leftovers from already folded nodes). The iterator returns the next entry -// to a predicate. -class PredicateEntryIterator : public StackObj { - Node* _current; - - public: - explicit PredicateEntryIterator(Node* start) : _current(start) {}; - - bool has_next() const; - Node* next_entry(); -}; #endif // SHARE_OPTO_PREDICATES_HPP diff --git a/src/hotspot/share/opto/reg_split.cpp b/src/hotspot/share/opto/reg_split.cpp index 9f89c683b34..6d948aff011 100644 --- a/src/hotspot/share/opto/reg_split.cpp +++ b/src/hotspot/share/opto/reg_split.cpp @@ -306,8 +306,8 @@ static Node* clone_node(Node* def, Block *b, Compile* C) { C->record_failure(C2Compiler::retry_no_subsuming_loads()); } else { // Bailout without retry - assert(false, "RA Split failed: attempt to clone node with anti_dependence"); - C->record_method_not_compilable("RA Split failed: attempt to clone node with anti_dependence"); + assert(C->failure_is_artificial(), "RA Split failed: attempt to clone node with anti_dependence"); + C->record_method_not_compilable("RA Split failed: attempt to clone node with anti_dependence" DEBUG_ONLY(COMMA true)); } return nullptr; } diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 37536198229..8eb26c6c519 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -468,11 +468,11 @@ bool LibraryCallKit::inline_vector_nary_operation(int n) { Node* operation = nullptr; if (opc == Op_CallLeafVector) { assert(UseVectorStubs, "sanity"); - operation = gen_call_to_svml(opr->get_con(), elem_bt, num_elem, opd1, opd2); + operation = gen_call_to_vector_math(opr->get_con(), elem_bt, num_elem, opd1, opd2); if (operation == nullptr) { - log_if_needed(" ** svml call failed for %s_%s_%d", - (elem_bt == T_FLOAT)?"float":"double", - VectorSupport::svmlname[opr->get_con() - VectorSupport::VECTOR_OP_SVML_START], + log_if_needed(" ** Vector math call failed for %s_%s_%d", + (elem_bt == T_FLOAT) ? "float" : "double", + VectorSupport::mathname[opr->get_con() - VectorSupport::VECTOR_OP_MATH_START], num_elem * type2aelembytes(elem_bt)); return false; } @@ -2071,12 +2071,12 @@ bool LibraryCallKit::inline_vector_rearrange() { return true; } -static address get_svml_address(int vop, int bits, BasicType bt, char* name_ptr, int name_len) { +static address get_vector_math_address(int vop, int bits, BasicType bt, char* name_ptr, int name_len) { address addr = nullptr; assert(UseVectorStubs, "sanity"); assert(name_ptr != nullptr, "unexpected"); - assert((vop >= VectorSupport::VECTOR_OP_SVML_START) && (vop <= VectorSupport::VECTOR_OP_SVML_END), "unexpected"); - int op = vop - VectorSupport::VECTOR_OP_SVML_START; + assert((vop >= VectorSupport::VECTOR_OP_MATH_START) && (vop <= VectorSupport::VECTOR_OP_MATH_END), "unexpected"); + int op = vop - VectorSupport::VECTOR_OP_MATH_START; switch(bits) { case 64: //fallthough @@ -2084,21 +2084,34 @@ static address get_svml_address(int vop, int bits, BasicType bt, char* name_ptr, case 256: //fallthough case 512: if (bt == T_FLOAT) { - snprintf(name_ptr, name_len, "vector_%s_float%d", VectorSupport::svmlname[op], bits); + snprintf(name_ptr, name_len, "vector_%s_float_%dbits_fixed", VectorSupport::mathname[op], bits); addr = StubRoutines::_vector_f_math[exact_log2(bits/64)][op]; } else { assert(bt == T_DOUBLE, "must be FP type only"); - snprintf(name_ptr, name_len, "vector_%s_double%d", VectorSupport::svmlname[op], bits); + snprintf(name_ptr, name_len, "vector_%s_double_%dbits_fixed", VectorSupport::mathname[op], bits); addr = StubRoutines::_vector_d_math[exact_log2(bits/64)][op]; } break; default: - snprintf(name_ptr, name_len, "invalid"); - addr = nullptr; - Unimplemented(); + if (!Matcher::supports_scalable_vector() || !Matcher::vector_size_supported(bt, bits/type2aelembytes(bt)) ) { + snprintf(name_ptr, name_len, "invalid"); + addr = nullptr; + Unimplemented(); + } break; } + if (addr == nullptr && Matcher::supports_scalable_vector()) { + if (bt == T_FLOAT) { + snprintf(name_ptr, name_len, "vector_%s_float_%dbits_scalable", VectorSupport::mathname[op], bits); + addr = StubRoutines::_vector_f_math[VectorSupport::VEC_SIZE_SCALABLE][op]; + } else { + assert(bt == T_DOUBLE, "must be FP type only"); + snprintf(name_ptr, name_len, "vector_%s_double_%dbits_scalable", VectorSupport::mathname[op], bits); + addr = StubRoutines::_vector_d_math[VectorSupport::VEC_SIZE_SCALABLE][op]; + } + } + return addr; } @@ -2246,16 +2259,16 @@ bool LibraryCallKit::inline_vector_select_from() { return true; } -Node* LibraryCallKit::gen_call_to_svml(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2) { +Node* LibraryCallKit::gen_call_to_vector_math(int vector_api_op_id, BasicType bt, int num_elem, Node* opd1, Node* opd2) { assert(UseVectorStubs, "sanity"); - assert(vector_api_op_id >= VectorSupport::VECTOR_OP_SVML_START && vector_api_op_id <= VectorSupport::VECTOR_OP_SVML_END, "need valid op id"); + assert(vector_api_op_id >= VectorSupport::VECTOR_OP_MATH_START && vector_api_op_id <= VectorSupport::VECTOR_OP_MATH_END, "need valid op id"); assert(opd1 != nullptr, "must not be null"); const TypeVect* vt = TypeVect::make(bt, num_elem); const TypeFunc* call_type = OptoRuntime::Math_Vector_Vector_Type(opd2 != nullptr ? 2 : 1, vt, vt); char name[100] = ""; - // Get address for svml method. - address addr = get_svml_address(vector_api_op_id, vt->length_in_bytes() * BitsPerByte, bt, name, 100); + // Get address for vector math method. + address addr = get_vector_math_address(vector_api_op_id, vt->length_in_bytes() * BitsPerByte, bt, name, 100); if (addr == nullptr) { return nullptr; diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index b2b94e6096f..f195c8b3c67 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -2948,9 +2948,10 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) // We must release the Threads_lock before we can post a jvmti event // in Thread::start. { + ConditionalMutexLocker throttle_ml(ThreadsLockThrottle_lock, UseThreadsLockThrottleLock); // Ensure that the C++ Thread and OSThread structures aren't freed before // we operate. - MutexLocker mu(Threads_lock); + MutexLocker ml(Threads_lock); // Since JDK 5 the java.lang.Thread threadStatus is used to prevent // re-starting an already started thread, so we should usually find diff --git a/src/hotspot/share/prims/upcallLinker.cpp b/src/hotspot/share/prims/upcallLinker.cpp index b02746911a8..1abce57652a 100644 --- a/src/hotspot/share/prims/upcallLinker.cpp +++ b/src/hotspot/share/prims/upcallLinker.cpp @@ -22,7 +22,7 @@ */ #include "precompiled.hpp" -#include "classfile/javaClasses.hpp" +#include "classfile/javaClasses.inline.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "compiler/compilationPolicy.hpp" @@ -73,7 +73,7 @@ JavaThread* UpcallLinker::maybe_attach_and_get_thread() { } // modelled after JavaCallWrapper::JavaCallWrapper -JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context, jobject receiver) { +JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context) { JavaThread* thread = maybe_attach_and_get_thread(); guarantee(thread->thread_state() == _thread_in_native, "wrong thread state for upcall"); context->thread = thread; @@ -108,8 +108,6 @@ JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context, jobject recei debug_only(thread->inc_java_call_counter()); thread->set_active_handles(context->new_handles); // install new handle block and reset Java frame linkage - thread->set_vm_result(JNIHandles::resolve(receiver)); - return thread; } @@ -138,11 +136,10 @@ void UpcallLinker::on_exit(UpcallStub::FrameData* context) { } void UpcallLinker::handle_uncaught_exception(oop exception) { - ResourceMark rm; - // Based on CATCH macro tty->print_cr("Uncaught exception:"); - exception->print(); - ShouldNotReachHere(); + Handle exception_h(Thread::current(), exception); + java_lang_Throwable::print_stack_trace(exception_h, tty); + fatal("Unrecoverable uncaught exception encountered"); } JVM_ENTRY(jlong, UL_MakeUpcallStub(JNIEnv *env, jclass unused, jobject mh, jobject abi, jobject conv, @@ -150,36 +147,30 @@ JVM_ENTRY(jlong, UL_MakeUpcallStub(JNIEnv *env, jclass unused, jobject mh, jobje ResourceMark rm(THREAD); Handle mh_h(THREAD, JNIHandles::resolve(mh)); jobject mh_j = JNIHandles::make_global(mh_h); + oop type = java_lang_invoke_MethodHandle::type(mh_h()); - oop lform = java_lang_invoke_MethodHandle::form(mh_h()); - oop vmentry = java_lang_invoke_LambdaForm::vmentry(lform); - Method* entry = java_lang_invoke_MemberName::vmtarget(vmentry); - const methodHandle mh_entry(THREAD, entry); - - assert(entry->method_holder()->is_initialized(), "no clinit barrier"); - CompilationPolicy::compile_if_required(mh_entry, CHECK_0); - - assert(entry->is_static(), "static only"); // Fill in the signature array, for the calling-convention call. - const int total_out_args = entry->size_of_parameters(); - assert(total_out_args > 0, "receiver arg"); + const int total_out_args = java_lang_invoke_MethodType::ptype_slot_count(type) + 1; // +1 for receiver + bool create_new = true; + TempNewSymbol signature = java_lang_invoke_MethodType::as_signature(type, create_new); BasicType* out_sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_out_args); BasicType ret_type; { int i = 0; - SignatureStream ss(entry->signature()); + out_sig_bt[i++] = T_OBJECT; // receiver MH + SignatureStream ss(signature); for (; !ss.at_return_type(); ss.next()) { out_sig_bt[i++] = ss.type(); // Collect remaining bits of signature if (ss.type() == T_LONG || ss.type() == T_DOUBLE) out_sig_bt[i++] = T_VOID; // Longs & doubles take 2 Java slots } - assert(i == total_out_args, ""); + assert(i == total_out_args, "%d != %d", i, total_out_args); ret_type = ss.type(); } return (jlong) UpcallLinker::make_upcall_stub( - mh_j, entry, out_sig_bt, total_out_args, ret_type, + mh_j, signature, out_sig_bt, total_out_args, ret_type, abi, conv, needs_return_buffer, checked_cast(ret_buf_size)); JVM_END diff --git a/src/hotspot/share/prims/upcallLinker.hpp b/src/hotspot/share/prims/upcallLinker.hpp index d80516b2566..765ed63fc5a 100644 --- a/src/hotspot/share/prims/upcallLinker.hpp +++ b/src/hotspot/share/prims/upcallLinker.hpp @@ -34,10 +34,10 @@ class UpcallLinker { private: static JavaThread* maybe_attach_and_get_thread(); - static JavaThread* on_entry(UpcallStub::FrameData* context, jobject receiver); + static JavaThread* on_entry(UpcallStub::FrameData* context); static void on_exit(UpcallStub::FrameData* context); public: - static address make_upcall_stub(jobject mh, Method* entry, + static address make_upcall_stub(jobject mh, Symbol* signature, BasicType* out_sig_bt, int total_out_args, BasicType ret_type, jobject jabi, jobject jconv, diff --git a/src/hotspot/share/prims/vectorSupport.cpp b/src/hotspot/share/prims/vectorSupport.cpp index e0517c91e95..65bc6c48fee 100644 --- a/src/hotspot/share/prims/vectorSupport.cpp +++ b/src/hotspot/share/prims/vectorSupport.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ #endif // COMPILER2 #ifdef COMPILER2 -const char* VectorSupport::svmlname[VectorSupport::NUM_SVML_OP] = { +const char* VectorSupport::mathname[VectorSupport::NUM_VECTOR_OP_MATH] = { "tan", "tanh", "sin", diff --git a/src/hotspot/share/prims/vectorSupport.hpp b/src/hotspot/share/prims/vectorSupport.hpp index 7302e006064..6f8e52e9ec0 100644 --- a/src/hotspot/share/prims/vectorSupport.hpp +++ b/src/hotspot/share/prims/vectorSupport.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -121,9 +121,9 @@ class VectorSupport : AllStatic { VECTOR_OP_EXPM1 = 117, VECTOR_OP_HYPOT = 118, - VECTOR_OP_SVML_START = VECTOR_OP_TAN, - VECTOR_OP_SVML_END = VECTOR_OP_HYPOT, - NUM_SVML_OP = VECTOR_OP_SVML_END - VECTOR_OP_SVML_START + 1 + VECTOR_OP_MATH_START = VECTOR_OP_TAN, + VECTOR_OP_MATH_END = VECTOR_OP_HYPOT, + NUM_VECTOR_OP_MATH = VECTOR_OP_MATH_END - VECTOR_OP_MATH_START + 1 }; enum { @@ -131,7 +131,8 @@ class VectorSupport : AllStatic { VEC_SIZE_128 = 1, VEC_SIZE_256 = 2, VEC_SIZE_512 = 3, - NUM_VEC_SIZES = 4 + VEC_SIZE_SCALABLE = 4, + NUM_VEC_SIZES = 5 }; enum { @@ -139,7 +140,7 @@ class VectorSupport : AllStatic { MODE_BITS_COERCED_LONG_TO_MASK = 1 }; - static const char* svmlname[VectorSupport::NUM_SVML_OP]; + static const char* mathname[VectorSupport::NUM_VECTOR_OP_MATH]; static int vop2ideal(jint vop, BasicType bt); diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index ca440b69913..24f6156224d 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -2159,7 +2159,8 @@ WB_ENTRY(jboolean, WB_IsJVMCISupportedByGC(JNIEnv* env)) WB_END WB_ENTRY(jboolean, WB_CanWriteJavaHeapArchive(JNIEnv* env)) - return HeapShared::can_write(); + return HeapShared::can_write() + && ArchiveHeapLoader::can_use(); // work-around JDK-8341371 WB_END diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index e193271eff6..17078a69ab9 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -719,6 +719,8 @@ void frame::print_on_error(outputStream* st, char* buf, int buflen, bool verbose st->print("v ~MethodHandlesAdapterBlob " PTR_FORMAT, p2i(pc())); } else if (_cb->is_uncommon_trap_stub()) { st->print("v ~UncommonTrapBlob " PTR_FORMAT, p2i(pc())); + } else if (_cb->is_upcall_stub()) { + st->print("v ~UpcallStub::%s " PTR_FORMAT, _cb->name(), p2i(pc())); } else { st->print("v blob " PTR_FORMAT, p2i(pc())); } @@ -1116,6 +1118,19 @@ void frame::oops_entry_do(OopClosure* f, const RegisterMap* map) const { entry_frame_call_wrapper()->oops_do(f); } +void frame::oops_upcall_do(OopClosure* f, const RegisterMap* map) const { + assert(map != nullptr, "map must be set"); + if (map->include_argument_oops()) { + // Upcall stubs call a MethodHandle impl method of which only the receiver + // is ever an oop. + // Currently we should not be able to get here, since there are no + // safepoints in the one resolve stub we can get into (handle_wrong_method) + // Leave this here as a trap in case we ever do: + ShouldNotReachHere(); // not implemented + } + _cb->as_upcall_stub()->oops_do(f, *this); +} + bool frame::is_deoptimized_frame() const { assert(_deopt_state != unknown, "not answerable"); if (_deopt_state == is_deoptimized) { @@ -1147,7 +1162,7 @@ void frame::oops_do_internal(OopClosure* f, NMethodClosure* cf, } else if (is_entry_frame()) { oops_entry_do(f, map); } else if (is_upcall_stub_frame()) { - _cb->as_upcall_stub()->oops_do(f, *this); + oops_upcall_do(f, map); } else if (CodeCache::contains(pc())) { oops_nmethod_do(f, cf, df, derived_mode, map); } else { diff --git a/src/hotspot/share/runtime/frame.hpp b/src/hotspot/share/runtime/frame.hpp index 1c57e3de4da..50aafce3837 100644 --- a/src/hotspot/share/runtime/frame.hpp +++ b/src/hotspot/share/runtime/frame.hpp @@ -464,6 +464,7 @@ class frame { const RegisterMap* map, bool use_interpreter_oop_map_cache) const; void oops_entry_do(OopClosure* f, const RegisterMap* map) const; + void oops_upcall_do(OopClosure* f, const RegisterMap* map) const; void oops_nmethod_do(OopClosure* f, NMethodClosure* cf, DerivedOopClosure* df, DerivedPointerIterationMode derived_mode, const RegisterMap* map) const; diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 10e13307e80..6f8f924d300 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -2146,6 +2146,10 @@ const int ObjectAlignmentInBytes = 8; \ product(bool, StressSecondarySupers, false, DIAGNOSTIC, \ "Use a terrible hash function in order to generate many collisions.") \ + \ + product(bool, UseThreadsLockThrottleLock, true, DIAGNOSTIC, \ + "Use an extra lock during Thread start and exit to alleviate" \ + "contention on Threads_lock.") \ // end of RUNTIME_FLAGS diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index a0ba783c364..769c7695192 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -66,6 +66,7 @@ Monitor* CodeCache_lock = nullptr; Mutex* TouchedMethodLog_lock = nullptr; Mutex* RetData_lock = nullptr; Monitor* VMOperation_lock = nullptr; +Monitor* ThreadsLockThrottle_lock = nullptr; Monitor* Threads_lock = nullptr; Mutex* NonJavaThreadsList_lock = nullptr; Mutex* NonJavaThreadsListSync_lock = nullptr; @@ -317,6 +318,8 @@ void mutex_init() { MUTEX_DEFN(JVMCIRuntime_lock , PaddedMonitor, safepoint, true); #endif + MUTEX_DEFN(ThreadsLockThrottle_lock , PaddedMonitor, safepoint); + // These locks have relative rankings, and inherit safepoint checking attributes from that rank. MUTEX_DEFL(VtableStubs_lock , PaddedMutex , CompiledIC_lock); // Also holds DumpTimeTable_lock MUTEX_DEFL(CodeCache_lock , PaddedMonitor, VtableStubs_lock); diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 3da85958501..98cb27d0b81 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -61,6 +61,8 @@ extern Monitor* CodeCache_lock; // a lock on the CodeCache extern Mutex* TouchedMethodLog_lock; // a lock on allocation of LogExecutedMethods info extern Mutex* RetData_lock; // a lock on installation of RetData inside method data extern Monitor* VMOperation_lock; // a lock on queue of vm_operations waiting to execute +extern Monitor* ThreadsLockThrottle_lock; // used by Thread start/exit to reduce competition for Threads_lock, + // so a VM thread calling a safepoint is prioritized extern Monitor* Threads_lock; // a lock on the Threads table of active Java threads // (also used by Safepoints too to block threads creation/destruction) extern Mutex* NonJavaThreadsList_lock; // a lock on the NonJavaThreads list diff --git a/src/hotspot/share/runtime/osThread.hpp b/src/hotspot/share/runtime/osThread.hpp index b0e0588a6a2..597cf8e4d3f 100644 --- a/src/hotspot/share/runtime/osThread.hpp +++ b/src/hotspot/share/runtime/osThread.hpp @@ -25,111 +25,8 @@ #ifndef SHARE_RUNTIME_OSTHREAD_HPP #define SHARE_RUNTIME_OSTHREAD_HPP -#include "runtime/frame.hpp" -#include "runtime/handles.hpp" -#include "runtime/javaFrameAnchor.hpp" -#include "runtime/objectMonitor.hpp" -#include "runtime/suspendedThreadTask.hpp" #include "utilities/macros.hpp" - -#if defined(LINUX) || defined(AIX) || defined(BSD) -#include "suspendResume_posix.hpp" -#endif - -class Monitor; - -// The OSThread class holds OS-specific thread information. It is equivalent -// to the sys_thread_t structure of the classic JVM implementation. - -// The thread states represented by the ThreadState values are platform-specific -// and are likely to be only approximate, because most OSes don't give you access -// to precise thread state information. - -// Note: the ThreadState is legacy code and is not correctly implemented. -// Uses of ThreadState need to be replaced by the state in the JavaThread. - -enum ThreadState { - ALLOCATED, // Memory has been allocated but not initialized - INITIALIZED, // The thread has been initialized but yet started - RUNNABLE, // Has been started and is runnable, but not necessarily running - MONITOR_WAIT, // Waiting on a contended monitor lock - CONDVAR_WAIT, // Waiting on a condition variable - OBJECT_WAIT, // Waiting on an Object.wait() call - BREAKPOINTED, // Suspended at breakpoint - SLEEPING, // Thread.sleep() - ZOMBIE // All done, but not reclaimed yet -}; - -typedef int (*OSThreadStartFunc)(void*); - -class OSThread: public CHeapObj { - friend class VMStructs; - friend class JVMCIVMStructs; - private: - volatile ThreadState _state; // Thread state *hint* - - // Methods - public: - void set_state(ThreadState state) { _state = state; } - ThreadState get_state() { return _state; } - - OSThread(); - ~OSThread(); - - // Printing - void print_on(outputStream* st) const; - void print() const; - - // Platform dependent stuff +// The actual class declaration is platform specific. #include OS_HEADER(osThread) - public: - - thread_id_t thread_id() const { return _thread_id; } - - void set_thread_id(thread_id_t id) { _thread_id = id; } - - private: - // _thread_id is kernel thread id (similar to LWP id on Solaris). Each - // thread has a unique thread_id (BsdThreads or NPTL). It can be used - // to access /proc. - thread_id_t _thread_id; -}; - - -// Utility class for use with condition variables: -class OSThreadWaitState : public StackObj { - OSThread* _osthread; - ThreadState _old_state; - public: - OSThreadWaitState(OSThread* osthread, bool is_object_wait) { - _osthread = osthread; - _old_state = osthread->get_state(); - if (is_object_wait) { - osthread->set_state(OBJECT_WAIT); - } else { - osthread->set_state(CONDVAR_WAIT); - } - } - ~OSThreadWaitState() { - _osthread->set_state(_old_state); - } -}; - - -// Utility class for use with contended monitors: -class OSThreadContendState : public StackObj { - OSThread* _osthread; - ThreadState _old_state; - public: - OSThreadContendState(OSThread* osthread) { - _osthread = osthread; - _old_state = osthread->get_state(); - osthread->set_state(MONITOR_WAIT); - } - ~OSThreadContendState() { - _osthread->set_state(_old_state); - } -}; - #endif // SHARE_RUNTIME_OSTHREAD_HPP diff --git a/src/hotspot/share/runtime/osThread.cpp b/src/hotspot/share/runtime/osThreadBase.cpp similarity index 87% rename from src/hotspot/share/runtime/osThread.cpp rename to src/hotspot/share/runtime/osThreadBase.cpp index edaefaa1070..7bb7ae6aa69 100644 --- a/src/hotspot/share/runtime/osThread.cpp +++ b/src/hotspot/share/runtime/osThreadBase.cpp @@ -24,19 +24,11 @@ #include "precompiled.hpp" #include "oops/oop.inline.hpp" -#include "runtime/osThread.hpp" - -OSThread::OSThread() { - pd_initialize(); -} - -OSThread::~OSThread() { - pd_destroy(); -} +#include "runtime/osThreadBase.hpp" // Printing -void OSThread::print_on(outputStream *st) const { - st->print("nid=" UINT64_FORMAT " ", (uint64_t)thread_id()); +void OSThreadBase::print_on(outputStream *st) const { + st->print("nid=" UINTX_FORMAT " ", thread_id_for_printing()); switch (_state) { case ALLOCATED: st->print("allocated "); break; case INITIALIZED: st->print("initialized "); break; @@ -51,4 +43,4 @@ void OSThread::print_on(outputStream *st) const { } } -void OSThread::print() const { print_on(tty); } +void OSThreadBase::print() const { print_on(tty); } diff --git a/src/hotspot/share/runtime/osThreadBase.hpp b/src/hotspot/share/runtime/osThreadBase.hpp new file mode 100644 index 00000000000..4063da18519 --- /dev/null +++ b/src/hotspot/share/runtime/osThreadBase.hpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * 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_RUNTIME_OSTHREAD_BASE_HPP +#define SHARE_RUNTIME_OSTHREAD_BASE_HPP + +#include "memory/allocation.hpp" + +class Monitor; + +// The OSThread class holds OS-specific thread information. It is equivalent +// to the sys_thread_t structure of the classic JVM implementation. + +// The thread states represented by the ThreadState values are platform-specific +// and are likely to be only approximate, because most OSes don't give you access +// to precise thread state information. + +// Note: the ThreadState is legacy code and is not correctly implemented. +// Uses of ThreadState need to be replaced by the state in the JavaThread. + +enum ThreadState { + ALLOCATED, // Memory has been allocated but not initialized + INITIALIZED, // The thread has been initialized but yet started + RUNNABLE, // Has been started and is runnable, but not necessarily running + MONITOR_WAIT, // Waiting on a contended monitor lock + CONDVAR_WAIT, // Waiting on a condition variable + OBJECT_WAIT, // Waiting on an Object.wait() call + BREAKPOINTED, // Suspended at breakpoint + SLEEPING, // Thread.sleep() + ZOMBIE // All done, but not reclaimed yet +}; + +typedef int (*OSThreadStartFunc)(void*); + +class OSThreadBase: public CHeapObj { + friend class VMStructs; + friend class JVMCIVMStructs; + private: + volatile ThreadState _state; // Thread state *hint* + + // Methods + public: + OSThreadBase() {} + virtual ~OSThreadBase() {} + NONCOPYABLE(OSThreadBase); + + void set_state(ThreadState state) { _state = state; } + ThreadState get_state() { return _state; } + + + virtual uintx thread_id_for_printing() const = 0; + + // Printing + void print_on(outputStream* st) const; + void print() const; +}; + + +// Utility class for use with condition variables: +class OSThreadWaitState : public StackObj { + OSThreadBase* _osthread; + ThreadState _old_state; + public: + OSThreadWaitState(OSThreadBase* osthread, bool is_object_wait) { + _osthread = osthread; + _old_state = osthread->get_state(); + if (is_object_wait) { + osthread->set_state(OBJECT_WAIT); + } else { + osthread->set_state(CONDVAR_WAIT); + } + } + ~OSThreadWaitState() { + _osthread->set_state(_old_state); + } +}; + + +// Utility class for use with contended monitors: +class OSThreadContendState : public StackObj { + OSThreadBase* _osthread; + ThreadState _old_state; + public: + OSThreadContendState(OSThreadBase* osthread) { + _osthread = osthread; + _old_state = osthread->get_state(); + osthread->set_state(MONITOR_WAIT); + } + ~OSThreadContendState() { + _osthread->set_state(_old_state); + } +}; + +#endif // SHARE_RUNTIME_OSTHREAD_BASE_HPP diff --git a/src/hotspot/share/runtime/statSampler.cpp b/src/hotspot/share/runtime/statSampler.cpp index 5fd038bf845..bbd8d3096bb 100644 --- a/src/hotspot/share/runtime/statSampler.cpp +++ b/src/hotspot/share/runtime/statSampler.cpp @@ -201,7 +201,8 @@ void StatSampler::assert_system_property(const char* name, const char* value, TR // convert Java String to utf8 string char* system_value = java_lang_String::as_utf8_string(value_oop); - assert(strcmp(value, system_value) == 0, "property value mustn't differ from System.getProperty"); + assert(strcmp(value, system_value) == 0, "property value mustn't differ from System.getProperty. Our value is: %s, System.getProperty is: %s", + value, system_value); #endif // ASSERT } diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index a2b8c1da644..c881b64b592 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -176,8 +176,8 @@ address StubRoutines::_dtanh = nullptr; address StubRoutines::_f2hf = nullptr; address StubRoutines::_hf2f = nullptr; -address StubRoutines::_vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP] = {{nullptr}, {nullptr}}; -address StubRoutines::_vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP] = {{nullptr}, {nullptr}}; +address StubRoutines::_vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH] = {{nullptr}, {nullptr}}; +address StubRoutines::_vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH] = {{nullptr}, {nullptr}}; address StubRoutines::_method_entry_barrier = nullptr; address StubRoutines::_array_sort = nullptr; @@ -188,6 +188,7 @@ address StubRoutines::_cont_returnBarrier = nullptr; address StubRoutines::_cont_returnBarrierExc = nullptr; address StubRoutines::_upcall_stub_exception_handler = nullptr; +address StubRoutines::_upcall_stub_load_target = nullptr; address StubRoutines::_lookup_secondary_supers_table_slow_path_stub = nullptr; address StubRoutines::_lookup_secondary_supers_table_stubs[Klass::SECONDARY_SUPERS_TABLE_SIZE] = { nullptr }; diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index b58c591bbf7..f025742b605 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -294,10 +294,11 @@ class StubRoutines: AllStatic { static address _cont_returnBarrierExc; // Vector Math Routines - static address _vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP]; - static address _vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_SVML_OP]; + static address _vector_f_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH]; + static address _vector_d_math[VectorSupport::NUM_VEC_SIZES][VectorSupport::NUM_VECTOR_OP_MATH]; static address _upcall_stub_exception_handler; + static address _upcall_stub_load_target; static address _lookup_secondary_supers_table_stubs[]; static address _lookup_secondary_supers_table_slow_path_stub; @@ -506,6 +507,11 @@ class StubRoutines: AllStatic { return _upcall_stub_exception_handler; } + static address upcall_stub_load_target() { + assert(_upcall_stub_load_target != nullptr, "not implemented"); + return _upcall_stub_load_target; + } + static address lookup_secondary_supers_table_stub(u1 slot) { assert(slot < Klass::SECONDARY_SUPERS_TABLE_SIZE, "out of bounds"); assert(_lookup_secondary_supers_table_stubs[slot] != nullptr, "not implemented"); diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp index 0eff5be2cd4..39693be5b2f 100644 --- a/src/hotspot/share/runtime/threads.cpp +++ b/src/hotspot/share/runtime/threads.cpp @@ -1061,7 +1061,9 @@ void Threads::add(JavaThread* p, bool force_daemon) { void Threads::remove(JavaThread* p, bool is_daemon) { // Extra scope needed for Thread_lock, so we can check // that we do not remove thread without safepoint code notice - { MonitorLocker ml(Threads_lock); + { + ConditionalMutexLocker throttle_ml(ThreadsLockThrottle_lock, UseThreadsLockThrottleLock); + MonitorLocker ml(Threads_lock); if (ThreadIdTable::is_initialized()) { // This cleanup must be done before the current thread's GC barrier @@ -1109,7 +1111,7 @@ void Threads::remove(JavaThread* p, bool is_daemon) { // Notify threads waiting in EscapeBarriers EscapeBarrier::thread_removed(p); - } // unlock Threads_lock + } // unlock Threads_lock and ThreadsLockThrottle_lock // Reduce the ObjectMonitor ceiling for the exiting thread. ObjectSynchronizer::dec_in_use_list_ceiling(); diff --git a/src/java.base/share/classes/java/io/DataOutputStream.java b/src/java.base/share/classes/java/io/DataOutputStream.java index d16ae73f913..4b22d65bd39 100644 --- a/src/java.base/share/classes/java/io/DataOutputStream.java +++ b/src/java.base/share/classes/java/io/DataOutputStream.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * 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 +26,13 @@ package java.io; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.util.ByteArray; +import static jdk.internal.util.ModifiedUtf.putChar; +import static jdk.internal.util.ModifiedUtf.utfLen; + /** * A data output stream lets an application write primitive Java data * types to an output stream in a portable way. An application can @@ -44,6 +50,8 @@ * @since 1.0 */ public class DataOutputStream extends FilterOutputStream implements DataOutput { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + /** * The number of bytes written to the data output stream so far. * If this counter overflows, it will be wrapped to Integer.MAX_VALUE. @@ -352,15 +360,11 @@ public final void writeUTF(String str) throws IOException { * {@code str} would exceed 65535 bytes in length * @throws IOException if some other I/O error occurs. */ + @SuppressWarnings("deprecation") static int writeUTF(String str, DataOutput out) throws IOException { final int strlen = str.length(); - int utflen = strlen; // optimized for ASCII - - for (int i = 0; i < strlen; i++) { - int c = str.charAt(i); - if (c >= 0x80 || c == 0) - utflen += (c >= 0x800) ? 2 : 1; - } + int countNonZeroAscii = JLA.countNonZeroAscii(str); + int utflen = utfLen(str, countNonZeroAscii); if (utflen > 65535 || /* overflow */ utflen < strlen) throw new UTFDataFormatException(tooLongMsg(str, utflen)); @@ -377,25 +381,11 @@ static int writeUTF(String str, DataOutput out) throws IOException { int count = 0; ByteArray.setUnsignedShort(bytearr, count, utflen); count += 2; - int i = 0; - for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII - int c = str.charAt(i); - if (c >= 0x80 || c == 0) break; - bytearr[count++] = (byte) c; - } + str.getBytes(0, countNonZeroAscii, bytearr, count); + count += countNonZeroAscii; - for (; i < strlen; i++) { - int c = str.charAt(i); - if (c < 0x80 && c != 0) { - bytearr[count++] = (byte) c; - } else if (c >= 0x800) { - bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); - bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F)); - bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); - } else { - bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); - bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); - } + for (int i = countNonZeroAscii; i < strlen;) { + count = putChar(bytearr, count, str.charAt(i++)); } out.write(bytearr, 0, utflen + 2); return utflen + 2; diff --git a/src/java.base/share/classes/java/io/FileInputStream.java b/src/java.base/share/classes/java/io/FileInputStream.java index 60b289637fd..180b2e416a9 100644 --- a/src/java.base/share/classes/java/io/FileInputStream.java +++ b/src/java.base/share/classes/java/io/FileInputStream.java @@ -82,10 +82,9 @@ public class FileInputStream extends InputStream private volatile boolean closed; /** - * Creates a {@code FileInputStream} by - * opening a connection to an actual file, - * the file named by the path name {@code name} - * in the file system. {@linkplain java.nio.file##links Symbolic links} + * Creates a {@code FileInputStream} to read from an existing file + * named by the path name {@code name}. + * {@linkplain java.nio.file##links Symbolic links} * are automatically redirected to the target of the link. * A new {@code FileDescriptor} * object is created to represent this file @@ -115,10 +114,8 @@ public FileInputStream(String name) throws FileNotFoundException { } /** - * Creates a {@code FileInputStream} by - * opening a connection to an actual file, - * the file named by the {@code File} - * object {@code file} in the file system. + * Creates a {@code FileInputStream} to read from an existing file + * represented by the {@code File} object {@code file}. * {@linkplain java.nio.file##links Symbolic links} * are automatically redirected to the target of the link. * A new {@code FileDescriptor} object diff --git a/src/java.base/share/classes/java/io/ObjectOutputStream.java b/src/java.base/share/classes/java/io/ObjectOutputStream.java index 3650b101353..bde069a1774 100644 --- a/src/java.base/share/classes/java/io/ObjectOutputStream.java +++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java @@ -1,5 +1,6 @@ /* * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * 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 +35,13 @@ import java.util.StringJoiner; import jdk.internal.util.ByteArray; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import sun.reflect.misc.ReflectUtil; +import static jdk.internal.util.ModifiedUtf.putChar; +import static jdk.internal.util.ModifiedUtf.utfLen; + /** * An ObjectOutputStream writes primitive data types and graphs of Java objects * to an OutputStream. The objects can be read (reconstituted) using an @@ -169,6 +175,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants { + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); private static class Caches { /** cache of subclass security audit results */ @@ -885,7 +892,7 @@ public void writeChars(String str) throws IOException { * stream */ public void writeUTF(String str) throws IOException { - bout.writeUTF(str); + bout.writeUTFInternal(str, false); } /** @@ -1317,14 +1324,7 @@ private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared) */ private void writeString(String str, boolean unshared) throws IOException { handles.assign(unshared ? null : str); - long utflen = bout.getUTFLength(str); - if (utflen <= 0xFFFF) { - bout.writeByte(TC_STRING); - bout.writeUTF(str, utflen); - } else { - bout.writeByte(TC_LONGSTRING); - bout.writeLongUTF(str, utflen); - } + bout.writeUTFInternal(str, true); } /** @@ -1994,26 +1994,27 @@ public void writeDouble(double v) throws IOException { } } - public void writeBytes(String s) throws IOException { - int endoff = s.length(); - int cpos = 0; - int csize = 0; - for (int off = 0; off < endoff; ) { - if (cpos >= csize) { - cpos = 0; - csize = Math.min(endoff - off, CHAR_BUF_SIZE); - s.getChars(off, off + csize, cbuf, 0); - } - if (pos >= MAX_BLOCK_SIZE) { + @SuppressWarnings("deprecation") + void writeBytes(String s, int len) throws IOException { + int pos = this.pos; + for (int strpos = 0; strpos < len;) { + int rem = MAX_BLOCK_SIZE - pos; + int csize = Math.min(len - strpos, rem); + s.getBytes(strpos, strpos + csize, buf, pos); + pos += csize; + strpos += csize; + + if (pos == MAX_BLOCK_SIZE) { + this.pos = pos; drain(); + pos = 0; } - int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos); - int stop = pos + n; - while (pos < stop) { - buf[pos++] = (byte) cbuf[cpos++]; - } - off += n; } + this.pos = pos; + } + + public void writeBytes(String s) throws IOException { + writeBytes(s, s.length()); } public void writeChars(String s) throws IOException { @@ -2026,8 +2027,47 @@ public void writeChars(String s) throws IOException { } } - public void writeUTF(String s) throws IOException { - writeUTF(s, getUTFLength(s)); + public void writeUTF(String str) throws IOException { + writeUTFInternal(str, false); + } + + private void writeUTFInternal(String str, boolean writeHeader) throws IOException { + int strlen = str.length(); + int countNonZeroAscii = JLA.countNonZeroAscii(str); + int utflen = utfLen(str, countNonZeroAscii); + if (utflen <= 0xFFFF) { + if(writeHeader) { + writeByte(TC_STRING); + } + writeShort(utflen); + } else { + if(writeHeader) { + writeByte(TC_LONGSTRING); + } + writeLong(utflen); + } + + if (countNonZeroAscii != 0) { + writeBytes(str, countNonZeroAscii); + } + if (countNonZeroAscii != strlen) { + writeMoreUTF(str, countNonZeroAscii); + } + } + + private void writeMoreUTF(String str, int stroff) throws IOException { + int pos = this.pos; + for (int strlen = str.length(); stroff < strlen;) { + char c = str.charAt(stroff++); + int csize = c != 0 && c < 0x80 ? 1 : c >= 0x800 ? 3 : 2; + if (pos + csize >= MAX_BLOCK_SIZE) { + this.pos = pos; + drain(); + pos = 0; + } + pos = putChar(buf, pos, c); + } + this.pos = pos; } @@ -2153,112 +2193,6 @@ void writeDoubles(double[] v, int off, int len) throws IOException { } } } - - /** - * Returns the length in bytes of the UTF encoding of the given string. - */ - long getUTFLength(String s) { - int len = s.length(); - long utflen = 0; - for (int off = 0; off < len; ) { - int csize = Math.min(len - off, CHAR_BUF_SIZE); - s.getChars(off, off + csize, cbuf, 0); - for (int cpos = 0; cpos < csize; cpos++) { - char c = cbuf[cpos]; - if (c >= 0x0001 && c <= 0x007F) { - utflen++; - } else if (c > 0x07FF) { - utflen += 3; - } else { - utflen += 2; - } - } - off += csize; - } - return utflen; - } - - /** - * Writes the given string in UTF format. This method is used in - * situations where the UTF encoding length of the string is already - * known; specifying it explicitly avoids a prescan of the string to - * determine its UTF length. - */ - void writeUTF(String s, long utflen) throws IOException { - if (utflen > 0xFFFFL) { - throw new UTFDataFormatException(); - } - writeShort((int) utflen); - if (utflen == (long) s.length()) { - writeBytes(s); - } else { - writeUTFBody(s); - } - } - - /** - * Writes given string in "long" UTF format. "Long" UTF format is - * identical to standard UTF, except that it uses an 8 byte header - * (instead of the standard 2 bytes) to convey the UTF encoding length. - */ - void writeLongUTF(String s) throws IOException { - writeLongUTF(s, getUTFLength(s)); - } - - /** - * Writes given string in "long" UTF format, where the UTF encoding - * length of the string is already known. - */ - void writeLongUTF(String s, long utflen) throws IOException { - writeLong(utflen); - if (utflen == (long) s.length()) { - writeBytes(s); - } else { - writeUTFBody(s); - } - } - - /** - * Writes the "body" (i.e., the UTF representation minus the 2-byte or - * 8-byte length header) of the UTF encoding for the given string. - */ - private void writeUTFBody(String s) throws IOException { - int limit = MAX_BLOCK_SIZE - 3; - int len = s.length(); - for (int off = 0; off < len; ) { - int csize = Math.min(len - off, CHAR_BUF_SIZE); - s.getChars(off, off + csize, cbuf, 0); - for (int cpos = 0; cpos < csize; cpos++) { - char c = cbuf[cpos]; - if (pos <= limit) { - if (c <= 0x007F && c != 0) { - buf[pos++] = (byte) c; - } else if (c > 0x07FF) { - buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F)); - buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F)); - buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F)); - pos += 3; - } else { - buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F)); - buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F)); - pos += 2; - } - } else { // write one byte at a time to normalize block - if (c <= 0x007F && c != 0) { - write(c); - } else if (c > 0x07FF) { - write(0xE0 | ((c >> 12) & 0x0F)); - write(0x80 | ((c >> 6) & 0x3F)); - write(0x80 | ((c >> 0) & 0x3F)); - } else { - write(0xC0 | ((c >> 6) & 0x1F)); - write(0x80 | ((c >> 0) & 0x3F)); - } - } - } - off += csize; - } - } } /** diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/EnclosingMethodAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/EnclosingMethodAttribute.java index 768019fa1d3..2c919008501 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/EnclosingMethodAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/EnclosingMethodAttribute.java @@ -92,7 +92,7 @@ default Optional enclosingMethodType() { * immediately enclosed by a method or constructor} */ default Optional enclosingMethodTypeSymbol() { - return enclosingMethod().map(Util::methodTypeSymbol); + return enclosingMethodType().map(Util::methodTypeSymbol); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantDynamicEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantDynamicEntry.java index 144c8a539d7..507ff906274 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantDynamicEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantDynamicEntry.java @@ -50,7 +50,7 @@ public sealed interface ConstantDynamicEntry * {@return a symbolic descriptor for the dynamic constant's type} */ default ClassDesc typeSymbol() { - return Util.fieldTypeSymbol(nameAndType()); + return Util.fieldTypeSymbol(type()); } @Override diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java index 1c0d6e55e31..12c9789133b 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantPoolBuilder.java @@ -470,10 +470,11 @@ default StringEntry stringEntry(String value) { } /** - * {@return A {@link ConstantValueEntry} descripbing the provided + * {@return A {@link ConstantValueEntry} describing the provided * Integer, Long, Float, Double, or String constant} * * @param c the constant + * @see ConstantValueEntry#constantValue() */ default ConstantValueEntry constantValueEntry(ConstantDesc c) { if (c instanceof Integer i) return intEntry(i); diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantValueEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantValueEntry.java index 340baeb905f..720e3fd5d5c 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantValueEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/ConstantValueEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * 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,13 +24,14 @@ */ package java.lang.classfile.constantpool; +import java.lang.classfile.Attributes; import java.lang.constant.ConstantDesc; import jdk.internal.javac.PreviewFeature; /** * Models a constant pool entry that can be used as the constant in a - * {@code ConstantValue} attribute; this includes the four primitive constant - * types and {@linkplain String} constants. + * {@link Attributes#constantValue() ConstantValue} attribute; this includes the four + * primitive constant types and {@linkplain String} constants. * * @sealedGraph * @since 22 @@ -42,6 +43,8 @@ public sealed interface ConstantValueEntry extends LoadableConstantEntry /** * {@return the constant value} The constant value will be an {@link Integer}, * {@link Long}, {@link Float}, {@link Double}, or {@link String}. + * + * @see ConstantPoolBuilder#constantValueEntry(ConstantDesc) */ @Override ConstantDesc constantValue(); diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/FieldRefEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/FieldRefEntry.java index 628abdac6fe..75533770b35 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/FieldRefEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/FieldRefEntry.java @@ -44,6 +44,6 @@ public sealed interface FieldRefEntry extends MemberRefEntry * {@return a symbolic descriptor for the field's type} */ default ClassDesc typeSymbol() { - return Util.fieldTypeSymbol(nameAndType()); + return Util.fieldTypeSymbol(type()); } } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/InterfaceMethodRefEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/InterfaceMethodRefEntry.java index 43faa488bb9..b97defdc1e1 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/InterfaceMethodRefEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/InterfaceMethodRefEntry.java @@ -45,6 +45,6 @@ public sealed interface InterfaceMethodRefEntry * {@return a symbolic descriptor for the interface method's type} */ default MethodTypeDesc typeSymbol() { - return Util.methodTypeSymbol(nameAndType()); + return Util.methodTypeSymbol(type()); } } diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/InvokeDynamicEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/InvokeDynamicEntry.java index d9a1c299972..f06c3d4c782 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/InvokeDynamicEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/InvokeDynamicEntry.java @@ -47,7 +47,7 @@ public sealed interface InvokeDynamicEntry * {@return a symbolic descriptor for the call site's invocation type} */ default MethodTypeDesc typeSymbol() { - return Util.methodTypeSymbol(nameAndType()); + return Util.methodTypeSymbol(type()); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/constantpool/MethodRefEntry.java b/src/java.base/share/classes/java/lang/classfile/constantpool/MethodRefEntry.java index 39684db4621..3ff8dfdc0f4 100644 --- a/src/java.base/share/classes/java/lang/classfile/constantpool/MethodRefEntry.java +++ b/src/java.base/share/classes/java/lang/classfile/constantpool/MethodRefEntry.java @@ -44,6 +44,6 @@ public sealed interface MethodRefEntry extends MemberRefEntry * {@return a symbolic descriptor for the method's type} */ default MethodTypeDesc typeSymbol() { - return Util.methodTypeSymbol(nameAndType()); + return Util.methodTypeSymbol(type()); } } diff --git a/src/java.base/share/classes/java/lang/classfile/instruction/InvokeInstruction.java b/src/java.base/share/classes/java/lang/classfile/instruction/InvokeInstruction.java index ff68abce3d2..74b8dc942a2 100644 --- a/src/java.base/share/classes/java/lang/classfile/instruction/InvokeInstruction.java +++ b/src/java.base/share/classes/java/lang/classfile/instruction/InvokeInstruction.java @@ -94,7 +94,7 @@ default Utf8Entry type() { * {@return a symbolic descriptor for the method type} */ default MethodTypeDesc typeSymbol() { - return Util.methodTypeSymbol(method().nameAndType()); + return Util.methodTypeSymbol(method().type()); } diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java index 92c02c433c5..10f282065fd 100644 --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -34,12 +34,10 @@ import java.lang.classfile.ClassBuilder; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeBuilder; -import java.lang.classfile.FieldBuilder; import java.lang.classfile.MethodBuilder; import java.lang.classfile.Opcode; import java.lang.classfile.TypeKind; import java.lang.constant.ClassDesc; -import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodTypeDesc; import java.lang.reflect.Modifier; import java.util.LinkedHashSet; @@ -51,16 +49,15 @@ import java.lang.classfile.attribute.ExceptionsAttribute; import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.ConstantPoolBuilder; -import java.lang.classfile.constantpool.MethodRefEntry; + import static java.lang.constant.ConstantDescs.*; import static java.lang.invoke.MethodHandleNatives.Constants.NESTMATE_CLASS; import static java.lang.invoke.MethodHandleNatives.Constants.STRONG_LOADER_LINK; -import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE; -import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG; import static java.lang.invoke.MethodType.methodType; import jdk.internal.constant.ConstantUtils; import jdk.internal.constant.MethodTypeDescImpl; import jdk.internal.constant.ReferenceClassDescImpl; +import jdk.internal.vm.annotation.Stable; import sun.invoke.util.Wrapper; /** @@ -71,7 +68,7 @@ */ /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory { private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$"; - private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final @Stable String[] ARG_NAME_CACHE = {"arg$1", "arg$2", "arg$3", "arg$4", "arg$5", "arg$6", "arg$7", "arg$8"}; private static final ClassDesc[] EMPTY_CLASSDESC_ARRAY = ConstantUtils.EMPTY_CLASSDESC; // For dumping generated classes to disk, for debugging purposes @@ -96,7 +93,6 @@ private final MethodTypeDesc implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;" private final MethodType constructorType; // Generated class constructor type "(CC)void" private final MethodTypeDesc constructorTypeDesc;// Type descriptor for the generated class constructor type "(CC)void" - private final String[] argNames; // Generated names for the constructor arguments private final ClassDesc[] argDescs; // Type descriptors for the constructor arguments private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1" private final ConstantPoolBuilder pool = ConstantPoolBuilder.of(); @@ -174,18 +170,24 @@ public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, implKind == MethodHandleInfo.REF_invokeSpecial || implKind == MethodHandleInfo.REF_invokeStatic && implClass.isHidden(); int parameterCount = factoryType.parameterCount(); + ClassDesc[] argDescs; + MethodTypeDesc constructorTypeDesc; if (parameterCount > 0) { - argNames = new String[parameterCount]; argDescs = new ClassDesc[parameterCount]; for (int i = 0; i < parameterCount; i++) { - argNames[i] = "arg$" + (i + 1); argDescs[i] = classDesc(factoryType.parameterType(i)); } + constructorTypeDesc = MethodTypeDescImpl.ofValidated(CD_void, argDescs); } else { - argNames = EMPTY_STRING_ARRAY; argDescs = EMPTY_CLASSDESC_ARRAY; + constructorTypeDesc = MTD_void; } - constructorTypeDesc = MethodTypeDescImpl.ofValidated(CD_void, argDescs); + this.argDescs = argDescs; + this.constructorTypeDesc = constructorTypeDesc; + } + + private static String argName(int i) { + return i < ARG_NAME_CACHE.length ? ARG_NAME_CACHE[i] : "arg$" + (i + 1); } private static String lambdaClassName(Class targetClass) { @@ -313,7 +315,7 @@ public void accept(ClassBuilder clb) { .withInterfaceSymbols(interfaces); // Generate final fields to be filled in by constructor for (int i = 0; i < argDescs.length; i++) { - clb.withField(argNames[i], argDescs[i], ACC_PRIVATE | ACC_FINAL); + clb.withField(argName(i), argDescs[i], ACC_PRIVATE | ACC_FINAL); } generateConstructor(clb); @@ -394,10 +396,9 @@ public void accept(CodeBuilder cob) { .invokespecial(CD_Object, INIT_NAME, MTD_void); int parameterCount = factoryType.parameterCount(); for (int i = 0; i < parameterCount; i++) { - cob.aload(0); - Class argType = factoryType.parameterType(i); - cob.loadLocal(TypeKind.from(argType), cob.parameterSlot(i)); - cob.putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i]))); + cob.aload(0) + .loadLocal(TypeKind.from(factoryType.parameterType(i)), cob.parameterSlot(i)) + .putfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); } cob.return_(); } @@ -449,7 +450,7 @@ public void accept(CodeBuilder cob) { cob.dup() .loadConstant(i) .aload(0) - .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i]))); + .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); TypeConvertingMethodAdapter.boxIfTypePrimitive(cob, TypeKind.from(argDescs[i])); cob.aastore(); } @@ -506,9 +507,9 @@ public void accept(CodeBuilder cob) { cob.ldc(cp.constantDynamicEntry(cp.bsmEntry(cp.methodHandleEntry(BSM_CLASS_DATA), List.of()), cp.nameAndTypeEntry(DEFAULT_NAME, CD_MethodHandle))); } - for (int i = 0; i < argNames.length; i++) { + for (int i = 0; i < argDescs.length; i++) { cob.aload(0) - .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argNames[i], argDescs[i]))); + .getfield(pool.fieldRefEntry(lambdaClassEntry, pool.nameAndTypeEntry(argName(i), argDescs[i]))); } convertArgumentTypes(cob, methodType); diff --git a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index 4a905a3030b..5d307a9ff04 100644 --- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -891,10 +891,9 @@ private Name emitSelectAlternative(CodeBuilder cob, Name selectAlternativeName, emitStaticInvoke(cob, invokeBasicName); // goto L_done - cob.goto_w(L_done); - - // L_fallback: - cob.labelBinding(L_fallback); + cob.goto_w(L_done) + // L_fallback: + .labelBinding(L_fallback); // invoke selectAlternativeName.arguments[2] System.arraycopy(preForkClasses, 0, localClasses, 0, preForkClasses.length); @@ -945,26 +944,23 @@ private Name emitGuardWithCatch(CodeBuilder cob, int pos) { .dropParameterTypes(0,1) .changeReturnType(returnType); - cob.exceptionCatch(L_startBlock, L_endBlock, L_handler, CD_Throwable); - - // Normal case - cob.labelBinding(L_startBlock); + cob.exceptionCatch(L_startBlock, L_endBlock, L_handler, CD_Throwable) + // Normal case + .labelBinding(L_startBlock); // load target emitPushArgument(cob, invoker, 0); emitPushArguments(cob, args, 1); // skip 1st argument: method handle - cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())); - cob.labelBinding(L_endBlock); - cob.goto_w(L_done); - - // Exceptional case - cob.labelBinding(L_handler); - - // Check exception's type - cob.dup(); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())) + .labelBinding(L_endBlock) + .goto_w(L_done) + // Exceptional case + .labelBinding(L_handler) + // Check exception's type + .dup(); // load exception class emitPushArgument(cob, invoker, 1); - cob.swap(); - cob.invokevirtual(CD_Class, "isInstance", MTD_boolean_Object); + cob.swap() + .invokevirtual(CD_Class, "isInstance", MTD_boolean_Object); Label L_rethrow = cob.newLabel(); cob.ifeq(L_rethrow); @@ -974,13 +970,11 @@ private Name emitGuardWithCatch(CodeBuilder cob, int pos) { cob.swap(); emitPushArguments(cob, args, 1); // skip 1st argument: method handle MethodType catcherType = type.insertParameterTypes(0, Throwable.class); - cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(catcherType.basicType())); - cob.goto_w(L_done); - - cob.labelBinding(L_rethrow); - cob.athrow(); - - cob.labelBinding(L_done); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(catcherType.basicType())) + .goto_w(L_done) + .labelBinding(L_rethrow) + .athrow() + .labelBinding(L_done); return result; } @@ -1075,8 +1069,8 @@ private Name emitTryFinally(CodeBuilder cob, int pos) { cob.labelBinding(lFrom); emitPushArgument(cob, invoker, 0); // load target emitPushArguments(cob, args, 1); // load args (skip 0: method handle) - cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())); - cob.labelBinding(lTo); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", methodDesc(type.basicType())) + .labelBinding(lTo); // FINALLY_NORMAL: int index = extendLocalsMap(new Class[]{ returnType }); @@ -1084,17 +1078,16 @@ private Name emitTryFinally(CodeBuilder cob, int pos) { emitStoreInsn(cob, basicReturnType.basicTypeKind(), index); } emitPushArgument(cob, invoker, 1); // load cleanup - cob.loadConstant(null); + cob.aconst_null(); if (isNonVoid) { emitLoadInsn(cob, basicReturnType.basicTypeKind(), index); } emitPushArguments(cob, args, 1); // load args (skip 0: method handle) - cob.invokevirtual(CD_MethodHandle, "invokeBasic", cleanupDesc); - cob.goto_w(lDone); - - // CATCH: - cob.labelBinding(lCatch); - cob.dup(); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", cleanupDesc) + .goto_w(lDone) + // CATCH: + .labelBinding(lCatch) + .dup(); // FINALLY_EXCEPTIONAL: emitPushArgument(cob, invoker, 1); // load cleanup @@ -1107,10 +1100,9 @@ private Name emitTryFinally(CodeBuilder cob, int pos) { if (isNonVoid) { emitPopInsn(cob, basicReturnType); } - cob.athrow(); - - // DONE: - cob.labelBinding(lDone); + cob.athrow() + // DONE: + .labelBinding(lDone); return result; } @@ -1147,26 +1139,24 @@ private Name emitTableSwitch(CodeBuilder cob, int pos, int numCases) { } emitPushArgument(cob, invoker, 0); // push switch input - cob.tableswitch(0, numCases - 1, defaultLabel, cases); - - cob.labelBinding(defaultLabel); + cob.tableswitch(0, numCases - 1, defaultLabel, cases) + .labelBinding(defaultLabel); emitPushArgument(cob, invoker, 1); // push default handle emitPushArguments(cob, args, 1); // again, skip collector - cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor); - cob.goto_(endLabel); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor) + .goto_(endLabel); for (int i = 0; i < numCases; i++) { cob.labelBinding(cases.get(i).target()); // Load the particular case: emitLoadInsn(cob, TypeKind.REFERENCE, casesLocal); - cob.loadConstant(i); - cob.aaload(); + cob.loadConstant(i) + .aaload(); // invoke it: emitPushArguments(cob, args, 1); // again, skip collector - cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor); - - cob.goto_(endLabel); + cob.invokevirtual(CD_MethodHandle, "invokeBasic", caseDescriptor) + .goto_(endLabel); } cob.labelBinding(endLabel); @@ -1335,16 +1325,14 @@ private Name emitLoop(CodeBuilder cob, int pos) { // invoke fini emitLoopHandleInvoke(cob, invoker, finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex); - cob.goto_w(lDone); - - // this is the beginning of the next loop clause - cob.labelBinding(lNext); + cob.goto_w(lDone) + // this is the beginning of the next loop clause + .labelBinding(lNext); } - cob.goto_w(lLoop); - - // DONE: - cob.labelBinding(lDone); + cob.goto_w(lLoop) + // DONE: + .labelBinding(lDone); return result; } @@ -1370,8 +1358,8 @@ private void emitLoopHandleInvoke(CodeBuilder cob, Name holder, int handles, int int firstLoopStateSlot) { // load handle for clause emitPushClauseArray(cob, clauseDataSlot, handles); - cob.loadConstant(clause); - cob.aaload(); + cob.loadConstant(clause) + .aaload(); // load loop state (preceding the other arguments) if (pushLocalState) { for (int s = 0; s < loopLocalStateTypes.length; ++s) { @@ -1385,8 +1373,8 @@ private void emitLoopHandleInvoke(CodeBuilder cob, Name holder, int handles, int private void emitPushClauseArray(CodeBuilder cob, int clauseDataSlot, int which) { emitLoadInsn(cob, TypeKind.REFERENCE, clauseDataSlot); - cob.loadConstant(which - 1); - cob.aaload(); + cob.loadConstant(which - 1) + .aaload(); } private void emitZero(CodeBuilder cob, BasicType type) { @@ -1519,14 +1507,14 @@ public void accept(MethodBuilder mb) { @Override public void accept(CodeBuilder cob) { // create parameter array - cob.loadConstant(invokerType.parameterCount()); - cob.anewarray(CD_Object); + cob.loadConstant(invokerType.parameterCount()) + .anewarray(CD_Object); // fill parameter array for (int i = 0; i < invokerType.parameterCount(); i++) { Class ptype = invokerType.parameterType(i); - cob.dup(); - cob.loadConstant(i); + cob.dup() + .loadConstant(i); emitLoadInsn(cob, basicType(ptype).basicTypeKind(), i); // box if primitive type if (ptype.isPrimitive()) { @@ -1535,10 +1523,10 @@ public void accept(CodeBuilder cob) { cob.aastore(); } // invoke - cob.aload(0); - cob.getfield(CD_MethodHandle, "form", CD_LambdaForm); - cob.swap(); // swap form and array; avoid local variable - cob.invokevirtual(CD_LambdaForm, "interpretWithArguments", MethodTypeDescImpl.ofValidated(CD_Object, CD_Object_array)); + cob.aload(0) + .getfield(CD_MethodHandle, "form", CD_LambdaForm) + .swap() // swap form and array; avoid local variable + .invokevirtual(CD_LambdaForm, "interpretWithArguments", MethodTypeDescImpl.ofValidated(CD_Object, CD_Object_array)); // maybe unbox Class rtype = invokerType.returnType(); @@ -1592,9 +1580,9 @@ public void accept(CodeBuilder cob) { // Load arguments from array for (int i = 0; i < dstType.parameterCount(); i++) { - cob.aload(1); - cob.loadConstant(i); - cob.aaload(); + cob.aload(1) + .loadConstant(i) + .aaload(); // Maybe unbox Class dptype = dstType.parameterType(i); @@ -1645,9 +1633,9 @@ private void bogusMethod(ClassBuilder clb, Object os) { clb.withMethodBody("dummy", MTD_void, ACC_STATIC, new Consumer<>() { @Override public void accept(CodeBuilder cob) { - cob.ldc(os.toString()); - cob.pop(); - cob.return_(); + cob.ldc(os.toString()) + .pop() + .return_(); } }); } diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandle.java b/src/java.base/share/classes/java/lang/invoke/MethodHandle.java index 104248c27e6..d3270600707 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandle.java @@ -27,6 +27,8 @@ import jdk.internal.loader.ClassLoaders; +import jdk.internal.vm.annotation.DontInline; +import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import java.lang.constant.ClassDesc; @@ -856,6 +858,7 @@ public Object invokeWithArguments(java.util.List arguments) throws Throwable * @throws WrongMethodTypeException if the conversion cannot be made * @see MethodHandles#explicitCastArguments */ + @ForceInline public final MethodHandle asType(MethodType newType) { // Fast path alternative to a heavyweight {@code asType} call. // Return 'this' if the conversion will be a no-op. @@ -867,7 +870,7 @@ public final MethodHandle asType(MethodType newType) { if (at != null) { return at; } - return setAsTypeCache(asTypeUncached(newType)); + return setAsTypeCache(newType); } private MethodHandle asTypeCached(MethodType newType) { @@ -885,7 +888,16 @@ private MethodHandle asTypeCached(MethodType newType) { return null; } - private MethodHandle setAsTypeCache(MethodHandle at) { + /* + * We disable inlining here to prevent complex code in the slow path + * of MethodHandle::asType from being inlined into that method. + * Excessive inlining into MethodHandle::asType can cause that method + * to become too big, which will then cause performance issues during + * var handle and method handle calls. + */ + @DontInline + private MethodHandle setAsTypeCache(MethodType newType) { + MethodHandle at = asTypeUncached(newType); // Don't introduce a strong reference in the cache if newType depends on any class loader other than // current method handle already does to avoid class loader leaks. if (isSafeToCache(at.type)) { diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java index c6a5c2763f4..dc4133ae244 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java @@ -373,46 +373,43 @@ private static byte[] createTemplate(ClassLoader loader, ClassDesc proxyDesc, Cl String methodName, List methods) { return ClassFile.of(ClassHierarchyResolverOption.of(ClassHierarchyResolver.ofClassLoading(loader))) .build(proxyDesc, clb -> { - clb.withSuperclass(CD_Object); - clb.withFlags(ACC_FINAL | ACC_SYNTHETIC); - clb.withInterfaceSymbols(ifaceDesc); - - // static and instance fields - clb.withField(TYPE_NAME, CD_Class, ACC_PRIVATE | ACC_STATIC | ACC_FINAL); - clb.withField(TARGET_NAME, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL); + clb.withSuperclass(CD_Object) + .withFlags(ACC_FINAL | ACC_SYNTHETIC) + .withInterfaceSymbols(ifaceDesc) + // static and instance fields + .withField(TYPE_NAME, CD_Class, ACC_PRIVATE | ACC_STATIC | ACC_FINAL) + .withField(TARGET_NAME, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL); for (var mi : methods) { clb.withField(mi.fieldName, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL); } // clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> { - cob.loadConstant(ifaceDesc); - cob.putstatic(proxyDesc, TYPE_NAME, CD_Class); - cob.return_(); + cob.loadConstant(ifaceDesc) + .putstatic(proxyDesc, TYPE_NAME, CD_Class) + .return_(); }); // (Lookup, MethodHandle target, MethodHandle callerBoundTarget) clb.withMethodBody(INIT_NAME, MTD_void_Lookup_MethodHandle_MethodHandle, 0, cob -> { - cob.aload(0); - cob.invokespecial(CD_Object, INIT_NAME, MTD_void); - - // call ensureOriginalLookup to verify the given Lookup has access - cob.aload(1); - cob.invokestatic(proxyDesc, "ensureOriginalLookup", MTD_void_Lookup); - - // this.target = target; - cob.aload(0); - cob.aload(2); - cob.putfield(proxyDesc, TARGET_NAME, CD_MethodHandle); + cob.aload(0) + .invokespecial(CD_Object, INIT_NAME, MTD_void) + // call ensureOriginalLookup to verify the given Lookup has access + .aload(1) + .invokestatic(proxyDesc, ENSURE_ORIGINAL_LOOKUP, MTD_void_Lookup) + // this.target = target; + .aload(0) + .aload(2) + .putfield(proxyDesc, TARGET_NAME, CD_MethodHandle); // method handles adjusted to the method type of each method for (var mi : methods) { // this.m = callerBoundTarget.asType(xxType); - cob.aload(0); - cob.aload(3); - cob.loadConstant(mi.desc); - cob.invokevirtual(CD_MethodHandle, "asType", MTD_MethodHandle_MethodType); - cob.putfield(proxyDesc, mi.fieldName, CD_MethodHandle); + cob.aload(0) + .aload(3) + .loadConstant(mi.desc) + .invokevirtual(CD_MethodHandle, "asType", MTD_MethodHandle_MethodType) + .putfield(proxyDesc, mi.fieldName, CD_MethodHandle); } // complete @@ -425,26 +422,26 @@ private static byte[] createTemplate(ClassLoader loader, ClassDesc proxyDesc, Cl clb.withMethodBody(ENSURE_ORIGINAL_LOOKUP, MTD_void_Lookup, ACC_PRIVATE | ACC_STATIC, cob -> { var failLabel = cob.newLabel(); // check lookupClass - cob.aload(0); - cob.invokevirtual(CD_MethodHandles_Lookup, "lookupClass", MTD_Class); - cob.loadConstant(proxyDesc); - cob.if_acmpne(failLabel); - // check original access - cob.aload(0); - cob.invokevirtual(CD_MethodHandles_Lookup, "lookupModes", MTD_int); - cob.loadConstant(Lookup.ORIGINAL); - cob.iand(); - cob.ifeq(failLabel); - // success - cob.return_(); - // throw exception - cob.labelBinding(failLabel); - cob.new_(CD_IllegalAccessException); - cob.dup(); - cob.aload(0); // lookup - cob.invokevirtual(CD_Object, "toString", MTD_String); - cob.invokespecial(CD_IllegalAccessException, INIT_NAME, MTD_void_String); - cob.athrow(); + cob.aload(0) + .invokevirtual(CD_MethodHandles_Lookup, "lookupClass", MTD_Class) + .loadConstant(proxyDesc) + .if_acmpne(failLabel) + // check original access + .aload(0) + .invokevirtual(CD_MethodHandles_Lookup, "lookupModes", MTD_int) + .loadConstant(Lookup.ORIGINAL) + .iand() + .ifeq(failLabel) + // success + .return_() + // throw exception + .labelBinding(failLabel) + .new_(CD_IllegalAccessException) + .dup() + .aload(0) // lookup + .invokevirtual(CD_Object, "toString", MTD_String) + .invokespecial(CD_IllegalAccessException, INIT_NAME, MTD_void_String) + .athrow(); }); // implementation methods @@ -453,14 +450,14 @@ private static byte[] createTemplate(ClassLoader loader, ClassDesc proxyDesc, Cl clb.withMethodBody(methodName, mi.desc, ACC_PUBLIC, cob -> cob .trying(bcb -> { // return this.handleField.invokeExact(arguments...); - bcb.aload(0); - bcb.getfield(proxyDesc, mi.fieldName, CD_MethodHandle); + bcb.aload(0) + .getfield(proxyDesc, mi.fieldName, CD_MethodHandle); for (int j = 0; j < mi.desc.parameterCount(); j++) { bcb.loadLocal(TypeKind.from(mi.desc.parameterType(j)), bcb.parameterSlot(j)); } - bcb.invokevirtual(CD_MethodHandle, "invokeExact", mi.desc); - bcb.return_(TypeKind.from(mi.desc.returnType())); + bcb.invokevirtual(CD_MethodHandle, "invokeExact", mi.desc) + .return_(TypeKind.from(mi.desc.returnType())); }, ctb -> ctb // catch (Error | RuntimeException | Declared ex) { throw ex; } .catchingMulti(mi.thrown, CodeBuilder::athrow) diff --git a/src/java.base/share/classes/java/lang/package-info.java b/src/java.base/share/classes/java/lang/package-info.java index 0484ecb2952..9ca4482c819 100644 --- a/src/java.base/share/classes/java/lang/package-info.java +++ b/src/java.base/share/classes/java/lang/package-info.java @@ -29,8 +29,8 @@ * Object}, which is the root of the class hierarchy, and {@link * Class}, instances of which represent classes at run time. * - *

Frequently it is necessary to represent a value of primitive - * type as if it were an object.The {@index + *

Frequently it is necessary to represent a + * value of primitive type as if it were an object.The {@index * "wrapper classes"} {@link Boolean}, {@link Byte}, {@link * Character}, {@link Short}, {@link Integer}, {@link Long}, {@link * Float}, and {@link Double} serve this purpose. An object of type diff --git a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java index f380cf1070d..0c0144b24db 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -29,7 +29,6 @@ import java.lang.classfile.CodeBuilder; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDesc; -import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; @@ -55,6 +54,7 @@ import jdk.internal.misc.PreviewFeatures; import jdk.internal.vm.annotation.Stable; +import static java.lang.constant.ConstantDescs.*; import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE; import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG; import java.util.Arrays; @@ -86,15 +86,15 @@ private SwitchBootstraps() {} private static final ClassDesc CD_Objects = ReferenceClassDescImpl.ofValidated("Ljava/util/Objects;"); private static final MethodTypeDesc CHECK_INDEX_DESCRIPTOR = - MethodTypeDescImpl.ofValidated(ConstantDescs.CD_int, ConstantDescs.CD_int, ConstantDescs.CD_int); - private static final MethodTypeDesc MTD_TYPE_SWITCH = MethodTypeDescImpl.ofValidated(ConstantDescs.CD_int, - ConstantDescs.CD_Object, - ConstantDescs.CD_int); - private static final MethodTypeDesc MTD_TYPE_SWITCH_EXTRA = MethodTypeDescImpl.ofValidated(ConstantDescs.CD_int, - ConstantDescs.CD_Object, - ConstantDescs.CD_int, + MethodTypeDescImpl.ofValidated(CD_int, CD_int, CD_int); + private static final MethodTypeDesc MTD_TYPE_SWITCH = MethodTypeDescImpl.ofValidated(CD_int, + CD_Object, + CD_int); + private static final MethodTypeDesc MTD_TYPE_SWITCH_EXTRA = MethodTypeDescImpl.ofValidated(CD_int, + CD_Object, + CD_int, CD_BiPredicate, - ConstantDescs.CD_List); + CD_List); private static final MethodType MT_TYPE_SWITCH_EXTRA = MethodType.methodType(int.class, Object.class, int.class, @@ -484,19 +484,19 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto return cb -> { // Objects.checkIndex(RESTART_IDX, labelConstants + 1) - cb.iload(RESTART_IDX); - cb.loadConstant(labelConstants.length + 1); - cb.invokestatic(CD_Objects, "checkIndex", CHECK_INDEX_DESCRIPTOR); - cb.pop(); - cb.aload(SELECTOR_OBJ); + cb.iload(RESTART_IDX) + .loadConstant(labelConstants.length + 1) + .invokestatic(CD_Objects, "checkIndex", CHECK_INDEX_DESCRIPTOR) + .pop() + .aload(SELECTOR_OBJ); Label nonNullLabel = cb.newLabel(); - cb.ifnonnull(nonNullLabel); - cb.iconst_m1(); - cb.ireturn(); - cb.labelBinding(nonNullLabel); + cb.ifnonnull(nonNullLabel) + .iconst_m1() + .ireturn() + .labelBinding(nonNullLabel); if (labelConstants.length == 0) { cb.loadConstant(0) - .ireturn(); + .ireturn(); return; } cb.iload(RESTART_IDX); @@ -535,132 +535,132 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto if (!selectorType.isPrimitive() && !Wrapper.isWrapperNumericOrBooleanType(selectorType)) { // Object o = ... // o instanceof Wrapped(float) - cb.aload(SELECTOR_OBJ); - cb.instanceOf(Wrapper.forBasicType(classLabel).wrapperClassDescriptor()); - cb.ifeq(next); + cb.aload(SELECTOR_OBJ) + .instanceOf(Wrapper.forBasicType(classLabel).wrapperClassDescriptor()) + .ifeq(next); } else if (!unconditionalExactnessCheck(Wrapper.asPrimitiveType(selectorType), classLabel)) { // Integer i = ... or int i = ... // o instanceof float Label notNumber = cb.newLabel(); - cb.aload(SELECTOR_OBJ); - cb.instanceOf(ConstantDescs.CD_Number); + cb.aload(SELECTOR_OBJ) + .instanceOf(CD_Number); if (selectorType == long.class || selectorType == float.class || selectorType == double.class || selectorType == Long.class || selectorType == Float.class || selectorType == Double.class) { cb.ifeq(next); } else { cb.ifeq(notNumber); } - cb.aload(SELECTOR_OBJ); - cb.checkcast(ConstantDescs.CD_Number); + cb.aload(SELECTOR_OBJ) + .checkcast(CD_Number); if (selectorType == long.class || selectorType == Long.class) { - cb.invokevirtual(ConstantDescs.CD_Number, + cb.invokevirtual(CD_Number, "longValue", - MethodTypeDesc.of(ConstantDescs.CD_long)); + MethodTypeDesc.of(CD_long)); } else if (selectorType == float.class || selectorType == Float.class) { - cb.invokevirtual(ConstantDescs.CD_Number, + cb.invokevirtual(CD_Number, "floatValue", - MethodTypeDesc.of(ConstantDescs.CD_float)); + MethodTypeDesc.of(CD_float)); } else if (selectorType == double.class || selectorType == Double.class) { - cb.invokevirtual(ConstantDescs.CD_Number, + cb.invokevirtual(CD_Number, "doubleValue", - MethodTypeDesc.of(ConstantDescs.CD_double)); + MethodTypeDesc.of(CD_double)); } else { Label compare = cb.newLabel(); - cb.invokevirtual(ConstantDescs.CD_Number, + cb.invokevirtual(CD_Number, "intValue", - MethodTypeDesc.of(ConstantDescs.CD_int)); - cb.goto_(compare); - cb.labelBinding(notNumber); - cb.aload(SELECTOR_OBJ); - cb.instanceOf(ConstantDescs.CD_Character); - cb.ifeq(next); - cb.aload(SELECTOR_OBJ); - cb.checkcast(ConstantDescs.CD_Character); - cb.invokevirtual(ConstantDescs.CD_Character, + MethodTypeDesc.of(CD_int)) + .goto_(compare) + .labelBinding(notNumber) + .aload(SELECTOR_OBJ) + .instanceOf(CD_Character) + .ifeq(next) + .aload(SELECTOR_OBJ) + .checkcast(CD_Character) + .invokevirtual(CD_Character, "charValue", - MethodTypeDesc.of(ConstantDescs.CD_char)); - cb.labelBinding(compare); + MethodTypeDesc.of(CD_char)) + .labelBinding(compare); } TypePairs typePair = TypePairs.of(Wrapper.asPrimitiveType(selectorType), classLabel); String methodName = TypePairs.typePairToName.get(typePair); cb.invokestatic(referenceClassDesc(ExactConversionsSupport.class), methodName, - MethodTypeDesc.of(ConstantDescs.CD_boolean, classDesc(typePair.from))); - cb.ifeq(next); + MethodTypeDesc.of(CD_boolean, classDesc(typePair.from))) + .ifeq(next); } } else { Optional classLabelConstableOpt = classLabel.describeConstable(); if (classLabelConstableOpt.isPresent()) { - cb.aload(SELECTOR_OBJ); - cb.instanceOf(classLabelConstableOpt.orElseThrow()); - cb.ifeq(next); + cb.aload(SELECTOR_OBJ) + .instanceOf(classLabelConstableOpt.orElseThrow()) + .ifeq(next); } else { - cb.aload(EXTRA_CLASS_LABELS); - cb.loadConstant(extraClassLabels.size()); - cb.invokeinterface(ConstantDescs.CD_List, + cb.aload(EXTRA_CLASS_LABELS) + .loadConstant(extraClassLabels.size()) + .invokeinterface(CD_List, "get", - MethodTypeDesc.of(ConstantDescs.CD_Object, - ConstantDescs.CD_int)); - cb.checkcast(ConstantDescs.CD_Class); - cb.aload(SELECTOR_OBJ); - cb.invokevirtual(ConstantDescs.CD_Class, + MethodTypeDesc.of(CD_Object, + CD_int)) + .checkcast(CD_Class) + .aload(SELECTOR_OBJ) + .invokevirtual(CD_Class, "isInstance", - MethodTypeDesc.of(ConstantDescs.CD_boolean, - ConstantDescs.CD_Object)); - cb.ifeq(next); + MethodTypeDesc.of(CD_boolean, + CD_Object)) + .ifeq(next); extraClassLabels.add(classLabel); } } } else if (caseLabel instanceof EnumDesc enumLabel) { int enumIdx = enumDescs.size(); enumDescs.add(enumLabel); - cb.aload(ENUM_CACHE); - cb.loadConstant(enumIdx); - cb.invokestatic(ConstantDescs.CD_Integer, + cb.aload(ENUM_CACHE) + .loadConstant(enumIdx) + .invokestatic(CD_Integer, "valueOf", - MethodTypeDesc.of(ConstantDescs.CD_Integer, - ConstantDescs.CD_int)); - cb.aload(SELECTOR_OBJ); - cb.invokeinterface(CD_BiPredicate, + MethodTypeDesc.of(CD_Integer, + CD_int)) + .aload(SELECTOR_OBJ) + .invokeinterface(CD_BiPredicate, "test", - MethodTypeDesc.of(ConstantDescs.CD_boolean, - ConstantDescs.CD_Object, - ConstantDescs.CD_Object)); - cb.ifeq(next); + MethodTypeDesc.of(CD_boolean, + CD_Object, + CD_Object)) + .ifeq(next); } else if (caseLabel instanceof String stringLabel) { - cb.ldc(stringLabel); - cb.aload(SELECTOR_OBJ); - cb.invokevirtual(ConstantDescs.CD_Object, + cb.ldc(stringLabel) + .aload(SELECTOR_OBJ) + .invokevirtual(CD_Object, "equals", - MethodTypeDesc.of(ConstantDescs.CD_boolean, - ConstantDescs.CD_Object)); - cb.ifeq(next); + MethodTypeDesc.of(CD_boolean, + CD_Object)) + .ifeq(next); } else if (caseLabel instanceof Integer integerLabel) { Label compare = cb.newLabel(); Label notNumber = cb.newLabel(); - cb.aload(SELECTOR_OBJ); - cb.instanceOf(ConstantDescs.CD_Number); - cb.ifeq(notNumber); - cb.aload(SELECTOR_OBJ); - cb.checkcast(ConstantDescs.CD_Number); - cb.invokevirtual(ConstantDescs.CD_Number, + cb.aload(SELECTOR_OBJ) + .instanceOf(CD_Number) + .ifeq(notNumber) + .aload(SELECTOR_OBJ) + .checkcast(CD_Number) + .invokevirtual(CD_Number, "intValue", - MethodTypeDesc.of(ConstantDescs.CD_int)); - cb.goto_(compare); - cb.labelBinding(notNumber); - cb.aload(SELECTOR_OBJ); - cb.instanceOf(ConstantDescs.CD_Character); - cb.ifeq(next); - cb.aload(SELECTOR_OBJ); - cb.checkcast(ConstantDescs.CD_Character); - cb.invokevirtual(ConstantDescs.CD_Character, + MethodTypeDesc.of(CD_int)) + .goto_(compare) + .labelBinding(notNumber) + .aload(SELECTOR_OBJ) + .instanceOf(CD_Character) + .ifeq(next) + .aload(SELECTOR_OBJ) + .checkcast(CD_Character) + .invokevirtual(CD_Character, "charValue", - MethodTypeDesc.of(ConstantDescs.CD_char)); - cb.labelBinding(compare); + MethodTypeDesc.of(CD_char)) + .labelBinding(compare) - cb.loadConstant(integerLabel); - cb.if_icmpne(next); + .loadConstant(integerLabel) + .if_icmpne(next); } else if ((caseLabel instanceof Long || caseLabel instanceof Float || caseLabel instanceof Double || @@ -674,23 +674,23 @@ private static Consumer generateTypeSwitchSkeleton(Class selecto cb.invokestatic(caseLabelWrapper.wrapperClassDescriptor(), "valueOf", MethodTypeDesc.of(caseLabelWrapper.wrapperClassDescriptor(), - caseLabelWrapper.basicClassDescriptor())); - cb.aload(SELECTOR_OBJ); - cb.invokevirtual(ConstantDescs.CD_Object, + caseLabelWrapper.basicClassDescriptor())) + .aload(SELECTOR_OBJ) + .invokevirtual(CD_Object, "equals", - MethodTypeDesc.of(ConstantDescs.CD_boolean, - ConstantDescs.CD_Object)); - cb.ifeq(next); + MethodTypeDesc.of(CD_boolean, + CD_Object)) + .ifeq(next); } else { throw new InternalError("Unsupported label type: " + caseLabel.getClass()); } - cb.loadConstant(idx); - cb.ireturn(); + cb.loadConstant(idx) + .ireturn(); } - cb.labelBinding(dflt); - cb.loadConstant(labelConstants.length); - cb.ireturn(); + cb.labelBinding(dflt) + .loadConstant(labelConstants.length) + .ireturn(); }; } diff --git a/src/java.base/share/classes/java/util/ArrayList.java b/src/java.base/share/classes/java/util/ArrayList.java index bcf7b79e780..c00b130a553 100644 --- a/src/java.base/share/classes/java/util/ArrayList.java +++ b/src/java.base/share/classes/java/util/ArrayList.java @@ -1808,6 +1808,7 @@ private void replaceAllRange(UnaryOperator operator, int i, int end) { @Override public void sort(Comparator c) { sortRange(c, 0, size); + modCount++; } @SuppressWarnings("unchecked") @@ -1816,7 +1817,6 @@ private void sortRange(Comparator c, int fromIndex, int toIndex) { Arrays.sort((E[]) elementData, fromIndex, toIndex, c); if (modCount != expectedModCount) throw new ConcurrentModificationException(); - modCount++; } void checkInvariants() { diff --git a/src/java.base/share/classes/java/util/Currency.java b/src/java.base/share/classes/java/util/Currency.java index 14d1cf4351d..b11b774e614 100644 --- a/src/java.base/share/classes/java/util/Currency.java +++ b/src/java.base/share/classes/java/util/Currency.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * 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 @@ * with {@code Currency} or monetary values as it provides better handling of floating * point numbers and their operations. * - * @spec http://www.iso.org/iso/home/standards/currency_codes.htm ISO - ISO 4217 - Currency codes + * @spec https://www.iso.org/iso-4217-currency-codes.html ISO - ISO 4217 - Currency codes * @see java.math.BigDecimal * @since 1.4 */ diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index 73132b81c09..5550d399a7f 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -2324,12 +2324,11 @@ public String getDisplayName(Locale inLocale) { // If we cannot get the message format pattern, then we use a simple // hard-coded pattern. This should not occur in practice unless the // installation is missing some core files (FormatData etc.). - StringBuilder result = new StringBuilder(); - result.append((String)displayNames[1]); - if (displayNames.length > 2) { - result.append(" ("); - result.append((String)displayNames[2]); - result.append(')'); + StringBuilder result = new StringBuilder((String) displayNames[1]); + if (displayNames[2] != null) { + result.append(" (") + .append((String) displayNames[2]) + .append(')'); } return result.toString(); } diff --git a/src/java.base/share/classes/java/util/zip/ZipEntry.java b/src/java.base/share/classes/java/util/zip/ZipEntry.java index 243779a7c49..b7ecd1bea8f 100644 --- a/src/java.base/share/classes/java/util/zip/ZipEntry.java +++ b/src/java.base/share/classes/java/util/zip/ZipEntry.java @@ -564,20 +564,20 @@ void setExtra0(byte[] extra, boolean doZIP64, boolean isLOC) { // be the magic value and it "accidentally" has some // bytes in extra match the id. if (sz >= 16) { - size = get64(extra, off); - csize = get64(extra, off + 8); + size = get64S(extra, off); + csize = get64S(extra, off + 8); } } else { // CEN extra zip64 if (size == ZIP64_MAGICVAL) { if (off + 8 > len) // invalid zip64 extra break; // fields, just skip - size = get64(extra, off); + size = get64S(extra, off); } if (csize == ZIP64_MAGICVAL) { if (off + 16 > len) // invalid zip64 extra break; // fields, just skip - csize = get64(extra, off + 8); + csize = get64S(extra, off + 8); } } } @@ -588,15 +588,15 @@ void setExtra0(byte[] extra, boolean doZIP64, boolean isLOC) { int pos = off + 4; // reserved 4 bytes if (get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24) break; - long wtime = get64(extra, pos + 4); + long wtime = get64S(extra, pos + 4); if (wtime != WINDOWS_TIME_NOT_AVAILABLE) { mtime = winTimeToFileTime(wtime); } - wtime = get64(extra, pos + 12); + wtime = get64S(extra, pos + 12); if (wtime != WINDOWS_TIME_NOT_AVAILABLE) { atime = winTimeToFileTime(wtime); } - wtime = get64(extra, pos + 20); + wtime = get64S(extra, pos + 20); if (wtime != WINDOWS_TIME_NOT_AVAILABLE) { ctime = winTimeToFileTime(wtime); } diff --git a/src/java.base/share/classes/java/util/zip/ZipError.java b/src/java.base/share/classes/java/util/zip/ZipError.java index 2aa37bef010..933cc447091 100644 --- a/src/java.base/share/classes/java/util/zip/ZipError.java +++ b/src/java.base/share/classes/java/util/zip/ZipError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved. * 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,9 +28,12 @@ /** * Signals that an unrecoverable error has occurred. * + * @deprecated ZipError is no longer used and is obsolete. + * {@link ZipException} should be used instead. * @author Dave Bristor * @since 1.6 */ +@Deprecated(since="24", forRemoval = true) public class ZipError extends InternalError { @java.io.Serial private static final long serialVersionUID = 853973422266861979L; diff --git a/src/java.base/share/classes/java/util/zip/ZipFile.java b/src/java.base/share/classes/java/util/zip/ZipFile.java index 43b2261f1c6..21b9593c017 100644 --- a/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -411,13 +411,10 @@ public InputStream getInputStream(ZipEntry entry) throws IOException { case DEFLATED: // Inflater likes a bit of slack // MORE: Compute good size for inflater stream: - long size = CENLEN(zsrc.cen, pos) + 2; + long size = CENSIZ(zsrc.cen, pos); if (size > 65536) { size = 8192; } - if (size <= 0) { - size = 4096; - } InputStream is = new ZipFileInflaterInputStream(in, res, (int) size); synchronized (istreams) { istreams.add(is); @@ -906,21 +903,21 @@ private void checkZIP64(byte[] cen, int cenpos) { if (size == ZIP64_MAGICVAL) { if (sz < 8 || (off + 8) > end) break; - size = get64(cen, off); + size = get64S(cen, off); sz -= 8; off += 8; } if (rem == ZIP64_MAGICVAL) { if (sz < 8 || (off + 8) > end) break; - rem = get64(cen, off); + rem = get64S(cen, off); sz -= 8; off += 8; } if (pos == ZIP64_MAGICVAL) { if (sz < 8 || (off + 8) > end) break; - pos = get64(cen, off); + pos = get64S(cen, off); sz -= 8; off += 8; } @@ -1239,12 +1236,12 @@ private int checkAndAddEntry(int pos, int index) int nlen = CENNAM(cen, pos); int elen = CENEXT(cen, pos); int clen = CENCOM(cen, pos); - long headerSize = (long)CENHDR + nlen + clen + elen; + int headerSize = CENHDR + nlen + clen + elen; // CEN header size + name length + comment length + extra length // should not exceed 65,535 bytes per the PKWare APP.NOTE // 4.4.10, 4.4.11, & 4.4.12. Also check that current CEN header will // not exceed the length of the CEN array - if (headerSize > 0xFFFF || pos + headerSize > cen.length) { + if (headerSize > 0xFFFF || pos > cen.length - headerSize) { zerror("invalid CEN header (bad header size)"); } @@ -1376,7 +1373,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, // Check the uncompressed size is not negative if (size == ZIP64_MAGICVAL) { if ( blockSize >= Long.BYTES) { - if (get64(cen, off) < 0) { + if (get64S(cen, off) < 0) { zerror("Invalid zip64 extra block size value"); } off += Long.BYTES; @@ -1388,7 +1385,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, // Check the compressed size is not negative if (csize == ZIP64_MAGICVAL) { if (blockSize >= Long.BYTES) { - if (get64(cen, off) < 0) { + if (get64S(cen, off) < 0) { zerror("Invalid zip64 extra block compressed size value"); } off += Long.BYTES; @@ -1400,7 +1397,7 @@ private void checkZip64ExtraFieldValues(int off, int blockSize, long csize, // Check the LOC offset is not negative if (locoff == ZIP64_MAGICVAL) { if (blockSize >= Long.BYTES) { - if (get64(cen, off) < 0) { + if (get64S(cen, off) < 0) { zerror("Invalid zip64 extra block LOC OFFSET value"); } // Note: We do not need to adjust the following fields as @@ -1608,7 +1605,7 @@ private final int readAt(byte[] buf, int off, int len, long pos) private static class End { - int centot; // 4 bytes + long centot; // 4 bytes long cenlen; // 4 bytes long cenoff; // 4 bytes long endpos; // 4 bytes @@ -1641,10 +1638,7 @@ private End findEND() throws IOException { } // Now scan the block backwards for END header signature for (int i = buf.length - ENDHDR; i >= 0; i--) { - if (buf[i+0] == (byte)'P' && - buf[i+1] == (byte)'K' && - buf[i+2] == (byte)'\005' && - buf[i+3] == (byte)'\006') { + if (get32(buf, i) == ENDSIG) { // Found ENDSIG header byte[] endbuf = Arrays.copyOfRange(buf, i, i + ENDHDR); end.centot = ENDTOT(endbuf); @@ -1664,9 +1658,9 @@ private End findEND() throws IOException { if (cenpos < 0 || locpos < 0 || readFullyAt(sbuf, 0, sbuf.length, cenpos) != 4 || - GETSIG(sbuf) != CENSIG || + get32(sbuf, 0) != CENSIG || readFullyAt(sbuf, 0, sbuf.length, locpos) != 4 || - GETSIG(sbuf) != LOCSIG) { + get32(sbuf, 0) != LOCSIG) { continue; } } @@ -1681,13 +1675,13 @@ private End findEND() throws IOException { byte[] loc64 = new byte[ZIP64_LOCHDR]; if (end.endpos < ZIP64_LOCHDR || readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) - != loc64.length || GETSIG(loc64) != ZIP64_LOCSIG) { + != loc64.length || get32(loc64, 0) != ZIP64_LOCSIG) { return end; } long end64pos = ZIP64_LOCOFF(loc64); byte[] end64buf = new byte[ZIP64_ENDHDR]; if (readFullyAt(end64buf, 0, end64buf.length, end64pos) - != end64buf.length || GETSIG(end64buf) != ZIP64_ENDSIG) { + != end64buf.length || get32(end64buf, 0) != ZIP64_ENDSIG) { return end; } // end64 candidate found, @@ -1703,7 +1697,7 @@ private End findEND() throws IOException { // to use the end64 values end.cenlen = cenlen64; end.cenoff = cenoff64; - end.centot = (int)centot64; // assume total < 2g + end.centot = centot64; end.endpos = end64pos; } catch (IOException x) {} // no ZIP64 loc/end return end; @@ -1739,11 +1733,14 @@ private void initCEN(int knownTotal) throws IOException { if (end.cenlen > MAX_CEN_SIZE) { zerror("invalid END header (central directory size too large)"); } + if (end.centot < 0 || end.centot > end.cenlen / CENHDR) { + zerror("invalid END header (total entries count too large)"); + } cen = this.cen = new byte[(int)end.cenlen]; if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen) { zerror("read CEN tables failed"); } - this.total = end.centot; + this.total = Math.toIntExact(end.centot); } else { cen = this.cen; this.total = knownTotal; @@ -1768,18 +1765,18 @@ private void initCEN(int knownTotal) throws IOException { // Iterate through the entries in the central directory int idx = 0; // Index into the entries array int pos = 0; - int entryPos = CENHDR; - int limit = cen.length; manifestNum = 0; - while (entryPos <= limit) { + int limit = cen.length - CENHDR; + while (pos <= limit) { if (idx >= entriesLength) { // This will only happen if the ZIP file has an incorrect // ENDTOT field, which usually means it contains more than // 65535 entries. - initCEN(countCENHeaders(cen, limit)); + initCEN(countCENHeaders(cen)); return; } + int entryPos = pos + CENHDR; // Checks the entry and adds values to entries[idx ... idx+2] int nlen = checkAndAddEntry(pos, idx); idx += 3; @@ -1810,7 +1807,6 @@ private void initCEN(int knownTotal) throws IOException { } // skip to the start of the next entry pos = nextEntryPos(pos, entryPos, nlen); - entryPos = pos + CENHDR; } // Adjust the total entries @@ -2034,17 +2030,20 @@ private int getMetaVersion(int off, int len) { /** * Returns the number of CEN headers in a central directory. - * Will not throw, even if the ZIP file is corrupt. * * @param cen copy of the bytes in a ZIP file's central directory - * @param size number of bytes in central directory + * @throws ZipException if a CEN header exceeds the length of the CEN array */ - private static int countCENHeaders(byte[] cen, int size) { + private static int countCENHeaders(byte[] cen) throws ZipException { int count = 0; - for (int p = 0; - p + CENHDR <= size; - p += CENHDR + CENNAM(cen, p) + CENEXT(cen, p) + CENCOM(cen, p)) + for (int p = 0; p <= cen.length - CENHDR;) { + int headerSize = CENHDR + CENNAM(cen, p) + CENEXT(cen, p) + CENCOM(cen, p); + if (p > cen.length - headerSize) { + zerror("invalid CEN header (bad header size)"); + } + p += headerSize; count++; + } return count; } } diff --git a/src/java.base/share/classes/java/util/zip/ZipInputStream.java b/src/java.base/share/classes/java/util/zip/ZipInputStream.java index 5302cf75160..3a433cf5c6d 100644 --- a/src/java.base/share/classes/java/util/zip/ZipInputStream.java +++ b/src/java.base/share/classes/java/util/zip/ZipInputStream.java @@ -603,14 +603,14 @@ private void readEnd(ZipEntry e) throws IOException { long sig = get32(tmpbuf, 0); if (sig != EXTSIG) { // no EXTSIG present e.crc = sig; - e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC); - e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC); + e.csize = get64S(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC); + e.size = get64S(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC); ((PushbackInputStream)in).unread( tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC, ZIP64_EXTCRC); } else { e.crc = get32(tmpbuf, ZIP64_EXTCRC); - e.csize = get64(tmpbuf, ZIP64_EXTSIZ); - e.size = get64(tmpbuf, ZIP64_EXTLEN); + e.csize = get64S(tmpbuf, ZIP64_EXTSIZ); + e.size = get64S(tmpbuf, ZIP64_EXTLEN); } } else { readFully(tmpbuf, 0, EXTHDR); diff --git a/src/java.base/share/classes/java/util/zip/ZipUtils.java b/src/java.base/share/classes/java/util/zip/ZipUtils.java index ab37fc03a56..5b1d896f420 100644 --- a/src/java.base/share/classes/java/util/zip/ZipUtils.java +++ b/src/java.base/share/classes/java/util/zip/ZipUtils.java @@ -39,6 +39,7 @@ import jdk.internal.access.JavaNioAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.Unsafe; +import jdk.internal.util.Preconditions; class ZipUtils { @@ -170,7 +171,10 @@ static LocalDateTime javaEpochToLocalDateTime(long time) { * The bytes are assumed to be in Intel (little-endian) byte order. */ public static final int get16(byte[] b, int off) { - return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8); + Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER); + Preconditions.checkIndex(off + 1, b.length, Preconditions.AIOOBE_FORMATTER); + return Short.toUnsignedInt( + UNSAFE.getShortUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false)); } /** @@ -178,15 +182,20 @@ public static final int get16(byte[] b, int off) { * The bytes are assumed to be in Intel (little-endian) byte order. */ public static final long get32(byte[] b, int off) { - return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL; + Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER); + Preconditions.checkIndex(off + 3, b.length, Preconditions.AIOOBE_FORMATTER); + return Integer.toUnsignedLong( + UNSAFE.getIntUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false)); } /** * Fetches signed 64-bit value from byte array at specified offset. * The bytes are assumed to be in Intel (little-endian) byte order. */ - public static final long get64(byte[] b, int off) { - return get32(b, off) | (get32(b, off+4) << 32); + public static final long get64S(byte[] b, int off) { + Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER); + Preconditions.checkIndex(off + 7, b.length, Preconditions.AIOOBE_FORMATTER); + return UNSAFE.getLongUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false); } /** @@ -195,28 +204,9 @@ public static final long get64(byte[] b, int off) { * */ public static final int get32S(byte[] b, int off) { - return (get16(b, off) | (get16(b, off+2) << 16)); - } - - // fields access methods - static final int CH(byte[] b, int n) { - return b[n] & 0xff ; - } - - static final int SH(byte[] b, int n) { - return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8); - } - - static final long LG(byte[] b, int n) { - return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL; - } - - static final long LL(byte[] b, int n) { - return (LG(b, n)) | (LG(b, n + 4) << 32); - } - - static final long GETSIG(byte[] b) { - return LG(b, 0); + Preconditions.checkIndex(off, b.length, Preconditions.AIOOBE_FORMATTER); + Preconditions.checkIndex(off + 3, b.length, Preconditions.AIOOBE_FORMATTER); + return UNSAFE.getIntUnaligned(b, off + Unsafe.ARRAY_BYTE_BASE_OFFSET, false); } /* @@ -231,56 +221,56 @@ static final long GETSIG(byte[] b) { // local file (LOC) header fields - static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature - static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract - static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags - static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method - static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time - static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data - static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size - static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size - static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length - static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length + static final long LOCSIG(byte[] b) { return get32(b, 0); } // signature + static final int LOCVER(byte[] b) { return get16(b, 4); } // version needed to extract + static final int LOCFLG(byte[] b) { return get16(b, 6); } // general purpose bit flags + static final int LOCHOW(byte[] b) { return get16(b, 8); } // compression method + static final long LOCTIM(byte[] b) { return get32(b, 10);} // modification time + static final long LOCCRC(byte[] b) { return get32(b, 14);} // crc of uncompressed data + static final long LOCSIZ(byte[] b) { return get32(b, 18);} // compressed data size + static final long LOCLEN(byte[] b) { return get32(b, 22);} // uncompressed data size + static final int LOCNAM(byte[] b) { return get16(b, 26);} // filename length + static final int LOCEXT(byte[] b) { return get16(b, 28);} // extra field length // extra local (EXT) header fields - static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data - static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size - static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size + static final long EXTCRC(byte[] b) { return get32(b, 4);} // crc of uncompressed data + static final long EXTSIZ(byte[] b) { return get32(b, 8);} // compressed size + static final long EXTLEN(byte[] b) { return get32(b, 12);} // uncompressed size // end of central directory header (END) fields - static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk - static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries - static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size - static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset - static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of ZIP file comment - static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);} - - // zip64 end of central directory recoder fields - static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk - static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries - static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size - static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset - static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset + static final int ENDSUB(byte[] b) { return get16(b, 8); } // number of entries on this disk + static final int ENDTOT(byte[] b) { return get16(b, 10);} // total number of entries + static final long ENDSIZ(byte[] b) { return get32(b, 12);} // central directory size + static final long ENDOFF(byte[] b) { return get32(b, 16);} // central directory offset + static final int ENDCOM(byte[] b) { return get16(b, 20);} // size of ZIP file comment + static final int ENDCOM(byte[] b, int off) { return get16(b, off + 20);} + + // zip64 end of central directory record fields + static final long ZIP64_ENDTOD(byte[] b) { return get64S(b, 24);} // total number of entries on disk + static final long ZIP64_ENDTOT(byte[] b) { return get64S(b, 32);} // total number of entries + static final long ZIP64_ENDSIZ(byte[] b) { return get64S(b, 40);} // central directory size + static final long ZIP64_ENDOFF(byte[] b) { return get64S(b, 48);} // central directory offset + static final long ZIP64_LOCOFF(byte[] b) { return get64S(b, 8);} // zip64 end offset // central directory header (CEN) fields - static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); } - static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); } - static final int CENVEM_FA(byte[] b, int pos) { return CH(b, pos + 5); } // file attribute compatibility - static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); } - static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); } - static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);} - static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);} - static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);} - static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);} - static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);} - static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);} - static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);} - static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);} - static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);} - static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);} - static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);} - static final int CENATX_PERMS(byte[] b, int pos) { return SH(b, pos + 40);} // posix permission data - static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);} + static final long CENSIG(byte[] b, int pos) { return get32(b, pos + 0); } + static final int CENVEM(byte[] b, int pos) { return get16(b, pos + 4); } + static final int CENVEM_FA(byte[] b, int pos) { return Byte.toUnsignedInt(b[pos + 5]); } // file attribute compatibility + static final int CENVER(byte[] b, int pos) { return get16(b, pos + 6); } + static final int CENFLG(byte[] b, int pos) { return get16(b, pos + 8); } + static final int CENHOW(byte[] b, int pos) { return get16(b, pos + 10);} + static final long CENTIM(byte[] b, int pos) { return get32(b, pos + 12);} + static final long CENCRC(byte[] b, int pos) { return get32(b, pos + 16);} + static final long CENSIZ(byte[] b, int pos) { return get32(b, pos + 20);} + static final long CENLEN(byte[] b, int pos) { return get32(b, pos + 24);} + static final int CENNAM(byte[] b, int pos) { return get16(b, pos + 28);} + static final int CENEXT(byte[] b, int pos) { return get16(b, pos + 30);} + static final int CENCOM(byte[] b, int pos) { return get16(b, pos + 32);} + static final int CENDSK(byte[] b, int pos) { return get16(b, pos + 34);} + static final int CENATT(byte[] b, int pos) { return get16(b, pos + 36);} + static final long CENATX(byte[] b, int pos) { return get32(b, pos + 38);} + static final int CENATX_PERMS(byte[] b, int pos) { return get16(b, pos + 40);} // posix permission data + static final long CENOFF(byte[] b, int pos) { return get32(b, pos + 42);} // The END header is followed by a variable length comment of size < 64k. static final long END_MAXLEN = 0xFFFF + ENDHDR; @@ -293,16 +283,16 @@ static void loadLibrary() { jdk.internal.loader.BootLoader.loadLibrary("zip"); } - private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); - private static final long byteBufferArrayOffset = unsafe.objectFieldOffset(ByteBuffer.class, "hb"); - private static final long byteBufferOffsetOffset = unsafe.objectFieldOffset(ByteBuffer.class, "offset"); + private static final long byteBufferArrayOffset = UNSAFE.objectFieldOffset(ByteBuffer.class, "hb"); + private static final long byteBufferOffsetOffset = UNSAFE.objectFieldOffset(ByteBuffer.class, "offset"); static byte[] getBufferArray(ByteBuffer byteBuffer) { - return (byte[]) unsafe.getReference(byteBuffer, byteBufferArrayOffset); + return (byte[]) UNSAFE.getReference(byteBuffer, byteBufferArrayOffset); } static int getBufferOffset(ByteBuffer byteBuffer) { - return unsafe.getInt(byteBuffer, byteBufferOffsetOffset); + return UNSAFE.getInt(byteBuffer, byteBufferOffsetOffset); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java index d3258427937..6b77f6ff1ad 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java @@ -801,7 +801,12 @@ public TypeKind typeKind() { @Override public void writeTo(DirectCodeBuilder writer) { - writer.writeLocalVar(op, slot); + var op = this.op; + if (op.sizeIfFixed() == 1) { + writer.writeBytecode(op); + } else { + writer.writeLocalVar(op, slot); + } } @Override @@ -832,7 +837,12 @@ public TypeKind typeKind() { @Override public void writeTo(DirectCodeBuilder writer) { - writer.writeLocalVar(op, slot); + var op = this.op; + if (op.sizeIfFixed() == 1) { + writer.writeBytecode(op); + } else { + writer.writeLocalVar(op, slot); + } } @Override @@ -1061,7 +1071,7 @@ public boolean isInterface() { @Override public int count() { return op == Opcode.INVOKEINTERFACE - ? Util.parameterSlots(Util.methodTypeSymbol(methodEntry.nameAndType())) + 1 + ? Util.parameterSlots(Util.methodTypeSymbol(methodEntry.type())) + 1 : 0; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index 1d8d298857a..447e7e25c45 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -246,66 +246,69 @@ private void inflate() { this.contentHash = hash; charLen = rawLen; state = State.BYTE; + } else { + inflateNonAscii(singleBytes, hash); } - else { - char[] chararr = new char[rawLen]; - int chararr_count = singleBytes; - // Inflate prefix of bytes to characters - JLA.inflateBytesToChars(rawBytes, offset, chararr, 0, singleBytes); - - int px = offset + singleBytes; - int utfend = offset + rawLen; - while (px < utfend) { - int c = (int) rawBytes[px] & 0xff; - switch (c >> 4) { - case 0, 1, 2, 3, 4, 5, 6, 7: { - // 0xxx xxxx - px++; - chararr[chararr_count++] = (char) c; - hash = 31 * hash + c; - break; + } + + private void inflateNonAscii(int singleBytes, int hash) { + char[] chararr = new char[rawLen]; + int chararr_count = singleBytes; + // Inflate prefix of bytes to characters + JLA.inflateBytesToChars(rawBytes, offset, chararr, 0, singleBytes); + + int px = offset + singleBytes; + int utfend = offset + rawLen; + while (px < utfend) { + int c = (int) rawBytes[px] & 0xff; + switch (c >> 4) { + case 0, 1, 2, 3, 4, 5, 6, 7: { + // 0xxx xxxx + px++; + chararr[chararr_count++] = (char) c; + hash = 31 * hash + c; + break; + } + case 12, 13: { + // 110x xxxx 10xx xxxx + px += 2; + if (px > utfend) { + throw malformedInput(utfend); } - case 12, 13: { - // 110x xxxx 10xx xxxx - px += 2; - if (px > utfend) { - throw malformedInput(utfend); - } - int char2 = rawBytes[px - 1]; - if ((char2 & 0xC0) != 0x80) { - throw malformedInput(px); - } - char v = (char) (((c & 0x1F) << 6) | (char2 & 0x3F)); - chararr[chararr_count++] = v; - hash = 31 * hash + v; - break; + int char2 = rawBytes[px - 1]; + if ((char2 & 0xC0) != 0x80) { + throw malformedInput(px); } - case 14: { - // 1110 xxxx 10xx xxxx 10xx xxxx - px += 3; - if (px > utfend) { - throw malformedInput(utfend); - } - int char2 = rawBytes[px - 2]; - int char3 = rawBytes[px - 1]; - if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) { - throw malformedInput(px - 1); - } - char v = (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | (char3 & 0x3F)); - chararr[chararr_count++] = v; - hash = 31 * hash + v; - break; + char v = (char) (((c & 0x1F) << 6) | (char2 & 0x3F)); + chararr[chararr_count++] = v; + hash = 31 * hash + v; + break; + } + case 14: { + // 1110 xxxx 10xx xxxx 10xx xxxx + px += 3; + if (px > utfend) { + throw malformedInput(utfend); } - default: - // 10xx xxxx, 1111 xxxx - throw malformedInput(px); + int char2 = rawBytes[px - 2]; + int char3 = rawBytes[px - 1]; + if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) { + throw malformedInput(px - 1); + } + char v = (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | (char3 & 0x3F)); + chararr[chararr_count++] = v; + hash = 31 * hash + v; + break; } + default: + // 10xx xxxx, 1111 xxxx + throw malformedInput(px); } - this.contentHash = hash; - charLen = chararr_count; - this.chars = chararr; - state = State.CHAR; } + this.contentHash = hash; + charLen = chararr_count; + this.chars = chararr; + state = State.CHAR; } private ConstantPoolException malformedInput(int px) { @@ -461,14 +464,13 @@ public boolean equalsString(String s) { @Override void writeTo(BufWriterImpl pool) { - pool.writeU1(TAG_UTF8); if (rawBytes != null) { - pool.writeU2(rawLen); + pool.writeU1U2(TAG_UTF8, rawLen); pool.writeBytes(rawBytes, offset, rawLen); } else { // state == STRING and no raw bytes - pool.writeUTF(stringValue); + pool.writeUtfEntry(stringValue); } } @@ -502,8 +504,7 @@ public T ref1() { } void writeTo(BufWriterImpl pool) { - pool.writeU1(tag()); - pool.writeU2(ref1.index()); + pool.writeU1U2(tag(), ref1.index()); } @Override @@ -532,9 +533,7 @@ public U ref2() { } void writeTo(BufWriterImpl pool) { - pool.writeU1(tag()); - pool.writeU2(ref1.index()); - pool.writeU2(ref2.index()); + pool.writeU1U2U2(tag(), ref1.index(), ref2.index()); } @Override @@ -864,9 +863,7 @@ public NameAndTypeEntryImpl nameAndType() { } void writeTo(BufWriterImpl pool) { - pool.writeU1(tag()); - pool.writeU2(bsmIndex); - pool.writeU2(nameAndType.index()); + pool.writeU1U2U2(tag(), bsmIndex, nameAndType.index()); } @Override @@ -984,9 +981,7 @@ public DirectMethodHandleDesc asSymbol() { @Override void writeTo(BufWriterImpl pool) { - pool.writeU1(TAG_METHOD_HANDLE); - pool.writeU1(refKind); - pool.writeU2(reference.index()); + pool.writeU1U1U2(TAG_METHOD_HANDLE, refKind, reference.index()); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java index e33012ef183..02f6167d436 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AnnotationReader.java @@ -315,8 +315,7 @@ public static void writeTypeAnnotation(BufWriterImpl buf, TypeAnnotation ta) { case TypeAnnotation.TypeParameterTarget tpt -> buf.writeU1(tpt.typeParameterIndex()); case TypeAnnotation.SupertypeTarget st -> buf.writeU2(st.supertypeIndex()); case TypeAnnotation.TypeParameterBoundTarget tpbt -> { - buf.writeU1(tpbt.typeParameterIndex()); - buf.writeU1(tpbt.boundIndex()); + buf.writeU1U1(tpbt.typeParameterIndex(), tpbt.boundIndex()); } case TypeAnnotation.EmptyTarget _ -> { // nothing to write @@ -327,9 +326,7 @@ public static void writeTypeAnnotation(BufWriterImpl buf, TypeAnnotation ta) { buf.writeU2(lvt.table().size()); for (var e : lvt.table()) { int startPc = labelToBci(lr, e.startLabel(), ta); - buf.writeU2(startPc); - buf.writeU2(labelToBci(lr, e.endLabel(), ta) - startPc); - buf.writeU2(e.index()); + buf.writeU2U2U2(startPc, labelToBci(lr, e.endLabel(), ta) - startPc, e.index()); } } case TypeAnnotation.CatchTarget ct -> buf.writeU2(ct.exceptionTableIndex()); @@ -343,8 +340,7 @@ public static void writeTypeAnnotation(BufWriterImpl buf, TypeAnnotation ta) { // target_path buf.writeU1(ta.targetPath().size()); for (TypeAnnotation.TypePathComponent component : ta.targetPath()) { - buf.writeU1(component.typePathKind().tag()); - buf.writeU1(component.typeArgumentIndex()); + buf.writeU1U1(component.typePathKind().tag(), component.typeArgumentIndex()); } // annotation data diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java index fb9ecc98902..6060170a8d1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AttributeHolder.java @@ -24,14 +24,15 @@ */ package jdk.internal.classfile.impl; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; import java.lang.classfile.Attribute; import java.lang.classfile.AttributeMapper; public class AttributeHolder { - private final List> attributes = new ArrayList<>(); + private static final Attribute[] EMPTY_ATTRIBUTE_ARRAY = {}; + private int attributesCount = 0; + private Attribute[] attributes = EMPTY_ATTRIBUTE_ARRAY; public > void withAttribute(Attribute a) { if (a == null) @@ -39,36 +40,54 @@ public > void withAttribute(Attribute a) { @SuppressWarnings("unchecked") AttributeMapper am = (AttributeMapper) a.attributeMapper(); - if (!am.allowMultiple() && isPresent(am)) { - remove(am); + int attributesCount = this.attributesCount; + var attributes = this.attributes; + if (!am.allowMultiple()) { + // remove if + for (int i = attributesCount - 1; i >= 0; i--) { + if (attributes[i].attributeMapper() == am) { + attributesCount--; + System.arraycopy(attributes, i + 1, attributes, i, attributesCount - i); + } + } } - attributes.add(a); + + // add attribute + if (attributesCount >= attributes.length) { + int newCapacity = attributesCount + 4; + this.attributes = attributes = Arrays.copyOf(attributes, newCapacity); + } + attributes[attributesCount] = a; + this.attributesCount = attributesCount + 1; } public int size() { - return attributes.size(); + return attributesCount; } public void writeTo(BufWriterImpl buf) { - Util.writeAttributes(buf, attributes); + int attributesCount = this.attributesCount; + buf.writeU2(attributesCount); + for (int i = 0; i < attributesCount; i++) { + Util.writeAttribute(buf, attributes[i]); + } } @SuppressWarnings("unchecked") > A get(AttributeMapper am) { - for (Attribute a : attributes) + for (int i = 0; i < attributesCount; i++) { + Attribute a = attributes[i]; if (a.attributeMapper() == am) - return (A)a; + return (A) a; + } return null; } boolean isPresent(AttributeMapper am) { - for (Attribute a : attributes) - if (a.attributeMapper() == am) + for (int i = 0; i < attributesCount; i++) { + if (attributes[i].attributeMapper() == am) return true; + } return false; } - - private void remove(AttributeMapper am) { - attributes.removeIf(a -> a.attributeMapper() == am); - } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java index 4cc6c205fe4..cf5b98dafb1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java @@ -38,6 +38,10 @@ import jdk.internal.access.SharedSecrets; import jdk.internal.vm.annotation.ForceInline; +import static java.lang.classfile.constantpool.PoolEntry.TAG_UTF8; +import static jdk.internal.util.ModifiedUtf.putChar; +import static jdk.internal.util.ModifiedUtf.utfLen; + public final class BufWriterImpl implements BufWriter { private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); @@ -111,6 +115,83 @@ public void writeU2(int x) { this.offset = offset + 2; } + @ForceInline + public void writeU1U1(int x1, int x2) { + reserveSpace(2); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) x1; + elems[offset + 1] = (byte) x2; + this.offset = offset + 2; + } + + public void writeU1U2(int u1, int u2) { + reserveSpace(3); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) u1; + elems[offset + 1] = (byte) (u2 >> 8); + elems[offset + 2] = (byte) u2; + this.offset = offset + 3; + } + + public void writeU1U1U1(int x1, int x2, int x3) { + reserveSpace(3); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) x1; + elems[offset + 1] = (byte) x2; + elems[offset + 2] = (byte) x3; + this.offset = offset + 3; + } + + public void writeU1U1U2(int x1, int x2, int x3) { + reserveSpace(4); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) x1; + elems[offset + 1] = (byte) x2; + elems[offset + 2] = (byte) (x3 >> 8); + elems[offset + 3] = (byte) x3; + this.offset = offset + 4; + } + + public void writeU1U2U2(int x1, int x2, int x3) { + reserveSpace(5); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) x1; + elems[offset + 1] = (byte) (x2 >> 8); + elems[offset + 2] = (byte) x2; + elems[offset + 3] = (byte) (x3 >> 8); + elems[offset + 4] = (byte) x3; + this.offset = offset + 5; + } + + public void writeU2U2(int x1, int x2) { + reserveSpace(4); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) (x1 >> 8); + elems[offset + 1] = (byte) x1; + elems[offset + 2] = (byte) (x2 >> 8); + elems[offset + 3] = (byte) x2; + this.offset = offset + 4; + } + + public void writeU2U2U2(int x1, int x2, int x3) { + reserveSpace(6); + byte[] elems = this.elems; + int offset = this.offset; + elems[offset ] = (byte) (x1 >> 8); + elems[offset + 1] = (byte) x1; + elems[offset + 2] = (byte) (x2 >> 8); + elems[offset + 3] = (byte) x2; + elems[offset + 4] = (byte) (x3 >> 8); + elems[offset + 5] = (byte) x3; + this.offset = offset + 6; + } + @Override public void writeInt(int x) { reserveSpace(4); @@ -159,46 +240,28 @@ public void writeBytes(BufWriterImpl other) { } @SuppressWarnings("deprecation") - void writeUTF(String str) { + void writeUtfEntry(String str) { int strlen = str.length(); int countNonZeroAscii = JLA.countNonZeroAscii(str); - int utflen = strlen; - if (countNonZeroAscii != strlen) { - for (int i = countNonZeroAscii; i < strlen; i++) { - int c = str.charAt(i); - if (c >= 0x80 || c == 0) - utflen += (c >= 0x800) ? 2 : 1; - } - } + int utflen = utfLen(str, countNonZeroAscii); if (utflen > 65535) { throw new IllegalArgumentException("string too long"); } - reserveSpace(utflen + 2); + reserveSpace(utflen + 3); int offset = this.offset; byte[] elems = this.elems; - elems[offset ] = (byte) (utflen >> 8); - elems[offset + 1] = (byte) utflen; - offset += 2; + elems[offset ] = (byte) TAG_UTF8; + elems[offset + 1] = (byte) (utflen >> 8); + elems[offset + 2] = (byte) utflen; + offset += 3; str.getBytes(0, countNonZeroAscii, elems, offset); offset += countNonZeroAscii; - for (int i = countNonZeroAscii; i < strlen; ++i) { - char c = str.charAt(i); - if (c >= '\001' && c <= '\177') { - elems[offset++] = (byte) c; - } else if (c > '\u07FF') { - elems[offset ] = (byte) (0xE0 | c >> 12 & 0xF); - elems[offset + 1] = (byte) (0x80 | c >> 6 & 0x3F); - elems[offset + 2] = (byte) (0x80 | c & 0x3F); - offset += 3; - } else { - elems[offset ] = (byte) (0xC0 | c >> 6 & 0x1F); - elems[offset + 1] = (byte) (0x80 | c & 0x3F); - offset += 2; - } + for (int i = countNonZeroAscii; i < strlen; i++) { + offset = putChar(elems, offset, str.charAt(i)); } this.offset = offset; @@ -285,13 +348,21 @@ public void copyTo(byte[] array, int bufferOffset) { // writeIndex methods ensure that any CP info written // is relative to the correct constant pool - @ForceInline - @Override - public void writeIndex(PoolEntry entry) { + public int cpIndex(PoolEntry entry) { int idx = AbstractPoolEntry.maybeClone(constantPool, entry).index(); if (idx < 1 || idx > Character.MAX_VALUE) throw invalidIndex(idx, entry); - writeU2(idx); + return idx; + } + + @ForceInline + @Override + public void writeIndex(PoolEntry entry) { + writeU2(cpIndex(entry)); + } + + public void writeIndex(int bytecode, PoolEntry entry) { + writeU1U2(bytecode, cpIndex(entry)); } static IllegalArgumentException invalidIndex(int idx, PoolEntry entry) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java index 6994a62c0ab..bca80c1ed4b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BytecodeHelpers.java @@ -88,9 +88,9 @@ public static Opcode aload(int slot) { case 2 -> Opcode.ALOAD_2; case 3 -> Opcode.ALOAD_3; default -> { - if ((slot & 0xFF) == slot) + if ((slot & ~0xFF) == 0) yield Opcode.ALOAD; - if ((slot & 0xFFFF) == slot) + if ((slot & ~0xFFFF) == 0) yield Opcode.ALOAD_W; throw slotOutOfBounds(slot); } @@ -104,9 +104,9 @@ public static Opcode fload(int slot) { case 2 -> Opcode.FLOAD_2; case 3 -> Opcode.FLOAD_3; default -> { - if ((slot & 0xFF) == slot) + if ((slot & ~0xFF) == 0) yield Opcode.FLOAD; - if ((slot & 0xFFFF) == slot) + if ((slot & ~0xFFFF) == 0) yield Opcode.FLOAD_W; throw slotOutOfBounds(slot); } @@ -120,9 +120,9 @@ public static Opcode dload(int slot) { case 2 -> Opcode.DLOAD_2; case 3 -> Opcode.DLOAD_3; default -> { - if ((slot & 0xFF) == slot) + if ((slot & ~0xFF) == 0) yield Opcode.DLOAD; - if ((slot & 0xFFFF) == slot) + if ((slot & ~0xFFFF) == 0) yield Opcode.DLOAD_W; throw slotOutOfBounds(slot); } @@ -136,9 +136,9 @@ public static Opcode lload(int slot) { case 2 -> Opcode.LLOAD_2; case 3 -> Opcode.LLOAD_3; default -> { - if ((slot & 0xFF) == slot) + if ((slot & ~0xFF) == 0) yield Opcode.LLOAD; - if ((slot & 0xFFFF) == slot) + if ((slot & ~0xFFFF) == 0) yield Opcode.LLOAD_W; throw slotOutOfBounds(slot); } @@ -152,9 +152,9 @@ public static Opcode iload(int slot) { case 2 -> Opcode.ILOAD_2; case 3 -> Opcode.ILOAD_3; default -> { - if ((slot & 0xFF) == slot) + if ((slot & ~0xFF) == 0) yield Opcode.ILOAD; - if ((slot & 0xFFFF) == slot) + if ((slot & ~0xFFFF) == 0) yield Opcode.ILOAD_W; throw slotOutOfBounds(slot); } @@ -180,9 +180,9 @@ public static Opcode astore(int slot) { case 2 -> Opcode.ASTORE_2; case 3 -> Opcode.ASTORE_3; default -> { - if ((slot & 0xFF) == slot) + if ((slot & ~0xFF) == 0) yield Opcode.ASTORE; - if ((slot & 0xFFFF) == slot) + if ((slot & ~0xFFFF) == 0) yield Opcode.ASTORE_W; throw slotOutOfBounds(slot); } @@ -196,9 +196,9 @@ public static Opcode fstore(int slot) { case 2 -> Opcode.FSTORE_2; case 3 -> Opcode.FSTORE_3; default -> { - if ((slot & 0xFF) == slot) + if ((slot & ~0xFF) == 0) yield Opcode.FSTORE; - if ((slot & 0xFFFF) == slot) + if ((slot & ~0xFFFF) == 0) yield Opcode.FSTORE_W; throw slotOutOfBounds(slot); } @@ -212,9 +212,9 @@ public static Opcode dstore(int slot) { case 2 -> Opcode.DSTORE_2; case 3 -> Opcode.DSTORE_3; default -> { - if ((slot & 0xFF) == slot) + if ((slot & ~0xFF) == 0) yield Opcode.DSTORE; - if ((slot & 0xFFFF) == slot) + if ((slot & ~0xFFFF) == 0) yield Opcode.DSTORE_W; throw slotOutOfBounds(slot); } @@ -228,9 +228,9 @@ public static Opcode lstore(int slot) { case 2 -> Opcode.LSTORE_2; case 3 -> Opcode.LSTORE_3; default -> { - if ((slot & 0xFF) == slot) + if ((slot & ~0xFF) == 0) yield Opcode.LSTORE; - if ((slot & 0xFFFF) == slot) + if ((slot & ~0xFFFF) == 0) yield Opcode.LSTORE_W; throw slotOutOfBounds(slot); } @@ -244,9 +244,9 @@ public static Opcode istore(int slot) { case 2 -> Opcode.ISTORE_2; case 3 -> Opcode.ISTORE_3; default -> { - if ((slot & 0xFF) == slot) + if ((slot & ~0xFF) == 0) yield Opcode.ISTORE; - if ((slot & 0xFFFF) == slot) + if ((slot & ~0xFFFF) == 0) yield Opcode.ISTORE_W; throw slotOutOfBounds(slot); } @@ -264,6 +264,11 @@ public static Opcode returnOpcode(TypeKind tk) { }; } + public static int returnBytecode(TypeKind tk) { + int kind = Math.max(0, tk.ordinal() - 4); // BYTE, SHORT, CHAR, BOOLEAN becomes INT + return IRETURN + kind; + } + public static Opcode arrayLoadOpcode(TypeKind tk) { return switch (tk) { case BYTE, BOOLEAN -> Opcode.BALOAD; @@ -278,6 +283,20 @@ public static Opcode arrayLoadOpcode(TypeKind tk) { }; } + public static int arrayLoadBytecode(TypeKind tk) { + return switch (tk) { + case BYTE, BOOLEAN -> BALOAD; + case SHORT -> SALOAD; + case INT -> IALOAD; + case FLOAT -> FALOAD; + case LONG -> LALOAD; + case DOUBLE -> DALOAD; + case REFERENCE -> AALOAD; + case CHAR -> CALOAD; + case VOID -> throw new IllegalArgumentException("void not an allowable array type"); + }; + } + public static Opcode arrayStoreOpcode(TypeKind tk) { return switch (tk) { case BYTE, BOOLEAN -> Opcode.BASTORE; @@ -292,6 +311,20 @@ public static Opcode arrayStoreOpcode(TypeKind tk) { }; } + public static int arrayStoreBytecode(TypeKind tk) { + return switch (tk) { + case BYTE, BOOLEAN -> BASTORE; + case SHORT -> SASTORE; + case INT -> IASTORE; + case FLOAT -> FASTORE; + case LONG -> LASTORE; + case DOUBLE -> DASTORE; + case REFERENCE -> AASTORE; + case CHAR -> CASTORE; + case VOID -> throw new IllegalArgumentException("void not an allowable array type"); + }; + } + public static Opcode reverseBranchOpcode(Opcode op) { return switch (op) { case IFEQ -> Opcode.IFNE; @@ -314,6 +347,29 @@ public static Opcode reverseBranchOpcode(Opcode op) { }; } + public static int reverseBranchOpcode(int bytecode) { + return switch (bytecode) { + case IFEQ -> IFNE; + case IFNE -> IFEQ; + case IFLT -> IFGE; + case IFGE -> IFLT; + case IFGT -> IFLE; + case IFLE -> IFGT; + case IF_ICMPEQ -> IF_ICMPNE; + case IF_ICMPNE -> IF_ICMPEQ; + case IF_ICMPLT -> IF_ICMPGE; + case IF_ICMPGE -> IF_ICMPLT; + case IF_ICMPGT -> IF_ICMPLE; + case IF_ICMPLE -> IF_ICMPGT; + case IF_ACMPEQ -> IF_ACMPNE; + case IF_ACMPNE -> IF_ACMPEQ; + case IFNULL -> IFNONNULL; + case IFNONNULL -> IFNULL; + default -> throw new IllegalArgumentException( + String.format("Wrong opcode kind specified; found %d, expected %s", bytecode, Opcode.Kind.BRANCH)); + }; + } + public static Opcode convertOpcode(TypeKind from, TypeKind to) { return switch (from) { case INT -> @@ -377,20 +433,20 @@ public static TypeKind convertToType(Opcode opcode) { public static void validateSlot(Opcode opcode, int slot, boolean load) { int size = opcode.sizeIfFixed(); if (size == 1 && slot == (load ? intrinsicLoadSlot(opcode) : intrinsicStoreSlot(opcode)) || - size == 2 && slot == (slot & 0xFF) || - size == 4 && slot == (slot & 0xFFFF)) + size == 2 && (slot & ~0xFF) == 0 || + size == 4 && (slot & ~0xFFFF) == 0) return; throw slotOutOfBounds(opcode, slot); } public static void validateSlot(int slot) { - if ((slot & 0xFFFF) != slot) + if ((slot & ~0xFFFF) != 0) throw slotOutOfBounds(slot); } public static boolean validateAndIsWideIinc(int slot, int val) { var ret = false; - if ((slot & 0xFF) != slot) { + if ((slot & ~0xFF) != 0) { validateSlot(slot); ret = true; } @@ -404,8 +460,8 @@ public static boolean validateAndIsWideIinc(int slot, int val) { } public static void validateRet(Opcode opcode, int slot) { - if (opcode == Opcode.RET && slot == (slot & 0xFF) || - opcode == Opcode.RET_W && slot == (slot & 0xFFFF)) + if (opcode == Opcode.RET && (slot & ~0xFF) == 0 || + opcode == Opcode.RET_W && (slot & ~0xFFFF) == 0) return; Objects.requireNonNull(opcode); throw slotOutOfBounds(opcode, slot); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java index 2ef39504e9b..0f183ad427f 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java @@ -353,7 +353,11 @@ private static boolean checkTag(int tag, Class cls) { static T checkType(PoolEntry e, int index, Class cls) { if (cls.isInstance(e)) return cls.cast(e); - throw new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + index); + throw checkTypeError(index, cls); + } + + private static ConstantPoolException checkTypeError(int index, Class cls) { + return new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + index); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java index 5f02ae708ea..b599f2b61aa 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * 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 +31,7 @@ import java.lang.constant.MethodTypeDesc; import java.lang.reflect.AccessFlag; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -54,9 +56,13 @@ public final class DirectClassBuilder /** The value of default class access flags */ static final int DEFAULT_CLASS_FLAGS = ClassFile.ACC_PUBLIC; + static final Util.Writable[] EMPTY_WRITABLE_ARRAY = {}; + static final ClassEntry[] EMPTY_CLASS_ENTRY_ARRAY = {}; final ClassEntry thisClassEntry; - private final List fields = new ArrayList<>(); - private final List methods = new ArrayList<>(); + private Util.Writable[] fields = EMPTY_WRITABLE_ARRAY; + private Util.Writable[] methods = EMPTY_WRITABLE_ARRAY; + private int fieldsCount = 0; + private int methodsCount = 0; private ClassEntry superclassEntry; private List interfaceEntries; private int majorVersion; @@ -137,12 +143,20 @@ public ClassBuilder transformMethod(MethodModel method, MethodTransform transfor // internal / for use by elements ClassBuilder withField(Util.Writable field) { - fields.add(field); + if (fieldsCount >= fields.length) { + int newCapacity = fieldsCount + 8; + this.fields = Arrays.copyOf(fields, newCapacity); + } + fields[fieldsCount++] = field; return this; } ClassBuilder withMethod(Util.Writable method) { - methods.add(method); + if (methodsCount >= methods.length) { + int newCapacity = methodsCount + 8; + this.methods = Arrays.copyOf(methods, newCapacity); + } + methods[methodsCount++] = method; return this; } @@ -184,9 +198,7 @@ public byte[] build() { else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisClassEntry.asInternalName())) superclass = constantPool.classEntry(ConstantDescs.CD_Object); int interfaceEntriesSize = interfaceEntries.size(); - List ies = new ArrayList<>(interfaceEntriesSize); - for (int i = 0; i < interfaceEntriesSize; i++) - ies.add(AbstractPoolEntry.maybeClone(constantPool, interfaceEntries.get(i))); + ClassEntry[] ies = interfaceEntriesSize == 0 ? EMPTY_CLASS_ENTRY_ARRAY : buildInterfaceEnties(interfaceEntriesSize); // We maintain two writers, and then we join them at the end int size = sizeHint == 0 ? 256 : sizeHint; @@ -195,8 +207,8 @@ else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisC // The tail consists of fields and methods, and attributes // This should trigger all the CP/BSM mutation - Util.writeList(tail, fields); - Util.writeList(tail, methods); + Util.writeList(tail, fields, fieldsCount); + Util.writeList(tail, methods, methodsCount); int attributesOffset = tail.size(); attributes.writeTo(tail); @@ -211,12 +223,21 @@ else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisC | ((minorVersion & 0xFFFFL) << 16) | (majorVersion & 0xFFFFL)); constantPool.writeTo(head); - head.writeU2(flags); - head.writeIndex(thisClassEntry); + head.writeU2U2(flags, head.cpIndex(thisClassEntry)); head.writeIndexOrZero(superclass); - Util.writeListIndices(head, ies); + head.writeU2(interfaceEntriesSize); + for (int i = 0; i < interfaceEntriesSize; i++) { + head.writeIndex(ies[i]); + } // Join head and tail into an exact-size buffer return BufWriterImpl.join(head, tail); } + + private ClassEntry[] buildInterfaceEnties(int interfaceEntriesSize) { + var ies = new ClassEntry[interfaceEntriesSize]; + for (int i = 0; i < interfaceEntriesSize; i++) + ies[i] = AbstractPoolEntry.maybeClone(constantPool, interfaceEntries.get(i)); + return ies; + } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index ee312a97dad..72c37ade9ac 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -25,6 +25,8 @@ */ package jdk.internal.classfile.impl; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; import java.lang.classfile.Attribute; import java.lang.classfile.Attributes; import java.lang.classfile.ClassFile; @@ -52,8 +54,8 @@ import java.lang.classfile.instruction.LocalVariable; import java.lang.classfile.instruction.LocalVariableType; import java.lang.classfile.instruction.SwitchCase; -import java.lang.constant.ConstantDesc; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; @@ -62,18 +64,27 @@ import java.util.function.Consumer; import java.util.function.Function; -import static java.lang.classfile.Opcode.*; - import static jdk.internal.classfile.impl.BytecodeHelpers.*; +import static jdk.internal.classfile.impl.RawBytecodeHelper.*; public final class DirectCodeBuilder extends AbstractDirectBuilder implements TerminalCodeBuilder { - private final List characterRanges = new ArrayList<>(); + private static final CharacterRange[] EMPTY_CHARACTER_RANGE = {}; + private static final DeferredLabel[] EMPTY_LABEL_ARRAY = {}; + private static final LocalVariable[] EMPTY_LOCAL_VARIABLE_ARRAY = {}; + private static final LocalVariableType[] EMPTY_LOCAL_VARIABLE_TYPE_ARRAY = {}; + private static final AbstractPseudoInstruction.ExceptionCatchImpl[] EMPTY_HANDLER_ARRAY = {}; + private static final DeferredLabel[] EMPTY_DEFERRED_LABEL_ARRAY = {}; + final List handlers = new ArrayList<>(); - private final List localVariables = new ArrayList<>(); - private final List localVariableTypes = new ArrayList<>(); - private final boolean transformFwdJumps, transformBackJumps; + private CharacterRange[] characterRanges = EMPTY_CHARACTER_RANGE; + private LocalVariable[] localVariables = EMPTY_LOCAL_VARIABLE_ARRAY; + private LocalVariableType[] localVariableTypes = EMPTY_LOCAL_VARIABLE_TYPE_ARRAY; + private int characterRangesCount = 0; + private int localVariablesCount = 0; + private int localVariableTypesCount = 0; + private final boolean transformDeferredJumps, transformKnownJumps; private final Label startLabel, endLabel; final MethodInfo methodInfo; final BufWriterImpl bytecodesBufWriter; @@ -83,7 +94,8 @@ public final class DirectCodeBuilder private DedupLineNumberTableAttribute lineNumberWriter; private int topLocal; - List deferredLabels; + private DeferredLabel[] deferredLabels = EMPTY_DEFERRED_LABEL_ARRAY; + private int deferredLabelsCount = 0; /* Locals management lazily computed maxLocal = -1 @@ -117,12 +129,12 @@ private DirectCodeBuilder(MethodInfo methodInfo, SplitConstantPool constantPool, ClassFileImpl context, CodeModel original, - boolean transformFwdJumps) { + boolean transformDeferredJumps) { super(constantPool, context); setOriginal(original); this.methodInfo = methodInfo; - this.transformFwdJumps = transformFwdJumps; - this.transformBackJumps = context.fixShortJumps(); + this.transformDeferredJumps = transformDeferredJumps; + this.transformKnownJumps = context.fixShortJumps(); bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength()) : new BufWriterImpl(constantPool, context); this.startLabel = new LabelImpl(this, 0); @@ -208,9 +220,7 @@ private void writeExceptionHandlers(BufWriterImpl buf, int pos) { throw new IllegalArgumentException("Unbound label in exception handler"); } } else { - buf.writeU2(startPc); - buf.writeU2(endPc); - buf.writeU2(handlerPc); + buf.writeU2U2U2(startPc, endPc, handlerPc); buf.writeIndexOrZero(h.catchTypeEntry()); handlersSize++; } @@ -227,15 +237,16 @@ private void buildContent() { processDeferredLabels(); if (context.passDebugElements()) { - if (!characterRanges.isEmpty()) { + if (characterRangesCount > 0) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.characterRangeTable()) { @Override public void writeBody(BufWriterImpl b) { int pos = b.size(); - int crSize = characterRanges.size(); + int crSize = characterRangesCount; b.writeU2(crSize); - for (CharacterRange cr : characterRanges) { + for (int i = 0; i < characterRangesCount; i++) { + CharacterRange cr = characterRanges[i]; var start = labelToBci(cr.startScope()); var end = labelToBci(cr.endScope()); if (start == -1 || end == -1) { @@ -245,28 +256,28 @@ public void writeBody(BufWriterImpl b) { throw new IllegalArgumentException("Unbound label in character range"); } } else { - b.writeU2(start); - b.writeU2(end - 1); + b.writeU2U2(start, end - 1); b.writeInt(cr.characterRangeStart()); b.writeInt(cr.characterRangeEnd()); b.writeU2(cr.flags()); } } - if (crSize < characterRanges.size()) + if (crSize < characterRangesCount) b.patchU2(pos, crSize); } }; attributes.withAttribute(a); } - if (!localVariables.isEmpty()) { + if (localVariablesCount > 0) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTable()) { @Override public void writeBody(BufWriterImpl b) { int pos = b.size(); - int lvSize = localVariables.size(); + int lvSize = localVariablesCount; b.writeU2(lvSize); - for (LocalVariable l : localVariables) { + for (int i = 0; i < localVariablesCount; i++) { + LocalVariable l = localVariables[i]; if (!Util.writeLocalVariable(b, l)) { if (context.dropDeadLabels()) { lvSize--; @@ -275,21 +286,22 @@ public void writeBody(BufWriterImpl b) { } } } - if (lvSize < localVariables.size()) + if (lvSize < localVariablesCount) b.patchU2(pos, lvSize); } }; attributes.withAttribute(a); } - if (!localVariableTypes.isEmpty()) { + if (localVariableTypesCount > 0) { Attribute a = new UnboundAttribute.AdHocAttribute<>(Attributes.localVariableTypeTable()) { @Override public void writeBody(BufWriterImpl b) { int pos = b.size(); - int lvtSize = localVariableTypes.size(); - b.writeU2(localVariableTypes.size()); - for (LocalVariableType l : localVariableTypes) { + int lvtSize = localVariableTypesCount; + b.writeU2(lvtSize); + for (int i = 0; i < localVariableTypesCount; i++) { + LocalVariableType l = localVariableTypes[i]; if (!Util.writeLocalVariable(b, l)) { if (context.dropDeadLabels()) { lvtSize--; @@ -298,7 +310,7 @@ public void writeBody(BufWriterImpl b) { } } } - if (lvtSize < localVariableTypes.size()) + if (lvtSize < localVariableTypesCount) b.patchU2(pos, lvtSize); } }; @@ -315,22 +327,20 @@ public void writeBody(BufWriterImpl b) { private void writeCounters(boolean codeMatch, BufWriterImpl buf) { if (codeMatch) { var originalAttribute = (CodeImpl) original; - buf.writeU2(originalAttribute.maxStack()); - buf.writeU2(originalAttribute.maxLocals()); + buf.writeU2U2(originalAttribute.maxStack(), originalAttribute.maxLocals()); } else { StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); - buf.writeU2(cntr.maxStack()); - buf.writeU2(cntr.maxLocals()); + buf.writeU2U2(cntr.maxStack(), cntr.maxLocals()); } } private void generateStackMaps(BufWriterImpl buf) throws IllegalArgumentException { //new instance of generator immediately calculates maxStack, maxLocals, all frames, // patches dead bytecode blocks and removes them from exception table - StackMapGenerator gen = StackMapGenerator.of(DirectCodeBuilder.this, buf); - attributes.withAttribute(gen.stackMapTableAttribute()); - buf.writeU2(gen.maxStack()); - buf.writeU2(gen.maxLocals()); + var dcb = DirectCodeBuilder.this; + StackMapGenerator gen = StackMapGenerator.of(dcb, buf); + dcb.attributes.withAttribute(gen.stackMapTableAttribute()); + buf.writeU2U2(gen.maxStack(), gen.maxLocals()); } private void tryGenerateStackMaps(boolean codeMatch, BufWriterImpl buf) { @@ -352,20 +362,22 @@ private void tryGenerateStackMaps(boolean codeMatch, BufWriterImpl buf) { @Override public void writeBody(BufWriterImpl buf) { - buf.setLabelContext(DirectCodeBuilder.this); + DirectCodeBuilder dcb = DirectCodeBuilder.this; + buf.setLabelContext(dcb); int codeLength = curPc(); if (codeLength == 0 || codeLength >= 65536) { throw new IllegalArgumentException(String.format( "Code length %d is outside the allowed range in %s%s", codeLength, - methodInfo.methodName().stringValue(), - methodInfo.methodTypeSymbol().displayDescriptor())); + dcb.methodInfo.methodName().stringValue(), + dcb.methodInfo.methodTypeSymbol().displayDescriptor())); } - if (codeAndExceptionsMatch(codeLength)) { + var context = dcb.context; + if (dcb.original != null && codeAndExceptionsMatch(codeLength)) { if (context.stackMapsWhenRequired()) { - attributes.withAttribute(original.findAttribute(Attributes.stackMapTable()).orElse(null)); + dcb.attributes.withAttribute(dcb.original.findAttribute(Attributes.stackMapTable()).orElse(null)); writeCounters(true, buf); } else if (context.generateStackMaps()) { generateStackMaps(buf); @@ -383,9 +395,9 @@ public void writeBody(BufWriterImpl buf) { } buf.writeInt(codeLength); - buf.writeBytes(bytecodesBufWriter); - writeExceptionHandlers(buf); - attributes.writeTo(buf); + buf.writeBytes(dcb.bytecodesBufWriter); + dcb.writeExceptionHandlers(buf); + dcb.attributes.writeTo(buf); buf.setLabelContext(null); } }; @@ -405,8 +417,7 @@ public DedupLineNumberTableAttribute(ConstantPoolBuilder constantPool, ClassFile private void push() { //subsequent identical line numbers are skipped if (lastPc >= 0 && lastLine != writtenLine) { - buf.writeU2(lastPc); - buf.writeU2(lastLine); + buf.writeU2U2(lastPc, lastLine); writtenLine = lastLine; } } @@ -456,32 +467,16 @@ private boolean codeAndExceptionsMatch(int codeLength) { private record DeferredLabel(int labelPc, int size, int instructionPc, Label label) { } - private void writeLabelOffset(int nBytes, int instructionPc, Label label) { - int targetBci = labelToBci(label); - if (targetBci == -1) { - int pc = bytecodesBufWriter.skip(nBytes); - if (deferredLabels == null) - deferredLabels = new ArrayList<>(); - deferredLabels.add(new DeferredLabel(pc, nBytes, instructionPc, label)); - } - else { - int branchOffset = targetBci - instructionPc; - if (nBytes == 2 && (short)branchOffset != branchOffset) throw new LabelOverflowException(); - bytecodesBufWriter.writeIntBytes(nBytes, branchOffset); - } - } - private void processDeferredLabels() { - if (deferredLabels != null) { - for (DeferredLabel dl : deferredLabels) { - int branchOffset = labelToBci(dl.label) - dl.instructionPc; - if (dl.size == 2) { - if ((short)branchOffset != branchOffset) throw new LabelOverflowException(); - bytecodesBufWriter.patchU2(dl.labelPc, branchOffset); - } else { - assert dl.size == 4; - bytecodesBufWriter.patchInt(dl.labelPc, branchOffset); - } + for (int i = 0; i < deferredLabelsCount; i++) { + DeferredLabel dl = deferredLabels[i]; + int branchOffset = labelToBci(dl.label) - dl.instructionPc; + if (dl.size == 2) { + if ((short) branchOffset != branchOffset) throw new LabelOverflowException(); + bytecodesBufWriter.patchU2(dl.labelPc, branchOffset); + } else { + assert dl.size == 4; + bytecodesBufWriter.patchInt(dl.labelPc, branchOffset); } } } @@ -489,72 +484,140 @@ private void processDeferredLabels() { // Instruction writing public void writeBytecode(Opcode opcode) { + assert !opcode.isWide(); + bytecodesBufWriter.writeU1(opcode.bytecode()); + } + + // Instruction version, refer to opcode + public void writeLocalVar(Opcode opcode, int slot) { if (opcode.isWide()) { - bytecodesBufWriter.writeU2(opcode.bytecode()); + bytecodesBufWriter.writeU2U2(opcode.bytecode(), slot); } else { - bytecodesBufWriter.writeU1(opcode.bytecode()); + bytecodesBufWriter.writeU1U1(opcode.bytecode(), slot); } } - public void writeLocalVar(Opcode opcode, int localVar) { - writeBytecode(opcode); - switch (opcode.sizeIfFixed()) { - case 1 -> { } - case 2 -> bytecodesBufWriter.writeU1(localVar); - case 4 -> bytecodesBufWriter.writeU2(localVar); - default -> throw new IllegalArgumentException("Unexpected instruction size: " + opcode); + // Shortcut version, refer to and validate slot + private void writeLocalVar(int bytecode, int slot) { + // TODO validation like (slot & 0xFFFF) == slot + if (slot < 256) { + bytecodesBufWriter.writeU1U1(bytecode, slot); + } else { + bytecodesBufWriter.writeU1U1U2(WIDE, bytecode, slot); } } public void writeIncrement(boolean wide, int slot, int val) { if (wide) { - bytecodesBufWriter.writeU1(RawBytecodeHelper.WIDE); - } - bytecodesBufWriter.writeU1(RawBytecodeHelper.IINC); - if (wide) { - bytecodesBufWriter.writeU2(slot); - bytecodesBufWriter.writeU2(val); + bytecodesBufWriter.writeU2U2U2((WIDE << 8) | IINC, slot, val); } else { - bytecodesBufWriter.writeU1(slot); - bytecodesBufWriter.writeU1(val); + bytecodesBufWriter.writeU1U1U1(IINC, slot, val); } } public void writeBranch(Opcode op, Label target) { + if (op.sizeIfFixed() == 3) { + writeShortJump(op.bytecode(), target); + } else { + writeLongJump(op.bytecode(), target); + } + } + + private void writeLongLabelOffset(int instructionPc, Label label) { + int targetBci = labelToBci(label); + + // algebraic union of jump | (instructionPc, target), distinguished by null == target. + int jumpOrInstructionPc; + Label nullOrTarget; + if (targetBci == -1) { + jumpOrInstructionPc = instructionPc; + nullOrTarget = label; + } else { + jumpOrInstructionPc = targetBci - instructionPc; + nullOrTarget = null; + } + + writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget); + } + + private void writeShortJump(int bytecode, Label target) { int instructionPc = curPc(); int targetBci = labelToBci(target); + + // algebraic union of jump | (instructionPc, target), distinguished by null == target. + int jumpOrInstructionPc; + Label nullOrTarget; + if (targetBci == -1) { + jumpOrInstructionPc = instructionPc; + nullOrTarget = target; + } else { + jumpOrInstructionPc = targetBci - instructionPc; + nullOrTarget = null; + } + //transform short-opcode forward jumps if enforced, and backward jumps if enabled and overflowing - if (op.sizeIfFixed() == 3 && (targetBci == -1 - ? transformFwdJumps - : (transformBackJumps - && targetBci - instructionPc < Short.MIN_VALUE))) { - if (op == GOTO) { - writeBytecode(GOTO_W); - writeLabelOffset(4, instructionPc, target); - } else if (op == JSR) { - writeBytecode(JSR_W); - writeLabelOffset(4, instructionPc, target); + if (transformDeferredJumps || transformKnownJumps && nullOrTarget == null && jumpOrInstructionPc < Short.MIN_VALUE) { + fixShortJump(bytecode, jumpOrInstructionPc, nullOrTarget); + } else { + bytecodesBufWriter.writeU1(bytecode); + writeParsedShortLabel(jumpOrInstructionPc, nullOrTarget); + } + } + + private void writeLongJump(int bytecode, Label target) { + int instructionPc = curPc(); + bytecodesBufWriter.writeU1(bytecode); + writeLongLabelOffset(instructionPc, target); + } + + private void fixShortJump(int bytecode, int jumpOrInstructionPc, Label nullOrTarget) { + if (bytecode == GOTO) { + bytecodesBufWriter.writeU1(GOTO_W); + writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget); + } else if (bytecode == JSR) { + bytecodesBufWriter.writeU1(JSR_W); + writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget); + } else { + bytecodesBufWriter.writeU1U2( + BytecodeHelpers.reverseBranchOpcode(bytecode), // u1 + 8); // u1 + s2 + u1 + s4 // s2 + bytecodesBufWriter.writeU1(GOTO_W); // u1 + if (nullOrTarget == null) { + jumpOrInstructionPc -= 3; // jump -= 3; } else { - writeBytecode(BytecodeHelpers.reverseBranchOpcode(op)); - Label bypassJump = newLabel(); - writeLabelOffset(2, instructionPc, bypassJump); - writeBytecode(GOTO_W); - writeLabelOffset(4, instructionPc + 3, target); - labelBinding(bypassJump); + jumpOrInstructionPc += 3; // instructionPc += 3; } + writeParsedLongLabel(jumpOrInstructionPc, nullOrTarget); // s4 + } + } + + private void writeParsedShortLabel(int jumpOrInstructionPc, Label nullOrTarget) { + if (nullOrTarget == null) { + if ((short) jumpOrInstructionPc != jumpOrInstructionPc) + throw new LabelOverflowException(); + bytecodesBufWriter.writeU2(jumpOrInstructionPc); } else { - writeBytecode(op); - writeLabelOffset(op.sizeIfFixed() == 3 ? 2 : 4, instructionPc, target); + int pc = bytecodesBufWriter.skip(2); + addLabel(new DeferredLabel(pc, 2, jumpOrInstructionPc, nullOrTarget)); + } + } + + private void writeParsedLongLabel(int jumpOrInstructionPc, Label nullOrTarget) { + if (nullOrTarget == null) { + bytecodesBufWriter.writeInt(jumpOrInstructionPc); + } else { + int pc = bytecodesBufWriter.skip(4); + addLabel(new DeferredLabel(pc, 4, jumpOrInstructionPc, nullOrTarget)); } } public void writeLookupSwitch(Label defaultTarget, List cases) { int instructionPc = curPc(); - writeBytecode(LOOKUPSWITCH); + bytecodesBufWriter.writeU1(LOOKUPSWITCH); int pad = 4 - (curPc() % 4); if (pad != 4) bytecodesBufWriter.skip(pad); // padding content can be anything - writeLabelOffset(4, instructionPc, defaultTarget); + writeLongLabelOffset(instructionPc, defaultTarget); bytecodesBufWriter.writeInt(cases.size()); cases = new ArrayList<>(cases); cases.sort(new Comparator<>() { @@ -565,17 +628,18 @@ public int compare(SwitchCase c1, SwitchCase c2) { }); for (var c : cases) { bytecodesBufWriter.writeInt(c.caseValue()); - writeLabelOffset(4, instructionPc, c.target()); + var target = c.target(); + writeLongLabelOffset(instructionPc, target); } } public void writeTableSwitch(int low, int high, Label defaultTarget, List cases) { int instructionPc = curPc(); - writeBytecode(TABLESWITCH); + bytecodesBufWriter.writeU1(TABLESWITCH); int pad = 4 - (curPc() % 4); if (pad != 4) bytecodesBufWriter.skip(pad); // padding content can be anything - writeLabelOffset(4, instructionPc, defaultTarget); + writeLongLabelOffset(instructionPc, defaultTarget); bytecodesBufWriter.writeInt(low); bytecodesBufWriter.writeInt(high); var caseMap = new HashMap(cases.size()); @@ -583,85 +647,74 @@ public void writeTableSwitch(int low, int high, Label defaultTarget, List 256 + // rewrite to _W if index is >= 256 int index = AbstractPoolEntry.maybeClone(constantPool, value).index(); - Opcode op = opcode; if (value instanceof LongEntry || value instanceof DoubleEntry) { - op = LDC2_W; + opcode = Opcode.LDC2_W; } else if (index >= 256) - op = LDC_W; + opcode = Opcode.LDC_W; - writeBytecode(op); - if (op.sizeIfFixed() == 3) { - bytecodesBufWriter.writeU2(index); + assert !opcode.isWide(); + if (opcode.sizeIfFixed() == 3) { + bytecodesBufWriter.writeU1U2(opcode.bytecode(), index); } else { - bytecodesBufWriter.writeU1(index); + bytecodesBufWriter.writeU1U1(opcode.bytecode(), index); } } @@ -677,7 +730,11 @@ public int labelToBci(Label label) { if (context == this) { return lab.getBCI(); } - else if (context == mruParent) { + return labelToBci(context, lab); + } + + private int labelToBci(LabelContext context, LabelImpl lab) { + if (context == mruParent) { return mruParentTable[lab.getBCI()] - 1; } else if (context instanceof CodeAttribute parent) { @@ -717,14 +774,18 @@ public void setLabelTarget(Label label) { @Override public void setLabelTarget(Label label, int bci) { LabelImpl lab = (LabelImpl) label; - LabelContext context = lab.labelContext(); - - if (context == this) { + if (lab.labelContext() == this) { if (lab.getBCI() != -1) throw new IllegalArgumentException("Setting label target for already-set label"); lab.setBCI(bci); + } else { + setLabelTarget(lab, bci); } - else if (context == mruParent) { + } + + private void setLabelTarget(LabelImpl lab, int bci) { + LabelContext context = lab.labelContext(); + if (context == mruParent) { mruParentTable[lab.getBCI()] = bci + 1; } else if (context instanceof CodeAttribute parent) { @@ -739,7 +800,7 @@ public int[] apply(CodeAttribute x) { mruParent = parent; mruParentTable = table; - mruParentTable[lab.getBCI()] = bci + 1; + table[lab.getBCI()] = bci + 1; } else if (context instanceof BufferedCodeBuilder) { // Hijack the label @@ -751,7 +812,19 @@ else if (context instanceof BufferedCodeBuilder) { } public void addCharacterRange(CharacterRange element) { - characterRanges.add(element); + if (characterRangesCount >= characterRanges.length) { + int newCapacity = characterRangesCount + 8; + this.characterRanges = Arrays.copyOf(characterRanges, newCapacity); + } + characterRanges[characterRangesCount++] = element; + } + + public void addLabel(DeferredLabel label) { + if (deferredLabelsCount >= deferredLabels.length) { + int newCapacity = deferredLabelsCount + 8; + this.deferredLabels = Arrays.copyOf(deferredLabels, newCapacity); + } + deferredLabels[deferredLabelsCount++] = label; } public void addHandler(ExceptionCatch element) { @@ -763,11 +836,19 @@ public void addHandler(ExceptionCatch element) { } public void addLocalVariable(LocalVariable element) { - localVariables.add(element); + if (localVariablesCount >= localVariables.length) { + int newCapacity = localVariablesCount + 8; + this.localVariables = Arrays.copyOf(localVariables, newCapacity); + } + localVariables[localVariablesCount++] = element; } public void addLocalVariableType(LocalVariableType element) { - localVariableTypes.add(element); + if (localVariableTypesCount >= localVariableTypes.length) { + int newCapacity = localVariableTypesCount + 8; + this.localVariableTypes = Arrays.copyOf(localVariableTypes, newCapacity); + } + localVariableTypes[localVariableTypesCount++] = element; } @Override @@ -788,28 +869,54 @@ public LabelOverflowException() { // Fast overrides to avoid intermediate instructions // These are helpful for direct class building + @Override + public CodeBuilder return_() { + bytecodesBufWriter.writeU1(RETURN); + return this; + } + @Override public CodeBuilder return_(TypeKind tk) { - writeBytecode(BytecodeHelpers.returnOpcode(tk)); + bytecodesBufWriter.writeU1(returnBytecode(tk)); return this; } @Override public CodeBuilder storeLocal(TypeKind tk, int slot) { - writeLocalVar(BytecodeHelpers.storeOpcode(tk, slot), slot); + return switch (tk) { + case INT, SHORT, BYTE, CHAR, BOOLEAN + -> istore(slot); + case LONG -> lstore(slot); + case DOUBLE -> dstore(slot); + case FLOAT -> fstore(slot); + case REFERENCE -> astore(slot); + case VOID -> throw new IllegalArgumentException("void"); + }; + } + + @Override + public CodeBuilder labelBinding(Label label) { + setLabelTarget(label, curPc()); return this; } @Override public CodeBuilder loadLocal(TypeKind tk, int slot) { - writeLocalVar(BytecodeHelpers.loadOpcode(tk, slot), slot); - return this; + return switch (tk) { + case INT, SHORT, BYTE, CHAR, BOOLEAN + -> iload(slot); + case LONG -> lload(slot); + case DOUBLE -> dload(slot); + case FLOAT -> fload(slot); + case REFERENCE -> aload(slot); + case VOID -> throw new IllegalArgumentException("void"); + }; } @Override public CodeBuilder invoke(Opcode opcode, MemberRefEntry ref) { - if (opcode == INVOKEINTERFACE) { - int slots = Util.parameterSlots(Util.methodTypeSymbol(ref.nameAndType())) + 1; + if (opcode == Opcode.INVOKEINTERFACE) { + int slots = Util.parameterSlots(Util.methodTypeSymbol(ref.type())) + 1; writeInvokeInterface(opcode, (InterfaceMethodRefEntry) ref, slots); } else { writeInvokeNormal(opcode, ref); @@ -817,21 +924,45 @@ public CodeBuilder invoke(Opcode opcode, MemberRefEntry ref) { return this; } + @Override + public CodeBuilder invokespecial(ClassDesc owner, String name, MethodTypeDesc type) { + bytecodesBufWriter.writeIndex(INVOKESPECIAL, constantPool().methodRefEntry(owner, name, type)); + return this; + } + + @Override + public CodeBuilder invokestatic(ClassDesc owner, String name, MethodTypeDesc type) { + bytecodesBufWriter.writeIndex(INVOKESTATIC, constantPool().methodRefEntry(owner, name, type)); + return this; + } + + @Override + public CodeBuilder invokevirtual(ClassDesc owner, String name, MethodTypeDesc type) { + bytecodesBufWriter.writeIndex(INVOKEVIRTUAL, constantPool().methodRefEntry(owner, name, type)); + return this; + } + + @Override + public CodeBuilder getfield(ClassDesc owner, String name, ClassDesc type) { + bytecodesBufWriter.writeIndex(GETFIELD, constantPool().fieldRefEntry(owner, name, type)); + return this; + } + @Override public CodeBuilder fieldAccess(Opcode opcode, FieldRefEntry ref) { - writeFieldAccess(opcode, ref); + bytecodesBufWriter.writeIndex(opcode.bytecode(), ref); return this; } @Override public CodeBuilder arrayLoad(TypeKind tk) { - writeBytecode(BytecodeHelpers.arrayLoadOpcode(tk)); + bytecodesBufWriter.writeU1(BytecodeHelpers.arrayLoadBytecode(tk)); return this; } @Override public CodeBuilder arrayStore(TypeKind tk) { - writeBytecode(BytecodeHelpers.arrayStoreOpcode(tk)); + bytecodesBufWriter.writeU1(BytecodeHelpers.arrayStoreBytecode(tk)); return this; } @@ -843,19 +974,23 @@ public CodeBuilder branch(Opcode op, Label target) { @Override public CodeBuilder nop() { - writeBytecode(NOP); + bytecodesBufWriter.writeU1(NOP); return this; } @Override public CodeBuilder aconst_null() { - writeBytecode(ACONST_NULL); + bytecodesBufWriter.writeU1(ACONST_NULL); return this; } @Override public CodeBuilder aload(int slot) { - writeLocalVar(BytecodeHelpers.aload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ALOAD_0 + slot); + } else { + writeLocalVar(ALOAD, slot); + } return this; } @@ -867,350 +1002,496 @@ public CodeBuilder anewarray(ClassEntry entry) { @Override public CodeBuilder arraylength() { - writeBytecode(ARRAYLENGTH); + bytecodesBufWriter.writeU1(ARRAYLENGTH); + return this; + } + + @Override + public CodeBuilder areturn() { + bytecodesBufWriter.writeU1(ARETURN); return this; } @Override public CodeBuilder astore(int slot) { - writeLocalVar(BytecodeHelpers.astore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ASTORE_0 + slot); + } else { + writeLocalVar(ASTORE, slot); + } return this; } @Override public CodeBuilder athrow() { - writeBytecode(ATHROW); + bytecodesBufWriter.writeU1(ATHROW); return this; } @Override public CodeBuilder bipush(int b) { BytecodeHelpers.validateBipush(b); - writeArgumentConstant(BIPUSH, b); + bytecodesBufWriter.writeU1U1(BIPUSH, b); return this; } @Override public CodeBuilder checkcast(ClassEntry type) { - writeTypeCheck(CHECKCAST, type); + bytecodesBufWriter.writeIndex(CHECKCAST, type); return this; } @Override public CodeBuilder d2f() { - writeBytecode(D2F); + bytecodesBufWriter.writeU1(D2F); return this; } @Override public CodeBuilder d2i() { - writeBytecode(D2I); + bytecodesBufWriter.writeU1(D2I); return this; } @Override public CodeBuilder d2l() { - writeBytecode(D2L); + bytecodesBufWriter.writeU1(D2L); return this; } @Override public CodeBuilder dadd() { - writeBytecode(DADD); + bytecodesBufWriter.writeU1(DADD); return this; } @Override public CodeBuilder dcmpg() { - writeBytecode(DCMPG); + bytecodesBufWriter.writeU1(DCMPG); return this; } @Override public CodeBuilder dcmpl() { - writeBytecode(DCMPL); + bytecodesBufWriter.writeU1(DCMPL); return this; } @Override public CodeBuilder dconst_0() { - writeBytecode(DCONST_0); + bytecodesBufWriter.writeU1(DCONST_0); return this; } @Override public CodeBuilder dconst_1() { - writeBytecode(DCONST_1); + bytecodesBufWriter.writeU1(DCONST_1); return this; } @Override public CodeBuilder ddiv() { - writeBytecode(DDIV); + bytecodesBufWriter.writeU1(DDIV); return this; } @Override public CodeBuilder dload(int slot) { - writeLocalVar(BytecodeHelpers.dload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(DLOAD_0 + slot); + } else { + writeLocalVar(DLOAD, slot); + } return this; } @Override public CodeBuilder dmul() { - writeBytecode(DMUL); + bytecodesBufWriter.writeU1(DMUL); return this; } @Override public CodeBuilder dneg() { - writeBytecode(DNEG); + bytecodesBufWriter.writeU1(DNEG); return this; } @Override public CodeBuilder drem() { - writeBytecode(DREM); + bytecodesBufWriter.writeU1(DREM); + return this; + } + + @Override + public CodeBuilder dreturn() { + bytecodesBufWriter.writeU1(DRETURN); return this; } @Override public CodeBuilder dstore(int slot) { - writeLocalVar(BytecodeHelpers.dstore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(DSTORE_0 + slot); + } else { + writeLocalVar(DSTORE, slot); + } return this; } @Override public CodeBuilder dsub() { - writeBytecode(DSUB); + bytecodesBufWriter.writeU1(DSUB); return this; } @Override public CodeBuilder dup() { - writeBytecode(DUP); + bytecodesBufWriter.writeU1(DUP); return this; } @Override public CodeBuilder dup2() { - writeBytecode(DUP2); + bytecodesBufWriter.writeU1(DUP2); return this; } @Override public CodeBuilder dup2_x1() { - writeBytecode(DUP2_X1); + bytecodesBufWriter.writeU1(DUP2_X1); return this; } @Override public CodeBuilder dup2_x2() { - writeBytecode(DUP2_X2); + bytecodesBufWriter.writeU1(DUP2_X2); return this; } @Override public CodeBuilder dup_x1() { - writeBytecode(DUP_X1); + bytecodesBufWriter.writeU1(DUP_X1); return this; } @Override public CodeBuilder dup_x2() { - writeBytecode(DUP_X2); + bytecodesBufWriter.writeU1(DUP_X2); return this; } @Override public CodeBuilder f2d() { - writeBytecode(F2D); + bytecodesBufWriter.writeU1(F2D); return this; } @Override public CodeBuilder f2i() { - writeBytecode(F2I); + bytecodesBufWriter.writeU1(F2I); return this; } @Override public CodeBuilder f2l() { - writeBytecode(F2L); + bytecodesBufWriter.writeU1(F2L); return this; } @Override public CodeBuilder fadd() { - writeBytecode(FADD); + bytecodesBufWriter.writeU1(FADD); return this; } @Override public CodeBuilder fcmpg() { - writeBytecode(FCMPG); + bytecodesBufWriter.writeU1(FCMPG); return this; } @Override public CodeBuilder fcmpl() { - writeBytecode(FCMPL); + bytecodesBufWriter.writeU1(FCMPL); return this; } @Override public CodeBuilder fconst_0() { - writeBytecode(FCONST_0); + bytecodesBufWriter.writeU1(FCONST_0); return this; } @Override public CodeBuilder fconst_1() { - writeBytecode(FCONST_1); + bytecodesBufWriter.writeU1(FCONST_1); return this; } @Override public CodeBuilder fconst_2() { - writeBytecode(FCONST_2); + bytecodesBufWriter.writeU1(FCONST_2); return this; } @Override public CodeBuilder fdiv() { - writeBytecode(FDIV); + bytecodesBufWriter.writeU1(FDIV); return this; } @Override public CodeBuilder fload(int slot) { - writeLocalVar(BytecodeHelpers.fload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(FLOAD_0 + slot); + } else { + writeLocalVar(FLOAD, slot); + } return this; } @Override public CodeBuilder fmul() { - writeBytecode(FMUL); + bytecodesBufWriter.writeU1(FMUL); return this; } @Override public CodeBuilder fneg() { - writeBytecode(FNEG); + bytecodesBufWriter.writeU1(FNEG); return this; } @Override public CodeBuilder frem() { - writeBytecode(FREM); + bytecodesBufWriter.writeU1(FREM); + return this; + } + + @Override + public CodeBuilder freturn() { + bytecodesBufWriter.writeU1(FRETURN); return this; } @Override public CodeBuilder fstore(int slot) { - writeLocalVar(BytecodeHelpers.fstore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(FSTORE_0 + slot); + } else { + writeLocalVar(FSTORE, slot); + } return this; } @Override public CodeBuilder fsub() { - writeBytecode(FSUB); + bytecodesBufWriter.writeU1(FSUB); + return this; + } + + @Override + public CodeBuilder getstatic(ClassDesc owner, String name, ClassDesc type) { + bytecodesBufWriter.writeIndex(GETSTATIC, constantPool().fieldRefEntry(owner, name, type)); + return this; + } + + @Override + public CodeBuilder goto_(Label target) { + writeShortJump(GOTO, target); return this; } @Override public CodeBuilder i2b() { - writeBytecode(I2B); + bytecodesBufWriter.writeU1(I2B); return this; } @Override public CodeBuilder i2c() { - writeBytecode(I2C); + bytecodesBufWriter.writeU1(I2C); return this; } @Override public CodeBuilder i2d() { - writeBytecode(I2D); + bytecodesBufWriter.writeU1(I2D); return this; } @Override public CodeBuilder i2f() { - writeBytecode(I2F); + bytecodesBufWriter.writeU1(I2F); return this; } @Override public CodeBuilder i2l() { - writeBytecode(I2L); + bytecodesBufWriter.writeU1(I2L); return this; } @Override public CodeBuilder i2s() { - writeBytecode(I2S); + bytecodesBufWriter.writeU1(I2S); return this; } @Override public CodeBuilder iadd() { - writeBytecode(IADD); + bytecodesBufWriter.writeU1(IADD); return this; } @Override public CodeBuilder iand() { - writeBytecode(IAND); + bytecodesBufWriter.writeU1(IAND); return this; } @Override public CodeBuilder iconst_0() { - writeBytecode(ICONST_0); + bytecodesBufWriter.writeU1(ICONST_0); return this; } @Override public CodeBuilder iconst_1() { - writeBytecode(ICONST_1); + bytecodesBufWriter.writeU1(ICONST_1); return this; } @Override public CodeBuilder iconst_2() { - writeBytecode(ICONST_2); + bytecodesBufWriter.writeU1(ICONST_2); return this; } @Override public CodeBuilder iconst_3() { - writeBytecode(ICONST_3); + bytecodesBufWriter.writeU1(ICONST_3); return this; } @Override public CodeBuilder iconst_4() { - writeBytecode(ICONST_4); + bytecodesBufWriter.writeU1(ICONST_4); return this; } @Override public CodeBuilder iconst_5() { - writeBytecode(ICONST_5); + bytecodesBufWriter.writeU1(ICONST_5); return this; } @Override public CodeBuilder iconst_m1() { - writeBytecode(ICONST_M1); + bytecodesBufWriter.writeU1(ICONST_M1); return this; } @Override public CodeBuilder idiv() { - writeBytecode(IDIV); + bytecodesBufWriter.writeU1(IDIV); + return this; + } + + @Override + public CodeBuilder if_acmpeq(Label target) { + writeShortJump(IF_ACMPEQ, target); + return this; + } + + @Override + public CodeBuilder if_acmpne(Label target) { + writeShortJump(IF_ACMPNE, target); + return this; + } + + @Override + public CodeBuilder if_icmpeq(Label target) { + writeShortJump(IF_ICMPEQ, target); + return this; + } + + @Override + public CodeBuilder if_icmpge(Label target) { + writeShortJump(IF_ICMPGE, target); + return this; + } + + @Override + public CodeBuilder if_icmpgt(Label target) { + writeShortJump(IF_ICMPGT, target); + return this; + } + + @Override + public CodeBuilder if_icmple(Label target) { + writeShortJump(IF_ICMPLE, target); + return this; + } + + @Override + public CodeBuilder if_icmplt(Label target) { + writeShortJump(IF_ICMPLT, target); + return this; + } + + @Override + public CodeBuilder if_icmpne(Label target) { + writeShortJump(IF_ICMPNE, target); + return this; + } + + @Override + public CodeBuilder ifnonnull(Label target) { + writeShortJump(IFNONNULL, target); + return this; + } + + @Override + public CodeBuilder ifnull(Label target) { + writeShortJump(IFNULL, target); + return this; + } + + @Override + public CodeBuilder ifeq(Label target) { + writeShortJump(IFEQ, target); + return this; + } + + @Override + public CodeBuilder ifge(Label target) { + writeShortJump(IFGE, target); + return this; + } + + @Override + public CodeBuilder ifgt(Label target) { + writeShortJump(IFGT, target); + return this; + } + + @Override + public CodeBuilder ifle(Label target) { + writeShortJump(IFLE, target); + return this; + } + + @Override + public CodeBuilder iflt(Label target) { + writeShortJump(IFLT, target); + return this; + } + + @Override + public CodeBuilder ifne(Label target) { + writeShortJump(IFNE, target); return this; } @@ -1222,25 +1503,29 @@ public CodeBuilder iinc(int slot, int val) { @Override public CodeBuilder iload(int slot) { - writeLocalVar(BytecodeHelpers.iload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ILOAD_0 + slot); + } else { + writeLocalVar(ILOAD, slot); + } return this; } @Override public CodeBuilder imul() { - writeBytecode(IMUL); + bytecodesBufWriter.writeU1(IMUL); return this; } @Override public CodeBuilder ineg() { - writeBytecode(INEG); + bytecodesBufWriter.writeU1(INEG); return this; } @Override public CodeBuilder instanceOf(ClassEntry target) { - writeTypeCheck(INSTANCEOF, target); + bytecodesBufWriter.writeIndex(INSTANCEOF, target); return this; } @@ -1252,85 +1537,95 @@ public CodeBuilder invokedynamic(InvokeDynamicEntry ref) { @Override public CodeBuilder invokeinterface(InterfaceMethodRefEntry ref) { - writeInvokeInterface(INVOKEINTERFACE, ref, Util.parameterSlots(ref.typeSymbol()) + 1); + writeInvokeInterface(Opcode.INVOKEINTERFACE, ref, Util.parameterSlots(ref.typeSymbol()) + 1); return this; } @Override public CodeBuilder invokespecial(InterfaceMethodRefEntry ref) { - writeInvokeNormal(INVOKESPECIAL, ref); + bytecodesBufWriter.writeIndex(INVOKESPECIAL, ref); return this; } @Override public CodeBuilder invokespecial(MethodRefEntry ref) { - writeInvokeNormal(INVOKESPECIAL, ref); + bytecodesBufWriter.writeIndex(INVOKESPECIAL, ref); return this; } @Override public CodeBuilder invokestatic(InterfaceMethodRefEntry ref) { - writeInvokeNormal(INVOKESTATIC, ref); + bytecodesBufWriter.writeIndex(INVOKESTATIC, ref); return this; } @Override public CodeBuilder invokestatic(MethodRefEntry ref) { - writeInvokeNormal(INVOKESTATIC, ref); + bytecodesBufWriter.writeIndex(INVOKESTATIC, ref); return this; } @Override public CodeBuilder invokevirtual(MethodRefEntry ref) { - writeInvokeNormal(INVOKEVIRTUAL, ref); + bytecodesBufWriter.writeIndex(INVOKEVIRTUAL, ref); return this; } @Override public CodeBuilder ior() { - writeBytecode(IOR); + bytecodesBufWriter.writeU1(IOR); return this; } @Override public CodeBuilder irem() { - writeBytecode(IREM); + bytecodesBufWriter.writeU1(IREM); + return this; + } + + @Override + public CodeBuilder ireturn() { + bytecodesBufWriter.writeU1(IRETURN); return this; } @Override public CodeBuilder ishl() { - writeBytecode(ISHL); + bytecodesBufWriter.writeU1(ISHL); return this; } @Override public CodeBuilder ishr() { - writeBytecode(ISHR); + bytecodesBufWriter.writeU1(ISHR); return this; } @Override public CodeBuilder istore(int slot) { - writeLocalVar(BytecodeHelpers.istore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(ISTORE_0 + slot); + } else { + writeLocalVar(ISTORE, slot); + } return this; } @Override public CodeBuilder isub() { - writeBytecode(ISUB); + bytecodesBufWriter.writeU1(ISUB); return this; } @Override public CodeBuilder iushr() { - writeBytecode(IUSHR); + bytecodesBufWriter.writeU1(IUSHR); return this; } @Override public CodeBuilder ixor() { - writeBytecode(IXOR); + bytecodesBufWriter.writeU1(IXOR); return this; } @@ -1342,49 +1637,49 @@ public CodeBuilder lookupswitch(Label defaultTarget, List cases) { @Override public CodeBuilder l2d() { - writeBytecode(L2D); + bytecodesBufWriter.writeU1(L2D); return this; } @Override public CodeBuilder l2f() { - writeBytecode(L2F); + bytecodesBufWriter.writeU1(L2F); return this; } @Override public CodeBuilder l2i() { - writeBytecode(L2I); + bytecodesBufWriter.writeU1(L2I); return this; } @Override public CodeBuilder ladd() { - writeBytecode(LADD); + bytecodesBufWriter.writeU1(LADD); return this; } @Override public CodeBuilder land() { - writeBytecode(LAND); + bytecodesBufWriter.writeU1(LAND); return this; } @Override public CodeBuilder lcmp() { - writeBytecode(LCMP); + bytecodesBufWriter.writeU1(LCMP); return this; } @Override public CodeBuilder lconst_0() { - writeBytecode(LCONST_0); + bytecodesBufWriter.writeU1(LCONST_0); return this; } @Override public CodeBuilder lconst_1() { - writeBytecode(LCONST_1); + bytecodesBufWriter.writeU1(LCONST_1); return this; } @@ -1396,85 +1691,99 @@ public CodeBuilder ldc(LoadableConstantEntry entry) { @Override public CodeBuilder ldiv() { - writeBytecode(LDIV); + bytecodesBufWriter.writeU1(LDIV); return this; } @Override public CodeBuilder lload(int slot) { - writeLocalVar(BytecodeHelpers.lload(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(LLOAD_0 + slot); + } else { + writeLocalVar(LLOAD, slot); + } return this; } @Override public CodeBuilder lmul() { - writeBytecode(LMUL); + bytecodesBufWriter.writeU1(LMUL); return this; } @Override public CodeBuilder lneg() { - writeBytecode(LNEG); + bytecodesBufWriter.writeU1(LNEG); return this; } @Override public CodeBuilder lor() { - writeBytecode(LOR); + bytecodesBufWriter.writeU1(LOR); return this; } @Override public CodeBuilder lrem() { - writeBytecode(LREM); + bytecodesBufWriter.writeU1(LREM); + return this; + } + + @Override + public CodeBuilder lreturn() { + bytecodesBufWriter.writeU1(LRETURN); return this; } @Override public CodeBuilder lshl() { - writeBytecode(LSHL); + bytecodesBufWriter.writeU1(LSHL); return this; } @Override public CodeBuilder lshr() { - writeBytecode(LSHR); + bytecodesBufWriter.writeU1(LSHR); return this; } @Override public CodeBuilder lstore(int slot) { - writeLocalVar(BytecodeHelpers.lstore(slot), slot); + if (slot >= 0 && slot <= 3) { + bytecodesBufWriter.writeU1(LSTORE_0 + slot); + } else { + writeLocalVar(LSTORE, slot); + } return this; } @Override public CodeBuilder lsub() { - writeBytecode(LSUB); + bytecodesBufWriter.writeU1(LSUB); return this; } @Override public CodeBuilder lushr() { - writeBytecode(LUSHR); + bytecodesBufWriter.writeU1(LUSHR); return this; } @Override public CodeBuilder lxor() { - writeBytecode(LXOR); + bytecodesBufWriter.writeU1(LXOR); return this; } @Override public CodeBuilder monitorenter() { - writeBytecode(MONITORENTER); + bytecodesBufWriter.writeU1(MONITORENTER); return this; } @Override public CodeBuilder monitorexit() { - writeBytecode(MONITOREXIT); + bytecodesBufWriter.writeU1(MONITOREXIT); return this; } @@ -1501,26 +1810,26 @@ public CodeBuilder newarray(TypeKind typeKind) { @Override public CodeBuilder pop() { - writeBytecode(POP); + bytecodesBufWriter.writeU1(POP); return this; } @Override public CodeBuilder pop2() { - writeBytecode(POP2); + bytecodesBufWriter.writeU1(POP2); return this; } @Override public CodeBuilder sipush(int s) { BytecodeHelpers.validateSipush(s); - writeArgumentConstant(SIPUSH, s); + bytecodesBufWriter.writeU1U2(SIPUSH, s); return this; } @Override public CodeBuilder swap() { - writeBytecode(SWAP); + bytecodesBufWriter.writeU1(SWAP); return this; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java index ec4e148baf3..a841cbf47f4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectMethodBuilder.java @@ -148,9 +148,7 @@ public DirectMethodBuilder run(Consumer handler) { @Override public void writeTo(BufWriterImpl buf) { - buf.writeU2(flags); - buf.writeIndex(name); - buf.writeIndex(desc); + buf.writeU2U2U2(flags, buf.cpIndex(name), buf.cpIndex(desc)); attributes.writeTo(buf); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java b/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java index d5ebbbebe7a..be350cb5814 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/EntryMap.java @@ -113,6 +113,7 @@ public void put(int hash, int index) { throw new IllegalArgumentException("hash must be nonzero"); int ptr = (hash & mask1) << 1; + var data = this.data; int k = data[ptr]; if (k == 0) { data[ptr] = hash; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java index 2aaf5f033c0..aac0fafaef1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/LabelImpl.java @@ -24,8 +24,6 @@ */ package jdk.internal.classfile.impl; -import java.util.Objects; - import java.lang.classfile.Label; import java.lang.classfile.instruction.LabelTarget; @@ -52,7 +50,7 @@ public final class LabelImpl private int bci; public LabelImpl(LabelContext labelContext, int bci) { - this.labelContext = Objects.requireNonNull(labelContext); + this.labelContext = labelContext; this.bci = bci; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java b/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java index 2403ae150e9..659f41f9699 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/RawBytecodeHelper.java @@ -299,7 +299,6 @@ public static int align(int n) { private int nextBci; private int bci; private int opcode; - private boolean isWide; public static CodeRange of(byte[] array) { return new CodeRange(array, array.length); @@ -341,14 +340,14 @@ public void reset(int nextBci) { * be valid and can be accessed unchecked. */ public int opcode() { - return opcode; + return opcode & 0xFF; } /** * Returns whether the current functional opcode is in wide. */ public boolean isWide() { - return isWide; + return (opcode & (WIDE << 8)) != 0; } /** @@ -411,7 +410,7 @@ public int destW() { // *load, *store, iinc public int getIndex() { - return isWide ? getU2Unchecked(bci + 2) : getIndexU1(); + return isWide() ? getU2Unchecked(bci + 2) : getIndexU1(); } // ldc @@ -443,12 +442,11 @@ public boolean next() { int len = LENGTHS[code & 0xFF]; // & 0xFF eliminates bound check this.bci = bci; opcode = code; - isWide = false; if (len <= 0) { len = checkSpecialInstruction(bci, end, code); // sets opcode } - if (len <= 0 || (nextBci += len) > end) { + if ((nextBci += len) > end) { opcode = ILLEGAL; } @@ -457,37 +455,34 @@ public boolean next() { // Put rarely used code in another method to reduce code size private int checkSpecialInstruction(int bci, int end, int code) { + int len = -1; if (code == WIDE) { - if (bci + 1 >= end) { - return -1; + if (bci + 1 < end) { + opcode = (WIDE << 8) | (code = getIndexU1()); + // Validated in UtilTest.testOpcodeLengthTable + len = LENGTHS[code] * 2; } - opcode = code = getIndexU1(); - isWide = true; - // Validated in UtilTest.testOpcodeLengthTable - return LENGTHS[code] * 2; - } - if (code == TABLESWITCH) { + } else if (code == TABLESWITCH) { int alignedBci = align(bci + 1); - if (alignedBci + 3 * 4 >= end) { - return -1; + if (alignedBci + 3 * 4 < end) { + int lo = getIntUnchecked(alignedBci + 1 * 4); + int hi = getIntUnchecked(alignedBci + 2 * 4); + long l = alignedBci - bci + (3L + (long) hi - lo + 1L) * 4L; + len = l > 0 && ((int) l == l) ? (int) l : -1; } - int lo = getIntUnchecked(alignedBci + 1 * 4); - int hi = getIntUnchecked(alignedBci + 2 * 4); - long l = alignedBci - bci + (3L + (long) hi - lo + 1L) * 4L; - return l > 0 && ((int) l == l) ? (int) l : -1; - } - if (code == LOOKUPSWITCH) { + } else if (code == LOOKUPSWITCH) { int alignedBci = align(bci + 1); - if (alignedBci + 2 * 4 >= end) { - return -1; - } - int npairs = getIntUnchecked(alignedBci + 4); - if (npairs < 0) { - return -1; + if (alignedBci + 2 * 4 < end) { + int npairs = getIntUnchecked(alignedBci + 4); + if (npairs >= 0) { + long l = alignedBci - bci + (2L + 2L * npairs) * 4L; + len = l > 0 && ((int) l == l) ? (int) l : -1; + } } - long l = alignedBci - bci + (2L + 2L * npairs) * 4L; - return l > 0 && ((int) l == l) ? (int) l : -1; } - return -1; + if (len <= 0) { + opcode = ILLEGAL; + } + return len; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index 2193eb76108..0b99766b385 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java @@ -34,7 +34,6 @@ import java.lang.classfile.BootstrapMethodEntry; import java.lang.classfile.attribute.BootstrapMethodsAttribute; import java.lang.classfile.constantpool.*; -import java.util.Objects; import jdk.internal.constant.ConstantUtils; @@ -87,20 +86,27 @@ public int bootstrapMethodCount() { @Override public PoolEntry entryByIndex(int index) { if (index <= 0 || index >= size()) { - throw new ConstantPoolException("Bad CP index: " + index); + throw badCP(index); } PoolEntry pe = (index < parentSize) ? parent.entryByIndex(index) : myEntries[index - parentSize]; if (pe == null) { - throw new ConstantPoolException("Unusable CP index: " + index); + throw unusableCP(index); } return pe; } + private static ConstantPoolException badCP(int index) { + return new ConstantPoolException("Bad CP index: " + index); + } + + private static ConstantPoolException unusableCP(int index) { + return new ConstantPoolException("Unusable CP index: " + index); + } + @Override public T entryByIndex(int index, Class cls) { - Objects.requireNonNull(cls); return ClassReaderImpl.checkType(entryByIndex(index), index, cls); } @@ -165,8 +171,10 @@ void writeTo(BufWriterImpl buf) { } private EntryMap map() { + int parentSize = this.parentSize; + var map = this.map; if (map == null) { - map = new EntryMap(Math.max(size, 1024), .75f); + this.map = map = new EntryMap(Math.max(size, 1024), .75f); // Doing a full scan here yields fall-off-the-cliff performance results, // especially if we only need a few entries that are already @@ -203,8 +211,10 @@ private void fullScan() { } private EntryMap bsmMap() { + int bsmSize = this.bsmSize; + var bsmMap = this.bsmMap; if (bsmMap == null) { - bsmMap = new EntryMap(Math.max(bsmSize, 16), .75f); + this.bsmMap = bsmMap = new EntryMap(Math.max(bsmSize, 16), .75f); for (int i=0; i { var cpe = cp.entryByIndex(bcs.getIndexU2()); var nameAndType = opcode == INVOKEDYNAMIC ? ((DynamicConstantPoolEntry)cpe).nameAndType() : ((MemberRefEntry)cpe).nameAndType(); - var mtd = Util.methodTypeSymbol(nameAndType); + var mtd = Util.methodTypeSymbol(nameAndType.type()); var delta = Util.slotSize(mtd.returnType()) - Util.parameterSlots(mtd); if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) { delta--; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index 884f7b8bbc8..54b4139a9e0 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -48,7 +48,7 @@ public class StackMapDecoder { private static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247, SAME_EXTENDED = 251; - private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = new StackMapFrameInfo[0]; + private static final StackMapFrameInfo[] NO_STACK_FRAME_INFOS = {}; private final ClassReader classReader; private final int pos; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index ae983520683..9bd5084061a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved. * 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 +40,6 @@ import java.lang.constant.MethodTypeDesc; import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -58,8 +58,8 @@ *

* The {@linkplain #generate() frames computation} consists of following steps: *

    - *
  1. {@linkplain #detectFrameOffsets() Detection} of mandatory stack map frames offsets: