diff --git a/make/autoconf/lib-tests.m4 b/make/autoconf/lib-tests.m4 index faaf229eacda..89f9bf425e1c 100644 --- a/make/autoconf/lib-tests.m4 +++ b/make/autoconf/lib-tests.m4 @@ -28,7 +28,7 @@ ################################################################################ # Minimum supported versions -JTREG_MINIMUM_VERSION=8.2.1 +JTREG_MINIMUM_VERSION=8.3 GTEST_MINIMUM_VERSION=1.14.0 ################################################################################ diff --git a/make/conf/github-actions.conf b/make/conf/github-actions.conf index f6c7f39683cc..c2854cd1f5e0 100644 --- a/make/conf/github-actions.conf +++ b/make/conf/github-actions.conf @@ -26,7 +26,7 @@ # Versions and download locations for dependencies used by GitHub Actions (GHA) GTEST_VERSION=1.14.0 -JTREG_VERSION=8.2.1+1 +JTREG_VERSION=8.3+1 LINUX_X64_BOOT_JDK_EXT=tar.gz LINUX_X64_BOOT_JDK_URL=https://github.com/SAP/SapMachine/releases/download/sapmachine-26.0.1/sapmachine-jdk-26.0.1_linux-x64_bin.tar.gz diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js index 20315cda97d2..32f07325c058 100644 --- a/make/conf/jib-profiles.js +++ b/make/conf/jib-profiles.js @@ -644,7 +644,7 @@ var getJibProfilesProfiles = function (input, common, data) { // Bootcycle profiles runs the build with itself as the boot jdk. This can // be done in two ways. Either using the builtin bootcycle target in the // build system. Or by supplying the main jdk build as bootjdk to configure. - [ "linux-x64", "macosx-x64", "windows-x64", "linux-aarch64" ] + [ "linux-x64", "macosx-aarch64", "macosx-x64", "windows-x64", "linux-aarch64" ] .forEach(function (name) { var bootcycleName = name + "-bootcycle"; var bootcyclePrebuiltName = name + "-bootcycle-prebuilt"; @@ -1174,9 +1174,9 @@ var getJibProfilesDependencies = function (input, common) { jtreg: { server: "jpg", product: "jtreg", - version: "8.2.1", + version: "8.3", build_number: "1", - file: "bundles/jtreg-8.2.1+1.zip", + file: "bundles/jtreg-8.3+1.zip", environment_name: "JT_HOME", environment_path: input.get("jtreg", "home_path") + "/bin", configure_args: "--with-jtreg=" + input.get("jtreg", "home_path"), diff --git a/make/hotspot/lib/JvmOverrideFiles.gmk b/make/hotspot/lib/JvmOverrideFiles.gmk index 80f3582043cd..b417cc19b03b 100644 --- a/make/hotspot/lib/JvmOverrideFiles.gmk +++ b/make/hotspot/lib/JvmOverrideFiles.gmk @@ -33,10 +33,6 @@ ifeq ($(INCLUDE), true) ifeq ($(TOOLCHAIN_TYPE), gcc) BUILD_LIBJVM_vmStructs.cpp_CXXFLAGS := -fno-var-tracking-assignments - ifeq ($(DEBUG_LEVEL), release) - # Need extra inlining to collapse shared marking code into the hot marking loop - BUILD_LIBJVM_shenandoahMark.cpp_CXXFLAGS := --param inline-unit-growth=1000 - endif # disable lto in g1ParScanThreadState because of special inlining/flattening used there ifeq ($(call check-jvm-feature, link-time-opt), true) BUILD_LIBJVM_g1ParScanThreadState.cpp_CXXFLAGS := -fno-lto diff --git a/make/modules/jdk.management/Lib.gmk b/make/modules/jdk.management/Lib.gmk index 8991414b44e2..f65348e9381a 100644 --- a/make/modules/jdk.management/Lib.gmk +++ b/make/modules/jdk.management/Lib.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -41,8 +41,6 @@ endif $(eval $(call SetupJdkLibrary, BUILD_LIBMANAGEMENT_EXT, \ NAME := management_ext, \ OPTIMIZATION := HIGH, \ - DISABLED_WARNINGS_gcc_DiagnosticCommandImpl.c := unused-variable, \ - DISABLED_WARNINGS_clang_DiagnosticCommandImpl.c := unused-variable, \ DISABLED_WARNINGS_clang_UnixOperatingSystem.c := format-nonliteral, \ CFLAGS := $(LIBMANAGEMENT_EXT_CFLAGS), \ JDK_LIBS := java.base:libjava java.base:libjvm, \ diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 49f3419dfb63..05e4321b6632 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2512,25 +2512,25 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } -const RegMask& Matcher::divI_proj_mask() { +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI. -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL. -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL. -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } diff --git a/src/hotspot/cpu/aarch64/aarch64_atomic.ad b/src/hotspot/cpu/aarch64/aarch64_atomic.ad index 3b05a6372157..13fbe7815183 100644 --- a/src/hotspot/cpu/aarch64/aarch64_atomic.ad +++ b/src/hotspot/cpu/aarch64/aarch64_atomic.ad @@ -43,8 +43,7 @@ instruct compareAndExchangeB(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::byte, memory_order_release, $res$$Register); __ sxtbw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -59,8 +58,7 @@ instruct compareAndExchangeS(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::halfword, memory_order_release, $res$$Register); __ sxthw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -75,8 +73,7 @@ instruct compareAndExchangeI(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -90,8 +87,7 @@ instruct compareAndExchangeL(iRegLNoSp res, indirect mem, iRegL oldval, iRegL ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -106,8 +102,7 @@ instruct compareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -122,8 +117,7 @@ instruct compareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP ne %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_release, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -138,8 +132,7 @@ instruct compareAndExchangeBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::byte, memory_order_seq_cst, $res$$Register); __ sxtbw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -155,8 +148,7 @@ instruct compareAndExchangeSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::halfword, memory_order_seq_cst, $res$$Register); __ sxthw($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -172,8 +164,7 @@ instruct compareAndExchangeIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -188,8 +179,7 @@ instruct compareAndExchangeLAcq(iRegLNoSp res, indirect mem, iRegL oldval, iRegL %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -204,8 +194,7 @@ instruct compareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::word, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -220,8 +209,7 @@ instruct compareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::xword, memory_order_seq_cst, $res$$Register); %} ins_pipe(pipe_slow); %} @@ -235,9 +223,7 @@ instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -252,9 +238,7 @@ instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -269,9 +253,7 @@ instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -286,9 +268,7 @@ instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -304,9 +284,7 @@ instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -322,9 +300,7 @@ instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -340,9 +316,7 @@ instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -358,9 +332,7 @@ instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -376,9 +348,7 @@ instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -394,9 +364,7 @@ instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -412,9 +380,7 @@ instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -430,9 +396,7 @@ instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP new "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ false, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -447,9 +411,7 @@ instruct weakCompareAndSwapB(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -464,9 +426,7 @@ instruct weakCompareAndSwapS(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -481,9 +441,7 @@ instruct weakCompareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -498,9 +456,7 @@ instruct weakCompareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -516,9 +472,7 @@ instruct weakCompareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -534,9 +488,7 @@ instruct weakCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ false, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -552,9 +504,7 @@ instruct weakCompareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::byte, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::byte, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -570,9 +520,7 @@ instruct weakCompareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::halfword, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::halfword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -588,9 +536,7 @@ instruct weakCompareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -606,9 +552,7 @@ instruct weakCompareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -624,9 +568,7 @@ instruct weakCompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::word, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -642,9 +584,7 @@ instruct weakCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::xword, /*acquire*/ true, /*release*/ true, - /*weak*/ true, noreg); + __ cmpxchg_weak($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); diff --git a/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 b/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 index dc51754e7f9e..d6b3abd1e6fa 100644 --- a/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 +++ b/src/hotspot/cpu/aarch64/aarch64_atomic_ad.m4 @@ -53,8 +53,7 @@ ifelse($7,Acq,INDENT(predicate(needs_acquiring_load_exclusive(n));),`dnl') %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($7,Acq,true,false), /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::$4, ifelse($7,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); __ $6($res$$Register, $res$$Register); %} ins_pipe(pipe_slow); @@ -76,8 +75,7 @@ ifelse($1$6,PAcq,INDENT(predicate(needs_acquiring_load_exclusive(n) && (n->as_Lo %} ins_encode %{ __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($6,Acq,true,false), /*release*/ true, - /*weak*/ false, $res$$Register); + Assembler::$4, ifelse($6,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); %} ins_pipe(pipe_slow); %}')dnl @@ -112,9 +110,7 @@ ifelse($6,Acq,INDENT(predicate(needs_acquiring_load_exclusive(n));),`dnl') "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($6,Acq,true,false), /*release*/ true, - /*weak*/ ifelse($7,Weak,true,false), noreg); + __ ifelse($7,Weak,cmpxchg_weak,cmpxchg)($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::$4, ifelse($6,Acq,memory_order_seq_cst,memory_order_release)); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); @@ -137,9 +133,7 @@ ifelse($1$6,PAcq,INDENT(predicate(needs_acquiring_load_exclusive(n) && (n->as_Lo "csetw $res, EQ\t# $res <-- (EQ ? 1 : 0)" %} ins_encode %{ - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, - Assembler::$4, /*acquire*/ ifelse($6,Acq,true,false), /*release*/ true, - /*weak*/ ifelse($7,Weak,true,false), noreg); + __ ifelse($7,Weak,cmpxchg_weak,cmpxchg)($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::$4, ifelse($6,Acq,memory_order_seq_cst,memory_order_release)); __ csetw($res$$Register, Assembler::EQ); %} ins_pipe(pipe_slow); diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp index 87451b5a07a3..202f3227e2d2 100644 --- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp @@ -1492,12 +1492,12 @@ void LIR_Assembler::emit_opTypeCheck(LIR_OpTypeCheck* op) { } void LIR_Assembler::casw(Register addr, Register newval, Register cmpval) { - __ cmpxchg(addr, cmpval, newval, Assembler::word, /* acquire*/ true, /* release*/ true, /* weak*/ false, rscratch1); + __ cmpxchg(addr, cmpval, newval, Assembler::word, memory_order_seq_cst, rscratch1); __ cset(rscratch1, Assembler::NE); } void LIR_Assembler::casl(Register addr, Register newval, Register cmpval) { - __ cmpxchg(addr, cmpval, newval, Assembler::xword, /* acquire*/ true, /* release*/ true, /* weak*/ false, rscratch1); + __ cmpxchg(addr, cmpval, newval, Assembler::xword, memory_order_seq_cst, rscratch1); __ cset(rscratch1, Assembler::NE); } diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp index cb9e308197e5..e46a338e649e 100644 --- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp @@ -204,8 +204,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, // Try to lock. Transition lock-bits 0b01 => 0b00 orr(t1_mark, t1_mark, markWord::unlocked_value); eor(t3_t, t1_mark, markWord::unlocked_value); - cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, - /*acquire*/ true, /*release*/ false, /*weak*/ false, noreg); + cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, memory_order_acquire); br(Assembler::NE, slow_path); bind(push); @@ -285,8 +284,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1, // Try to CAS owner (no owner => current thread's _monitor_owner_id). ldr(rscratch2, Address(rthread, JavaThread::monitor_owner_id_offset())); - cmpxchg(t2_owner_addr, zr, rscratch2, Assembler::xword, /*acquire*/ true, - /*release*/ false, /*weak*/ false, t3_owner); + cmpxchg(t2_owner_addr, zr, rscratch2, Assembler::xword, memory_order_acquire, t3_owner); br(Assembler::EQ, monitor_locked); // Check if recursive. @@ -371,8 +369,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box, Register t1, // Try to unlock. Transition lock bits 0b00 => 0b01 assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); orr(t3_t, t1_mark, markWord::unlocked_value); - cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, - /*acquire*/ false, /*release*/ true, /*weak*/ false, noreg); + cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword, memory_order_release); br(Assembler::EQ, unlocked); bind(push_and_slow_path); diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.hpp index 231710df7d74..ac4740645b84 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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. * @@ -66,7 +66,6 @@ public: enum { - pc_return_offset = 0, // All frames link_offset = 0, return_addr_offset = 1, diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad index 18fc27a4af4c..375a0a897601 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.ad @@ -283,7 +283,7 @@ instruct g1CompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_release, $res$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, $newval$$Register /* new_val */, @@ -316,7 +316,7 @@ instruct g1CompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRe RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_seq_cst, $res$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, $newval$$Register /* new_val */, @@ -346,7 +346,7 @@ instruct g1CompareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - false /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_release, $res$$Register); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -377,7 +377,7 @@ instruct g1CompareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRe RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - true /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_seq_cst, $res$$Register); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -409,8 +409,7 @@ instruct g1CompareAndSwapP(iRegINoSp res, indirect mem, iRegP newval, iRegPNoSp $tmp2$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_release); __ cset($res$$Register, Assembler::EQ); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -442,8 +441,7 @@ instruct g1CompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP newval, iRegPNo $tmp2$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, memory_order_seq_cst); __ cset($res$$Register, Assembler::EQ); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -475,8 +473,7 @@ instruct g1CompareAndSwapN(iRegINoSp res, indirect mem, iRegN newval, iRegPNoSp $tmp3$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - false /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_release); __ cset($res$$Register, Assembler::EQ); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, @@ -509,8 +506,7 @@ instruct g1CompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN newval, iRegPNo $tmp3$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - true /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, memory_order_seq_cst); __ cset($res$$Register, Assembler::EQ); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, diff --git a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 index 8fb1f7e8e428..63b464ceb8c3 100644 --- a/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 +++ b/src/hotspot/cpu/aarch64/gc/g1/g1_aarch64.m4 @@ -151,7 +151,7 @@ instruct g1CompareAndExchangeP$1(iRegPNoSp res, indirect mem, iRegP oldval, iReg RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - $3 /* acquire */, true /* release */, false /* weak */, $res$$Register); + ifelse($1,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, $newval$$Register /* new_val */, @@ -160,8 +160,8 @@ instruct g1CompareAndExchangeP$1(iRegPNoSp res, indirect mem, iRegP oldval, iReg %} ins_pipe(pipe_slow); %}')dnl -CAEP_INSN(,,false) -CAEP_INSN(Acq,_acq,true) +CAEP_INSN(,) +CAEP_INSN(Acq,_acq) dnl define(`CAEN_INSN', ` @@ -185,7 +185,7 @@ instruct g1CompareAndExchangeN$1(iRegNNoSp res, indirect mem, iRegN oldval, iReg RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - $3 /* acquire */, true /* release */, false /* weak */, $res$$Register); + ifelse($1,Acq,memory_order_seq_cst,memory_order_release), $res$$Register); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -195,8 +195,8 @@ instruct g1CompareAndExchangeN$1(iRegNNoSp res, indirect mem, iRegN oldval, iReg %} ins_pipe(pipe_slow); %}')dnl -CAEN_INSN(,,false) -CAEN_INSN(Acq,_acq,true) +CAEN_INSN(,) +CAEN_INSN(Acq,_acq) dnl define(`CASP_INSN', ` @@ -221,8 +221,7 @@ instruct g1CompareAndSwapP$1(iRegINoSp res, indirect mem, iRegP newval, iRegPNoS $tmp2$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, - $3 /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::xword, ifelse($1,Acq,memory_order_seq_cst,memory_order_release)); __ cset($res$$Register, Assembler::EQ); write_barrier_post(masm, this, $mem$$Register /* store_addr */, @@ -232,8 +231,8 @@ instruct g1CompareAndSwapP$1(iRegINoSp res, indirect mem, iRegP newval, iRegPNoS %} ins_pipe(pipe_slow); %}')dnl -CASP_INSN(,,false) -CASP_INSN(Acq,_acq,true) +CASP_INSN(,) +CASP_INSN(Acq,_acq) dnl define(`CASN_INSN', ` @@ -258,8 +257,7 @@ instruct g1CompareAndSwapN$1(iRegINoSp res, indirect mem, iRegN newval, iRegPNoS $tmp3$$Register /* tmp2 */, RegSet::of($mem$$Register, $oldval$$Register, $newval$$Register) /* preserve */, RegSet::of($res$$Register) /* no_preserve */); - __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, - $3 /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval$$Register, $newval$$Register, Assembler::word, ifelse($1,Acq,memory_order_seq_cst,memory_order_release)); __ cset($res$$Register, Assembler::EQ); __ decode_heap_oop($tmp1$$Register, $newval$$Register); write_barrier_post(masm, this, @@ -270,8 +268,8 @@ instruct g1CompareAndSwapN$1(iRegINoSp res, indirect mem, iRegN newval, iRegPNoS %} ins_pipe(pipe_slow); %}')dnl -CASN_INSN(,,false) -CASN_INSN(Acq,_acq,true) +CASN_INSN(,) +CASN_INSN(Acq,_acq) dnl define(`XCHGP_INSN', ` diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index c590b6699c01..7406aa0c1c4a 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -456,79 +456,38 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - // At this point we know that marking is in progress. - // If do_load() is true then we have to emit the - // load of the previous value; otherwise it has already - // been loaded into _pre_val. - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { __ bind(*stub->entry()); - assert(stub->pre_val()->is_register(), "Precondition."); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - Register pre_val_reg = stub->pre_val()->as_register(); + Register obj = stub->obj()->as_register(); if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /*wide*/); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, /* wide = */ false); } - __ cbz(pre_val_reg, *stub->continuation()); - ce->store_parameter(stub->pre_val()->as_register(), 0); - __ far_call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin())); + __ cbz(obj, *stub->continuation()); + ce->store_parameter(obj, 0); + __ far_call(RuntimeAddress(bs->keepalive_barrier_stub())); __ b(*stub->continuation()); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { __ bind(*stub->entry()); - DecoratorSet decorators = stub->decorators(); - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); - - assert(res == r0, "result must arrive in r0"); - - if (res != obj) { - __ mov(res, obj); - } - - if (is_strong) { - // Check for object in cset. - if (AOTCodeCache::is_on_for_dump()) { - __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::cset_base_address())); - __ ldr(tmp2, Address(tmp2)); - __ lea(tmp1, ExternalAddress(AOTRuntimeConstants::grain_shift_address())); - __ ldrw(tmp1, Address(tmp1)); - __ lsrv(tmp1, res, tmp1); - } else { - __ mov(tmp2, ShenandoahHeap::in_cset_fast_test_addr()); - __ lsr(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - } - __ ldrb(tmp2, Address(tmp2, tmp1)); - __ cbz(tmp2, *stub->continuation()); - } + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == r0, "C1 must know about our slow call result register"); - ce->store_parameter(res, 0); + ce->store_parameter(obj, 0); ce->store_parameter(addr, 1); - if (is_strong) { - if (is_native) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); - } else { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); - } - } else if (is_weak) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - } else { - assert(is_phantom, "only remaining strength"); - __ far_call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); + __ far_call(RuntimeAddress(bs->load_reference_barrier_stub(stub->decorators()))); + if (obj != slow_result) { + __ mov(obj, slow_result); } __ b(*stub->continuation()); @@ -538,89 +497,27 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) { - __ prologue("shenandoah_pre_barrier", false); - - // arg0 : previous value of memory - - BarrierSet* bs = BarrierSet::barrier_set(); - - const Register pre_val = r0; - const Register thread = rthread; - const Register tmp = rscratch1; - - Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); - - Label done; - Label runtime; - - // Is marking still active? - Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ ldrb(tmp, gc_state); - __ tbz(tmp, ShenandoahHeap::MARKING_BITPOS, done); - - // Can we store original value in the thread's buffer? - __ ldr(tmp, queue_index); - __ cbz(tmp, runtime); - - __ sub(tmp, tmp, wordSize); - __ str(tmp, queue_index); - __ ldr(rscratch2, buffer); - __ add(tmp, tmp, rscratch2); - __ load_parameter(0, rscratch2); - __ str(rscratch2, Address(tmp, 0)); - __ b(done); - - __ bind(runtime); - __ push_call_clobbered_registers(); - __ load_parameter(0, pre_val); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); - __ pop_call_clobbered_registers(); - __ bind(done); - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ prologue("shenandoah_keepalive_barrier", false); + const Register tmp_obj = r0; + const Register tmp1 = r1; + const Register tmp2 = r2; + __ push(RegSet::of(tmp1, tmp2, tmp_obj), sp); + __ load_parameter(0, tmp_obj); + satb_barrier(sasm, noreg, tmp_obj, rthread, tmp1, tmp2); + __ pop(RegSet::of(tmp1, tmp2, tmp_obj), sp); __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); - // arg0 : object to be resolved - - __ push_call_clobbered_registers(); - __ load_parameter(0, r0); - __ load_parameter(1, r1); - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - if (is_strong) { - if (is_native) { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong))); - } else { - if (UseCompressedOops) { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow))); - } else { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong))); - } - } - } else if (is_weak) { - assert(!is_native, "weak must not be called off-heap"); - if (UseCompressedOops) { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow))); - } else { - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak))); - } - } else { - assert(is_phantom, "only remaining strength"); - assert(is_native, "phantom must only be called off-heap"); - __ lea(lr, RuntimeAddress(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom))); - } - __ blr(lr); - __ mov(rscratch1, r0); - __ pop_call_clobbered_registers(); - __ mov(r0, rscratch1); - + const Register tmp_obj = r0; + const Register tmp_addr = r1; + __ push(RegSet::of(tmp_addr), sp); + __ load_parameter(0, tmp_obj); + __ load_parameter(1, tmp_addr); + load_reference_barrier(sasm, tmp_obj, Address(tmp_addr, 0), decorators); + __ pop(RegSet::of(tmp_addr), sp); __ epilogue(); } @@ -700,8 +597,14 @@ void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, Mac ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp1, tmp2, tmp3, narrow); + atomic_memory_order order = acquire ? memory_order_seq_cst : memory_order_release; + // CAS! - __ cmpxchg(addr, oldval, newval, op_size, acquire, /* release */ true, weak, exchange ? res : noreg); + if (weak) { + __ cmpxchg_weak(addr, oldval, newval, op_size, order, exchange ? res : noreg); + } else { + __ cmpxchg(addr, oldval, newval, op_size, order, exchange ? res : noreg); + } // If we need a boolean result out of CAS, set the flag appropriately and promote the result. if (!exchange) { diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index bab4fb3b37a1..d25dd8871f9e 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -32,7 +32,7 @@ #include "gc/shenandoah/shenandoahBarrierSet.hpp" #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; #endif @@ -76,10 +76,11 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register tmp, Label& slow_path); #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); + + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif #ifdef COMPILER2 diff --git a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp index 1eb96cdb6e7c..7c320d835e7b 100644 --- a/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/z/zBarrierSetAssembler_aarch64.cpp @@ -283,10 +283,7 @@ void ZBarrierSetAssembler::store_barrier_medium(MacroAssembler* masm, // If we get this far, we know there is a young raw null value in the field. __ relocate(barrier_Relocation::spec(), ZBarrierRelocationFormatStoreGoodBeforeMov); __ movzw(rtmp1, barrier_Relocation::unpatched); - __ cmpxchg(rtmp2, zr, rtmp1, - Assembler::xword, - false /* acquire */, false /* release */, true /* weak */, - rtmp3); + __ cmpxchg_weak(rtmp2, zr, rtmp1, Assembler::xword, memory_order_relaxed, rtmp3); __ br(Assembler::NE, slow_path); __ bind(slow_path_continuation); diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index ad2e92438232..74e0395c81ec 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -207,8 +207,7 @@ instruct zCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newva Address ref_addr($mem$$Register); z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); - __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, memory_order_release); __ cset($res$$Register, Assembler::EQ); %} @@ -231,8 +230,7 @@ instruct zCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP ne Address ref_addr($mem$$Register); z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); - __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, noreg); + __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, memory_order_seq_cst); __ cset($res$$Register, Assembler::EQ); %} @@ -255,7 +253,7 @@ instruct zCompareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP n z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - false /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_release, $res$$Register); z_uncolor(masm, this, $res$$Register); %} @@ -278,7 +276,7 @@ instruct zCompareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iReg z_store_barrier(masm, this, ref_addr, $newval$$Register, $newval_tmp$$Register, rscratch2, true /* is_atomic */); z_color(masm, this, $oldval_tmp$$Register, $oldval$$Register); __ cmpxchg($mem$$Register, $oldval_tmp$$Register, $newval_tmp$$Register, Assembler::xword, - true /* acquire */, true /* release */, false /* weak */, $res$$Register); + memory_order_seq_cst, $res$$Register); z_uncolor(masm, this, $res$$Register); %} diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index 1c052b67503d..d5e220fd4a35 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -2231,8 +2231,7 @@ void MacroAssembler::profile_receiver_type(Register recv, Register mdp, int mdp_ // offset is no longer needed after the address is computed. lea(rscratch2, Address(mdp, offset)); - cmpxchg(/*addr*/ rscratch2, /*expected*/ zr, /*new*/ recv, Assembler::xword, - /*acquire*/ false, /*release*/ false, /*weak*/ true, noreg); + cmpxchg_weak(/*addr*/ rscratch2, /*expected*/ zr, /*new*/ recv, Assembler::xword, memory_order_relaxed); // CAS success means the slot now has the receiver we want. CAS failure means // something had claimed the slot concurrently: it can be the same receiver we want, @@ -3494,9 +3493,33 @@ void MacroAssembler::reinit_heapbase() void MacroAssembler::cmpxchg(Register addr, Register expected, Register new_val, enum operand_size size, - bool acquire, bool release, + enum atomic_memory_order order, bool weak, Register result) { + bool acquire, release; + + switch (order) { + case memory_order_relaxed: + acquire = false; + release = false; + break; + case memory_order_acquire: + acquire = true; + release = false; + break; + case memory_order_release: + acquire = false; + release = true; + break; + case memory_order_acq_rel: + case memory_order_seq_cst: + acquire = true; + release = true; + break; + default: + ShouldNotReachHere(); + } + if (result == noreg) result = rscratch1; BLOCK_COMMENT("cmpxchg {"); if (UseLSE) { @@ -7180,8 +7203,7 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register t1, R assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); orr(mark, mark, markWord::unlocked_value); eor(t, mark, markWord::unlocked_value); - cmpxchg(/*addr*/ obj, /*expected*/ mark, /*new*/ t, Assembler::xword, - /*acquire*/ true, /*release*/ false, /*weak*/ false, noreg); + cmpxchg(/*addr*/ obj, /*expected*/ mark, /*new*/ t, Assembler::xword, memory_order_acquire); br(Assembler::NE, slow); bind(push); @@ -7249,8 +7271,7 @@ void MacroAssembler::fast_unlock(Register obj, Register t1, Register t2, Registe // Try to unlock. Transition lock bits 0b00 => 0b01 assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea"); orr(t, mark, markWord::unlocked_value); - cmpxchg(obj, mark, t, Assembler::xword, - /*acquire*/ false, /*release*/ true, /*weak*/ false, noreg); + cmpxchg(obj, mark, t, Assembler::xword, memory_order_release); br(Assembler::EQ, unlocked); bind(push_and_slow); diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp index 8f1e662765ef..9c722cd297ec 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp @@ -32,6 +32,7 @@ #include "metaprogramming/enableIf.hpp" #include "oops/compressedOops.hpp" #include "oops/compressedKlass.hpp" +#include "runtime/atomicAccess.hpp" #include "runtime/vm_version.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" @@ -1239,12 +1240,25 @@ class MacroAssembler: public Assembler { str(rscratch1, adr); } +private: // A generic CAS; success or failure is in the EQ flag. // Clobbers rscratch1 void cmpxchg(Register addr, Register expected, Register new_val, - enum operand_size size, - bool acquire, bool release, bool weak, - Register result); + enum operand_size size, enum atomic_memory_order order, + bool weak, Register result); + +public: + void cmpxchg(Register addr, Register expected, Register new_val, + enum operand_size size, enum atomic_memory_order order, + Register result = noreg) { + cmpxchg(addr, expected, new_val, size, order, /* weak */ false, result); + } + + void cmpxchg_weak(Register addr, Register expected, Register new_val, + enum operand_size size, enum atomic_memory_order order, + Register result = noreg) { + cmpxchg(addr, expected, new_val, size, order, /* weak */ true, result); + } #ifdef ASSERT // Template short-hand support to clean-up after a failed call to trampoline diff --git a/src/hotspot/cpu/aarch64/register_aarch64.hpp b/src/hotspot/cpu/aarch64/register_aarch64.hpp index ab83307d526f..8d8856d3cf97 100644 --- a/src/hotspot/cpu/aarch64/register_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/register_aarch64.hpp @@ -28,6 +28,7 @@ #include "asm/register.hpp" #include "utilities/checkedCast.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/powerOfTwo.hpp" class VMRegImpl; @@ -513,25 +514,25 @@ template bool vs_write_before_read(const VSeq& vout, const VSeq& vi template VSeq vs_front(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base(), v.delta()); } template VSeq vs_back(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base() + N / 2 * v.delta(), v.delta()); } template VSeq vs_even(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base(), v.delta() * 2); } template VSeq vs_odd(const VSeq& v) { - static_assert(N > 0 && ((N & 1) == 0), "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); return VSeq(v.base() + v.delta(), v.delta() * 2); } diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index f89b6e2d579c..2ad7e00817c0 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -5442,6 +5442,7 @@ class StubGenerator: public StubCodeGenerator { // address supplied in base. template void vs_ldpq(const VSeq& v, Register base) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ ldpq(v[i], v[i+1], Address(base, 16 * i)); } @@ -5452,7 +5453,7 @@ class StubGenerator: public StubCodeGenerator { // in base using post-increment addressing template void vs_ldpq_post(const VSeq& v, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ ldpq(v[i], v[i+1], __ post(base, 32)); } @@ -5463,7 +5464,7 @@ class StubGenerator: public StubCodeGenerator { // supplied in base using post-increment addressing template void vs_stpq_post(const VSeq& v, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ stpq(v[i], v[i+1], __ post(base, 32)); } @@ -5474,7 +5475,7 @@ class StubGenerator: public StubCodeGenerator { // using post-increment addressing. template void vs_ld2_post(const VSeq& v, Assembler::SIMD_Arrangement T, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ ld2(v[i], v[i+1], T, __ post(base, 32)); } @@ -5485,7 +5486,7 @@ class StubGenerator: public StubCodeGenerator { // post-increment addressing. template void vs_st2_post(const VSeq& v, Assembler::SIMD_Arrangement T, Register base) { - static_assert((N & (N - 1)) == 0, "sequence length must be even"); + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N; i += 2) { __ st2(v[i], v[i+1], T, __ post(base, 32)); } @@ -5530,6 +5531,7 @@ class StubGenerator: public StubCodeGenerator { // offsets array template void vs_ldpq_indexed(const VSeq& v, Register base, int start, int (&offsets)[N/2]) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N/2; i++) { __ ldpq(v[2*i], v[2*i+1], Address(base, start + offsets[i])); } @@ -5577,6 +5579,7 @@ class StubGenerator: public StubCodeGenerator { template void vs_ld2_indexed(const VSeq& v, Assembler::SIMD_Arrangement T, Register base, Register tmp, int start, int (&offsets)[N/2]) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N/2; i++) { __ add(tmp, base, start + offsets[i]); __ ld2(v[2*i], v[2*i+1], T, tmp); @@ -5590,6 +5593,7 @@ class StubGenerator: public StubCodeGenerator { template void vs_st2_indexed(const VSeq& v, Assembler::SIMD_Arrangement T, Register base, Register tmp, int start, int (&offsets)[N/2]) { + static_assert(N > 0 && is_even(N), "sequence length must be even"); for (int i = 0; i < N/2; i++) { __ add(tmp, base, start + offsets[i]); __ st2(v[2*i], v[2*i+1], T, tmp); @@ -8654,6 +8658,123 @@ class StubGenerator: public StubCodeGenerator { return start; } + /** + * Arithmetic polynomial multiplication in Curve25519. The algorithm mimics + * the version in the IntegerPolynomial25519 class, including the use of all + * columns (no folding method). + * + * Arguments: + * + * Inputs: + * c_rarg0 - long[] aLimbs + * c_rarg1 - long[] bLimbs + * + * Output: + * c_rarg2 - long[] rLimbs result + */ + address generate_intpoly_mult_25519() { + StubId stub_id = StubId::stubgen_intpoly_mult_25519_id; + int entry_count = StubInfo::entry_count(stub_id); + assert(entry_count == 1, "sanity check"); + address start = load_archive_data(stub_id); + if (start != nullptr) { + return start; + } + __ align(CodeEntryAlignment); + StubCodeMark mark(this, stub_id); + start = __ pc(); + __ enter(); + + // Register Map + const Register aLimbs = c_rarg0; // r0 + const Register bLimbs = c_rarg1; // r1 + const Register rLimbs = c_rarg2; // r2 + + Register c[] = {r3, r4, r5, r6, r7, r8, r9, r10, r11, r12}; + Register a = r13; + Register b = r14; + Register term = r15; + Register low = r16; + Register high = r17; + + const int32_t limbs = 5; + const int32_t bpl = 51; + const int32_t rem = 64 - bpl; + const int32_t TERM = 19; + const int32_t columns = limbs * 2; + const uint64_t mask = (uint64_t) -1 >> rem; + const uint64_t CARRY_ADD = (uint64_t) 1 << (bpl - 1); + + __ mov(term, TERM); + for (int i = 0; i < columns; i++) { + __ mov(c[i], zr); + } + + // Perform high/low multiplication with signed 5x51 bit limbs + for (int i = 0; i < limbs; i++) { + __ ldr(b, Address(bLimbs, i * 8)); + for (int j = 0; j < limbs; j++) { + __ ldr(a, Address(aLimbs, j * 8)); + __ smulh(high, a, b); + __ mul(low, a, b); + __ extr(high, high, low, bpl); + __ andr(low, low, mask); + __ add(c[i + j], c[i + j], low); + __ add(c[i + j + 1], c[i + j + 1], high); + } + } + + for (int i = 0; i < limbs; i++) { + __ mul(c[i + 5], c[i + 5], term); + __ add(c[i], c[i], c[i + 5]); + } + + // Carry-add with reduction from high limb + Register tmp = low; + Register carry_add = high; + __ mov(carry_add, CARRY_ADD); + + // Limb 3 + __ add(tmp, c[3], carry_add); + __ asr(tmp, tmp, bpl); + __ add(c[4], c[4], tmp); + __ lsl(tmp, tmp, bpl); + __ sub(c[3], c[3], tmp); + + // Limb 4 + __ add(tmp, c[4], carry_add); + __ asr(tmp, tmp, bpl); + + // Reduce high order limb and fold back into low order limb + __ mul(term, tmp, term); + __ add(c[0], c[0], term); + + __ lsl(tmp, tmp, bpl); + __ sub(c[4], c[4], tmp); + + // Limbs 0 - 3 + for (int i = 0; i < (limbs - 1); i++) { + __ add(tmp, c[i], carry_add); + __ asr(tmp, tmp, bpl); + __ add(c[i + 1], c[i + 1], tmp); + __ lsl(tmp, tmp, bpl); + __ sub(c[i], c[i], tmp); + } + + for (int i = 0; i < limbs; i++) { + __ str(c[i], Address(rLimbs, i * 8)); + } + + __ mov(r0, 0); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(lr); + + // record the stub entry and end + store_archive_data(stub_id, start, __ pc()); + + return start; + } + void bcax5(Register a0, Register a1, Register a2, Register a3, Register a4, Register tmp0, Register tmp1, Register tmp2) { __ bic(tmp0, a2, a1); // for a0 @@ -13791,6 +13912,15 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_poly1305_processBlocks = generate_poly1305_processBlocks(); } + // The difference between AArch64 vs. x86_64 intrinsics implementation + // include the lack of square() intrinsics; usage caused a 3.3% performance + // degradation due to the efficiencies of the symmetric squaring shape in + // Java vs. the inefficiencies of the leaf calls and the additional cycles + // required for 64 bit multiplication in AArch64. + if (UseIntPoly25519Intrinsics) { + StubRoutines::_intpoly_mult_25519 = generate_intpoly_mult_25519(); + } + // generate Adler32 intrinsics code if (UseAdler32Intrinsics) { StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32(); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index e746447e013f..5462ccf2a764 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -661,6 +661,10 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UsePoly1305Intrinsics, true); } + if (FLAG_IS_DEFAULT(UseIntPoly25519Intrinsics)) { + FLAG_SET_DEFAULT(UseIntPoly25519Intrinsics, true); + } + if (FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) { FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, true); } diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 45ae283e05a2..7ae3381600e2 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1112,26 +1112,26 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 30 : FLOATPRESSURE; } -// Register for DIVI projection of divmodI -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } diff --git a/src/hotspot/cpu/arm/frame_arm.hpp b/src/hotspot/cpu/arm/frame_arm.hpp index 026bd993981c..2ef44414e1c0 100644 --- a/src/hotspot/cpu/arm/frame_arm.hpp +++ b/src/hotspot/cpu/arm/frame_arm.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ public: enum { - pc_return_offset = 0, // All frames link_offset = 0, return_addr_offset = 1, diff --git a/src/hotspot/cpu/ppc/assembler_ppc.cpp b/src/hotspot/cpu/ppc/assembler_ppc.cpp index ab16fc437e9e..406d0b446a41 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.cpp @@ -75,23 +75,46 @@ int Assembler::branch_destination(int inst, int pos) { return r; } -// Low-level andi-one-instruction-macro. -void Assembler::andi(Register a, Register s, const long ui16) { - if (is_power_of_2(((unsigned long) ui16)+1)) { +// Low-level andi-one-instruction-macro. May clobber CR0. +void Assembler::andi(Register a, Register s, julong int_or_long_const) { + // Instructions which don't set CR0 are preferred. + if (int_or_long_const == 0) { + // should not be handled as pow2minus1 + li(a, 0); + } else if (is_power_of_2(int_or_long_const + 1)) { // pow2minus1 - clrldi(a, s, 64 - log2i_exact((((unsigned long) ui16)+1))); - } else if (is_power_of_2((jlong) ui16)) { - // pow2 - rlwinm(a, s, 0, 31 - log2i_exact((jlong) ui16), 31 - log2i_exact((jlong) ui16)); - } else if (is_power_of_2((jlong)-ui16)) { - // negpow2 - clrrdi(a, s, log2i_exact((jlong)-ui16)); + clrldi(a, s, 64 - log2i_exact(int_or_long_const + 1)); + } else if (is_power_of_2(-int_or_long_const)) { + // negpow2 (includes (julong)min_jlong) + clrrdi(a, s, log2i_exact(-int_or_long_const)); + } else if (is_uimm((jlong)int_or_long_const, 32) && has_consecutive_ones(int_or_long_const)) { + // consecutive ones + rlwinm(a, s, 0, count_leading_zeros((uint32_t)int_or_long_const), + 31 - count_trailing_zeros((uint32_t)int_or_long_const)); + } else if (is_uimm((jlong)int_or_long_const, 16)) { + // side effect: clobbers CR0 + andi_(a, s, int_or_long_const); } else { - assert(is_uimm(ui16, 16), "must be 16-bit unsigned immediate"); - andi_(a, s, ui16); + assert(is_uimm((jlong)int_or_long_const, 32) && (int_or_long_const & 0xFFFF) == 0, + "not encodable: " UINT64_FORMAT_X, int_or_long_const); + // side effect: clobbers CR0 + andis_(a, s, int_or_long_const >> 16); } } +// Check if int_or_long_const is supported by Assembler::andi. +bool Assembler::andi_supports(julong int_or_long_const) { + // 16 bit always possible by andi_ (but other instructions are preferred) + if (is_uimm((jlong)int_or_long_const, 16)) return true; + + // special cases 32 bit: higher 16 bit and consecutive ones are supported + if (is_uimm((jlong)int_or_long_const, 32) && + ((int_or_long_const & 0xFFFF) == 0 || has_consecutive_ones(int_or_long_const))) return true; + + // special cases 64 bit: clrldi, clrrdi + return is_power_of_2(int_or_long_const + 1) || is_power_of_2(-int_or_long_const); +} + // RegisterOrConstant version. void Assembler::ld(Register d, RegisterOrConstant roc, Register s1) { if (roc.is_constant()) { diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index f62c93e466cf..77c7f63cd062 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -1048,6 +1048,13 @@ class Assembler : public AbstractAssembler { return (julong)x < maxplus1; } + // Test if x has exactly one consecutive range of one bits (e.g. 00111000) + static bool has_consecutive_ones(julong x) { + if (x == max_julong) return true; + if (x == 0) return false; + return is_power_of_2((x >> count_trailing_zeros(x)) + 1); + } + protected: // helpers @@ -1606,7 +1613,8 @@ class Assembler : public AbstractAssembler { inline void isel_0( Register d, ConditionRegister cr, Condition cc, Register b = noreg); // PPC 1, section 3.3.11, Fixed-Point Logical Instructions - void andi( Register a, Register s, long ui16); // optimized version + void andi( Register a, Register s, julong int_or_long_const); // optimized version, may clobber CR0 + static bool andi_supports(julong int_or_long_const); inline void andi_( Register a, Register s, int ui16); inline void andis_( Register a, Register s, int ui16); inline void ori( Register a, Register s, int ui16); diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index 78fae5c26775..1ec710aad291 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -1669,26 +1669,40 @@ void LIR_Assembler::logic_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr d = dest->as_register_lo(); l = left->as_register_lo(); } - long uimms = (unsigned long)uimm >> 16, - uimmss = (unsigned long)uimm >> 32; + long uimms = (unsigned long)uimm >> 16; switch (code) { case lir_logic_and: - if (uimmss != 0 || (uimms != 0 && (uimm & 0xFFFF) != 0) || is_power_of_2(uimm)) { - __ andi(d, l, uimm); // special cases - } else if (uimms != 0) { __ andis_(d, l, uimms); } - else { __ andi_(d, l, uimm); } + if (Assembler::andi_supports(uimm)) { + __ andi(d, l, uimm); // includes andis_ and special cases + } else { // for operands which are not generated by LIRGenerator::do_LogicOp + __ load_const_optimized(R0, uimm); + __ andr(d, l, R0); + } break; case lir_logic_or: - if (uimms != 0) { assert((uimm & 0xFFFF) == 0, "sanity"); __ oris(d, l, uimms); } - else { __ ori(d, l, uimm); } + if (Assembler::is_uimm(uimm, 16)) { + __ ori(d, l, uimm); + } else if ((uimm & 0xFFFF) == 0 && Assembler::is_uimm(uimms, 16)) { + __ oris(d, l, uimms); + } else { // for operands which are not generated by LIRGenerator::do_LogicOp + __ load_const_optimized(R0, uimm); + __ orr(d, l, R0); + } break; case lir_logic_xor: - if (uimm == -1) { __ nand(d, l, l); } // special case - else if (uimms != 0) { assert((uimm & 0xFFFF) == 0, "sanity"); __ xoris(d, l, uimms); } - else { __ xori(d, l, uimm); } + if (Assembler::is_uimm(uimm, 16)) { + __ xori(d, l, uimm); + } else if ((uimm & 0xFFFF) == 0 && Assembler::is_uimm(uimms, 16)) { + __ xoris(d, l, uimms); + } else if (uimm == -1) { + __ nand(d, l, l); // special case + } else { // for operands which are not generated by LIRGenerator::do_LogicOp + __ load_const_optimized(R0, uimm); + __ xorr(d, l, R0); + } break; default: ShouldNotReachHere(); diff --git a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp index a652a155f62b..56c069053c6b 100644 --- a/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRGenerator_ppc.cpp @@ -578,18 +578,13 @@ inline bool can_handle_logic_op_as_uimm(ValueType *type, Bytecodes::Code bc) { Assembler::is_uimm((jlong)((julong)int_or_long_const >> 16), 16)) return true; // see Assembler::andi - if (bc == Bytecodes::_iand && - (is_power_of_2(int_or_long_const+1) || - is_power_of_2(int_or_long_const) || - is_power_of_2(-int_or_long_const))) return true; - if (bc == Bytecodes::_land && - (is_power_of_2((unsigned long)int_or_long_const+1) || - (Assembler::is_uimm(int_or_long_const, 32) && is_power_of_2(int_or_long_const)) || - (int_or_long_const != min_jlong && is_power_of_2(-int_or_long_const)))) return true; + if ((bc == Bytecodes::_iand || bc == Bytecodes::_land)) + return Assembler::andi_supports(int_or_long_const); // special case: xor -1 - if ((bc == Bytecodes::_ixor || bc == Bytecodes::_lxor) && - int_or_long_const == -1) return true; + if ((bc == Bytecodes::_ixor || bc == Bytecodes::_lxor)) + return (int_or_long_const == -1); + return false; } diff --git a/src/hotspot/cpu/ppc/frame_ppc.hpp b/src/hotspot/cpu/ppc/frame_ppc.hpp index 14743c7d75ab..bf49bbb7e018 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2025 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -391,8 +391,6 @@ } enum { - // normal return address is 1 bundle past PC - pc_return_offset = 0, // size, in words, of frame metadata (e.g. pc and link) metadata_words = sizeof(java_abi) >> LogBytesPerWord, // size, in words, of metadata at frame bottom, i.e. it is not part of the diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 582327282fd8..b17f0f924aeb 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -56,10 +56,11 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler *masm, Register base, RegisterOrConstant ind_or_offs, Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { if (ShenandoahSATBBarrier) { __ block_comment("satb_barrier (shenandoahgc) {"); - satb_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); + satb_barrier_impl(masm, 0, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level, extra_stack_space); __ block_comment("} satb_barrier (shenandoahgc)"); } } @@ -68,10 +69,11 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler *masm, Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { if (ShenandoahLoadRefBarrier) { __ block_comment("load_reference_barrier (shenandoahgc) {"); - load_reference_barrier_impl(masm, decorators, base, ind_or_offs, dst, tmp1, tmp2, preservation_level); + load_reference_barrier_impl(masm, decorators, base, ind_or_offs, dst, tmp1, tmp2, preservation_level, extra_stack_space); __ block_comment("} load_reference_barrier (shenandoahgc)"); } } @@ -205,7 +207,8 @@ void ShenandoahBarrierSetAssembler::satb_barrier_impl(MacroAssembler *masm, Deco Register base, RegisterOrConstant ind_or_offs, Register pre_val, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { assert(ShenandoahSATBBarrier, "Should be checked by caller"); assert_different_registers(tmp1, tmp2, pre_val, noreg); @@ -299,7 +302,7 @@ void ShenandoahBarrierSetAssembler::satb_barrier_impl(MacroAssembler *masm, Deco if (preserve_gp_registers) { nbytes_save = (preserve_fp_registers ? MacroAssembler::num_volatile_gp_regs + MacroAssembler::num_volatile_fp_regs - : MacroAssembler::num_volatile_gp_regs) * BytesPerWord; + : MacroAssembler::num_volatile_gp_regs) * BytesPerWord + extra_stack_space; __ save_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers); } @@ -343,7 +346,8 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier_impl( Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level) { + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space) { if (ind_or_offs.is_register()) { assert_different_registers(tmp1, tmp2, base, ind_or_offs.as_register(), dst, noreg); } else { @@ -430,7 +434,7 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier_impl( if (preserve_gp_registers) { nbytes_save = (preserve_fp_registers ? MacroAssembler::num_volatile_gp_regs + MacroAssembler::num_volatile_fp_regs - : MacroAssembler::num_volatile_gp_regs) * BytesPerWord; + : MacroAssembler::num_volatile_gp_regs) * BytesPerWord + extra_stack_space; __ save_volatile_gprs(R1_SP, -nbytes_save, preserve_fp_registers); } @@ -693,243 +697,119 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler *ce, ShenandoahPreBarrierStub *stub) { - __ block_comment("gen_pre_barrier_stub (shenandoahgc) {"); - - ShenandoahBarrierSetC1 *bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { + __ block_comment("keepalive_barrier_stub (shenandoahgc) {"); __ bind(*stub->entry()); - // GC status has already been verified by 'ShenandoahBarrierSetC1::pre_barrier'. - // This stub is the slowpath of that function. + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); - assert(stub->pre_val()->is_register(), "pre_val must be a register"); - Register pre_val = stub->pre_val()->as_register(); + Register obj = stub->obj()->as_register(); - // If 'do_load()' returns false, the to-be-stored value is already available in 'stub->pre_val()' - // ("preloaded mode" of the store barrier). + // If 'do_load()' returns false, the to-be-stored value is already available in 'obj' if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, false); } - // Fast path: Reference is null. - __ cmpdi(CR0, pre_val, 0); + // Fast path: reference is null. + __ cmpdi(CR0, obj, 0); __ bc_far_optimized(Assembler::bcondCRbiIs1_bhintNoHint, __ bi0(CR0, Assembler::equal), *stub->continuation()); // Argument passing via the stack. - __ std(pre_val, -8, R1_SP); + __ std(obj, -8, R1_SP); - __ load_const_optimized(R0, bs->pre_barrier_c1_runtime_code_blob()->code_begin()); + address blob_addr = bs->keepalive_barrier_stub(); + __ load_const_optimized(R0, blob_addr); __ call_stub(R0); __ b(*stub->continuation()); - __ block_comment("} gen_pre_barrier_stub (shenandoahgc)"); + __ block_comment("} keepalive_barrier_stub (shenandoahgc)"); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler *ce, - ShenandoahLoadReferenceBarrierStub *stub) { - __ block_comment("gen_load_reference_barrier_stub (shenandoahgc) {"); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { + __ block_comment("load_reference_barrier_stub (shenandoahgc) {"); - ShenandoahBarrierSetC1 *bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); __ bind(*stub->entry()); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); + Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); - assert_different_registers(addr, res, tmp1, tmp2); - - assert(R3_RET == res, "res must be r3"); - - if (res != obj) { - __ mr(res, obj); - } - - DecoratorSet decorators = stub->decorators(); - - /* ==== Check whether region is in collection set ==== */ - // GC status (unstable) has already been verified by 'ShenandoahBarrierSetC1::load_reference_barrier_impl'. - // This stub is the slowpath of that function. - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - - if (is_strong) { - // Check whether object is in collection set. - __ load_const_optimized(tmp2, ShenandoahHeap::in_cset_fast_test_addr(), tmp1); - __ srdi(tmp1, obj, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ lbzx(tmp2, tmp1, tmp2); + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == R3_RET, "C1 must know about our slow call result register"); - __ andi_(tmp2, tmp2, 1); - __ bc_far_optimized(Assembler::bcondCRbiIs1_bhintNoHint, __ bi0(CR0, Assembler::equal), *stub->continuation()); - } - - address blob_addr = nullptr; + // Argument passing via the stack. + __ std(obj, -8, R1_SP); + __ std(addr, -16, R1_SP); - if (is_strong) { - if (is_native) { - blob_addr = bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin(); - } else { - blob_addr = bs->load_reference_barrier_strong_rt_code_blob()->code_begin(); - } - } else if (is_weak) { - blob_addr = bs->load_reference_barrier_weak_rt_code_blob()->code_begin(); - } else { - assert(is_phantom, "only remaining strength"); - blob_addr = bs->load_reference_barrier_phantom_rt_code_blob()->code_begin(); + address blob_addr = bs->load_reference_barrier_stub(stub->decorators()); + __ load_const_optimized(R0, blob_addr); + __ call_stub(R0); + if (obj != slow_result) { + __ mr(obj, slow_result); } - assert(blob_addr != nullptr, "code blob cannot be found"); - - // Argument passing via the stack. 'obj' is passed implicitly (as asserted above). - __ std(addr, -8, R1_SP); - - __ load_const_optimized(tmp1, blob_addr, tmp2); - __ call_stub(tmp1); - - // 'res' is 'R3_RET'. The result is thus already in the correct register. - __ b(*stub->continuation()); - __ block_comment("} gen_load_reference_barrier_stub (shenandoahgc)"); + __ block_comment("} load_reference_barrier_stub (shenandoahgc)"); } #undef __ #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler *sasm) { - __ block_comment("generate_c1_pre_barrier_runtime_stub (shenandoahgc) {"); +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ block_comment("keepalive_barrier_runtime_stub (shenandoahgc) {"); - Label runtime, skip_barrier; - BarrierSet *bs = BarrierSet::barrier_set(); - - // Argument passing via the stack. - const int caller_stack_slots = 3; - - Register R0_pre_val = R0; - __ ld(R0, -8, R1_SP); - Register R11_tmp1 = R11_scratch1; - __ std(R11_tmp1, -16, R1_SP); - Register R12_tmp2 = R12_scratch2; - __ std(R12_tmp2, -24, R1_SP); - - /* ==== Check whether marking is active ==== */ - // Even though gc status was checked in 'ShenandoahBarrierSetAssembler::gen_pre_barrier_stub', - // another check is required as a safepoint might have been reached in the meantime (JDK-8140588). - __ lbz(R12_tmp2, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), R16_thread); - - __ andi_(R12_tmp2, R12_tmp2, ShenandoahHeap::MARKING); - __ beq(CR0, skip_barrier); + Register obj = R3_ARG1; + Register tmp1 = R11_scratch1; + Register tmp2 = R12_scratch2; - /* ==== Add previous value directly to thread-local SATB mark queue ==== */ - // Check queue's capacity. Jump to runtime if no free slot is available. - __ ld(R12_tmp2, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()), R16_thread); - __ cmpdi(CR0, R12_tmp2, 0); - __ beq(CR0, runtime); + // Save registers we are about to clobber + __ std(obj, -16, R1_SP); + __ std(tmp1, -24, R1_SP); + __ std(tmp2, -32, R1_SP); - // Capacity suffices. Decrement the queue's size by one slot (size of one oop). - __ addi(R12_tmp2, R12_tmp2, -wordSize); - __ std(R12_tmp2, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()), R16_thread); + // Pull the arguments from stack + __ ld(obj, -8, R1_SP); - // Enqueue the previous value and skip the runtime invocation. - __ ld(R11_tmp1, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()), R16_thread); - __ stdx(R0_pre_val, R11_tmp1, R12_tmp2); - __ b(skip_barrier); + satb_barrier(sasm, noreg, noreg, obj, tmp1, tmp2, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, 4 * BytesPerWord); - __ bind(runtime); - - /* ==== Invoke runtime to commit SATB mark queue to gc and allocate a new buffer ==== */ - // Save to-be-preserved registers. - const int nbytes_save = (MacroAssembler::num_volatile_regs + caller_stack_slots) * BytesPerWord; - __ save_volatile_gprs(R1_SP, -nbytes_save); - __ save_LR(R11_tmp1); - __ push_frame_reg_args(nbytes_save, R11_tmp1); - - // Invoke runtime. - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), R0_pre_val); - - // Restore to-be-preserved registers. - __ pop_frame(); - __ restore_LR(R11_tmp1); - __ restore_volatile_gprs(R1_SP, -nbytes_save); - - __ bind(skip_barrier); - - // Restore spilled registers. - __ ld(R11_tmp1, -16, R1_SP); - __ ld(R12_tmp2, -24, R1_SP); + // Restore registers + __ ld(tmp2, -32, R1_SP); + __ ld(tmp1, -24, R1_SP); + __ ld(obj, -16, R1_SP); __ blr(); - __ block_comment("} generate_c1_pre_barrier_runtime_stub (shenandoahgc)"); + __ block_comment("} keepalive_barrier_runtime_stub (shenandoahgc)"); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler *sasm, - DecoratorSet decorators) { - __ block_comment("generate_c1_load_reference_barrier_runtime_stub (shenandoahgc) {"); - - // Argument passing via the stack. - const int caller_stack_slots = 1; - - // Save to-be-preserved registers. - const int nbytes_save = (MacroAssembler::num_volatile_regs - 1 // 'R3_ARG1' is skipped - + caller_stack_slots) * BytesPerWord; - __ save_volatile_gprs(R1_SP, -nbytes_save, true, false); - - // Load arguments from stack. - // No load required, as caller has already loaded obj into R3. - Register R3_obj = R3_ARG1; - Register R4_load_addr = R4_ARG2; - __ ld(R4_load_addr, -8, R1_SP); - - Register R11_tmp = R11_scratch1; - - /* ==== Invoke runtime ==== */ - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { + __ block_comment("load_reference_barrier_runtime_stub (shenandoahgc) {"); - address jrt_address = nullptr; + Register obj = R3_ARG1; + Register addr = R4_ARG2; + Register tmp1 = R11_scratch1; + Register tmp2 = R12_scratch2; - if (is_strong) { - if (is_native) { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } else { - if (UseCompressedOops) { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow); - } else { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } - } - } else if (is_weak) { - assert(!is_native, "weak load reference barrier must not be called off-heap"); - if (UseCompressedOops) { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow); - } else { - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); - } - } else { - assert(is_phantom, "reference type must be phantom"); - assert(is_native, "phantom load reference barrier must be called off-heap"); - jrt_address = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom); - } - assert(jrt_address != nullptr, "load reference barrier runtime routine cannot be found"); + // Save registers we are about to clobber + __ std(addr, -24, R1_SP); + __ std(tmp1, -32, R1_SP); + __ std(tmp2, -40, R1_SP); - __ save_LR(R11_tmp); - __ push_frame_reg_args(nbytes_save, R11_tmp); + // Pull the arguments from the stack + __ ld(obj, -8, R1_SP); + __ ld(addr, -16, R1_SP); - // Invoke runtime. Arguments are already stored in the corresponding registers. - __ call_VM_leaf(jrt_address, R3_obj, R4_load_addr); + load_reference_barrier(sasm, decorators, addr, noreg, obj, tmp1, tmp2, + MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, 5 * BytesPerWord); - // Restore to-be-preserved registers. - __ pop_frame(); - __ restore_LR(R11_tmp); - __ restore_volatile_gprs(R1_SP, -nbytes_save, true, false); // Skip 'R3_RET' register. + // Restore registers + __ ld(tmp2, -40, R1_SP); + __ ld(tmp1, -32, R1_SP); + __ ld(addr, -24, R1_SP); __ blr(); - __ block_comment("} generate_c1_load_reference_barrier_runtime_stub (shenandoahgc)"); + __ block_comment("} load_reference_barrier_runtime_stub (shenandoahgc)"); } #undef __ diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index bd1043c2d76b..8d741e6104b4 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -34,7 +34,7 @@ #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; @@ -56,7 +56,8 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register base, RegisterOrConstant ind_or_offs, Register pre_val, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); void card_barrier(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, @@ -66,7 +67,8 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); /* ==== Helper methods for barrier implementations ==== */ void gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators, @@ -78,28 +80,26 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { /* ==== C1 stubs ==== */ #ifdef COMPILER1 + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); - + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif /* ==== Available barriers (facades of the actual implementations) ==== */ void satb_barrier(MacroAssembler* masm, Register base, RegisterOrConstant ind_or_offs, Register tmp1, Register tmp2, Register tmp3, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); void load_reference_barrier(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, Register dst, Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level); + MacroAssembler::PreservationLevel preservation_level, + int extra_stack_space = 0); /* ==== Access api ==== */ virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, BasicType type, diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 9bec99e90cc8..896128f99cc8 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2351,26 +2351,26 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 28 : FLOATPRESSURE; } -// Register for DIVI projection of divmodI. -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI. -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL. -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL. -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } @@ -3556,9 +3556,6 @@ ins_attrib ins_alignment(1); ins_attrib ins_cannot_rematerialize(false); ins_attrib ins_should_rematerialize(false); -// Instruction has variable size depending on alignment. -ins_attrib ins_variable_size_depending_on_alignment(false); - // Instruction is a nop. ins_attrib ins_is_nop(false); @@ -7015,8 +7012,6 @@ instruct cmovF_reg(cmpOp cmp, flagsRegSrc crx, regF dst, regF src) %{ match(Set dst (CMoveF (Binary cmp crx) (Binary dst src))); ins_cost(DEFAULT_COST+BRANCH_COST); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} size(8); ins_encode %{ @@ -7034,8 +7029,6 @@ instruct cmovD_reg(cmpOp cmp, flagsRegSrc crx, regD dst, regD src) %{ match(Set dst (CMoveD (Binary cmp crx) (Binary dst src))); ins_cost(DEFAULT_COST+BRANCH_COST); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVEF $cmp, $crx, $dst, $src\n\t" %} size(8); ins_encode %{ @@ -8276,8 +8269,6 @@ instruct cmovI_bne_negI_reg(iRegIdst dst, flagsRegSrc crx, iRegIsrc src1) %{ effect(USE_DEF dst, USE src1, USE crx); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVE $dst, neg($src1), $crx" %} size(8); ins_encode %{ @@ -8334,8 +8325,6 @@ instruct cmovL_bne_negL_reg(iRegLdst dst, flagsRegSrc crx, iRegLsrc src1) %{ effect(USE_DEF dst, USE src1, USE crx); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVE $dst, neg($src1), $crx" %} size(8); ins_encode %{ @@ -9166,61 +9155,14 @@ instruct andI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{ ins_pipe(pipe_class_default); %} -// Left shifted Immediate And -instruct andI_reg_immIhi16(iRegIdst dst, iRegIsrc src1, immIhi16 src2, flagsRegCR0 cr0) %{ - match(Set dst (AndI src1 src2)); - effect(KILL cr0); - format %{ "ANDIS $dst, $src1, $src2.hi" %} - size(4); - ins_encode %{ - __ andis_($dst$$Register, $src1$$Register, (int)((unsigned short)(($src2$$constant & 0xFFFF0000) >> 16))); - %} - ins_pipe(pipe_class_default); -%} - -// Immediate And -instruct andI_reg_uimm16(iRegIdst dst, iRegIsrc src1, uimmI16 src2, flagsRegCR0 cr0) %{ +instruct andI_reg_immI(iRegIdst dst, iRegIsrc src1, immI src2, flagsRegCR0 cr0) %{ match(Set dst (AndI src1 src2)); + predicate(Assembler::andi_supports((juint)(n->in(2)->get_int()))); effect(KILL cr0); - format %{ "ANDI $dst, $src1, $src2" %} size(4); ins_encode %{ - // FIXME: avoid andi_ ? - __ andi_($dst$$Register, $src1$$Register, $src2$$constant); - %} - ins_pipe(pipe_class_default); -%} - -// Immediate And where the immediate is a negative power of 2. -instruct andI_reg_immInegpow2(iRegIdst dst, iRegIsrc src1, immInegpow2 src2) %{ - match(Set dst (AndI src1 src2)); - format %{ "ANDWI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrrdi($dst$$Register, $src1$$Register, log2i_exact(-(juint)$src2$$constant)); - %} - ins_pipe(pipe_class_default); -%} - -instruct andI_reg_immIpow2minus1(iRegIdst dst, iRegIsrc src1, immIpow2minus1 src2) %{ - match(Set dst (AndI src1 src2)); - format %{ "ANDWI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((juint)$src2$$constant + 1u)); - %} - ins_pipe(pipe_class_default); -%} - -instruct andI_reg_immIpowerOf2(iRegIdst dst, iRegIsrc src1, immIpowerOf2 src2) %{ - match(Set dst (AndI src1 src2)); - predicate(UseRotateAndMaskInstructionsPPC64); - format %{ "ANDWI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - int bitpos = 31 - log2i_exact((juint)$src2$$constant); - __ rlwinm($dst$$Register, $src1$$Register, 0, bitpos, bitpos); + __ andi($dst$$Register, $src1$$Register, (juint)$src2$$constant); // optimized version %} ins_pipe(pipe_class_default); %} @@ -9238,50 +9180,27 @@ instruct andL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{ ins_pipe(pipe_class_default); %} -// Immediate And long -instruct andL_reg_uimm16(iRegLdst dst, iRegLsrc src1, uimmL16 src2, flagsRegCR0 cr0) %{ +instruct andL_reg_immL(iRegLdst dst, iRegLsrc src1, immL src2, flagsRegCR0 cr0) %{ match(Set dst (AndL src1 src2)); + predicate(Assembler::andi_supports(n->in(2)->get_long())); effect(KILL cr0); - format %{ "ANDI $dst, $src1, $src2 \t// long" %} size(4); ins_encode %{ - // FIXME: avoid andi_ ? - __ andi_($dst$$Register, $src1$$Register, $src2$$constant); - %} - ins_pipe(pipe_class_default); -%} - -// Immediate And Long where the immediate is a negative power of 2. -instruct andL_reg_immLnegpow2(iRegLdst dst, iRegLsrc src1, immLnegpow2 src2) %{ - match(Set dst (AndL src1 src2)); - format %{ "ANDDI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrrdi($dst$$Register, $src1$$Register, log2i_exact(-(julong)$src2$$constant)); - %} - ins_pipe(pipe_class_default); -%} - -instruct andL_reg_immLpow2minus1(iRegLdst dst, iRegLsrc src1, immLpow2minus1 src2) %{ - match(Set dst (AndL src1 src2)); - format %{ "ANDDI $dst, $src1, $src2" %} - size(4); - ins_encode %{ - __ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((julong)$src2$$constant + 1ull)); + __ andi($dst$$Register, $src1$$Register, $src2$$constant); // optimized version %} ins_pipe(pipe_class_default); %} // AndL + ConvL2I. -instruct convL2I_andL_reg_immLpow2minus1(iRegIdst dst, iRegLsrc src1, immLpow2minus1 src2) %{ +instruct convL2I_andL_reg_immL(iRegIdst dst, iRegLsrc src1, immL src2, flagsRegCR0 cr0) %{ match(Set dst (ConvL2I (AndL src1 src2))); - ins_cost(DEFAULT_COST); - - format %{ "ANDDI $dst, $src1, $src2 \t// long + l2i" %} + predicate(Assembler::andi_supports(n->in(1)->in(2)->get_long())); + effect(KILL cr0); + format %{ "ANDI $dst, $src1, $src2 \t// long + l2i" %} size(4); ins_encode %{ - __ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((julong)$src2$$constant + 1ull)); + __ andi($dst$$Register, $src1$$Register, $src2$$constant); // optimized version %} ins_pipe(pipe_class_default); %} @@ -10044,8 +9963,6 @@ instruct cmovI_bso_stackSlotL(iRegIdst dst, flagsRegSrc crx, stackSlotL src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVI $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); @@ -10057,8 +9974,6 @@ instruct cmovI_bso_reg(iRegIdst dst, flagsRegSrc crx, regD src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVI $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); @@ -10219,8 +10134,6 @@ instruct cmovL_bso_stackSlotL(iRegLdst dst, flagsRegSrc crx, stackSlotL src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVL $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_stackSlotL(dst, crx, src) ); @@ -10232,8 +10145,6 @@ instruct cmovL_bso_reg(iRegLdst dst, flagsRegSrc crx, regD src) %{ effect(DEF dst, USE crx, USE src); predicate(false); - ins_variable_size_depending_on_alignment(true); - format %{ "CMOVL $crx, $dst, $src" %} size(8); ins_encode( enc_cmove_bso_reg(dst, crx, src) ); @@ -10853,86 +10764,24 @@ instruct cmpFUnordered_reg_reg(flagsReg crx, regF src1, regF src2) %{ ins_pipe(pipe_class_default); %} -instruct cmov_bns_less(flagsReg crx) %{ - // no match-rule, false predicate - effect(DEF crx); - predicate(false); - - ins_variable_size_depending_on_alignment(true); +// Compare floating, generate condition code. +instruct cmpF_reg_reg(flagsReg crx, regF src1, regF src2) %{ + match(Set crx (CmpF src1 src2)); + ins_cost(DEFAULT_COST+BRANCH_COST); - format %{ "CMOV $crx" %} - size(12); + format %{ "CMPF $crx, $src1, $src2" %} + size(16); ins_encode %{ Label done; - __ bns($crx$$CondRegister, done); // not unordered -> keep crx + __ fcmpu($crx$$CondRegister, $src1$$FloatRegister, $src2$$FloatRegister); + __ bns($crx$$CondRegister, done); __ li(R0, 0); - __ cmpwi($crx$$CondRegister, R0, 1); // unordered -> set crx to 'less' + __ cmpwi($crx$$CondRegister, R0, 1); __ bind(done); %} ins_pipe(pipe_class_default); %} -// Compare floating, generate condition code. -instruct cmpF_reg_reg_Ex(flagsReg crx, regF src1, regF src2) %{ - // FIXME: should we match 'If cmp (CmpF src1 src2))' ?? - // - // The following code sequence occurs a lot in mpegaudio: - // - // block BXX: - // 0: instruct cmpFUnordered_reg_reg (cmpF_reg_reg-0): - // cmpFUrd CR6, F11, F9 - // 4: instruct cmov_bns_less (cmpF_reg_reg-1): - // cmov CR6 - // 8: instruct branchConSched: - // B_FARle CR6, B56 P=0.500000 C=-1.000000 - match(Set crx (CmpF src1 src2)); - ins_cost(DEFAULT_COST+BRANCH_COST); - - format %{ "CMPF $crx, $src1, $src2 \t// postalloc expanded" %} - postalloc_expand %{ - // - // replaces - // - // region src1 src2 - // \ | | - // crx=cmpF_reg_reg - // - // with - // - // region src1 src2 - // \ | | - // crx=cmpFUnordered_reg_reg - // | - // ^ region - // | \ - // crx=cmov_bns_less - // - - // Create new nodes. - MachNode *m1 = new cmpFUnordered_reg_regNode(); - MachNode *m2 = new cmov_bns_lessNode(); - - // inputs for new nodes - m1->add_req(n_region, n_src1, n_src2); - m2->add_req(n_region); - m2->add_prec(m1); - - // operands for new nodes - m1->_opnds[0] = op_crx; - m1->_opnds[1] = op_src1; - m1->_opnds[2] = op_src2; - m2->_opnds[0] = op_crx; - - // registers for new nodes - ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - - // Insert new nodes. - nodes->push(m1); - nodes->push(m2); - %} -%} - // Compare float, generate -1,0,1 instruct cmpF3_reg_reg(iRegIdst dst, regF src1, regF src2, flagsRegCR0 cr0) %{ match(Set dst (CmpF3 src1 src2)); @@ -10968,53 +10817,21 @@ instruct cmpDUnordered_reg_reg(flagsReg crx, regD src1, regD src2) %{ ins_pipe(pipe_class_default); %} -instruct cmpD_reg_reg_Ex(flagsReg crx, regD src1, regD src2) %{ +instruct cmpD_reg_reg(flagsReg crx, regD src1, regD src2) %{ match(Set crx (CmpD src1 src2)); ins_cost(DEFAULT_COST+BRANCH_COST); - format %{ "CmpD $crx, $src1, $src2 \t// postalloc expanded" %} - postalloc_expand %{ - // - // replaces - // - // region src1 src2 - // \ | | - // crx=cmpD_reg_reg - // - // with - // - // region src1 src2 - // \ | | - // crx=cmpDUnordered_reg_reg - // | - // ^ region - // | \ - // crx=cmov_bns_less - // - - // create new nodes - MachNode *m1 = new cmpDUnordered_reg_regNode(); - MachNode *m2 = new cmov_bns_lessNode(); - - // inputs for new nodes - m1->add_req(n_region, n_src1, n_src2); - m2->add_req(n_region); - m2->add_prec(m1); - - // operands for new nodes - m1->_opnds[0] = op_crx; - m1->_opnds[1] = op_src1; - m1->_opnds[2] = op_src2; - m2->_opnds[0] = op_crx; - - // registers for new nodes - ra_->set_pair(m1->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - ra_->set_pair(m2->_idx, ra_->get_reg_second(this), ra_->get_reg_first(this)); // crx - - // Insert new nodes. - nodes->push(m1); - nodes->push(m2); + format %{ "CMPD $crx, $src1, $src2" %} + size(16); + ins_encode %{ + Label done; + __ fcmpu($crx$$CondRegister, $src1$$FloatRegister, $src2$$FloatRegister); + __ bns($crx$$CondRegister, done); + __ li(R0, 0); + __ cmpwi($crx$$CondRegister, R0, 1); + __ bind(done); %} + ins_pipe(pipe_class_default); %} // Compare double, generate -1,0,1 diff --git a/src/hotspot/cpu/riscv/frame_riscv.hpp b/src/hotspot/cpu/riscv/frame_riscv.hpp index ce5a8dde2304..d5f04ee3ff70 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.hpp +++ b/src/hotspot/cpu/riscv/frame_riscv.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. 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. * @@ -103,8 +103,6 @@ public: enum { - pc_return_offset = 0, - // All frames link_offset = -2, return_addr_offset = -1, diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index eec5f9a5165c..574c70c8ea4b 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -471,74 +471,39 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - // At this point we know that marking is in progress. - // If do_load() is true then we have to emit the - // load of the previous value; otherwise it has already - // been loaded into _pre_val. +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { __ bind(*stub->entry()); - assert(stub->pre_val()->is_register(), "Precondition."); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - Register pre_val_reg = stub->pre_val()->as_register(); + Register obj = stub->obj()->as_register(); if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /* wide */); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, false /* wide */); } - __ beqz(pre_val_reg, *stub->continuation(), /* is_far */ true); - ce->store_parameter(stub->pre_val()->as_register(), 0); - __ far_call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin())); + __ beqz(obj, *stub->continuation(), /* is_far */ true); + + ce->store_parameter(obj, 0); + __ far_call(RuntimeAddress(bs->keepalive_barrier_stub())); __ j(*stub->continuation()); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce, - ShenandoahLoadReferenceBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { __ bind(*stub->entry()); - DecoratorSet decorators = stub->decorators(); - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*) BarrierSet::barrier_set()->barrier_set_c1(); Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == x10, "C1 must know about our slow call result register"); - assert(res == x10, "result must arrive in x10"); - assert_different_registers(tmp1, tmp2, t0); - - if (res != obj) { - __ mv(res, obj); - } - - if (is_strong) { - // Check for object in cset. - __ mv(tmp2, ShenandoahHeap::in_cset_fast_test_addr()); - __ srli(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ add(tmp2, tmp2, tmp1); - __ lbu(tmp2, Address(tmp2)); - __ beqz(tmp2, *stub->continuation(), true /* is_far */); - } - - ce->store_parameter(res, 0); + ce->store_parameter(obj, 0); ce->store_parameter(addr, 1); - - if (is_strong) { - if (is_native) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); - } else { - __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); - } - } else if (is_weak) { - __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - } else { - assert(is_phantom, "only remaining strength"); - __ far_call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); + __ far_call(RuntimeAddress(bs->load_reference_barrier_stub(stub->decorators()))); + if (obj != slow_result) { + __ mv(obj, slow_result); } __ j(*stub->continuation()); @@ -548,92 +513,27 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) { - __ prologue("shenandoah_pre_barrier", false); - - // arg0 : previous value of memory - - BarrierSet* bs = BarrierSet::barrier_set(); - - const Register pre_val = x10; - const Register thread = xthread; - const Register tmp = t0; - - Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); - - Label done; - Label runtime; - - // Is marking still active? - Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ lb(tmp, gc_state); - __ test_bit(tmp, tmp, ShenandoahHeap::MARKING_BITPOS); - __ beqz(tmp, done); - - // Can we store original value in the thread's buffer? - __ ld(tmp, queue_index); - __ beqz(tmp, runtime); - - __ subi(tmp, tmp, wordSize); - __ sd(tmp, queue_index); - __ ld(t1, buffer); - __ add(tmp, tmp, t1); - __ load_parameter(0, t1); - __ sd(t1, Address(tmp, 0)); - __ j(done); - - __ bind(runtime); - __ push_call_clobbered_registers(); - __ load_parameter(0, pre_val); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val); - __ pop_call_clobbered_registers(); - __ bind(done); - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ prologue("shenandoah_keepalive_barrier", false); + const Register tmp_obj = x10; + const Register tmp1 = x11; + const Register tmp2 = x12; + __ push_reg(RegSet::of(tmp1, tmp2, tmp_obj), sp); + __ load_parameter(0, tmp_obj); + satb_barrier(sasm, noreg, tmp_obj, xthread, tmp1, tmp2); + __ pop_reg(RegSet::of(tmp1, tmp2, tmp_obj), sp); __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, - DecoratorSet decorators) { +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); - // arg0 : object to be resolved - - __ push_call_clobbered_registers(); - __ load_parameter(0, x10); - __ load_parameter(1, x11); - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - address target = nullptr; - if (is_strong) { - if (is_native) { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } else { - if (UseCompressedOops) { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow); - } else { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); - } - } - } else if (is_weak) { - assert(!is_native, "weak must not be called off-heap"); - if (UseCompressedOops) { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow); - } else { - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); - } - } else { - assert(is_phantom, "only remaining strength"); - assert(is_native, "phantom must only be called off-heap"); - target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom); - } - __ rt_call(target); - __ mv(t0, x10); - __ pop_call_clobbered_registers(); - __ mv(x10, t0); - + const Register tmp_obj = x10; + const Register tmp_addr = x11; + __ push_reg(RegSet::of(tmp_addr), sp); + __ load_parameter(0, tmp_obj); + __ load_parameter(1, tmp_addr); + load_reference_barrier(sasm, tmp_obj, Address(tmp_addr, 0), decorators); + __ pop_reg(RegSet::of(tmp_addr), sp); __ epilogue(); } diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index d41809f1ef7e..ecb63e68a016 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -33,7 +33,7 @@ #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; #endif @@ -81,10 +81,11 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register tmp, Label& slow_path); #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); + + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif #ifdef COMPILER2 diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 0c077dc84a30..7bfff4b2086c 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -2100,25 +2100,25 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.size() : FLOATPRESSURE; } -const RegMask& Matcher::divI_proj_mask() { +const RegMask& Matcher::firstI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODI projection of divmodI. -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for DIVL projection of divmodL. -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } -// Register for MODL projection of divmodL. -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { ShouldNotReachHere(); return RegMask::EMPTY; } diff --git a/src/hotspot/cpu/s390/frame_s390.hpp b/src/hotspot/cpu/s390/frame_s390.hpp index bcdeec43e1a2..664a49fdd21b 100644 --- a/src/hotspot/cpu/s390/frame_s390.hpp +++ b/src/hotspot/cpu/s390/frame_s390.hpp @@ -542,14 +542,6 @@ unsigned long flags, int max_frames = 0); enum { - // This enum value specifies the offset from the pc remembered by - // call instructions to the location where control returns to - // after a normal return. Most architectures remember the return - // location directly, i.e. the offset is zero. This is the case - // for z/Architecture, too. - // - // Normal return address is the instruction following the branch. - pc_return_offset = 0, metadata_words = 0, metadata_words_at_bottom = 0, metadata_words_at_top = 0, diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 2208a197ac98..c0e51bd2bfd3 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1929,23 +1929,23 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? 15 : FLOATPRESSURE; } -// Register for DIVI projection of divmodI -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { return _Z_RARG4_INT_REG_mask; } -// Register for MODI projection of divmodI -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { return _Z_RARG3_INT_REG_mask; } -// Register for DIVL projection of divmodL -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { return _Z_RARG4_LONG_REG_mask; } -// Register for MODL projection of divmodL -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { return _Z_RARG3_LONG_REG_mask; } diff --git a/src/hotspot/cpu/x86/frame_x86.hpp b/src/hotspot/cpu/x86/frame_x86.hpp index 546c40fffe40..d97e6b847b44 100644 --- a/src/hotspot/cpu/x86/frame_x86.hpp +++ b/src/hotspot/cpu/x86/frame_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,6 @@ public: enum { - pc_return_offset = 0, // All frames link_offset = 0, return_addr_offset = 1, diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index fdf10e5b5e6f..bdb98d4b4c02 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -154,11 +154,7 @@ void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm, Label runtime; assert(pre_val != noreg, "check this code"); - - if (obj != noreg) { - assert_different_registers(obj, pre_val, tmp); - assert(pre_val != rax, "check this code"); - } + assert_different_registers(obj, pre_val, tmp); Address index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); @@ -560,99 +556,42 @@ void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssemb #define __ ce->masm()-> -void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); - // At this point we know that marking is in progress. - // If do_load() is true then we have to emit the - // load of the previous value; otherwise it has already - // been loaded into _pre_val. - +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) { __ bind(*stub->entry()); - assert(stub->pre_val()->is_register(), "Precondition."); - Register pre_val_reg = stub->pre_val()->as_register(); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); + + Register obj = stub->obj()->as_register(); if (stub->do_load()) { - ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /*wide*/); + ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, /* wide = */ false); } - - __ cmpptr(pre_val_reg, NULL_WORD); + __ cmpptr(obj, NULL_WORD); __ jcc(Assembler::equal, *stub->continuation()); - ce->store_parameter(stub->pre_val()->as_register(), 0); - __ call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin())); - __ jmp(*stub->continuation()); + ce->store_parameter(obj, 0); + __ call(RuntimeAddress(bs->keepalive_barrier_stub())); + __ jmp(*stub->continuation()); } -void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { - ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) { __ bind(*stub->entry()); - DecoratorSet decorators = stub->decorators(); - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); Register obj = stub->obj()->as_register(); - Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); - Register tmp1 = stub->tmp1()->as_register(); - Register tmp2 = stub->tmp2()->as_register(); - assert_different_registers(obj, res, addr, tmp1, tmp2); - - Label slow_path; + Register slow_result = stub->slow_result()->as_register(); + assert_different_registers(obj, addr, slow_result); + assert(slow_result == rax, "C1 must know about our slow call result register"); - assert(res == rax, "result must arrive in rax"); - - if (res != obj) { - __ mov(res, obj); - } - - if (is_strong) { - // Check for object being in the collection set. - __ mov(tmp1, res); - if (AOTCodeCache::is_on_for_dump()) { - __ push(rcx); - __ lea(rcx, ExternalAddress(AOTRuntimeConstants::grain_shift_address())); - __ movl(rcx, Address(rcx)); - if (tmp1 != rcx) { - __ mov(tmp1, res); - __ shrptr(tmp1); - __ pop(rcx); - } else { - assert_different_registers(tmp2, rcx); - __ mov(tmp2, res); - __ shrptr(tmp2); - __ pop(rcx); - __ movptr(tmp1, tmp2); - } - __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::cset_base_address())); - __ movptr(tmp2, Address(tmp2)); - } else { - __ shrptr(tmp1, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ movptr(tmp2, (intptr_t) ShenandoahHeap::in_cset_fast_test_addr()); - } - __ movbool(tmp2, Address(tmp2, tmp1, Address::times_1)); - __ testbool(tmp2); - __ jcc(Assembler::zero, *stub->continuation()); - } - - __ bind(slow_path); - ce->store_parameter(res, 0); + ce->store_parameter(obj, 0); ce->store_parameter(addr, 1); - if (is_strong) { - if (is_native) { - __ call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); - } else { - __ call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); - } - } else if (is_weak) { - __ call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - } else { - assert(is_phantom, "only remaining strength"); - __ call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); + __ call(RuntimeAddress(bs->load_reference_barrier_stub(stub->decorators()))); + if (obj != slow_result) { + __ mov(obj, slow_result); } + __ jmp(*stub->continuation()); } @@ -660,98 +599,28 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble #define __ sasm-> -void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) { - __ prologue("shenandoah_pre_barrier", false); - // arg0 : previous value of memory - - __ push(rax); - __ push(rdx); - - const Register pre_val = rax; - const Register thread = r15_thread; +void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) { + __ prologue("shenandoah_keepalive_barrier", false); + const Register tmp_obj = rax; const Register tmp = rdx; - - Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset())); - Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset())); - - Label done; - Label runtime; - - // Is SATB still active? - Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); - __ testb(gc_state, ShenandoahHeap::MARKING); - __ jcc(Assembler::zero, done); - - // Can we store original value in the thread's buffer? - - __ movptr(tmp, queue_index); - __ testptr(tmp, tmp); - __ jcc(Assembler::zero, runtime); - __ subptr(tmp, wordSize); - __ movptr(queue_index, tmp); - __ addptr(tmp, buffer); - - // prev_val (rax) - __ load_parameter(0, pre_val); - __ movptr(Address(tmp, 0), pre_val); - __ jmp(done); - - __ bind(runtime); - - __ save_live_registers_no_oop_map(true); - - // load the pre-value - __ load_parameter(0, rcx); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), rcx); - - __ restore_live_registers(true); - - __ bind(done); - - __ pop(rdx); - __ pop(rax); - + __ push(tmp); + __ push(tmp_obj); + __ load_parameter(0, tmp_obj); + satb_barrier(sasm, noreg, tmp_obj, tmp); + __ pop(tmp_obj); + __ pop(tmp); __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { +void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); - // arg0 : object to be resolved - - __ save_live_registers_no_oop_map(true); - - bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); - bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); - bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); - bool is_native = ShenandoahBarrierSet::is_native_access(decorators); - - __ load_parameter(0, c_rarg0); - __ load_parameter(1, c_rarg1); - if (is_strong) { - if (is_native) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), c_rarg0, c_rarg1); - } else { - if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow), c_rarg0, c_rarg1); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), c_rarg0, c_rarg1); - } - } - } else if (is_weak) { - assert(!is_native, "weak must not be called off-heap"); - if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow), c_rarg0, c_rarg1); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), c_rarg0, c_rarg1); - } - } else { - assert(is_phantom, "only remaining strength"); - assert(is_native, "phantom must only be called off-heap"); - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom), c_rarg0, c_rarg1); - } - - __ restore_live_registers_except_rax(true); - + const Register tmp_obj = rax; + const Register tmp_addr = rdx; + __ push(tmp_addr); + __ load_parameter(0, tmp_obj); + __ load_parameter(1, tmp_addr); + load_reference_barrier(sasm, tmp_obj, Address(tmp_addr, 0), decorators); + __ pop(tmp_addr); __ epilogue(); } diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index f608760ce42f..7f417d3c262c 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -33,7 +33,7 @@ #ifdef COMPILER1 class LIR_Assembler; -class ShenandoahPreBarrierStub; +class ShenandoahKeepaliveBarrierStub; class ShenandoahLoadReferenceBarrierStub; class StubAssembler; #endif @@ -73,10 +73,11 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { virtual void try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Label& slowpath); #ifdef COMPILER1 - void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); - void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); - void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); + void keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub); + void keepalive_barrier_c1_runtime_stub(StubAssembler* sasm); + + void load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); + void load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif #ifdef COMPILER2 diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp index 2ca1c1725427..53696ee6ef32 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.cpp +++ b/src/hotspot/cpu/x86/vm_version_x86.cpp @@ -489,6 +489,25 @@ class VM_Version_StubGenerator: public StubCodeGenerator { __ jmp(wrapup); __ bind(start_simd_check); + // Query CPUID 0xD sub-leaf 5, 6, and 7 offsets for AVX-512 XSAVE components + __ movl(rax, 0xD); + __ movl(rcx, 5); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::opmask_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + + __ movl(rax, 0xD); + __ movl(rcx, 6); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::zmm0to15_hi256_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + + __ movl(rax, 0xD); + __ movl(rcx, 7); + __ cpuid(); + __ lea(rsi, Address(rbp, in_bytes(VM_Version::zmm16to31_xstate_offset_offset()))); + __ movl(Address(rsi, 0), rbx); + // // Some OSs have a bug when upper 128/256bits of YMM/ZMM // registers are not restored after a signal processing. diff --git a/src/hotspot/cpu/x86/vm_version_x86.hpp b/src/hotspot/cpu/x86/vm_version_x86.hpp index 2fb1af71a103..d268665d0910 100644 --- a/src/hotspot/cpu/x86/vm_version_x86.hpp +++ b/src/hotspot/cpu/x86/vm_version_x86.hpp @@ -683,6 +683,11 @@ class VM_Version : public Abstract_VM_Version { uint32_t apx_xstate_size; // EAX: size of APX state (128) uint32_t apx_xstate_offset; // EBX: offset in standard XSAVE area + // cpuid function 0xD, subleaf 5, 6 and 7 (AVX-512 extended state) + uint32_t opmask_xstate_offset; // EBX: offset of Opmask component + uint32_t zmm0to15_hi256_xstate_offset; // EBX: offset of ZMM_Hi256 component + uint32_t zmm16to31_xstate_offset; // EBX: offset of Hi16_ZMM component + VM_Features feature_flags() const; // Asserts @@ -748,9 +753,15 @@ class VM_Version : public Abstract_VM_Version { static ByteSize apx_save_offset() { return byte_offset_of(CpuidInfo, apx_save); } static ByteSize apx_xstate_offset_offset() { return byte_offset_of(CpuidInfo, apx_xstate_offset); } static ByteSize apx_xstate_size_offset() { return byte_offset_of(CpuidInfo, apx_xstate_size); } + static ByteSize opmask_xstate_offset_offset() { return byte_offset_of(CpuidInfo, opmask_xstate_offset); } + static ByteSize zmm0to15_hi256_xstate_offset_offset() { return byte_offset_of(CpuidInfo, zmm0to15_hi256_xstate_offset); } + static ByteSize zmm16to31_xstate_offset_offset() { return byte_offset_of(CpuidInfo, zmm16to31_xstate_offset); } static uint32_t apx_xstate_offset() { return _cpuid_info.apx_xstate_offset; } static uint32_t apx_xstate_size() { return _cpuid_info.apx_xstate_size; } + static uint32_t opmask_xstate_offset() { return _cpuid_info.opmask_xstate_offset; } + static uint32_t zmm0to15_hi256_xstate_offset() { return _cpuid_info.zmm0to15_hi256_xstate_offset; } + static uint32_t zmm16to31_xstate_offset() { return _cpuid_info.zmm16to31_xstate_offset; } // The value used to check ymm register after signal handle static int ymm_test_value() { return 0xCAFEBABE; } diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 370437edee2c..3f953dbe725b 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -2764,23 +2764,23 @@ uint Matcher::float_pressure_limit() return (FLOATPRESSURE == -1) ? default_float_pressure_threshold : FLOATPRESSURE; } -// Register for DIVI projection of divmodI -const RegMask& Matcher::divI_proj_mask() { +// Register for the first projection of an int pair +const RegMask& Matcher::firstI_proj_mask() { return INT_RAX_REG_mask(); } -// Register for MODI projection of divmodI -const RegMask& Matcher::modI_proj_mask() { +// Register for the second projection of an int pair +const RegMask& Matcher::secondI_proj_mask() { return INT_RDX_REG_mask(); } -// Register for DIVL projection of divmodL -const RegMask& Matcher::divL_proj_mask() { +// Register for the first projection of a long pair +const RegMask& Matcher::firstL_proj_mask() { return LONG_RAX_REG_mask(); } -// Register for MODL projection of divmodL -const RegMask& Matcher::modL_proj_mask() { +// Register for the second projection of a long pair +const RegMask& Matcher::secondL_proj_mask() { return LONG_RDX_REG_mask(); } @@ -3179,6 +3179,13 @@ bool Matcher::match_rule_supported(int opcode) { break; case Op_VectorCmpMasked: + if (!UseCountTrailingZerosInstruction) { + return false; + } + if (UseAVX < 3 || !VM_Version::supports_bmi2()) { + return false; + } + break; case Op_VectorMaskGen: if (UseAVX < 3 || !VM_Version::supports_bmi2()) { return false; @@ -11372,6 +11379,34 @@ instruct mulL_mem_imm(rRegL dst, memory src, immL32 imm, rFlagsReg cr) ins_pipe(ialu_reg_mem_alu0); %} +instruct mulHiLoL_rReg(rax_RegL rax, rdx_RegL rdx, rRegL src, rFlagsReg cr) +%{ + match(MulHiLoL src rax); + match(MulHiLoL rax src); + effect(KILL cr); + + ins_cost(300); + format %{ "imulq RDX:RAX, RAX, $src\t# mulhilo" %} + ins_encode %{ + __ imulq($src$$Register); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + +instruct umulHiLoL_rReg(rax_RegL rax, rdx_RegL rdx, rRegL src, rFlagsReg cr) +%{ + match(UMulHiLoL src rax); + match(UMulHiLoL rax src); + effect(KILL cr); + + ins_cost(300); + format %{ "mulq RDX:RAX, RAX, $src\t# umulhilo" %} + ins_encode %{ + __ mulq($src$$Register); + %} + ins_pipe(ialu_reg_reg_alu0); +%} + instruct mulHiL_rReg(rdx_RegL dst, rRegL src, rax_RegL rax, rFlagsReg cr) %{ match(Set dst (MulHiL src rax)); diff --git a/src/hotspot/cpu/zero/frame_zero.hpp b/src/hotspot/cpu/zero/frame_zero.hpp index 190966155944..45d1cb82e829 100644 --- a/src/hotspot/cpu/zero/frame_zero.hpp +++ b/src/hotspot/cpu/zero/frame_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright 2007, 2008, 2009, 2010 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,7 +30,6 @@ public: enum { - pc_return_offset = 0, metadata_words = 0, // size, in words, of metadata at frame bottom, i.e. it is not part of the // caller/callee overlap diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 2b74cccb072c..0fc636483f5d 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -6387,7 +6387,7 @@ void os::jfr_report_memory_info() { // Send the RSS JFR event EventResidentSetSize event; event.set_size(pmex.WorkingSetSize); - event.set_peak(pmex.PeakWorkingSetSize); + event.set_peak(MAX2(pmex.PeakWorkingSetSize, pmex.WorkingSetSize)); event.commit(); } else { // Log a warning diff --git a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp index f48df178ce64..3ede62e14cd2 100644 --- a/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp +++ b/src/hotspot/os_cpu/linux_riscv/riscv_hwprobe.cpp @@ -261,13 +261,16 @@ void RiscvHwprobe::add_features_from_query_result() { // ====== non-extensions ====== // - if (is_valid(RISCV_HWPROBE_KEY_MARCHID)) { + // For value-type keys, the kernel returns (uint64_t)-1 when CPUs in the + // query set disagree (different core types). Skip these as the value is + // not meaningful for the system as a whole. + if (is_valid(RISCV_HWPROBE_KEY_MARCHID) && query[RISCV_HWPROBE_KEY_MARCHID].value != (uint64_t)-1) { VM_Version::marchid.enable_feature(query[RISCV_HWPROBE_KEY_MARCHID].value); } - if (is_valid(RISCV_HWPROBE_KEY_MIMPID)) { + if (is_valid(RISCV_HWPROBE_KEY_MIMPID) && query[RISCV_HWPROBE_KEY_MIMPID].value != (uint64_t)-1) { VM_Version::mimpid.enable_feature(query[RISCV_HWPROBE_KEY_MIMPID].value); } - if (is_valid(RISCV_HWPROBE_KEY_MVENDORID)) { + if (is_valid(RISCV_HWPROBE_KEY_MVENDORID) && query[RISCV_HWPROBE_KEY_MVENDORID].value != (uint64_t)-1) { VM_Version::mvendorid.enable_feature(query[RISCV_HWPROBE_KEY_MVENDORID].value); } // RISCV_HWPROBE_KEY_CPUPERF_0 is deprecated and returns similar values diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index 6750b71476b3..25ee449d8b10 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -381,9 +381,22 @@ size_t os::Posix::default_stack_size(os::ThreadType thr_type) { ///////////////////////////////////////////////////////////////////////////// // helper functions for fatal error handler +// XSAVE Buffer Layout (Intel SDM Vol. 1, Section 13.4.1) +// Bytes 0-511: Legacy x87/FPU and SSE state (includes XMM0-15) +// Bytes 512-575: XSAVE Header (64 bytes) +// Bytes 576-831: YMMH state (upper 128 bits of YMM0-15) +// YMMH[i] at: buffer + 576 + (i * 16) +// Bytes 832+: Extended state components (e.g., AVX-512, APX, etc.). +// Component offsets and sizes are +// enumerated by CPUID.(EAX=0xD, ECX=n). // XSAVE constants - from Intel SDM Vol. 1, Chapter 13 #define XSAVE_HDR_OFFSET 512 +#define XSAVE_HDR_SIZE 64 #define XFEATURE_APX (1ULL << 19) +#define XFEATURE_YMM (1ULL << 2) +#define XFEATURE_OPMASK (1ULL << 5) +#define XFEATURE_ZMM_HI256 (1ULL << 6) +#define XFEATURE_HI16_ZMM (1ULL << 7) // XSAVE header structure // See: Intel SDM Vol. 1, Section 13.4.2 "XSAVE Header" @@ -417,6 +430,118 @@ static apx_state* get_apx_state(const ucontext_t* uc) { return (apx_state*)(xsave + offset); } +static void print_xmm_registers(outputStream* st, const ucontext_t* uc) { + for (int i = 0; i < 16; ++i) { + const uint64_t* xmm = (const uint64_t*)&uc->uc_mcontext.fpregs->_xmm[i]; + st->print_cr("XMM[%d]=" INTPTR_FORMAT " " INTPTR_FORMAT, i, xmm[1], xmm[0]); + } +} + +static void print_ymm_registers(outputStream* st, const ucontext_t* uc, bool has_ymm_hi128) { + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + for (int i = 0; i < 16; ++i) { + const uint64_t* xmm = (const uint64_t*)&uc->uc_mcontext.fpregs->_xmm[i]; + uint64_t values[4] = {xmm[0], xmm[1], 0, 0}; + if (has_ymm_hi128) { + const uint64_t* ymmh = (const uint64_t*)(xsave + XSAVE_HDR_OFFSET + XSAVE_HDR_SIZE + (i * 16)); + values[2] = ymmh[0]; + values[3] = ymmh[1]; + } + st->print("YMM[%d]=", i); + for (int j = 3; j >= 0; --j) { + st->print("%s" INTPTR_FORMAT, (j == 3) ? "" : " ", values[j]); + } + st->cr(); + } +} + +static void print_zmm_registers(outputStream* st, const ucontext_t* uc, bool has_ymm_hi128, + bool has_zmm_hi256, bool has_hi16_zmm) { + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + + for (int i = 0; i < 32; ++i) { + uint64_t values[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + if (i < 16) { + const uint64_t* xmm = (const uint64_t*)&uc->uc_mcontext.fpregs->_xmm[i]; + values[0] = xmm[0]; + values[1] = xmm[1]; + + if (has_ymm_hi128) { + const uint64_t* ymmh = (const uint64_t*)(xsave + XSAVE_HDR_OFFSET + XSAVE_HDR_SIZE + (i * 16)); + values[2] = ymmh[0]; + values[3] = ymmh[1]; + } + + if (has_zmm_hi256) { + const uint32_t zmm_hi256_offset = VM_Version::zmm0to15_hi256_xstate_offset(); + const uint64_t* zmm_hi256 = (const uint64_t*)(xsave + zmm_hi256_offset + (i * 32)); + values[4] = zmm_hi256[0]; + values[5] = zmm_hi256[1]; + values[6] = zmm_hi256[2]; + values[7] = zmm_hi256[3]; + } + } else if (has_hi16_zmm) { + const uint32_t hi16_zmm_offset = VM_Version::zmm16to31_xstate_offset(); + const uint64_t* zmm = (const uint64_t*)(xsave + hi16_zmm_offset + ((i - 16) * 64)); + values[0] = zmm[0]; + values[1] = zmm[1]; + values[2] = zmm[2]; + values[3] = zmm[3]; + values[4] = zmm[4]; + values[5] = zmm[5]; + values[6] = zmm[6]; + values[7] = zmm[7]; + } + + st->print("ZMM[%d]=", i); + for (int j = 7; j >= 0; --j) { + st->print("%s" INTPTR_FORMAT, (j == 7) ? "" : " ", values[j]); + } + st->cr(); + } +} + +static void print_kmask_registers(outputStream* st, const ucontext_t* uc, bool has_opmask) { + const uint32_t opmask_offset = VM_Version::opmask_xstate_offset(); + if (!has_opmask || opmask_offset == 0) { + return; + } + + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + const uint64_t* kmask = (const uint64_t*)(xsave + opmask_offset); + + for (int i = 0; i < 8; ++i) { + st->print_cr("K[%d]=" INTPTR_FORMAT, i, kmask[i]); + } + st->cr(); +} + +static void print_vector_registers(outputStream* st, const ucontext_t* uc) { + if (uc->uc_mcontext.fpregs == nullptr) { + return; + } + + if (UseAVX < 2) { + return print_xmm_registers(st, uc); + } + + const char* xsave = (const char*)uc->uc_mcontext.fpregs; + const uint64_t* xstate_hdr_ptr = (const uint64_t*)(xsave + XSAVE_HDR_OFFSET); + const uint64_t xsave_state_bitmap = xstate_hdr_ptr[0]; + const bool has_ymm_hi128 = (xsave_state_bitmap & XFEATURE_YMM) != 0; + const bool has_opmask = (xsave_state_bitmap & XFEATURE_OPMASK) != 0; + const bool has_zmm_hi256 = (xsave_state_bitmap & XFEATURE_ZMM_HI256) != 0; + const bool has_hi16_zmm = (xsave_state_bitmap & XFEATURE_HI16_ZMM) != 0; + const bool should_print_zmm_registers = (UseAVX > 2) && (has_zmm_hi256 || has_hi16_zmm); + + if (!should_print_zmm_registers) { + return print_ymm_registers(st, uc, has_ymm_hi128); + } + + print_kmask_registers(st, uc, has_opmask); + print_zmm_registers(st, uc, has_ymm_hi128, has_zmm_hi256, has_hi16_zmm); +} void os::print_context(outputStream *st, const void *context) { if (context == nullptr) return; @@ -458,7 +583,7 @@ void os::print_context(outputStream *st, const void *context) { st->print(", ERR=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_ERR]); st->cr(); st->print(" TRAPNO=" INTPTR_FORMAT, (intptr_t)uc->uc_mcontext.gregs[REG_TRAPNO]); - // Add XMM registers + MXCSR. Note that C2 uses XMM to spill GPR values including pointers. + // Add vector registers + MXCSR. Note that C2 uses XMM to spill GPR values including pointers. st->cr(); st->cr(); // Sanity check: fpregs should point into the context. @@ -467,10 +592,7 @@ void os::print_context(outputStream *st, const void *context) { st->print_cr("bad uc->uc_mcontext.fpregs: " INTPTR_FORMAT " (uc: " INTPTR_FORMAT ")", p2i(uc->uc_mcontext.fpregs), p2i(uc)); } else { - for (int i = 0; i < 16; ++i) { - const int64_t* xmm_val_addr = (int64_t*)&(uc->uc_mcontext.fpregs->_xmm[i]); - st->print_cr("XMM[%d]=" INTPTR_FORMAT " " INTPTR_FORMAT, i, xmm_val_addr[1], xmm_val_addr[0]); - } + print_vector_registers(st, uc); st->print(" MXCSR=" UINT32_FORMAT_X_0, uc->uc_mcontext.fpregs->mxcsr); } st->cr(); diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index fac320c3ed70..fbd12038c94a 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -164,7 +164,7 @@ size_t AOTMetaspace::protection_zone_size() { return os::cds_core_region_alignment(); } -static bool shared_base_valid(char* shared_base) { +bool AOTMetaspace::shared_base_valid(char* shared_base) { // We check user input for SharedBaseAddress at dump time. // At CDS runtime, "shared_base" will be the (attempted) mapping start. It will also @@ -172,10 +172,15 @@ static bool shared_base_valid(char* shared_base) { // the prototype mark words) carry pre-computed narrow Klass IDs that refer to the mapping // start as base. // - // On AARCH64, The "shared_base" may not be later usable as encoding base, depending on the + // The "shared_base" may not be later usable as encoding base, depending on the // total size of the reserved area and the precomputed_narrow_klass_shift. This is checked // before reserving memory. Here we weed out values already known to be invalid later. - return AARCH64_ONLY(is_aligned(shared_base, 4 * G)) NOT_AARCH64(true); + // Since we cannot predict the range, we use the full maximum encoding range + // (4G). + constexpr size_t range = 4 * G; + address addr = (address)shared_base; + const int shift = ArchiveBuilder::precomputed_narrow_klass_shift(); + return CompressedKlassPointers::check_klass_decode_mode(addr, shift, range); } class DumpClassListCLDClosure : public CLDClosure { @@ -273,7 +278,7 @@ static char* compute_shared_base(size_t cds_max) { err = "too high"; } else if (shared_base_too_high(specified_base, aligned_base, cds_max)) { err = "too high"; - } else if (!shared_base_valid(aligned_base)) { + } else if (!AOTMetaspace::shared_base_valid(aligned_base)) { err = "invalid for this platform"; } else { return aligned_base; @@ -291,7 +296,7 @@ static char* compute_shared_base(size_t cds_max) { // Make sure the default value of SharedBaseAddress specified in globals.hpp is sane. assert(!shared_base_too_high(specified_base, aligned_base, cds_max), "Sanity"); - assert(shared_base_valid(aligned_base), "Sanity"); + assert(AOTMetaspace::shared_base_valid(aligned_base), "Sanity"); return aligned_base; } @@ -1971,14 +1976,12 @@ char* AOTMetaspace::reserve_address_space_for_archives(FileMapInfo* static_mapin const size_t total_range_size = archive_space_size + gap_size + class_space_size; - // Test that class space base address plus shift can be decoded by aarch64, when restored. - const int precomputed_narrow_klass_shift = ArchiveBuilder::precomputed_narrow_klass_shift(); - if (!CompressedKlassPointers::check_klass_decode_mode(base_address, precomputed_narrow_klass_shift, - total_range_size)) { - aot_log_info(aot)("CDS initialization: Cannot use SharedBaseAddress " PTR_FORMAT " with precomputed shift %d.", - p2i(base_address), precomputed_narrow_klass_shift); - use_archive_base_addr = false; - } + // The code for dumping the archive ensures that the base address is valid. + // Here we validate that the base address plus shift can be decoded when + // restored. + assert(shared_base_valid((char*)base_address), + "Cannot use SharedBaseAddress " PTR_FORMAT " with precomputed shift %d.", + p2i(base_address), ArchiveBuilder::precomputed_narrow_klass_shift()); assert(total_range_size > ccs_begin_offset, "must be"); if (use_windows_memory_mapping() && use_archive_base_addr) { diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp index 975b6be76d7c..cc90c9da3b08 100644 --- a/src/hotspot/share/cds/aotMetaspace.hpp +++ b/src/hotspot/share/cds/aotMetaspace.hpp @@ -188,6 +188,9 @@ class AOTMetaspace : AllStatic { static bool use_optimized_module_handling() { return NOT_CDS(false) CDS_ONLY(_use_optimized_module_handling); } static void disable_optimized_module_handling() { _use_optimized_module_handling = false; } + // Check if the supplied shared base address can be used as the encoding base. + static bool shared_base_valid(char* shared_base); + private: static void read_extra_data(JavaThread* current, const char* filename) NOT_CDS_RETURN; static void fork_and_dump_final_static_archive(TRAPS); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 2dd1d9d0824c..63d1f4af4ae6 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -41,6 +41,7 @@ #include "runtime/vmThread.hpp" #include "utilities/defaultStream.hpp" #include "utilities/formatBuffer.hpp" +#include "utilities/globalDefinitions.hpp" bool CDSConfig::_is_dumping_static_archive = false; bool CDSConfig::_is_dumping_preimage_static_archive = false; @@ -123,6 +124,12 @@ void CDSConfig::ergo_initialize() { // etc), there is usually no need to attach to this JVM. FLAG_SET_ERGO(DisableAttachMechanism, true); } + + if (!AOTMetaspace::shared_base_valid((char*)SharedBaseAddress)) { + log_warning(cds)("SharedBaseAddress " PTR_FORMAT " is invalid. Reverting to " PTR_FORMAT, + p2i((void*)SharedBaseAddress), p2i((void*)DEFAULT_SHARED_BASE_ADDRESS)); + FLAG_SET_ERGO(SharedBaseAddress, DEFAULT_SHARED_BASE_ADDRESS); + } } const char* CDSConfig::default_archive_path() { diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp index 7df498ca5b90..640cde848b8d 100644 --- a/src/hotspot/share/cds/cds_globals.hpp +++ b/src/hotspot/share/cds/cds_globals.hpp @@ -27,6 +27,9 @@ #include "runtime/globals_shared.hpp" +#define DEFAULT_SHARED_BASE_ADDRESS (LP64_ONLY(32*G) \ + NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0))) + // // Defines all globals flags used by CDS. // @@ -51,8 +54,7 @@ product(bool, PrintSharedArchiveAndExit, false, \ "Print shared archive file contents") \ \ - product(size_t, SharedBaseAddress, LP64_ONLY(32*G) \ - NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0)), \ + product(size_t, SharedBaseAddress, DEFAULT_SHARED_BASE_ADDRESS, \ "Address to allocate shared memory region for class data") \ range(0, SIZE_MAX) \ \ diff --git a/src/hotspot/share/ci/bcEscapeAnalyzer.cpp b/src/hotspot/share/ci/bcEscapeAnalyzer.cpp index 712f7af4139a..fd4ac33eb699 100644 --- a/src/hotspot/share/ci/bcEscapeAnalyzer.cpp +++ b/src/hotspot/share/ci/bcEscapeAnalyzer.cpp @@ -34,6 +34,7 @@ #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/copy.hpp" +#include "utilities/integerCast.hpp" #ifndef PRODUCT #define TRACE_BCEA(level, code) \ @@ -1077,17 +1078,32 @@ void BCEscapeAnalyzer::merge_block_states(StateInfo *blockstates, ciBlock *dest, } } +bool BCEscapeAnalyzer::datasize_overflow(uint numblocks, uint stkSize, uint numLocals, size_t& datasize) { + uint64_t datacount64 = (uint64_t)(numblocks + 1) * (stkSize + numLocals); + if (datacount64 > SIZE_MAX / sizeof(ArgumentMap)) { + return true; + } + datasize = integer_cast_permit_tautology(datacount64 * sizeof(ArgumentMap)); + return false; +} + void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { - int numblocks = _methodBlocks->num_blocks(); - int stkSize = _method->max_stack(); - int numLocals = _method->max_locals(); + uint numblocks = _methodBlocks->num_blocks(); + uint stkSize = _method->max_stack(); + uint numLocals = _method->max_locals(); StateInfo state; - int datacount = (numblocks + 1) * (stkSize + numLocals); - int datasize = datacount * sizeof(ArgumentMap); + size_t datasize; + if (datasize_overflow(numblocks, stkSize, numLocals, datasize)) { + _conservative = true; + return; + } + size_t datacount = datasize / sizeof(ArgumentMap); StateInfo *blockstates = (StateInfo *) arena->Amalloc(numblocks * sizeof(StateInfo)); ArgumentMap *statedata = (ArgumentMap *) arena->Amalloc(datasize); - for (int i = 0; i < datacount; i++) ::new ((void*)&statedata[i]) ArgumentMap(); + for (size_t i = 0; i < datacount; i++) { + ::new ((void*)&statedata[i]) ArgumentMap(); + } ArgumentMap *dp = statedata; state._vars = dp; dp += numLocals; @@ -1095,7 +1111,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { dp += stkSize; state._initialized = false; state._max_stack = stkSize; - for (int i = 0; i < numblocks; i++) { + for (uint i = 0; i < numblocks; i++) { blockstates[i]._vars = dp; dp += numLocals; blockstates[i]._stack = dp; @@ -1142,7 +1158,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { if (blk->is_handler() || blk->is_ret_target()) { // for an exception handler or a target of a ret instruction, we assume the worst case, // that any variable could contain any argument - for (int i = 0; i < numLocals; i++) { + for (uint i = 0; i < numLocals; i++) { state._vars[i] = allVars; } if (blk->is_handler()) { @@ -1155,7 +1171,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { state._stack[i] = allVars; } } else { - for (int i = 0; i < numLocals; i++) { + for (uint i = 0; i < numLocals; i++) { state._vars[i] = blkState->_vars[i]; } for (int i = 0; i < blkState->_stack_height; i++) { @@ -1170,7 +1186,7 @@ void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { DEBUG_ONLY(int handler_count = 0;) int blk_start = blk->start_bci(); int blk_end = blk->limit_bci(); - for (int i = 0; i < numblocks; i++) { + for (uint i = 0; i < numblocks; i++) { ciBlock *b = _methodBlocks->block(i); if (b->is_handler()) { int ex_start = b->ex_start_bci(); diff --git a/src/hotspot/share/ci/bcEscapeAnalyzer.hpp b/src/hotspot/share/ci/bcEscapeAnalyzer.hpp index b75cb6a56f49..7bdd4a58146e 100644 --- a/src/hotspot/share/ci/bcEscapeAnalyzer.hpp +++ b/src/hotspot/share/ci/bcEscapeAnalyzer.hpp @@ -152,6 +152,12 @@ class BCEscapeAnalyzer : public ArenaObj { // Copy dependencies from this analysis into "deps" void copy_dependencies(Dependencies *deps); + // Returns true if the datasize computation for iterate_blocks would + // overflow, i.e. the allocation size exceeds what can be represented. + // On success, sets datasize to the computed allocation size in bytes. + // Extracted as a public static method for testability (JDK-8216486). + static bool datasize_overflow(uint numblocks, uint stkSize, uint numLocals, size_t& datasize); + #ifndef PRODUCT // dump escape information void dump(); diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp index eecd94275850..c3805b4a054e 100644 --- a/src/hotspot/share/ci/ciMethod.hpp +++ b/src/hotspot/share/ci/ciMethod.hpp @@ -76,8 +76,8 @@ class ciMethod : public ciMetadata { // Code attributes. int _code_size; - int _max_stack; - int _max_locals; + uint _max_stack; + u2 _max_locals; vmIntrinsicID _intrinsic_id; int _handler_count; int _interpreter_invocation_count; diff --git a/src/hotspot/share/ci/ciMethodBlocks.hpp b/src/hotspot/share/ci/ciMethodBlocks.hpp index f1b446c2a87d..567ea1e39b48 100644 --- a/src/hotspot/share/ci/ciMethodBlocks.hpp +++ b/src/hotspot/share/ci/ciMethodBlocks.hpp @@ -38,7 +38,7 @@ class ciMethodBlocks : public ArenaObj { Arena *_arena; GrowableArray *_blocks; ciBlock **_bci_to_block; - int _num_blocks; + u2 _num_blocks; int _code_size; void do_analysis(); diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index 7e1391ed0f03..b70f89b26452 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -2424,9 +2424,6 @@ int AOTCodeAddressTable::id_for_address(address addr, RelocIterator reloc, CodeB id = search_address(addr, _stubs_addr, _stubs_max); if (id == BAD_ADDRESS_ID) { StubCodeDesc* desc = StubCodeDesc::desc_for(addr); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(addr + frame::pc_return_offset); - } const char* sub_name = (desc != nullptr) ? desc->name() : ""; assert(false, "Address " INTPTR_FORMAT " for Stub:%s is missing in AOT Code Cache addresses table", p2i(addr), sub_name); } else { diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 448bab6fbc2f..c65b9cb23d1d 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -291,6 +291,7 @@ class AOTStubData : public StackObj { do_var(bool, UseCRC32Intrinsics) \ do_var(bool, UseDilithiumIntrinsics) \ do_var(bool, UseGHASHIntrinsics) \ + do_var(bool, UseIntPoly25519Intrinsics) \ do_var(bool, UseKyberIntrinsics) \ do_var(bool, UseMD5Intrinsics) \ do_var(bool, UsePoly1305Intrinsics) \ diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 6f3a1b09c488..f1ca317d36f1 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -30,6 +30,7 @@ #include "code/dependencyContext.hpp" #include "code/nmethod.hpp" #include "code/pcDesc.hpp" +#include "code/vtableStubs.hpp" #include "compiler/compilationPolicy.hpp" #include "compiler/compileBroker.hpp" #include "compiler/compilerDefinitions.inline.hpp" @@ -1971,8 +1972,8 @@ void CodeCache::write_perf_map(const char* filename, outputStream* st) { AllCodeBlobsIterator iter(AllCodeBlobsIterator::not_unloading); while (iter.next()) { CodeBlob *cb = iter.method(); - if (is_stub_code_blob(cb)) { - // Individual stub routines are dumped after the main loop. + if (is_stub_code_blob(cb) || cb->is_vtable_blob()) { + // Individual stub routines and vtable stubs are dumped after the main loop. continue; } ResourceMark rm; @@ -1991,6 +1992,13 @@ void CodeCache::write_perf_map(const char* filename, outputStream* st) { (intptr_t)d->begin(), (intptr_t)d->size_in_bytes(), d->group(), d->name()); } + VtableStubs::vtable_stub_do([&](VtableStub* s) { + fs.print_cr(INTPTR_FORMAT " " INTPTR_FORMAT " %s [%d]", + (intptr_t)s->code_begin(), + (intptr_t)s->code_size(), + s->is_vtable_stub() ? "vtable stub" : "itable stub", + s->index()); + }); } #endif // LINUX diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 27f01797d393..5d7df4981023 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -3783,9 +3783,6 @@ const char* nmethod::reloc_string_for(u_char* begin, u_char* end) { address dest = r->destination(); if (StubRoutines::contains(dest)) { StubCodeDesc* desc = StubCodeDesc::desc_for(dest); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(dest + frame::pc_return_offset); - } if (desc != nullptr) { st.print(" Stub::%s", desc->name()); return st.as_string(); diff --git a/src/hotspot/share/code/oopRecorder.cpp b/src/hotspot/share/code/oopRecorder.cpp index c37651892cc7..93c74be27b9d 100644 --- a/src/hotspot/share/code/oopRecorder.cpp +++ b/src/hotspot/share/code/oopRecorder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -302,9 +302,6 @@ void ExternalsRecorder::print_statistics() { if (addr != nullptr) { if (StubRoutines::contains(addr)) { StubCodeDesc* desc = StubCodeDesc::desc_for(addr); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(addr + frame::pc_return_offset); - } const char* stub_name = (desc != nullptr) ? desc->name() : ""; tty->print(" stub: %s", stub_name); } else { diff --git a/src/hotspot/share/code/relocInfo.cpp b/src/hotspot/share/code/relocInfo.cpp index 73e4b6de7b4e..5295dc0f2873 100644 --- a/src/hotspot/share/code/relocInfo.cpp +++ b/src/hotspot/share/code/relocInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -922,9 +922,6 @@ void RelocIterator::print_current_on(outputStream* st) { st->print(" | [destination=" INTPTR_FORMAT "]", p2i(dest)); if (StubRoutines::contains(dest)) { StubCodeDesc* desc = StubCodeDesc::desc_for(dest); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(dest + frame::pc_return_offset); - } if (desc != nullptr) { st->print(" Stub::%s", desc->name()); } diff --git a/src/hotspot/share/code/vtableStubs.cpp b/src/hotspot/share/code/vtableStubs.cpp index 35b226a87981..df3e80bea411 100644 --- a/src/hotspot/share/code/vtableStubs.cpp +++ b/src/hotspot/share/code/vtableStubs.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -319,15 +319,6 @@ void vtableStubs_init() { VtableStubs::initialize(); } -void VtableStubs::vtable_stub_do(void f(VtableStub*)) { - for (int i = 0; i < N; i++) { - for (VtableStub* s = AtomicAccess::load_acquire(&_table[i]); s != nullptr; s = s->next()) { - f(s); - } - } -} - - //----------------------------------------------------------------------------------------------------- // Non-product code #ifndef PRODUCT diff --git a/src/hotspot/share/code/vtableStubs.hpp b/src/hotspot/share/code/vtableStubs.hpp index 06acd8f25b92..fd06bf7c6470 100644 --- a/src/hotspot/share/code/vtableStubs.hpp +++ b/src/hotspot/share/code/vtableStubs.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ #include "asm/macroAssembler.hpp" #include "code/vmreg.hpp" #include "memory/allStatic.hpp" +#include "runtime/atomicAccess.hpp" #include "utilities/checkedCast.hpp" // A VtableStub holds an individual code stub for a pair (vtable index, #args) for either itables or vtables @@ -111,7 +112,9 @@ class VtableStubs : AllStatic { static bool contains(address pc); // is pc within any stub? static VtableStub* stub_containing(address pc); // stub containing pc or nullptr static void initialize(); - static void vtable_stub_do(void f(VtableStub*)); // iterates over all vtable stubs + + template + static void vtable_stub_do(F f); }; @@ -142,13 +145,14 @@ class VtableStub { : _next(nullptr), _index(index), _ame_offset(-1), _npe_offset(-1), _type(is_vtable_stub ? Type::vtable_stub : Type::itable_stub) {} VtableStub* next() const { return _next; } - int index() const { return _index; } static VMReg receiver_location() { return _receiver_location; } void set_next(VtableStub* n) { _next = n; } public: + int index() const { return _index; } + int code_size() const { return VtableStubs::code_size_limit(is_vtable_stub()); } address code_begin() const { return (address)(this + 1); } - address code_end() const { return code_begin() + VtableStubs::code_size_limit(is_vtable_stub()); } + address code_end() const { return code_begin() + code_size(); } address entry_point() const { return code_begin(); } static int entry_offset() { return sizeof(class VtableStub); } @@ -189,4 +193,13 @@ class VtableStub { }; +template +void VtableStubs::vtable_stub_do(F f) { + for (int i = 0; i < N; i++) { + for (VtableStub* s = AtomicAccess::load_acquire(&_table[i]); s != nullptr; s = s->next()) { + f(s); + } + } +} + #endif // SHARE_CODE_VTABLESTUBS_HPP diff --git a/src/hotspot/share/compiler/disassembler.cpp b/src/hotspot/share/compiler/disassembler.cpp index 2c1ef235e076..9dc8956d98d1 100644 --- a/src/hotspot/share/compiler/disassembler.cpp +++ b/src/hotspot/share/compiler/disassembler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -591,9 +591,6 @@ void decode_env::print_address(address adr) { if (Universe::is_fully_initialized()) { if (StubRoutines::contains(adr)) { StubCodeDesc* desc = StubCodeDesc::desc_for(adr); - if (desc == nullptr) { - desc = StubCodeDesc::desc_for(adr + frame::pc_return_offset); - } if (desc != nullptr) { st->print("Stub::%s", desc->name()); if (desc->begin() != adr) { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index ebf7a1086fa2..eaa6afb5efa8 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -61,7 +61,7 @@ #include "gc/g1/g1RegionPinCache.inline.hpp" #include "gc/g1/g1RegionToSpaceMapper.hpp" #include "gc/g1/g1RemSet.hpp" -#include "gc/g1/g1ReviseYoungLengthTask.hpp" +#include "gc/g1/g1ReviseNumYoungRegionsTask.hpp" #include "gc/g1/g1RootClosures.hpp" #include "gc/g1/g1SATBMarkQueueSet.hpp" #include "gc/g1/g1ServiceThread.hpp" @@ -916,7 +916,7 @@ void G1CollectedHeap::verify_after_full_collection() { // At this point there should be no regions in the // entire heap tagged as young. - assert(check_young_list_empty(), "young list should be empty at this point"); + assert(check_no_young_regions(), "We should not have young regions at this point"); // Note: since we've just done a full GC, concurrent // marking is no longer active. Therefore we need not @@ -1295,7 +1295,7 @@ G1CollectedHeap::G1CollectedHeap() : _service_thread(nullptr), _periodic_gc_task(nullptr), _free_arena_memory_task(nullptr), - _revise_young_length_task(nullptr), + _revise_num_young_regions_task(nullptr), _workers(nullptr), _refinement_epoch(0), _last_synchronized_start(0), @@ -1604,9 +1604,9 @@ jint G1CollectedHeap::initialize() { _free_arena_memory_task = new G1MonotonicArenaFreeMemoryTask("Card Set Free Memory Task"); _service_thread->register_task(_free_arena_memory_task); - if (policy()->use_adaptive_young_list_length()) { - _revise_young_length_task = new G1ReviseYoungLengthTask("Revise Young Length List Task"); - _service_thread->register_task(_revise_young_length_task); + if (policy()->use_adaptive_num_young_regions()) { + _revise_num_young_regions_task = new G1ReviseNumYoungRegionsTask("Revise Num Young Regions Task"); + _service_thread->register_task(_revise_num_young_regions_task); } // Here we allocate the dummy G1HeapRegion that is required by the @@ -1654,6 +1654,9 @@ void G1CollectedHeap::stop() { // that are destroyed during shutdown. _cr->stop(); _service_thread->stop(); + VM_G1StopMarking op; + VMThread::execute(&op); + _cm->stop(); } @@ -2282,7 +2285,7 @@ bool G1CollectedHeap::block_is_obj(const HeapWord* addr) const { } size_t G1CollectedHeap::tlab_capacity() const { - return eden_target_length() * G1HeapRegion::GrainBytes; + return target_num_eden_regions() * G1HeapRegion::GrainBytes; } size_t G1CollectedHeap::tlab_used() const { @@ -2451,7 +2454,7 @@ G1HeapSummary G1CollectedHeap::create_g1_heap_summary() { size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked(); size_t eden_capacity_bytes = - (policy()->young_list_target_length() * G1HeapRegion::GrainBytes) - survivor_used_bytes; + (policy()->target_num_young_regions() * G1HeapRegion::GrainBytes) - survivor_used_bytes; VirtualSpaceSummary heap_summary = create_heap_space_summary(); return G1HeapSummary(heap_summary, heap_used, eden_used_bytes, eden_capacity_bytes, @@ -2723,13 +2726,14 @@ void G1CollectedHeap::do_collection_pause_at_safepoint(size_t allocation_word_si _bytes_used_during_gc = 0; - _cm->fully_initialize(); - policy()->decide_on_concurrent_start_pause(); // Record whether this pause may need to trigger a concurrent operation. Later, // when we signal the G1ConcurrentMarkThread, the collector state has already // been reset for the next pause. bool should_start_concurrent_mark_operation = collector_state()->is_in_concurrent_start_gc(); + if (should_start_concurrent_mark_operation) { + _cm->fully_initialize(); + } // Perform the collection. G1YoungCollector collector(gc_cause(), allocation_word_size); @@ -2989,7 +2993,7 @@ class NoYoungRegionsClosure: public G1HeapRegionClosure { bool success() { return _success; } }; -bool G1CollectedHeap::check_young_list_empty() { +bool G1CollectedHeap::check_no_young_regions() { bool ret = (young_regions_count() == 0); NoYoungRegionsClosure closure; @@ -3008,8 +3012,8 @@ void G1CollectedHeap::prepare_region_for_full_compaction(G1HeapRegion* hr) { } else if (hr->is_old()) { _old_set.remove(hr); } else if (hr->is_young()) { - // Note that emptying the eden and survivor lists is postponed and instead - // done as the first step when rebuilding the regions sets again. The reason + // Note that clearing eden and survivor region tracking is postponed and + // done as the first step when rebuilding the region sets again. The reason // for this is that during a full GC string deduplication needs to know if // a collected region was young or old when the full GC was initiated. hr->uninstall_surv_rate_group(); diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index fc31878097b3..718c230851f7 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -75,7 +75,7 @@ class G1GCPhaseTimes; class G1HeapSizingPolicy; class G1NewTracer; class G1RemSet; -class G1ReviseYoungLengthTask; +class G1ReviseNumYoungRegionsTask; class G1ServiceTask; class G1ServiceThread; class GCMemoryManager; @@ -176,7 +176,7 @@ class G1CollectedHeap : public CollectedHeap { G1ServiceThread* _service_thread; G1ServiceTask* _periodic_gc_task; G1MonotonicArenaFreeMemoryTask* _free_arena_memory_task; - G1ReviseYoungLengthTask* _revise_young_length_task; + G1ReviseNumYoungRegionsTask* _revise_num_young_regions_task; WorkerThreads* _workers; @@ -394,7 +394,6 @@ class G1CollectedHeap : public CollectedHeap { #define assert_used_and_recalculate_used_equal(g1h) do {} while(0) #endif - // The young region list. G1EdenRegions _eden; G1SurvivorRegions _survivor; @@ -1237,7 +1236,7 @@ class G1CollectedHeap : public CollectedHeap { G1SurvivorRegions* survivor() { return &_survivor; } - inline uint eden_target_length() const; + inline uint target_num_eden_regions() const; uint eden_regions_count() const { return _eden.length(); } uint eden_regions_count(uint node_index) const { return _eden.regions_on_node(node_index); } uint survivor_regions_count() const { return _survivor.length(); } @@ -1249,7 +1248,7 @@ class G1CollectedHeap : public CollectedHeap { uint humongous_regions_count() const { return _humongous_set.length(); } #ifdef ASSERT - bool check_young_list_empty(); + bool check_no_young_regions(); #endif bool is_marked(oop obj) const; diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index bad9ac18eeca..5d23a7d463e8 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -295,8 +295,8 @@ inline bool G1CollectedHeap::is_collection_set_candidate(const G1HeapRegion* r) return candidates->contains(r); } -inline uint G1CollectedHeap::eden_target_length() const { - return _policy->young_list_target_length() - survivor_regions_count(); +inline uint G1CollectedHeap::target_num_eden_regions() const { + return _policy->target_num_young_regions() - survivor_regions_count(); } #endif // SHARE_GC_G1_G1COLLECTEDHEAP_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.cpp b/src/hotspot/share/gc/g1/g1CollectionSet.cpp index b0eb493120b4..3a086d8b09bb 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp @@ -33,7 +33,6 @@ #include "gc/g1/g1ParScanThreadState.hpp" #include "gc/g1/g1Policy.hpp" #include "logging/logStream.hpp" -#include "runtime/orderAccess.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -128,8 +127,11 @@ void G1CollectionSet::add_old_region(G1HeapRegion* hr) { _g1h->register_old_collection_set_region_with_region_attr(hr); - assert(num_regions() < _max_num_regions, "Collection set now larger than maximum size."); - _regions[_num_regions++] = hr->hrm_index(); + uint local_num_regions = num_regions(); + assert(local_num_regions < _max_num_regions, "Collection set now larger than maximum size."); + _regions[local_num_regions] = hr->hrm_index(); + _num_regions.store_relaxed(local_num_regions + 1); + _num_initial_old_regions++; _g1h->old_set_remove(hr); @@ -162,14 +164,13 @@ void G1CollectionSet::stop_incremental_building() { void G1CollectionSet::clear() { assert_at_safepoint_on_vm_thread(); - _num_regions = 0; + _num_regions.store_relaxed(0); _groups.clear(); assert(_optional_groups.length() == 0, "must be"); } void G1CollectionSet::iterate(G1HeapRegionClosure* cl) const { - uint len = _num_regions; - OrderAccess::loadload(); + uint len = _num_regions.load_acquire(); for (uint i = 0; i < len; i++) { G1HeapRegion* r = _g1h->region_at(_regions[i]); @@ -233,8 +234,7 @@ void G1CollectionSet::add_young_region_common(G1HeapRegion* hr) { _regions[index] = hr->hrm_index(); // Concurrent readers must observe the store of the value in the array before an // update to the _num_regions field. - OrderAccess::storestore(); - _num_regions++; + _num_regions.fetch_then_add(1u, memory_order_release); } void G1CollectionSet::add_survivor_regions(G1HeapRegion* hr) { @@ -332,9 +332,9 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi log_trace(gc, ergo, cset)("Start choosing CSet. Pending cards: %zu target pause time: %1.2fms", pending_cards, target_pause_time_ms); - // The young list is laid with the survivor regions from the previous - // pause are appended to the RHS of the young list, i.e. - // [Newly Young Regions ++ Survivors from last pause]. + // Young region indexes are assigned with eden regions first, followed by + // survivor regions from the previous pause: + // [Eden regions ++ Survivors from last pause]. uint num_eden_regions = _g1h->eden_regions_count(); uint num_survivor_regions = survivors->length(); @@ -355,7 +355,7 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi num_eden_regions, num_survivor_regions, predicted_eden_time, predicted_base_time_ms, target_pause_time_ms, remaining_time_ms); - // Clear the fields that point to the survivor list - they are all young now. + // Set survivor regions as eden and clear survivor tracking for this pause. survivors->convert_to_eden(); phase_times()->record_young_cset_choice_time_ms((Ticks::now() - start_time).seconds() * 1000.0); @@ -426,7 +426,7 @@ double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms) uint min_old_cset_length = _policy->calc_min_old_cset_length(candidates()->last_marking_candidates_length()); uint max_old_cset_length = MAX2(min_old_cset_length, _policy->calc_max_old_cset_length()); - bool check_time_remaining = _policy->use_adaptive_young_list_length(); + bool check_time_remaining = _policy->use_adaptive_num_young_regions(); G1CSetCandidateGroupList* from_marking_groups = &candidates()->from_marking_groups(); diff --git a/src/hotspot/share/gc/g1/g1CollectionSet.hpp b/src/hotspot/share/gc/g1/g1CollectionSet.hpp index 5fa9868f2b24..eee985f259d9 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1COLLECTIONSET_HPP #include "gc/g1/g1CollectionSetCandidates.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -142,14 +143,14 @@ class G1CollectionSet { // All regions in _regions below _num_regions are assumed to be part of the // collection set. // We assume that at any time there is at most only one writer and (one or more) - // concurrent readers. This means synchronization using storestore and loadload - // barriers on the writer and reader respectively only are sufficient. + // concurrent readers. This means synchronization using release and acquire + // on the writer and reader respectively only are sufficient. // // This corresponds to the regions referenced by the candidate groups further below. uint* _regions; uint _max_num_regions; - volatile uint _num_regions; + Atomic _num_regions; // Old gen groups selected for evacuation. G1CSetCandidateGroupList _groups; @@ -285,7 +286,7 @@ class G1CollectionSet { // Returns the number of regions in the current collection set increment. uint num_regions_in_increment() const { return num_regions() - _regions_inc_part_start; } // Returns the total number of regions in the current collection set. - uint num_regions() const { return _num_regions; } + uint num_regions() const { return _num_regions.load_relaxed(); } // Iterate over the entire collection set (all increments calculated so far), applying // the given G1HeapRegionClosure on all of the regions. diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index 3637d4772297..ac1b29a6bd79 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -134,7 +134,7 @@ void G1CSetCandidateGroupList::append(G1CSetCandidateGroup* group) { assert(group->length() > 0, "Do not add empty groups"); assert(!_groups.contains(group), "Already added to list"); _groups.append(group); - _num_regions += group->length(); + _num_regions.store_relaxed(num_regions() + group->length()); } G1CSetCandidateGroup* G1CSetCandidateGroupList::at(uint index) { @@ -147,7 +147,7 @@ void G1CSetCandidateGroupList::clear(bool uninstall_group_cardset) { delete gr; } _groups.clear(); - _num_regions = 0; + _num_regions.store_relaxed(0); } void G1CSetCandidateGroupList::prepare_for_scan() { @@ -156,9 +156,9 @@ void G1CSetCandidateGroupList::prepare_for_scan() { } } -void G1CSetCandidateGroupList::remove_selected(uint count, uint num_regions) { +void G1CSetCandidateGroupList::remove_selected(uint count, uint num_regions_to_remove) { _groups.remove_till(count); - _num_regions -= num_regions; + _num_regions.store_relaxed(num_regions() - num_regions_to_remove); } void G1CSetCandidateGroupList::remove(G1CSetCandidateGroupList* other) { @@ -172,7 +172,7 @@ void G1CSetCandidateGroupList::remove(G1CSetCandidateGroupList* other) { // Create a list from scratch, copying over the elements from the candidate // list not in the other list. Finally deallocate and overwrite the old list. int new_length = _groups.length() - other->length(); - _num_regions = num_regions() - other->num_regions(); + _num_regions.store_relaxed(num_regions() - other->num_regions()); GrowableArray new_list(new_length, mtGC); uint other_idx = 0; diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp index 8a2235cf89c9..a70f9e395b64 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp @@ -29,6 +29,7 @@ #include "gc/g1/g1CollectionSetCandidates.hpp" #include "gc/shared/gc_globals.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "utilities/growableArray.hpp" @@ -147,7 +148,7 @@ using G1CSetCandidateGroupListIterator = GrowableArrayIterator _groups; - volatile uint _num_regions; + Atomic _num_regions; public: G1CSetCandidateGroupList(); @@ -163,7 +164,7 @@ class G1CSetCandidateGroupList { uint length() const { return (uint)_groups.length(); } - uint num_regions() const { return _num_regions; } + uint num_regions() const { return _num_regions.load_relaxed(); } void remove_selected(uint count, uint num_regions); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 11c93b092b14..6f9e4e2e9cf9 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -2036,6 +2036,26 @@ void G1ConcurrentMark::print_stats() { } } +bool G1ConcurrentMark::shutdown_cleanup_needed() const { + // Cleanup (aborting threads, setting abort flags) is needed throughout the whole cycle before + // stopping the CM thread. + return is_fully_initialized() && is_in_concurrent_cycle(); +} + +void G1ConcurrentMark::shutdown_concurrent_cycle() { + assert_at_safepoint_on_vm_thread(); + + abort_root_region_scan_at_safepoint(); + abort_marking_threads(); + + SATBMarkQueueSet& satb_mq_set = G1BarrierSet::satb_mark_queue_set(); + satb_mq_set.abandon_partial_marking(); + // This can be called either during or outside marking, we'll read + // the expected_active value from the SATB queue set. + satb_mq_set.set_active_all_threads(false, /* new active value */ + satb_mq_set.is_active() /* expected_active */); +} + bool G1ConcurrentMark::concurrent_cycle_abort() { assert_at_safepoint_on_vm_thread(); assert(_g1h->collector_state()->is_in_full_gc(), "must be"); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index f0071286e04c..73dabc12863b 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -569,6 +569,11 @@ class G1ConcurrentMark : public CHeapObj { uint worker_id_offset() const { return _worker_id_offset; } + // Fully allocates and initializes data structures for the concurrent cycle. + // Methods that use concurrent cycle state such as the concurrent mark threads, + // tasks, marking stack, statistics, TAMS or TARS require this initialization. + // Callers that run before the first concurrent start pause, which calls this, + // should guard calls with is_fully_initialized(). void fully_initialize(); bool is_fully_initialized() const { return _cm_thread != nullptr; } @@ -603,6 +608,8 @@ class G1ConcurrentMark : public CHeapObj { bool mark_stack_empty() const { return _global_mark_stack.is_empty(); } void concurrent_cycle_start(); + bool shutdown_cleanup_needed() const; + void shutdown_concurrent_cycle(); // Abandon current marking iteration due to a Full GC. bool concurrent_cycle_abort(); void concurrent_cycle_end(bool mark_cycle_completed); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp index a41d5cf54b98..948897a538c6 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.cpp @@ -77,7 +77,7 @@ double G1ConcurrentMarkThread::mmu_delay_end(G1Policy* policy, bool remark) { void G1ConcurrentMarkThread::delay_to_keep_mmu(bool remark) { G1Policy* policy = G1CollectedHeap::heap()->policy(); - if (policy->use_adaptive_young_list_length()) { + if (policy->use_adaptive_num_young_regions()) { double delay_end_sec = mmu_delay_end(policy, remark); // Wait for timeout or thread termination request. MonitorLocker ml(G1CGC_lock, Monitor::_no_safepoint_check_flag); diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp index a22442c2b7f8..a1c684ecf59f 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.hpp @@ -50,7 +50,8 @@ class G1ConcurrentMarkThread: public ConcurrentGCThread { Atomic _state; - ServiceState state() const { return _state.load_relaxed(); } + ServiceState state() const { return _state.load_acquire(); } + void set_state(ServiceState new_state) { _state.release_store(new_state); } // Returns whether we are in a "Full" cycle. bool is_in_full_concurrent_cycle() const; diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp index bea6fe4e4510..3225c253dbb0 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMarkThread.inline.hpp @@ -48,31 +48,31 @@ inline bool G1ConcurrentMarkThread::is_in_full_concurrent_cycle() const { inline void G1ConcurrentMarkThread::set_idle() { // Concurrent cycle may be aborted any time. assert(!is_idle(), "must not be idle"); - _state.store_relaxed(Idle); + set_state(Idle); } inline void G1ConcurrentMarkThread::start_full_cycle() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); assert(is_idle(), "cycle in progress"); - _state.store_relaxed(FullCycleMarking); + set_state(FullCycleMarking); } inline void G1ConcurrentMarkThread::start_undo_cycle() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); assert(is_idle(), "cycle in progress"); - _state.store_relaxed(UndoCycleResetForNextCycle); + set_state(UndoCycleResetForNextCycle); } inline void G1ConcurrentMarkThread::set_full_cycle_rebuild_and_scrub() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); assert(state() == FullCycleMarking, "must be"); - _state.store_relaxed(FullCycleRebuildOrScrub); + set_state(FullCycleRebuildOrScrub); } inline void G1ConcurrentMarkThread::set_full_cycle_reset_for_next_cycle() { assert(SafepointSynchronize::is_at_safepoint(), "must be"); assert(state() == FullCycleRebuildOrScrub, "must be"); - _state.store_relaxed(FullCycleResetForNextCycle); + set_state(FullCycleResetForNextCycle); } inline bool G1ConcurrentMarkThread::is_in_marking() const { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp index d58d980b6513..c1c820cb554d 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp @@ -381,7 +381,7 @@ void G1ConcurrentRefineSweepState::complete_refinement(jlong total_yield_during_ policy->record_refinement_stats(stats()); { - MutexLocker x(G1ReviseYoungLength_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1ReviseNumYoungRegions_lock, Mutex::_no_safepoint_check_flag); policy->record_dirtying_stats(TimeHelper::counter_to_millis(g1h->last_refinement_epoch_start()), TimeHelper::counter_to_millis(next_epoch_start), _stats.cards_pending(), @@ -575,7 +575,7 @@ bool G1ConcurrentRefine::adjust_num_threads_periodically() { if (!_needs_adjust) { Tickspan since_adjust = Ticks::now() - _last_adjust; if (since_adjust.milliseconds() < adjust_threads_period_ms()) { - _num_threads_wanted = 0; + _num_threads_wanted.store_relaxed(0); return false; } } @@ -592,7 +592,7 @@ bool G1ConcurrentRefine::adjust_num_threads_periodically() { _needs_adjust = true; } - return (_num_threads_wanted > 0) && !heap_was_locked(); + return (num_threads_wanted() > 0) && !heap_was_locked(); } void G1ConcurrentRefine::adjust_threads_wanted(size_t available_bytes) { @@ -603,7 +603,7 @@ void G1ConcurrentRefine::adjust_threads_wanted(size_t available_bytes) { size_t num_cards = policy->current_pending_cards(); - _threads_needed.update(_num_threads_wanted, + _threads_needed.update(num_threads_wanted(), available_bytes, num_cards, _pending_cards_target); @@ -613,7 +613,7 @@ void G1ConcurrentRefine::adjust_threads_wanted(size_t available_bytes) { new_wanted = _thread_control.max_num_threads(); } - _num_threads_wanted = new_wanted; + _num_threads_wanted.store_relaxed(new_wanted); log_debug(gc, refine)("Concurrent refinement: wanted %u, pending cards: %zu (pending-from-gc %zu), " "predicted: %zu, goal %zu, time-until-next-gc: %1.2fms pred-refine-rate %1.2fc/ms log-rate %1.2fc/ms", diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp index 50fb412f3aff..62e56c14c681 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp @@ -28,6 +28,7 @@ #include "gc/g1/g1ConcurrentRefineStats.hpp" #include "gc/g1/g1ConcurrentRefineThreadsNeeded.hpp" #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/growableArray.hpp" @@ -212,7 +213,7 @@ class G1ConcurrentRefineSweepState { // class G1ConcurrentRefine : public CHeapObj { G1Policy* _policy; - volatile uint _num_threads_wanted; + Atomic _num_threads_wanted; size_t _pending_cards_target; Ticks _last_adjust; Ticks _last_deactivate; @@ -306,7 +307,7 @@ class G1ConcurrentRefine : public CHeapObj { // obtaining the heap lock. bool heap_was_locked() const { return _heap_was_locked; } - uint num_threads_wanted() const { return _num_threads_wanted; } + uint num_threads_wanted() const { return _num_threads_wanted.load_relaxed(); } uint max_num_threads() const { return _thread_control.max_num_threads(); } // Iterate over all concurrent refinement threads applying the given closure. diff --git a/src/hotspot/share/gc/g1/g1HeapRegion.cpp b/src/hotspot/share/gc/g1/g1HeapRegion.cpp index 810bd4df2ee8..2c85e2fcc0d5 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegion.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegion.cpp @@ -413,6 +413,8 @@ bool G1HeapRegion::verify_code_roots(VerifyOption vo) const { return has_code_roots; } + rem_set()->reset_code_root_table_scanner(); + VerifyCodeRootNMethodClosure nm_cl(this); code_roots_do(&nm_cl); diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp index 13c7a6a8d3e4..ef42538d4d6e 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.cpp @@ -87,8 +87,12 @@ void G1HeapRegionRemSet::clear(bool only_cardset, bool keep_tracked) { } } -void G1HeapRegionRemSet::reset_table_scanner() { +void G1HeapRegionRemSet::reset_code_root_table_scanner() { _code_roots.reset_table_scanner(); +} + +void G1HeapRegionRemSet::reset_table_scanner() { + reset_code_root_table_scanner(); if (has_cset_group()) { card_set()->reset_table_scanner(); } diff --git a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp index 950098c706e9..2e97d6a75971 100644 --- a/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp +++ b/src/hotspot/share/gc/g1/g1HeapRegionRemSet.hpp @@ -154,6 +154,7 @@ class G1HeapRegionRemSet : public CHeapObj { // entries for this region in other remsets. void clear(bool only_cardset = false, bool keep_tracked = false); + void reset_code_root_table_scanner(); void reset_table_scanner(); G1MonotonicArenaMemoryStats card_set_memory_stats() const; diff --git a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp index 1b9704e8ad32..6158bc47c478 100644 --- a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp +++ b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp @@ -185,7 +185,7 @@ size_t G1HeapSizingPolicy::young_collection_shrink_amount(double cpu_usage_delta // going to use during this mutator phase. uint target_regions_to_shrink = _g1h->num_free_regions(); - uint needed_for_allocation = _g1h->eden_target_length(); + uint needed_for_allocation = _g1h->target_num_eden_regions(); if (_g1h->is_humongous(allocation_word_size)) { needed_for_allocation += (uint) _g1h->humongous_obj_size_in_regions(allocation_word_size); } diff --git a/src/hotspot/share/gc/g1/g1HeapTransition.cpp b/src/hotspot/share/gc/g1/g1HeapTransition.cpp index 690bda4e7e6a..e91afc792675 100644 --- a/src/hotspot/share/gc/g1/g1HeapTransition.cpp +++ b/src/hotspot/share/gc/g1/g1HeapTransition.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,13 +29,13 @@ #include "memory/metaspaceUtils.hpp" G1HeapTransition::Data::Data(G1CollectedHeap* g1_heap) : - _eden_length(g1_heap->eden_regions_count()), - _survivor_length(g1_heap->survivor_regions_count()), - _old_length(g1_heap->old_regions_count()), - _humongous_length(g1_heap->humongous_regions_count()), + _num_eden_regions(g1_heap->eden_regions_count()), + _num_survivor_regions(g1_heap->survivor_regions_count()), + _num_old_regions(g1_heap->old_regions_count()), + _num_humongous_regions(g1_heap->humongous_regions_count()), _meta_sizes(MetaspaceUtils::get_combined_statistics()), - _eden_length_per_node(nullptr), - _survivor_length_per_node(nullptr) { + _num_eden_regions_per_node(nullptr), + _num_survivor_regions_per_node(nullptr) { uint node_count = G1NUMA::numa()->num_active_nodes(); @@ -43,20 +43,20 @@ G1HeapTransition::Data::Data(G1CollectedHeap* g1_heap) : LogTarget(Debug, gc, heap, numa) lt; if (lt.is_enabled()) { - _eden_length_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); - _survivor_length_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); + _num_eden_regions_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); + _num_survivor_regions_per_node = NEW_C_HEAP_ARRAY(uint, node_count, mtGC); for (uint i = 0; i < node_count; i++) { - _eden_length_per_node[i] = g1_heap->eden_regions_count(i); - _survivor_length_per_node[i] = g1_heap->survivor_regions_count(i); + _num_eden_regions_per_node[i] = g1_heap->eden_regions_count(i); + _num_survivor_regions_per_node[i] = g1_heap->survivor_regions_count(i); } } } } G1HeapTransition::Data::~Data() { - FREE_C_HEAP_ARRAY(_eden_length_per_node); - FREE_C_HEAP_ARRAY(_survivor_length_per_node); + FREE_C_HEAP_ARRAY(_num_eden_regions_per_node); + FREE_C_HEAP_ARRAY(_num_survivor_regions_per_node); } G1HeapTransition::G1HeapTransition(G1CollectedHeap* g1_heap) : _g1_heap(g1_heap), _before(g1_heap) { } @@ -101,23 +101,23 @@ class G1HeapTransition::DetailedUsageClosure: public G1HeapRegionClosure { } }; -static void log_regions(const char* msg, size_t before_length, size_t after_length, size_t capacity, - uint* before_per_node_length, uint* after_per_node_length) { +static void log_regions(const char* msg, size_t num_before, size_t num_after, size_t capacity, + uint* num_per_node_before, uint* num_per_node_after) { LogTarget(Info, gc, heap) lt; if (lt.is_enabled()) { LogStream ls(lt); ls.print("%s regions: %zu->%zu(%zu)", - msg, before_length, after_length, capacity); + msg, num_before, num_after, capacity); // Not null only if gc+heap+numa at Debug level is enabled. - if (before_per_node_length != nullptr && after_per_node_length != nullptr) { + if (num_per_node_before != nullptr && num_per_node_after != nullptr) { G1NUMA* numa = G1NUMA::numa(); uint num_nodes = numa->num_active_nodes(); const uint* node_ids = numa->node_ids(); ls.print(" ("); for (uint i = 0; i < num_nodes; i++) { - ls.print("%u: %u->%u", node_ids[i], before_per_node_length[i], after_per_node_length[i]); + ls.print("%u: %u->%u", node_ids[i], num_per_node_before[i], num_per_node_after[i]); // Skip adding below if it is the last one. if (i != num_nodes - 1) { ls.print(", "); @@ -132,8 +132,8 @@ static void log_regions(const char* msg, size_t before_length, size_t after_leng void G1HeapTransition::print() { Data after(_g1_heap); - size_t eden_capacity_length_after_gc = _g1_heap->policy()->young_list_target_length() - after._survivor_length; - size_t survivor_capacity_length_before_gc = _g1_heap->policy()->max_survivor_regions(); + size_t num_eden_after_gc = _g1_heap->policy()->target_num_young_regions() - after._num_survivor_regions; + size_t num_survivor_before_gc = _g1_heap->policy()->max_survivor_regions(); DetailedUsage usage; if (log_is_enabled(Trace, gc, heap)) { @@ -141,32 +141,35 @@ void G1HeapTransition::print() { _g1_heap->heap_region_iterate(&blk); usage = blk._usage; assert(usage._eden_region_count == 0, "Expected no eden regions, but got %zu", usage._eden_region_count); - assert(usage._survivor_region_count == after._survivor_length, "Expected survivors to be %zu but was %zu", - after._survivor_length, usage._survivor_region_count); - assert(usage._old_region_count == after._old_length, "Expected old to be %zu but was %zu", - after._old_length, usage._old_region_count); - assert(usage._humongous_region_count == after._humongous_length, "Expected humongous to be %zu but was %zu", - after._humongous_length, usage._humongous_region_count); + assert(usage._survivor_region_count == after._num_survivor_regions, "Expected survivors to be %zu but was %zu", + after._num_survivor_regions, usage._survivor_region_count); + assert(usage._old_region_count == after._num_old_regions, "Expected old to be %zu but was %zu", + after._num_old_regions, usage._old_region_count); + assert(usage._humongous_region_count == after._num_humongous_regions, "Expected humongous to be %zu but was %zu", + after._num_humongous_regions, usage._humongous_region_count); } - log_regions("Eden", _before._eden_length, after._eden_length, eden_capacity_length_after_gc, - _before._eden_length_per_node, after._eden_length_per_node); + log_regions("Eden", _before._num_eden_regions, after._num_eden_regions, num_eden_after_gc, + _before._num_eden_regions_per_node, after._num_eden_regions_per_node); log_trace(gc, heap)(" Used: 0K, Waste: 0K"); - log_regions("Survivor", _before._survivor_length, after._survivor_length, survivor_capacity_length_before_gc, - _before._survivor_length_per_node, after._survivor_length_per_node); + log_regions("Survivor", _before._num_survivor_regions, after._num_survivor_regions, num_survivor_before_gc, + _before._num_survivor_regions_per_node, after._num_survivor_regions_per_node); log_trace(gc, heap)(" Used: %zuK, Waste: %zuK", - usage._survivor_used / K, ((after._survivor_length * G1HeapRegion::GrainBytes) - usage._survivor_used) / K); + usage._survivor_used / K, + ((after._num_survivor_regions * G1HeapRegion::GrainBytes) - usage._survivor_used) / K); log_info(gc, heap)("Old regions: %zu->%zu", - _before._old_length, after._old_length); + _before._num_old_regions, after._num_old_regions); log_trace(gc, heap)(" Used: %zuK, Waste: %zuK", - usage._old_used / K, ((after._old_length * G1HeapRegion::GrainBytes) - usage._old_used) / K); + usage._old_used / K, + ((after._num_old_regions * G1HeapRegion::GrainBytes) - usage._old_used) / K); log_info(gc, heap)("Humongous regions: %zu->%zu", - _before._humongous_length, after._humongous_length); + _before._num_humongous_regions, after._num_humongous_regions); log_trace(gc, heap)(" Used: %zuK, Waste: %zuK", - usage._humongous_used / K, ((after._humongous_length * G1HeapRegion::GrainBytes) - usage._humongous_used) / K); + usage._humongous_used / K, + ((after._num_humongous_regions * G1HeapRegion::GrainBytes) - usage._humongous_used) / K); MetaspaceUtils::print_metaspace_change(_before._meta_sizes); } diff --git a/src/hotspot/share/gc/g1/g1HeapTransition.hpp b/src/hotspot/share/gc/g1/g1HeapTransition.hpp index 18bcd153505b..4b69d26c5a66 100644 --- a/src/hotspot/share/gc/g1/g1HeapTransition.hpp +++ b/src/hotspot/share/gc/g1/g1HeapTransition.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,16 +35,16 @@ class G1HeapTransition { class DetailedUsageClosure; struct Data { - size_t _eden_length; - size_t _survivor_length; - size_t _old_length; - size_t _humongous_length; + size_t _num_eden_regions; + size_t _num_survivor_regions; + size_t _num_old_regions; + size_t _num_humongous_regions; const MetaspaceCombinedStats _meta_sizes; // Only includes current eden regions. - uint* _eden_length_per_node; + uint* _num_eden_regions_per_node; // Only includes current survivor regions. - uint* _survivor_length_per_node; + uint* _num_survivor_regions_per_node; Data(G1CollectedHeap* g1_heap); ~Data(); diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp index dba57d487ead..0af07282fc74 100644 --- a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp +++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp @@ -245,14 +245,14 @@ void G1MonitoringSupport::recalculate_sizes() { // use smaller value to subtract. _old_gen_used = _overall_used - MIN2(_overall_used, _eden_space_used + _survivor_space_used); - uint survivor_list_length = _g1h->survivor_regions_count(); + uint num_survivor_regions = _g1h->survivor_regions_count(); - uint young_list_target_length = _g1h->policy()->young_list_target_length(); - assert(young_list_target_length >= survivor_list_length, "invariant"); - uint eden_list_max_length = young_list_target_length - survivor_list_length; + uint target_num_young_regions = _g1h->policy()->target_num_young_regions(); + assert(target_num_young_regions >= num_survivor_regions, "invariant"); + uint max_num_eden_regions = target_num_young_regions - num_survivor_regions; // First calculate the committed sizes that can be calculated independently. - _survivor_space_committed = survivor_list_length * G1HeapRegion::GrainBytes; + _survivor_space_committed = num_survivor_regions * G1HeapRegion::GrainBytes; _old_gen_committed = G1HeapRegion::align_up_to_region_byte_size(_old_gen_used); // Next, start with the overall committed size. @@ -265,7 +265,7 @@ void G1MonitoringSupport::recalculate_sizes() { committed -= _survivor_space_committed + _old_gen_committed; // Next, calculate and remove the committed size for the eden. - _eden_space_committed = (size_t) eden_list_max_length * G1HeapRegion::GrainBytes; + _eden_space_committed = (size_t) max_num_eden_regions * G1HeapRegion::GrainBytes; // Somewhat defensive: be robust in case there are inaccuracies in // the calculations _eden_space_committed = MIN2(_eden_space_committed, committed); diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 6aeabb9ac9bb..e2c01f9a13e6 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -60,8 +60,8 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) : _ihop_control(create_ihop_control(&_predictor)), _policy_counters(new GCPolicyCounters("GarbageFirst", 1, 2)), _cur_pause_start_sec(0.0), - _young_list_desired_length(0), - _young_list_target_length(0), + _desired_num_young_regions(0), + _target_num_young_regions(0), _eden_surv_rate_group(new G1SurvRateGroup()), _survivor_surv_rate_group(new G1SurvRateGroup()), _reserve_factor((double) G1ReservePercent / 100.0), @@ -95,45 +95,45 @@ void G1Policy::init(G1CollectedHeap* g1h, G1CollectionSet* collection_set) { _free_regions_at_end_of_collection = _g1h->num_free_regions(); - update_young_length_bounds(); + update_young_regions_bounds(); } void G1Policy::record_young_gc_pause_start() { phase_times()->record_gc_pause_start(); } -class G1YoungLengthPredictor { +class G1NumYoungRegionsPredictor { const double _base_time_ms; const double _base_free_regions; const double _target_pause_time_ms; const G1Policy* const _policy; public: - G1YoungLengthPredictor(double base_time_ms, - double base_free_regions, - double target_pause_time_ms, - const G1Policy* policy) : + G1NumYoungRegionsPredictor(double base_time_ms, + double base_free_regions, + double target_pause_time_ms, + const G1Policy* policy) : _base_time_ms(base_time_ms), _base_free_regions(base_free_regions), _target_pause_time_ms(target_pause_time_ms), _policy(policy) {} - bool will_fit(uint young_length) const { - if (young_length >= _base_free_regions) { + bool will_fit(uint num_young_regions) const { + if (num_young_regions >= _base_free_regions) { // end condition 1: not enough space for the young regions return false; } size_t bytes_to_copy = 0; - const double copy_time_ms = _policy->predict_eden_copy_time_ms(young_length, &bytes_to_copy); - const double young_other_time_ms = _policy->analytics()->predict_young_other_time_ms(young_length); + const double copy_time_ms = _policy->predict_eden_copy_time_ms(num_young_regions, &bytes_to_copy); + const double young_other_time_ms = _policy->analytics()->predict_young_other_time_ms(num_young_regions); const double pause_time_ms = _base_time_ms + copy_time_ms + young_other_time_ms; if (pause_time_ms > _target_pause_time_ms) { // end condition 2: prediction is over the target pause time return false; } - const size_t free_bytes = (_base_free_regions - young_length) * G1HeapRegion::GrainBytes; + const size_t free_bytes = (_base_free_regions - num_young_regions) * G1HeapRegion::GrainBytes; // When copying, we will likely need more bytes free than is live in the region. // Add some safety margin to factor in the confidence of our guess, and the @@ -167,61 +167,62 @@ void G1Policy::record_new_heap_size(uint new_number_of_regions) { _ihop_control->update_target_occupancy(new_number_of_regions * G1HeapRegion::GrainBytes); } -uint G1Policy::calculate_desired_eden_length_by_mmu() const { - assert(use_adaptive_young_list_length(), "precondition"); +uint G1Policy::calculate_desired_num_eden_regions_by_mmu() const { + assert(use_adaptive_num_young_regions(), "precondition"); double now_sec = os::elapsedTime(); double when_ms = _mmu_tracker->when_max_gc_sec(now_sec) * 1000.0; double alloc_rate_ms = _analytics->predict_alloc_rate_ms(); return (uint) ceil(alloc_rate_ms * when_ms); } -void G1Policy::update_young_length_bounds() { +void G1Policy::update_young_regions_bounds() { assert(!Universe::is_fully_initialized() || SafepointSynchronize::is_at_safepoint(), "must be"); bool for_young_only_phase = collector_state()->is_in_young_only_phase(); - update_young_length_bounds(_analytics->predict_pending_cards(for_young_only_phase), + update_young_regions_bounds(_analytics->predict_pending_cards(for_young_only_phase), _analytics->predict_card_rs_length(for_young_only_phase), _analytics->predict_code_root_rs_length(for_young_only_phase)); } -void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { - uint old_young_list_target_length = young_list_target_length(); +void G1Policy::update_young_regions_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { + uint old_target_num_young_regions = target_num_young_regions(); - uint min_young_length_by_sizer = _young_gen_sizer.min_desired_young_length(); - uint max_young_length_by_sizer = _young_gen_sizer.max_desired_young_length(); + uint min_num_young_regions_by_sizer = _young_gen_sizer.min_desired_num_regions(); + uint max_num_young_regions_by_sizer = _young_gen_sizer.max_desired_num_regions(); - if (max_young_length_by_sizer < min_young_length_by_sizer) { - // This can happen due to races with heap_size_changed() at mutator time. Do not update the young gen - // lengths. Will be updated on the next regular call anyway. + if (max_num_young_regions_by_sizer < min_num_young_regions_by_sizer) { + // This can happen due to races with heap_size_changed() at mutator time. Do not update the + // young regions. Will be updated on the next regular call anyway. assert(!SafepointSynchronize::is_at_safepoint(), "must be"); return; } - uint new_young_list_desired_length = calculate_young_desired_length(pending_cards, - card_rs_length, - code_root_rs_length, - min_young_length_by_sizer, - max_young_length_by_sizer); - uint new_young_list_target_length = calculate_young_target_length(new_young_list_desired_length, min_young_length_by_sizer); + uint new_desired_num_young_regions = calculate_desired_num_young_regions(pending_cards, + card_rs_length, + code_root_rs_length, + min_num_young_regions_by_sizer, + max_num_young_regions_by_sizer); + uint new_target_num_young_regions = calculate_target_num_young_regions(new_desired_num_young_regions, + min_num_young_regions_by_sizer); - log_trace(gc, ergo, heap)("Young list length update: pending cards %zu card_rs_length %zu old target %u desired: %u target: %u", + log_trace(gc, ergo, heap)("Young num regions update: pending cards %zu card_rs_length %zu old target %u desired: %u target: %u", pending_cards, card_rs_length, - old_young_list_target_length, - new_young_list_desired_length, - new_young_list_target_length); + old_target_num_young_regions, + new_desired_num_young_regions, + new_target_num_young_regions); // Write back. This is not an attempt to control visibility order to other threads - // here; all the revising of the young gen length are best effort to keep pause time. + // here; all the revising of the number of young regions are best effort to keep pause time. // E.g. we could be "too late" revising young gen upwards to avoid GC because // there is some time left, or some threads could get different values for stopping // allocation. // That is "fine" - at most this will schedule a GC (hopefully only a little) too // early or too late. - _young_list_desired_length.store_relaxed(new_young_list_desired_length); - _young_list_target_length.store_relaxed(new_young_list_target_length); + _desired_num_young_regions.store_relaxed(new_desired_num_young_regions); + _target_num_young_regions.store_relaxed(new_target_num_young_regions); } -// Calculates desired young gen length. It is calculated from: +// Calculates desired number of young regions. It is calculated from: // // - sizer min/max bounds on young gen // - pause time goal for whole young gen evacuation @@ -236,40 +237,40 @@ void G1Policy::update_young_length_bounds(size_t pending_cards, size_t card_rs_l // value smaller than what is already allocated or what can actually be allocated. // This return value is only an expectation. // -uint G1Policy::calculate_young_desired_length(size_t pending_cards, - size_t card_rs_length, - size_t code_root_rs_length, - uint min_young_length_by_sizer, - uint max_young_length_by_sizer) const { +uint G1Policy::calculate_desired_num_young_regions(size_t pending_cards, + size_t card_rs_length, + size_t code_root_rs_length, + uint min_num_young_regions_by_sizer, + uint max_num_young_regions_by_sizer) const { - assert(min_young_length_by_sizer >= 1, "invariant"); - assert(max_young_length_by_sizer >= min_young_length_by_sizer, "invariant"); + assert(min_num_young_regions_by_sizer >= 1, "invariant"); + assert(max_num_young_regions_by_sizer >= min_num_young_regions_by_sizer, "invariant"); // Calculate the absolute and desired min bounds first. // This is how many survivor regions we already have. - const uint survivor_length = _g1h->survivor_regions_count(); + const uint num_survivor_regions = _g1h->survivor_regions_count(); // Size of the already allocated young gen. - const uint allocated_young_length = _g1h->young_regions_count(); - // This is the absolute minimum young length that we can return. Ensure that we + const uint allocated_num_young_regions = _g1h->young_regions_count(); + // This is the absolute minimum number of young regions that we can return. Ensure that we // don't go below any user-defined minimum bound. Also, we must have at least // one eden region, to ensure progress. But when revising during the ensuing // mutator phase we might have already allocated more than either of those, in // which case use that. - uint absolute_min_young_length = MAX3(min_young_length_by_sizer, - survivor_length + 1, - allocated_young_length); + uint absolute_min_num_young_regions = MAX3(min_num_young_regions_by_sizer, + num_survivor_regions + 1, + allocated_num_young_regions); // Calculate the absolute max bounds. After evac failure or when revising the - // young length we might have exceeded absolute min length or absolute_max_length, + // number of young regions we might have exceeded absolute min or max_num_young_regions, // so adjust the result accordingly. - uint absolute_max_young_length = MAX2(max_young_length_by_sizer, absolute_min_young_length); + uint absolute_max_num_young_regions = MAX2(max_num_young_regions_by_sizer, absolute_min_num_young_regions); - uint desired_eden_length_by_mmu = 0; - uint desired_eden_length_by_pause = 0; + uint desired_num_eden_regions_by_mmu = 0; + uint desired_num_eden_regions_by_pause = 0; - uint desired_young_length = 0; - if (use_adaptive_young_list_length()) { - desired_eden_length_by_mmu = calculate_desired_eden_length_by_mmu(); + uint desired_num_young_regions = 0; + if (use_adaptive_num_young_regions()) { + desired_num_eden_regions_by_mmu = calculate_desired_num_eden_regions_by_mmu(); double base_time_ms = predict_base_time_ms(pending_cards, card_rs_length, code_root_rs_length); double retained_time_ms = predict_retained_regions_evac_time(); @@ -278,55 +279,56 @@ uint G1Policy::calculate_young_desired_length(size_t pending_cards, log_trace(gc, ergo, heap)("Predicted total base time: total %f base_time %f retained_time %f", total_time_ms, base_time_ms, retained_time_ms); - desired_eden_length_by_pause = - calculate_desired_eden_length_by_pause(total_time_ms, - absolute_min_young_length - survivor_length, - absolute_max_young_length - survivor_length); + desired_num_eden_regions_by_pause = + calculate_desired_num_eden_regions_by_pause(total_time_ms, + absolute_min_num_young_regions - num_survivor_regions, + absolute_max_num_young_regions - num_survivor_regions); // Incorporate MMU concerns; assume that it overrides the pause time // goal, as the default value has been chosen to effectively disable it. - uint desired_eden_length = MAX2(desired_eden_length_by_pause, - desired_eden_length_by_mmu); + uint desired_num_eden_regions = MAX2(desired_num_eden_regions_by_pause, + desired_num_eden_regions_by_mmu); - desired_young_length = desired_eden_length + survivor_length; + desired_num_young_regions = desired_num_eden_regions + num_survivor_regions; } else { // The user asked for a fixed young gen so we'll fix the young gen // whether the next GC is young or mixed. - desired_young_length = min_young_length_by_sizer; + desired_num_young_regions = min_num_young_regions_by_sizer; } - // Clamp to absolute min/max after we determined desired lengths. - desired_young_length = clamp(desired_young_length, absolute_min_young_length, absolute_max_young_length); - - log_trace(gc, ergo, heap)("Young desired length %u " - "survivor length %u " - "allocated young length %u " - "absolute min young length %u " - "absolute max young length %u " - "desired eden length by mmu %u " - "desired eden length by pause %u ", - desired_young_length, survivor_length, - allocated_young_length, absolute_min_young_length, - absolute_max_young_length, desired_eden_length_by_mmu, - desired_eden_length_by_pause); - - assert(desired_young_length >= allocated_young_length, "must be"); - return desired_young_length; -} - -// Limit the desired (wished) young length by current free regions. If the request + // Clamp to absolute min/max after we determined desired number of regions. + desired_num_young_regions = clamp(desired_num_young_regions, absolute_min_num_young_regions, absolute_max_num_young_regions); + + log_trace(gc, ergo, heap)("Desired young regions %u " + "survivor regions %u " + "allocated young regions %u " + "absolute min young regions %u " + "absolute max young regions %u " + "desired eden regions by mmu %u " + "desired eden regions by pause %u ", + desired_num_young_regions, num_survivor_regions, + allocated_num_young_regions, absolute_min_num_young_regions, + absolute_max_num_young_regions, desired_num_eden_regions_by_mmu, + desired_num_eden_regions_by_pause); + + assert(desired_num_young_regions >= allocated_num_young_regions, "must be"); + return desired_num_young_regions; +} + +// Limit the desired (wished) number of young regions by current free regions. If the request // can be satisfied without using up reserve regions, do so, otherwise eat into // the reserve, giving away at most what the heap sizer allows. -uint G1Policy::calculate_young_target_length(uint desired_young_length, uint min_young_length_by_sizer) const { - uint allocated_young_length = _g1h->young_regions_count(); +uint G1Policy::calculate_target_num_young_regions(uint desired_num_young_regions, + uint min_num_young_regions_by_sizer) const { + uint num_young_regions = _g1h->young_regions_count(); uint receiving_additional_eden; - if (allocated_young_length >= desired_young_length) { + if (num_young_regions >= desired_num_young_regions) { // Already used up all we actually want (may happen as G1 revises the - // young list length concurrently). Do not allow more, potentially resulting in GC. + // number of young regions concurrently). Do not allow more, potentially resulting in GC. receiving_additional_eden = 0; - log_trace(gc, ergo, heap)("Young target length: Already used up desired young %u allocated %u", - desired_young_length, - allocated_young_length); + log_trace(gc, ergo, heap)("Target young regions: Already used up desired young regions %u allocated young regions %u", + desired_num_young_regions, + num_young_regions); } else { // Now look at how many free regions are there currently, and the heap reserve. // We will try our best not to "eat" into the reserve as long as we can. If we @@ -336,57 +338,57 @@ uint G1Policy::calculate_young_target_length(uint desired_young_length, uint min // The heap reserve needs to be snapshotted for consistent use in the following. // It can be concurrently modified by the mutator as it expands the heap. It can // only increase at that time, so this is a conservative snapshot. So at worst this - // method will return a too small young gen length in that case. + // method will return a too small number of young regions in that case. uint reserve_regions = _reserve_regions.load_relaxed(); - uint max_to_eat_into_reserve = MIN2(min_young_length_by_sizer, + uint max_to_eat_into_reserve = MIN2(min_num_young_regions_by_sizer, (reserve_regions + 1) / 2); - log_trace(gc, ergo, heap)("Young target length: Common " + log_trace(gc, ergo, heap)("Target young regions: Common " "free regions at end of collection %u " - "desired young length %u " + "desired number of young regions %u " "reserve region %u " "max to eat into reserve %u", _free_regions_at_end_of_collection, - desired_young_length, + desired_num_young_regions, reserve_regions, max_to_eat_into_reserve); uint survivor_regions_count = _g1h->survivor_regions_count(); - uint desired_eden_length = desired_young_length - survivor_regions_count; - uint allocated_eden_length = allocated_young_length - survivor_regions_count; + uint desired_num_eden_regions = desired_num_young_regions - survivor_regions_count; + uint num_eden_regions = num_young_regions - survivor_regions_count; if (_free_regions_at_end_of_collection <= reserve_regions) { - // Fully eat (or already eating) into the reserve, hand back at most absolute_min_length regions. + // Fully eat (or already eating) into the reserve. uint receiving_eden = MIN3(_free_regions_at_end_of_collection, - desired_eden_length, - max_to_eat_into_reserve); + desired_num_eden_regions, + max_to_eat_into_reserve); // Ensure that we provision for at least one Eden region. receiving_eden = MAX2(receiving_eden, 1u); // We could already have allocated more regions than what we could get // above. - receiving_additional_eden = allocated_eden_length < receiving_eden ? - receiving_eden - allocated_eden_length : 0; + receiving_additional_eden = num_eden_regions < receiving_eden ? + receiving_eden - num_eden_regions : 0; - log_trace(gc, ergo, heap)("Young target length: Fully eat into reserve " + log_trace(gc, ergo, heap)("Target young regions: Fully eat into reserve " "receiving eden %u receiving additional eden %u", receiving_eden, receiving_additional_eden); - } else if (_free_regions_at_end_of_collection < (desired_eden_length + reserve_regions)) { + } else if (_free_regions_at_end_of_collection < (desired_num_eden_regions + reserve_regions)) { // Partially eat into the reserve, at most max_to_eat_into_reserve regions. uint free_outside_reserve = _free_regions_at_end_of_collection - reserve_regions; - assert(free_outside_reserve < desired_eden_length, + assert(free_outside_reserve < desired_num_eden_regions, "must be %u %u", - free_outside_reserve, desired_eden_length); + free_outside_reserve, desired_num_eden_regions); - uint receiving_within_reserve = MIN2(desired_eden_length - free_outside_reserve, + uint receiving_within_reserve = MIN2(desired_num_eden_regions - free_outside_reserve, max_to_eat_into_reserve); uint receiving_eden = free_outside_reserve + receiving_within_reserve; // Again, we could have already allocated more than we could get. - receiving_additional_eden = allocated_eden_length < receiving_eden ? - receiving_eden - allocated_eden_length : 0; + receiving_additional_eden = num_eden_regions < receiving_eden ? + receiving_eden - num_eden_regions : 0; - log_trace(gc, ergo, heap)("Young target length: Partially eat into reserve " + log_trace(gc, ergo, heap)("Target young regions: Partially eat into reserve " "free outside reserve %u " "receiving within reserve %u " "receiving eden %u " @@ -395,116 +397,116 @@ uint G1Policy::calculate_young_target_length(uint desired_young_length, uint min receiving_eden, receiving_additional_eden); } else { // No need to use the reserve. - receiving_additional_eden = desired_young_length - allocated_young_length; - log_trace(gc, ergo, heap)("Young target length: No need to use reserve " + receiving_additional_eden = desired_num_young_regions - num_young_regions; + log_trace(gc, ergo, heap)("Target young regions: No need to use reserve " "receiving additional eden %u", receiving_additional_eden); } } - uint target_young_length = allocated_young_length + receiving_additional_eden; + uint target_num_young_regions = num_young_regions + receiving_additional_eden; - assert(target_young_length >= allocated_young_length, "must be"); + assert(target_num_young_regions >= num_young_regions, "must be"); - log_trace(gc, ergo, heap)("Young target length: " - "young target length %u " - "allocated young length %u " + log_trace(gc, ergo, heap)("Target num young regions: " + "target num young regions %u " + "allocated number of young regions %u " "received additional eden %u", - target_young_length, allocated_young_length, + target_num_young_regions, num_young_regions, receiving_additional_eden); - return target_young_length; + return target_num_young_regions; } -uint G1Policy::calculate_desired_eden_length_by_pause(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const { +uint G1Policy::calculate_desired_num_eden_regions_by_pause(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const { if (!next_gc_should_be_mixed()) { - return calculate_desired_eden_length_before_young_only(base_time_ms, - min_eden_length, - max_eden_length); + return calculate_desired_num_eden_regions_before_young_only(base_time_ms, + min_num_eden_regions, + max_num_eden_regions); } else { - return calculate_desired_eden_length_before_mixed(base_time_ms, - min_eden_length, - max_eden_length); + return calculate_desired_num_eden_regions_before_mixed(base_time_ms, + min_num_eden_regions, + max_num_eden_regions); } } -uint G1Policy::calculate_desired_eden_length_before_young_only(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const { - assert(use_adaptive_young_list_length(), "pre-condition"); +uint G1Policy::calculate_desired_num_eden_regions_before_young_only(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const { + assert(use_adaptive_num_young_regions(), "pre-condition"); - assert(min_eden_length <= max_eden_length, "must be %u %u", min_eden_length, max_eden_length); + assert(min_num_eden_regions <= max_num_eden_regions, "must be %u %u", min_num_eden_regions, max_num_eden_regions); - // Here, we will make sure that the shortest young length that + // Here, we will make sure that the smallest number of eden regions that // makes sense fits within the target pause time. - G1YoungLengthPredictor p(base_time_ms, - _free_regions_at_end_of_collection, - _mmu_tracker->max_gc_time() * 1000.0, - this); - if (p.will_fit(min_eden_length)) { - // The shortest young length will fit into the target pause time; - // we'll now check whether the absolute maximum number of young - // regions will fit in the target pause time. If not, we'll do - // a binary search between min_young_length and max_young_length. - if (p.will_fit(max_eden_length)) { - // The maximum young length will fit into the target pause time. - // We are done so set min young length to the maximum length (as - // the result is assumed to be returned in min_young_length). - min_eden_length = max_eden_length; + G1NumYoungRegionsPredictor p(base_time_ms, + _free_regions_at_end_of_collection, + _mmu_tracker->max_gc_time() * 1000.0, + this); + if (p.will_fit(min_num_eden_regions)) { + // The smallest number of eden regions will fit into the target pause time; + // we'll now check whether the absolute maximum number of young regions will fit + // in the target pause time. If not, we'll do a binary search between + // min_num_eden_regions and max_num_eden_regions. + if (p.will_fit(max_num_eden_regions)) { + // The maximum number of eden regions will fit into the target pause time. + // We are done, so set min_num_eden_regions to max_num_eden_regions (as the result is + // assumed to be returned in min_num_eden_regions). + min_num_eden_regions = max_num_eden_regions; } else { - // The maximum possible number of young regions will not fit within + // The maximum possible number of eden regions will not fit within // the target pause time so we'll search for the optimal - // length. The loop invariants are: + // number of eden regions. The loop invariants are: // - // min_young_length < max_young_length - // min_young_length is known to fit into the target pause time - // max_young_length is known not to fit into the target pause time + // min_num_eden_regions < max_num_eden_regions + // min_num_eden_regions is known to fit into the target pause time + // max_num_eden_regions is known not to fit into the target pause time // // Going into the loop we know the above hold as we've just // checked them. Every time around the loop we check whether - // the middle value between min_young_length and - // max_young_length fits into the target pause time. If it + // the middle value between min_num_eden_regions and + // max_num_eden_regions fits into the target pause time. If it // does, it becomes the new min. If it doesn't, it becomes // the new max. This way we maintain the loop invariants. - assert(min_eden_length < max_eden_length, "invariant"); - uint diff = (max_eden_length - min_eden_length) / 2; + precond(min_num_eden_regions < max_num_eden_regions); + uint diff = (max_num_eden_regions - min_num_eden_regions) / 2; while (diff > 0) { - uint eden_length = min_eden_length + diff; - if (p.will_fit(eden_length)) { - min_eden_length = eden_length; + uint num_eden_regions = min_num_eden_regions + diff; + if (p.will_fit(num_eden_regions)) { + min_num_eden_regions = num_eden_regions; } else { - max_eden_length = eden_length; + max_num_eden_regions = num_eden_regions; } - assert(min_eden_length < max_eden_length, "invariant"); - diff = (max_eden_length - min_eden_length) / 2; + postcond(min_num_eden_regions < max_num_eden_regions); + diff = (max_num_eden_regions - min_num_eden_regions) / 2; } - // The results is min_young_length which, according to the + // The result is min_num_eden_regions which, according to the // loop invariants, should fit within the target pause time. // These are the post-conditions of the binary search above: - assert(min_eden_length < max_eden_length, - "otherwise we should have discovered that max_eden_length " + assert(min_num_eden_regions < max_num_eden_regions, + "otherwise we should have discovered that max_num_eden_regions " "fits into the pause target and not done the binary search"); - assert(p.will_fit(min_eden_length), - "min_eden_length, the result of the binary search, should " + assert(p.will_fit(min_num_eden_regions), + "min_num_eden_regions, the result of the binary search, should " "fit into the pause target"); - assert(!p.will_fit(min_eden_length + 1), - "min_eden_length, the result of the binary search, should be " - "optimal, so no larger length should fit into the pause target"); + assert(!p.will_fit(min_num_eden_regions + 1), + "min_num_eden_regions, the result of the binary search, should be " + "optimal, so no larger number of eden regions should fit into the pause target"); } } else { - // Even the minimum length doesn't fit into the pause time - // target, return it as the result nevertheless. + // Even the minimum number of eden regions does not fit into the target pause time, + // return it as the result nevertheless. } - return min_eden_length; + return min_num_eden_regions; } -uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const { +uint G1Policy::calculate_desired_num_eden_regions_before_mixed(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const { uint min_marking_candidates = MIN2(calc_min_old_cset_length(candidates()->last_marking_candidates_length()), candidates()->from_marking_groups().num_regions()); double predicted_region_evac_time_ms = base_time_ms; @@ -517,9 +519,9 @@ uint G1Policy::calculate_desired_eden_length_before_mixed(double base_time_ms, selected_candidates += gr->length(); } - return calculate_desired_eden_length_before_young_only(predicted_region_evac_time_ms, - min_eden_length, - max_eden_length); + return calculate_desired_num_eden_regions_before_young_only(predicted_region_evac_time_ms, + min_num_eden_regions, + max_num_eden_regions); } double G1Policy::predict_survivor_regions_evac_time() const { @@ -572,10 +574,10 @@ G1GCPhaseTimes* G1Policy::phase_times() const { return _phase_times; } -void G1Policy::revise_young_list_target_length(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { - guarantee(use_adaptive_young_list_length(), "should not call this otherwise" ); +void G1Policy::revise_target_num_young_regions(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length) { + guarantee(use_adaptive_num_young_regions(), "should not call this otherwise" ); - update_young_length_bounds(pending_cards, card_rs_length, code_root_rs_length); + update_young_regions_bounds(pending_cards, card_rs_length, code_root_rs_length); } void G1Policy::record_full_collection_start() { @@ -601,7 +603,7 @@ void G1Policy::record_full_collection_end(size_t allocation_word_size) { _free_regions_at_end_of_collection = _g1h->num_free_regions(); _survivor_surv_rate_group->reset(); - update_young_length_bounds(); + update_young_regions_bounds(); record_pause(Pause::Full, start_time_sec, end_sec); } @@ -641,9 +643,9 @@ void G1Policy::record_dirtying_stats(double last_mutator_start_dirty_ms, double yield_duration_ms, size_t next_pending_cards_from_gc, size_t next_to_collection_set_cards) { - assert(SafepointSynchronize::is_at_safepoint() || G1ReviseYoungLength_lock->is_locked(), + assert(SafepointSynchronize::is_at_safepoint() || G1ReviseNumYoungRegions_lock->is_locked(), "must be (at safepoint %s locked %s)", - BOOL_TO_STR(SafepointSynchronize::is_at_safepoint()), BOOL_TO_STR(G1ReviseYoungLength_lock->is_locked())); + BOOL_TO_STR(SafepointSynchronize::is_at_safepoint()), BOOL_TO_STR(G1ReviseNumYoungRegions_lock->is_locked())); // Record mutator's card logging rate. // Unlike above for conc-refine rate, here we should not require a @@ -693,7 +695,7 @@ void G1Policy::record_young_collection_start() { record_pause_start_time(); // We only need to do this here as the policy will only be applied // to the GC we're about to start. so, no point is calculating this - // every time we calculate / recalculate the target young length. + // every time we calculate / recalculate the target number of young regions. update_survivors_policy(); assert(max_survivor_regions() + _g1h->num_used_regions() <= _g1h->max_num_regions(), @@ -992,7 +994,7 @@ G1CollectorState G1Policy::record_young_collection_end(bool concurrent_operation // Do not update dynamic IHOP due to G1 periodic collection as it is highly likely // that in this case we are not running in a "normal" operating mode. if (_g1h->gc_cause() != GCCause::_g1_periodic_collection) { - update_young_length_bounds(); + update_young_regions_bounds(); // Take snapshots of these values here as update_ihop_prediction // may complete the concurrent cycle and reset the values. @@ -1068,11 +1070,11 @@ bool G1Policy::update_ihop_prediction(double mutator_time_s, // The second clause prevents skewing the IHOP prediction with (typically) degenerate // back-to-back young-gen-size samples. if (this_gc_was_young_only && mutator_time_s > min_valid_time) { - // IHOP control wants to know the expected young gen length if it were not - // restrained by the heap reserve. Using the actual length would make the + // IHOP control wants to know the expected number of young regions if it were not + // restrained by the heap reserve. Using the current number of regions would make the // prediction too small and the limit the young gen every time we get to the // predicted target occupancy. - size_t young_gen_size = young_list_desired_length() * G1HeapRegion::GrainBytes; + size_t young_gen_size = desired_num_young_regions() * G1HeapRegion::GrainBytes; _ihop_control->record_expected_young_gen_size(young_gen_size); report = true; @@ -1188,7 +1190,7 @@ double G1Policy::predict_region_code_root_scan_time(G1HeapRegion* hr, bool for_y } bool G1Policy::should_allocate_mutator_region() const { - if (_g1h->young_regions_count() < young_list_target_length()) { + if (_g1h->young_regions_count() < target_num_young_regions()) { return true; } @@ -1205,8 +1207,8 @@ bool G1Policy::should_expand_on_mutator_allocation() const { return !is_init_completed(); } -bool G1Policy::use_adaptive_young_list_length() const { - return _young_gen_sizer.use_adaptive_young_list_length(); +bool G1Policy::use_adaptive_num_young_regions() const { + return _young_gen_sizer.use_adaptive_num_young_regions(); } size_t G1Policy::estimate_used_young_bytes_locked() const { @@ -1231,7 +1233,7 @@ void G1Policy::print_age_table() { // Calculates survivor space parameters. void G1Policy::update_survivors_policy() { double max_survivor_regions_d = - (double)young_list_target_length() / (double) SurvivorRatio; + (double)target_num_young_regions() / (double) SurvivorRatio; // Calculate desired survivor size based on desired max survivor regions (unconstrained // by remaining heap). Otherwise we may cause undesired promotions as we are @@ -1440,7 +1442,7 @@ bool G1Policy::try_get_available_bytes_estimate(size_t& available_bytes) const { size_t used_bytes = estimate_used_young_bytes_locked(); Heap_lock->unlock(); - size_t young_bytes = young_list_target_length() * G1HeapRegion::GrainBytes; + size_t young_bytes = target_num_young_regions() * G1HeapRegion::GrainBytes; available_bytes = young_bytes - MIN2(young_bytes, used_bytes); return true; } else { @@ -1532,7 +1534,7 @@ void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) { } stop_adding_survivor_regions(); - // Don't clear the survivor list handles until the start of + // Don't clear the survivor region tracking until the start of // the next evacuation pause - we need it in order to re-tag // the survivor regions from this evacuation pause as 'young' // at the start of the next. diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index b661daa9a3e8..1fa81fe60b66 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -79,11 +79,11 @@ class G1Policy: public CHeapObj { double _cur_pause_start_sec; - // Desired young gen length without taking actually available free regions into + // Desired number of young regions without taking actually available free regions into // account. - Atomic _young_list_desired_length; - // Actual target length given available free memory. - Atomic _young_list_target_length; + Atomic _desired_num_young_regions; + // Actual target number of young regions given available free memory. + Atomic _target_num_young_regions; // The survivor rate groups below must be initialized after the predictor because they // indirectly use it through the "this" object passed to their constructor. @@ -193,44 +193,45 @@ class G1Policy: public CHeapObj { // Lazily initialized mutable G1GCPhaseTimes* _phase_times; - // Updates the internal young gen maximum and target and desired lengths. + // Updates the internal young gen maximum and target and desired number of young regions. // If no parameters are passed, predict pending cards, card set remset length and // code root remset length using the prediction model. - void update_young_length_bounds(); - void update_young_length_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); + void update_young_regions_bounds(); + void update_young_regions_bounds(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); - // Calculate and return the minimum desired eden length based on the MMU target. - uint calculate_desired_eden_length_by_mmu() const; + // Calculate and return the minimum desired number of eden regions based on the MMU target. + uint calculate_desired_num_eden_regions_by_mmu() const; - // Calculate the desired eden length meeting the pause time goal. - // Min_eden_length and max_eden_length are the bounds + // Calculate the desired number of eden regions meeting the pause time goal. + // min_num_eden_regions and max_num_eden_regions are the bounds // (inclusive) within which eden can grow. - uint calculate_desired_eden_length_by_pause(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const; + uint calculate_desired_num_eden_regions_by_pause(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const; - // Calculate the desired eden length that can fit into the pause time + // Calculate the desired number of eden regions that can fit into the pause time // goal before young only gcs. - uint calculate_desired_eden_length_before_young_only(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const; + uint calculate_desired_num_eden_regions_before_young_only(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const; - // Calculates the desired eden length before mixed gc so that after adding the + // Calculates the desired number of eden regions before mixed gc so that after adding the // minimum amount of old gen regions from the collection set, the eden fits into // the pause time goal. - uint calculate_desired_eden_length_before_mixed(double base_time_ms, - uint min_eden_length, - uint max_eden_length) const; + uint calculate_desired_num_eden_regions_before_mixed(double base_time_ms, + uint min_num_eden_regions, + uint max_num_eden_regions) const; - // Calculate desired young length based on current situation without taking actually + // Calculate desired number of young regions based on current situation without taking actually // available free regions into account. - uint calculate_young_desired_length(size_t pending_cards, - size_t card_rs_length, - size_t code_root_rs_length, - uint min_young_length_by_sizer, - uint max_young_length_by_sizer) const; - // Limit the given desired young length to available free regions. - uint calculate_young_target_length(uint desired_young_length, uint min_young_length_by_sizer) const; + uint calculate_desired_num_young_regions(size_t pending_cards, + size_t card_rs_length, + size_t code_root_rs_length, + uint min_num_young_regions_by_sizer, + uint max_num_young_regions_by_sizer) const; + // Limit the given desired number of young regions to available free regions. + uint calculate_target_num_young_regions(uint desired_num_young_regions, + uint min_num_young_regions_by_sizer) const; double predict_survivor_regions_evac_time() const; double predict_retained_regions_evac_time() const; @@ -283,10 +284,10 @@ class G1Policy: public CHeapObj { G1GCPhaseTimes* phase_times() const; - // Check the current value of the young list RSet length and + // Check the current value of the young generation RSet length and // compare it against the last prediction. If the current value is - // higher, recalculate the young list target length prediction. - void revise_young_list_target_length(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); + // higher, recalculate the target number of young regions prediction. + void revise_target_num_young_regions(size_t pending_cards, size_t card_rs_length, size_t code_root_rs_length); // This should be called after the heap is resized. void record_new_heap_size(uint new_number_of_regions); @@ -347,13 +348,13 @@ class G1Policy: public CHeapObj { // This must be called at the very beginning of an evacuation pause. void decide_on_concurrent_start_pause(); - uint young_list_desired_length() const { return _young_list_desired_length.load_relaxed(); } - uint young_list_target_length() const { return _young_list_target_length.load_relaxed(); } + uint desired_num_young_regions() const { return _desired_num_young_regions.load_relaxed(); } + uint target_num_young_regions() const { return _target_num_young_regions.load_relaxed(); } bool should_allocate_mutator_region() const; bool should_expand_on_mutator_allocation() const; - bool use_adaptive_young_list_length() const; + bool use_adaptive_num_young_regions() const; // Try to get an estimate of the currently available bytes in the young gen. This // operation considers itself low-priority: if other threads need the resources diff --git a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.cpp b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.cpp similarity index 79% rename from src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.cpp rename to src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.cpp index 2f7acd9b710b..71c8d7bf772c 100644 --- a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.cpp +++ b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * 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,12 +24,12 @@ #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1Policy.hpp" -#include "gc/g1/g1ReviseYoungLengthTask.hpp" +#include "gc/g1/g1ReviseNumYoungRegionsTask.hpp" #include "gc/g1/g1ServiceThread.hpp" #include "gc/shared/suspendibleThreadSet.hpp" -jlong G1ReviseYoungLengthTask::reschedule_delay_ms() const { +jlong G1ReviseNumYoungRegionsTask::reschedule_delay_ms() const { G1Policy* policy = G1CollectedHeap::heap()->policy(); size_t available_bytes; if (policy->try_get_available_bytes_estimate(available_bytes)) { @@ -47,7 +47,7 @@ jlong G1ReviseYoungLengthTask::reschedule_delay_ms() const { } } -class G1ReviseYoungLengthTask::RemSetSamplingClosure : public G1HeapRegionClosure { +class G1ReviseNumYoungRegionsTask::RemSetSamplingClosure : public G1HeapRegionClosure { size_t _sampled_code_root_rs_length; public: @@ -62,16 +62,16 @@ class G1ReviseYoungLengthTask::RemSetSamplingClosure : public G1HeapRegionClosur size_t sampled_code_root_rs_length() const { return _sampled_code_root_rs_length; } }; -void G1ReviseYoungLengthTask::adjust_young_list_target_length() { +void G1ReviseNumYoungRegionsTask::adjust_target_num_young_regions() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1Policy* policy = g1h->policy(); - assert(policy->use_adaptive_young_list_length(), "should not call otherwise"); + assert(policy->use_adaptive_num_young_regions(), "should not call otherwise"); size_t pending_cards; size_t current_to_collection_set_cards; { - MutexLocker x(G1ReviseYoungLength_lock, Mutex::_no_safepoint_check_flag); + MutexLocker x(G1ReviseNumYoungRegions_lock, Mutex::_no_safepoint_check_flag); pending_cards = policy->current_pending_cards(); current_to_collection_set_cards = policy->current_to_collection_set_cards(); } @@ -79,18 +79,18 @@ void G1ReviseYoungLengthTask::adjust_young_list_target_length() { RemSetSamplingClosure cl; g1h->collection_set()->iterate(&cl); - policy->revise_young_list_target_length(pending_cards, + policy->revise_target_num_young_regions(pending_cards, current_to_collection_set_cards, cl.sampled_code_root_rs_length()); } -G1ReviseYoungLengthTask::G1ReviseYoungLengthTask(const char* name) : +G1ReviseNumYoungRegionsTask::G1ReviseNumYoungRegionsTask(const char* name) : G1ServiceTask(name) { } -void G1ReviseYoungLengthTask::execute() { +void G1ReviseNumYoungRegionsTask::execute() { SuspendibleThreadSetJoiner sts; - adjust_young_list_target_length(); + adjust_target_num_young_regions(); schedule(reschedule_delay_ms()); } diff --git a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.hpp b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.hpp similarity index 73% rename from src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.hpp rename to src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.hpp index baa8af75fb77..8bc9e2563669 100644 --- a/src/hotspot/share/gc/g1/g1ReviseYoungLengthTask.hpp +++ b/src/hotspot/share/gc/g1/g1ReviseNumYoungRegionsTask.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_GC_G1_G1REVISEYOUNGLENGTHTASK_HPP -#define SHARE_GC_G1_G1REVISEYOUNGLENGTHTASK_HPP +#ifndef SHARE_GC_G1_G1REVISENUMYOUNGREGIONSTASK_HPP +#define SHARE_GC_G1_G1REVISENUMYOUNGREGIONSTASK_HPP #include "gc/g1/g1CardSetMemory.hpp" #include "gc/g1/g1HeapRegionRemSet.hpp" @@ -32,18 +32,18 @@ #include "utilities/growableArray.hpp" #include "utilities/ticks.hpp" -// ServiceTask to revise the young generation target length. -class G1ReviseYoungLengthTask : public G1ServiceTask { +// ServiceTask to revise the target number of young regions. +class G1ReviseNumYoungRegionsTask : public G1ServiceTask { // The delay used to reschedule this task. jlong reschedule_delay_ms() const; class RemSetSamplingClosure; // Helper class for calculating remembered set summary. - // Adjust the target length (in regions) of the young gen, based on the - // current length of the remembered sets. + // Adjust the target number of young regions, based on the + // current occupancy of the remembered sets. // - // At the end of the GC G1 determines the length of the young gen based on + // At the end of the GC G1 determines the number of young regions based on // how much time the next GC can take, and when the next GC may occur // according to the MMU. // @@ -51,13 +51,13 @@ class G1ReviseYoungLengthTask : public G1ServiceTask { // the remembered sets (and many other components), so this thread constantly // reevaluates the prediction for the remembered set scanning costs, and potentially // resizes the young gen. This may do a premature GC or even increase the young - // gen size to keep pause time length goal. - void adjust_young_list_target_length(); + // gen size to keep pause time goal. + void adjust_target_num_young_regions(); public: - explicit G1ReviseYoungLengthTask(const char* name); + explicit G1ReviseNumYoungRegionsTask(const char* name); void execute() override; }; -#endif // SHARE_GC_G1_G1REVISEYOUNGLENGTHTASK_HPP \ No newline at end of file +#endif // SHARE_GC_G1_G1REVISENUMYOUNGREGIONSTASK_HPP diff --git a/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp b/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp index 7553936bb262..b913bdc2525b 100644 --- a/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp +++ b/src/hotspot/share/gc/g1/g1SATBMarkQueueSet.cpp @@ -114,12 +114,5 @@ class G1SATBMarkQueueFilterFn { }; void G1SATBMarkQueueSet::filter(SATBMarkQueue& queue) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - if (g1h->collector_state()->is_in_marking()) { - apply_filter(G1SATBMarkQueueFilterFn(), queue); - } else { - // is_in_marking() covers both the concurrent marking and the Remark pause. Outside - // of that, there can be no entry that requires SATB marking. - queue.set_empty(); - } + apply_filter(G1SATBMarkQueueFilterFn(), queue); } diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp index 86e55e8ac4f0..373ec9660da3 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.cpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp @@ -130,8 +130,13 @@ void VM_G1CollectForAllocation::doit() { } void VM_G1PauseConcurrent::doit() { - GCIdMark gc_id_mark(_gc_id); G1CollectedHeap* g1h = G1CollectedHeap::heap(); + if (_is_shutting_down) { + g1h->concurrent_mark()->shutdown_concurrent_cycle(); + return; + } + + GCIdMark gc_id_mark(_gc_id); GCTraceCPUTime tcpu(g1h->concurrent_mark()->gc_tracer_cm()); // GCTraceTime(...) only supports sub-phases, so a more verbose version @@ -150,12 +155,9 @@ void VM_G1PauseConcurrent::doit() { bool VM_G1PauseConcurrent::doit_prologue() { Heap_lock->lock(); G1CollectedHeap* g1h = G1CollectedHeap::heap(); - if (g1h->is_shutting_down()) { + _is_shutting_down = g1h->is_shutting_down(); + if (_is_shutting_down && !g1h->concurrent_mark()->shutdown_cleanup_needed()) { Heap_lock->unlock(); - // JVM shutdown has started. Abort concurrent marking to ensure that any further - // concurrent VM operations will not try to start and interfere with the shutdown - // process. - g1h->concurrent_mark()->abort_marking_threads(); return false; } return true; @@ -177,3 +179,19 @@ void VM_G1PauseCleanup::work() { G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); cm->cleanup(); } + +bool VM_G1StopMarking::doit_prologue() { + G1CollectedHeap* g1h = G1CollectedHeap::heap(); +#ifdef ASSERT + { + MutexLocker ml(Heap_lock); + assert(g1h->is_shutting_down(), "must be"); + } +#endif + return g1h->concurrent_mark()->shutdown_cleanup_needed(); +} + +void VM_G1StopMarking::doit() { + G1ConcurrentMark* cm = G1CollectedHeap::heap()->concurrent_mark(); + cm->shutdown_concurrent_cycle(); +} diff --git a/src/hotspot/share/gc/g1/g1VMOperations.hpp b/src/hotspot/share/gc/g1/g1VMOperations.hpp index 458d638b04e2..7d56ea1916f9 100644 --- a/src/hotspot/share/gc/g1/g1VMOperations.hpp +++ b/src/hotspot/share/gc/g1/g1VMOperations.hpp @@ -80,11 +80,12 @@ class VM_G1CollectForAllocation : public VM_CollectForAllocation { // Concurrent G1 stop-the-world operations such as remark and cleanup. class VM_G1PauseConcurrent : public VM_Operation { uint _gc_id; + bool _is_shutting_down; const char* _message; protected: VM_G1PauseConcurrent(const char* message) : - _gc_id(GCId::current()), _message(message) { } + _gc_id(GCId::current()), _is_shutting_down(false), _message(message) { } virtual void work() = 0; // Does this concurrent pause affect the memory pools? If so, update the collectionUsage() @@ -116,4 +117,15 @@ class VM_G1PauseCleanup : public VM_G1PauseConcurrent { void work() override; }; +class VM_G1StopMarking : public VM_Operation { +public: + VM_G1StopMarking() : VM_Operation() { } + VMOp_Type type() const override { return VMOp_G1StopMarking; } + + bool doit_prologue() override; + void doit() override; + + bool is_gc_operation() const override { return true; } +}; + #endif // SHARE_GC_G1_G1VMOPERATIONS_HPP diff --git a/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp b/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp index 60c79ec28df7..e817ebcab56f 100644 --- a/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp @@ -30,7 +30,7 @@ #include "runtime/globals_extension.hpp" G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), - _use_adaptive_sizing(true), _min_desired_young_length(), _max_desired_young_length(0) { + _use_adaptive_sizing(true), _min_desired_num_regions(), _max_desired_num_regions(0) { precond(!FLAG_IS_ERGO(NewRatio)); precond(!FLAG_IS_ERGO(NewSize)); @@ -100,16 +100,16 @@ G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), } if (user_specified_NewSize) { - _min_desired_young_length.store_relaxed(MAX2((uint)(NewSize / G1HeapRegion::GrainBytes), 1U)); + _min_desired_num_regions.store_relaxed(MAX2((uint)(NewSize / G1HeapRegion::GrainBytes), 1U)); } if (user_specified_MaxNewSize) { - _max_desired_young_length.store_relaxed(MAX2((uint)(MaxNewSize / G1HeapRegion::GrainBytes), 1U)); + _max_desired_num_regions.store_relaxed(MAX2((uint)(MaxNewSize / G1HeapRegion::GrainBytes), 1U)); } if (user_specified_NewSize && user_specified_MaxNewSize) { _sizer_kind = SizerMaxAndNewSize; - _use_adaptive_sizing = min_desired_young_length() != max_desired_young_length(); + _use_adaptive_sizing = min_desired_num_regions() != max_desired_num_regions(); } else if (user_specified_NewSize) { _sizer_kind = SizerNewSizeOnly; } else { @@ -118,52 +118,52 @@ G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), } } -uint G1YoungGenSizer::calculate_default_min_length(uint new_number_of_heap_regions) { +uint G1YoungGenSizer::calculate_default_min_num_regions(uint new_number_of_heap_regions) { uint default_value = (new_number_of_heap_regions * G1NewSizePercent) / 100; return MAX2(1U, default_value); } -uint G1YoungGenSizer::calculate_default_max_length(uint new_number_of_heap_regions) { +uint G1YoungGenSizer::calculate_default_max_num_regions(uint new_number_of_heap_regions) { uint default_value = (new_number_of_heap_regions * G1MaxNewSizePercent) / 100; return MAX2(1U, default_value); } -void G1YoungGenSizer::recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length) { +void G1YoungGenSizer::recalculate_min_max_num_regions(uint number_of_heap_regions, uint* min_num_young_regions, uint* max_num_young_regions) { assert(number_of_heap_regions > 0, "Heap must be initialized"); switch (_sizer_kind) { case SizerDefaults: - *min_young_length = calculate_default_min_length(number_of_heap_regions); - *max_young_length = calculate_default_max_length(number_of_heap_regions); + *min_num_young_regions = calculate_default_min_num_regions(number_of_heap_regions); + *max_num_young_regions = calculate_default_max_num_regions(number_of_heap_regions); break; case SizerNewSizeOnly: - *max_young_length = calculate_default_max_length(number_of_heap_regions); - *max_young_length = MAX2(*min_young_length, *max_young_length); + *max_num_young_regions = calculate_default_max_num_regions(number_of_heap_regions); + *max_num_young_regions = MAX2(*min_num_young_regions, *max_num_young_regions); break; case SizerMaxNewSizeOnly: - *min_young_length = calculate_default_min_length(number_of_heap_regions); - *min_young_length = MIN2(*min_young_length, *max_young_length); + *min_num_young_regions = calculate_default_min_num_regions(number_of_heap_regions); + *min_num_young_regions = MIN2(*min_num_young_regions, *max_num_young_regions); break; case SizerMaxAndNewSize: // Do nothing. Values set on the command line, don't update them at runtime. break; case SizerNewRatio: - *min_young_length = MAX2((uint)(number_of_heap_regions / (NewRatio + 1)), 1u); - *max_young_length = *min_young_length; + *min_num_young_regions = MAX2((uint)(number_of_heap_regions / (NewRatio + 1)), 1u); + *max_num_young_regions = *min_num_young_regions; break; default: ShouldNotReachHere(); } - assert(*min_young_length <= *max_young_length, "Invalid min/max young gen size values"); + assert(*min_num_young_regions <= *max_num_young_regions, "Invalid min/max young gen size values"); } void G1YoungGenSizer::adjust_max_new_size(uint number_of_heap_regions) { // We need to pass the desired values because recalculation may not update these // values in some cases. - uint unused_new_min = min_desired_young_length(); - uint new_max = max_desired_young_length(); - recalculate_min_max_young_length(number_of_heap_regions, &unused_new_min, &new_max); + uint unused_new_min = min_desired_num_regions(); + uint new_max = max_desired_num_regions(); + recalculate_min_max_num_regions(number_of_heap_regions, &unused_new_min, &new_max); size_t max_young_size = new_max * G1HeapRegion::GrainBytes; if (max_young_size != MaxNewSize) { @@ -172,9 +172,9 @@ void G1YoungGenSizer::adjust_max_new_size(uint number_of_heap_regions) { } void G1YoungGenSizer::heap_size_changed(uint new_number_of_heap_regions) { - uint min = min_desired_young_length(); - uint max = max_desired_young_length(); - recalculate_min_max_young_length(new_number_of_heap_regions, &min, &max); - _min_desired_young_length.store_relaxed(min); - _max_desired_young_length.store_relaxed(max); + uint min = min_desired_num_regions(); + uint max = max_desired_num_regions(); + recalculate_min_max_num_regions(new_number_of_heap_regions, &min, &max); + _min_desired_num_regions.store_relaxed(min); + _max_desired_num_regions.store_relaxed(max); } diff --git a/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp b/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp index c60c3c373a9a..ea269f72559f 100644 --- a/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp @@ -79,31 +79,31 @@ class G1YoungGenSizer { // true otherwise. bool _use_adaptive_sizing; - Atomic _min_desired_young_length; - Atomic _max_desired_young_length; + Atomic _min_desired_num_regions; + Atomic _max_desired_num_regions; - uint calculate_default_min_length(uint new_number_of_heap_regions); - uint calculate_default_max_length(uint new_number_of_heap_regions); + uint calculate_default_min_num_regions(uint new_number_of_heap_regions); + uint calculate_default_max_num_regions(uint new_number_of_heap_regions); - // Update the given values for minimum and maximum young gen length in regions - // given the number of heap regions depending on the kind of sizing algorithm. - void recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length); + // Recalculate the minimum and maximum number of young regions for the + // given number of heap regions according to the current sizing algorithm. + void recalculate_min_max_num_regions(uint number_of_heap_regions, uint* min_num_young_regions, uint* max_num_young_regions); public: G1YoungGenSizer(); - // Calculate the maximum length of the young gen given the number of regions + // Calculate the maximum size of the young gen given the number of regions // depending on the sizing algorithm. virtual void adjust_max_new_size(uint number_of_heap_regions); virtual void heap_size_changed(uint new_number_of_heap_regions); - uint min_desired_young_length() const { - return _min_desired_young_length.load_relaxed(); + uint min_desired_num_regions() const { + return _min_desired_num_regions.load_relaxed(); } - uint max_desired_young_length() const { - return _max_desired_young_length.load_relaxed(); + uint max_desired_num_regions() const { + return _max_desired_num_regions.load_relaxed(); } - bool use_adaptive_young_list_length() const { + bool use_adaptive_num_young_regions() const { return _use_adaptive_sizing; } }; diff --git a/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp b/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp index df6adeb80417..2ad3500de4a8 100644 --- a/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp +++ b/src/hotspot/share/gc/g1/jvmFlagConstraintsG1.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -165,7 +165,7 @@ JVMFlag::Error GCPauseIntervalMillisConstraintFuncG1(uintx value, bool verbose) JVMFlag::Error NewSizeConstraintFuncG1(size_t value, bool verbose) { #ifdef _LP64 - // Overflow would happen for uint type variable of YoungGenSizer::_min_desired_young_length + // Overflow would happen for uint type variable of YoungGenSizer::_min_desired_num_regions // when the value to be assigned exceeds uint range. // i.e. result of '(uint)(NewSize / region size(1~32MB))' // So maximum of NewSize should be 'max_juint * 1M' diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index b77294a2ac12..ea3a85861b8d 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -308,11 +308,25 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) { for (uint loop_count = 0; /* empty */; ++loop_count) { HeapWord* result; { + // This lock is needed to sync with the VM-init expansion below. ConditionalMutexLocker locker(Heap_lock, !is_init_completed()); result = mem_allocate_cas_noexpand(size, is_tlab); if (result != nullptr) { return result; } + + // Ensure that is_init_completed() does not transition while expanding the heap. + ConditionalMutexLocker ml_init(InitCompleted_lock, !is_init_completed(), Mutex::_no_safepoint_check_flag); + if (!is_init_completed()) { + // Rechecked !is_init_completed() implies we have mutual exclusion via + // `Heap_lock` and `InitCompleted_lock` + result = expand_heap_and_allocate(size, is_tlab); + // Return the result if it's tlab-allocation. If the result is null, + // callers will retry non-tlab allocation. + if (result != nullptr || is_tlab) { + return result; + } + } } // Read total_collections() under the lock so that multiple @@ -328,19 +342,6 @@ HeapWord* ParallelScavengeHeap::mem_allocate_work(size_t size, bool is_tlab) { return result; } - if (!is_init_completed()) { - // Double checked locking, this ensure that is_init_completed() does not - // transition while expanding the heap. - MonitorLocker ml(InitCompleted_lock, Monitor::_no_safepoint_check_flag); - if (!is_init_completed()) { - // Can't do GC; try heap expansion to satisfy the request. - result = expand_heap_and_allocate(size, is_tlab); - if (result != nullptr) { - return result; - } - } - } - gc_count = total_collections(); } diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp index 637ed6e64074..de0b838fe45d 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp @@ -31,8 +31,6 @@ #include "gc/shenandoah/shenandoahBarrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" -#include "gc/shenandoah/shenandoahHeapRegion.hpp" -#include "gc/shenandoah/shenandoahRuntime.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" #ifdef ASSERT @@ -41,43 +39,61 @@ #define __ gen->lir()-> #endif -void ShenandoahPreBarrierStub::emit_code(LIR_Assembler* ce) { +void ShenandoahKeepaliveBarrierStub::emit_code(LIR_Assembler* ce) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->gen_pre_barrier_stub(ce, this); + bs->keepalive_barrier_c1_stub(ce, this); } void ShenandoahLoadReferenceBarrierStub::emit_code(LIR_Assembler* ce) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->gen_load_reference_barrier_stub(ce, this); + bs->load_reference_barrier_c1_stub(ce, this); } ShenandoahBarrierSetC1::ShenandoahBarrierSetC1() : - _pre_barrier_c1_runtime_code_blob(nullptr), + _keepalive_barrier_c1_runtime_code_blob(nullptr), _load_reference_barrier_strong_rt_code_blob(nullptr), _load_reference_barrier_strong_native_rt_code_blob(nullptr), _load_reference_barrier_weak_rt_code_blob(nullptr), _load_reference_barrier_phantom_rt_code_blob(nullptr) {} -void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val) { - // First we test whether marking is in progress. +address ShenandoahBarrierSetC1::keepalive_barrier_stub() { + assert(_keepalive_barrier_c1_runtime_code_blob != nullptr, "Must be available"); + return _keepalive_barrier_c1_runtime_code_blob->code_begin(); +} - bool patch = (decorators & C1_NEEDS_PATCHING) != 0; - bool do_load = pre_val == LIR_OprFact::illegalOpr; +address ShenandoahBarrierSetC1::load_reference_barrier_stub(DecoratorSet decorators) { + bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); + bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); + bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); + bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + if (is_strong) { + if (is_native) { + assert(_load_reference_barrier_strong_native_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_strong_native_rt_code_blob->code_begin(); + } else { + assert(_load_reference_barrier_strong_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_strong_rt_code_blob->code_begin(); + } + } else if (is_weak) { + assert(_load_reference_barrier_weak_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_weak_rt_code_blob->code_begin(); + } else if (is_phantom) { + assert(_load_reference_barrier_phantom_rt_code_blob != nullptr, "Must be available"); + return _load_reference_barrier_phantom_rt_code_blob->code_begin(); + } + ShouldNotReachHere(); + return nullptr; +} + +void ShenandoahBarrierSetC1::enter_if_gc_state(LIRGenerator* gen, int flags, CodeStub* slow_stub) { LIR_Opr thrd = gen->getThreadPointer(); - LIR_Address* gc_state_addr = - new LIR_Address(thrd, - in_bytes(ShenandoahThreadLocalData::gc_state_offset()), - T_BYTE); - // Read the gc_state flag. LIR_Opr flag_val = gen->new_register(T_INT); - __ load(gc_state_addr, flag_val); - - // Create a mask to test if the marking bit is set. - LIR_Opr mask = LIR_OprFact::intConst(ShenandoahHeap::MARKING); LIR_Opr mask_reg = gen->new_register(T_INT); - __ move(mask, mask_reg); + LIR_Address* gc_state_addr = new LIR_Address(thrd, in_bytes(ShenandoahThreadLocalData::gc_state_offset()), T_BYTE); + __ load(gc_state_addr, flag_val); + __ move(LIR_OprFact::intConst(flags), mask_reg); if (two_operand_lir_form) { __ logical_and(flag_val, mask_reg, flag_val); } else { @@ -86,91 +102,54 @@ void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, flag_val = masked_flag; } __ cmp(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0)); + __ branch(lir_cond_notEqual, slow_stub); + __ branch_destination(slow_stub->continuation()); +} - LIR_PatchCode pre_val_patch_code = lir_patch_none; - - CodeStub* slow; - - if (do_load) { - assert(pre_val == LIR_OprFact::illegalOpr, "sanity"); - assert(addr_opr != LIR_OprFact::illegalOpr, "sanity"); - - if (patch) - pre_val_patch_code = lir_patch_normal; - - pre_val = gen->new_register(T_OBJECT); +void ShenandoahBarrierSetC1::keepalive_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { + CodeStub* slow_stub; + if (obj == LIR_OprFact::illegalOpr) { + // Caller wants us to do the load. + obj = gen->new_register(T_OBJECT); - if (!addr_opr->is_address()) { - assert(addr_opr->is_register(), "must be"); - addr_opr = LIR_OprFact::address(new LIR_Address(addr_opr, T_OBJECT)); + assert(addr != LIR_OprFact::illegalOpr, "sanity"); + if (!addr->is_address()) { + assert(addr->is_register(), "must be"); + addr = LIR_OprFact::address(new LIR_Address(addr, T_OBJECT)); } - slow = new ShenandoahPreBarrierStub(addr_opr, pre_val, pre_val_patch_code, info ? new CodeEmitInfo(info) : nullptr); + + slow_stub = new ShenandoahKeepaliveBarrierStub(obj, addr); } else { - assert(addr_opr == LIR_OprFact::illegalOpr, "sanity"); - assert(pre_val->is_register(), "must be"); - assert(pre_val->type() == T_OBJECT, "must be an object"); + // Caller gave us the obj to work with. + assert(addr == LIR_OprFact::illegalOpr, "sanity"); + assert(obj->is_register(), "must be"); + assert(obj->type() == T_OBJECT, "must be an object"); - slow = new ShenandoahPreBarrierStub(pre_val); + slow_stub = new ShenandoahKeepaliveBarrierStub(obj); } - __ branch(lir_cond_notEqual, slow); - __ branch_destination(slow->continuation()); -} - -LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { - if (ShenandoahLoadRefBarrier) { - return load_reference_barrier_impl(gen, obj, addr, decorators); - } else { - return obj; - } + enter_if_gc_state(gen, ShenandoahHeap::MARKING, slow_stub); } -LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { +void ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); obj = ensure_in_register(gen, obj, T_OBJECT); - assert(obj->is_register(), "must be a register at this point"); addr = ensure_in_register(gen, addr, T_ADDRESS); + assert(obj->is_register(), "must be a register at this point"); assert(addr->is_register(), "must be a register at this point"); - LIR_Opr result = gen->result_register_for(obj->value_type()); - LIR_Opr tmp1 = gen->new_register(T_ADDRESS); - LIR_Opr tmp2 = gen->new_register(T_ADDRESS); - LIR_Opr thrd = gen->getThreadPointer(); - LIR_Address* active_flag_addr = - new LIR_Address(thrd, - in_bytes(ShenandoahThreadLocalData::gc_state_offset()), - T_BYTE); - // Read and check the gc-state-flag. - LIR_Opr flag_val = gen->new_register(T_INT); - __ load(active_flag_addr, flag_val); + // Barrier slowpaths return value in this register. Declare it in the stub + // as clobbered. The obj would remain as result for both fast- and slow-paths. + LIR_Opr slow_result = gen->result_register_for(obj->value_type()); + + CodeStub* slow_stub = new ShenandoahLoadReferenceBarrierStub(obj, addr, slow_result, decorators); + int flags = ShenandoahHeap::HAS_FORWARDED; if (!ShenandoahBarrierSet::is_strong_access(decorators)) { flags |= ShenandoahHeap::WEAK_ROOTS; } - LIR_Opr mask = LIR_OprFact::intConst(flags); - LIR_Opr mask_reg = gen->new_register(T_INT); - __ move(mask, mask_reg); - - if (two_operand_lir_form) { - __ logical_and(flag_val, mask_reg, flag_val); - } else { - LIR_Opr masked_flag = gen->new_register(T_INT); - __ logical_and(flag_val, mask_reg, masked_flag); - flag_val = masked_flag; - } - __ cmp(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0)); - - CodeStub* slow = new ShenandoahLoadReferenceBarrierStub(obj, addr, result, tmp1, tmp2, decorators); - __ branch(lir_cond_notEqual, slow); - - // No barrier is needed, move obj to result now. - __ move(obj, result); - - // Slow-path re-enters here with result set. - __ branch_destination(slow->continuation()); - - return result; + enter_if_gc_state(gen, flags, slow_stub); } LIR_Opr ShenandoahBarrierSetC1::ensure_in_register(LIRGenerator* gen, LIR_Opr obj, BasicType type) { @@ -189,21 +168,21 @@ LIR_Opr ShenandoahBarrierSetC1::ensure_in_register(LIRGenerator* gen, LIR_Opr ob } void ShenandoahBarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) { - if (access.is_oop()) { - if (ShenandoahSATBBarrier) { - pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), access.resolved_addr(), LIR_OprFact::illegalOpr /* pre_val */); - } + DecoratorSet decorators = access.decorators(); + LIRGenerator* gen = access.gen(); + + if (ShenandoahSATBBarrier && access.is_oop()) { + keepalive_barrier(gen, /* obj = */ LIR_OprFact::illegalOpr, /* addr = */ access.resolved_addr(), decorators); } BarrierSetC1::store_at_resolved(access, value); if (ShenandoahCardBarrier && access.is_oop()) { - DecoratorSet decorators = access.decorators(); bool is_array = (decorators & IS_ARRAY) != 0; bool on_anonymous = (decorators & ON_UNKNOWN_OOP_REF) != 0; bool precise = is_array || on_anonymous; LIR_Opr post_addr = precise ? access.resolved_addr() : access.base().opr(); - post_barrier(access, post_addr); + card_barrier(gen, post_addr, decorators); } } @@ -230,7 +209,7 @@ void ShenandoahBarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) { LIR_Opr tmp = gen->new_register(T_OBJECT); BarrierSetC1::load_at_resolved(access, tmp); - tmp = load_reference_barrier(gen, tmp, access.resolved_addr(), decorators); + load_reference_barrier(gen, tmp, access.resolved_addr(), decorators); __ move(tmp, result); } else { BarrierSetC1::load_at_resolved(access, result); @@ -246,18 +225,17 @@ void ShenandoahBarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) Lcont_anonymous = new LabelObj(); generate_referent_check(access, Lcont_anonymous); } - pre_barrier(gen, access.access_emit_info(), decorators, LIR_OprFact::illegalOpr /* addr_opr */, - result /* pre_val */); + keepalive_barrier(gen, /* obj = */ result, /* addr = */ LIR_OprFact::illegalOpr, decorators); if (is_anonymous) { __ branch_destination(Lcont_anonymous->label()); } } } -class C1ShenandoahPreBarrierCodeGenClosure : public StubAssemblerCodeGenClosure { +class C1ShenandoahKeepaliveBarrierCodeGenClosure : public StubAssemblerCodeGenClosure { virtual OopMapSet* generate_code(StubAssembler* sasm) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->generate_c1_pre_barrier_runtime_stub(sasm); + bs->keepalive_barrier_c1_runtime_stub(sasm); return nullptr; } }; @@ -271,18 +249,20 @@ class C1ShenandoahLoadReferenceBarrierCodeGenClosure : public StubAssemblerCodeG virtual OopMapSet* generate_code(StubAssembler* sasm) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->generate_c1_load_reference_barrier_runtime_stub(sasm, _decorators); + bs->load_reference_barrier_c1_runtime_stub(sasm, _decorators); return nullptr; } }; bool ShenandoahBarrierSetC1::generate_c1_runtime_stubs(BufferBlob* buffer_blob) { - C1ShenandoahPreBarrierCodeGenClosure pre_code_gen_cl; - _pre_barrier_c1_runtime_code_blob = Runtime1::generate_blob(buffer_blob, StubId::NO_STUBID, - "shenandoah_pre_barrier_slow", - false, &pre_code_gen_cl); - if (_pre_barrier_c1_runtime_code_blob == nullptr) { - return false; + if (ShenandoahSATBBarrier) { + C1ShenandoahKeepaliveBarrierCodeGenClosure keepalive_code_gen_cl; + _keepalive_barrier_c1_runtime_code_blob = Runtime1::generate_blob(buffer_blob, StubId::NO_STUBID, + "shenandoah_keepalive_barrier_slow", + false, &keepalive_code_gen_cl); + if (_keepalive_barrier_c1_runtime_code_blob == nullptr) { + return false; + } } if (ShenandoahLoadRefBarrier) { C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_strong_code_gen_cl(ON_STRONG_OOP_REF); @@ -318,11 +298,9 @@ bool ShenandoahBarrierSetC1::generate_c1_runtime_stubs(BufferBlob* buffer_blob) return true; } -void ShenandoahBarrierSetC1::post_barrier(LIRAccess& access, LIR_Opr addr) { +void ShenandoahBarrierSetC1::card_barrier(LIRGenerator* gen, LIR_Opr addr, DecoratorSet decorators) { assert(ShenandoahCardBarrier, "Should have been checked by caller"); - DecoratorSet decorators = access.decorators(); - LIRGenerator* gen = access.gen(); bool in_heap = (decorators & IN_HEAP) != 0; if (!in_heap) { return; @@ -378,6 +356,7 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_cmpxchg_at_resolved(LIRAccess& access, LI return BarrierSetC1::atomic_cmpxchg_at_resolved(access, cmp_value, new_value); } + DecoratorSet decorators = access.decorators(); LIRGenerator* gen = access.gen(); LIR_Opr tmp = gen->new_register(T_OBJECT); @@ -386,22 +365,20 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_cmpxchg_at_resolved(LIRAccess& access, LI // Handle the previous value through SATB, as we are about to perform the store. __ load(addr->as_address_ptr(), tmp); if (ShenandoahSATBBarrier) { - pre_barrier(gen, access.access_emit_info(), access.decorators(), - /* addr_opr (unused) = */ LIR_OprFact::illegalOpr, - /* pre_val = */ tmp); + keepalive_barrier(gen, /* obj = */ tmp, /* addr = */ LIR_OprFact::illegalOpr, decorators); } // Perform LRB on location to fix it up for this and all following accesses. // This guarantees there are no false negatives due to concurrent evacuation, // and the value loaded later by CAS is sanitized by some LRB, or is null. if (ShenandoahLoadRefBarrier) { - load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, access.decorators()); + load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, decorators); } LIR_Opr result = BarrierSetC1::atomic_cmpxchg_at_resolved(access, cmp_value, new_value); if (ShenandoahCardBarrier) { - post_barrier(access, /* addr = */ addr); + card_barrier(gen, /* addr = */ addr, decorators); } return result; @@ -412,6 +389,7 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt return BarrierSetC1::atomic_xchg_at_resolved(access, value); } + DecoratorSet decorators = access.decorators(); LIRGenerator* gen = access.gen(); LIR_Opr tmp = gen->new_register(T_OBJECT); @@ -420,22 +398,20 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt // Handle the previous value through SATB, as we are about to perform the store. __ load(addr->as_address_ptr(), tmp); if (ShenandoahSATBBarrier) { - pre_barrier(gen, access.access_emit_info(), access.decorators(), - /* addr_opr (unused) = */ LIR_OprFact::illegalOpr, - /* pre_val = */ tmp); + keepalive_barrier(gen, /* obj = */ tmp, /* addr = */ LIR_OprFact::illegalOpr, decorators); } // Perform LRB on location to fix it up for this and all following accesses. // This is purely opportunistic: we would not have any false negatives here. // This guarantees the value loaded later by XCHG is sanitized by some LRB, or is null. if (ShenandoahLoadRefBarrier) { - load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, access.decorators()); + load_reference_barrier(gen, /* obj = */ tmp, /* addr = */ addr, decorators); } LIR_Opr result = BarrierSetC1::atomic_xchg_at_resolved(access, value); if (ShenandoahCardBarrier) { - post_barrier(access, /* addr = */ addr); + card_barrier(gen, /* addr = */ addr, decorators); } return result; diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp index 413777a61ee6..3f064c3569b9 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp @@ -29,63 +29,48 @@ #include "c1/c1_CodeStubs.hpp" #include "gc/shared/c1/barrierSetC1.hpp" -class ShenandoahPreBarrierStub: public CodeStub { +class ShenandoahKeepaliveBarrierStub: public CodeStub { friend class ShenandoahBarrierSetC1; private: - bool _do_load; + LIR_Opr _obj; LIR_Opr _addr; - LIR_Opr _pre_val; - LIR_PatchCode _patch_code; - CodeEmitInfo* _info; + bool _do_load; public: - // Version that _does_ generate a load of the previous value from addr. - // addr (the address of the field to be read) must be a LIR_Address - // pre_val (a temporary register) must be a register; - ShenandoahPreBarrierStub(LIR_Opr addr, LIR_Opr pre_val, LIR_PatchCode patch_code, CodeEmitInfo* info) : - _do_load(true), _addr(addr), _pre_val(pre_val), - _patch_code(patch_code), _info(info) + ShenandoahKeepaliveBarrierStub(LIR_Opr obj, LIR_Opr addr) : + _obj(obj), _addr(addr), _do_load(true) { - assert(_pre_val->is_register(), "should be temporary register"); + assert(_obj->is_register(), "should be temporary register"); assert(_addr->is_address(), "should be the address of the field"); FrameMap* f = Compilation::current()->frame_map(); - f->update_reserved_argument_area_size(2 * BytesPerWord); + f->update_reserved_argument_area_size(1 * BytesPerWord); } - // Version that _does not_ generate load of the previous value; the - // previous value is assumed to have already been loaded into pre_val. - ShenandoahPreBarrierStub(LIR_Opr pre_val) : - _do_load(false), _addr(LIR_OprFact::illegalOpr), _pre_val(pre_val), - _patch_code(lir_patch_none), _info(nullptr) + ShenandoahKeepaliveBarrierStub(LIR_Opr obj) : + _obj(obj), _addr(LIR_OprFact::illegalOpr), _do_load(false) { - assert(_pre_val->is_register(), "should be a register"); + assert(_obj->is_register(), "should be a register"); + FrameMap* f = Compilation::current()->frame_map(); + f->update_reserved_argument_area_size(1 * BytesPerWord); } LIR_Opr addr() const { return _addr; } - LIR_Opr pre_val() const { return _pre_val; } - LIR_PatchCode patch_code() const { return _patch_code; } - CodeEmitInfo* info() const { return _info; } + LIR_Opr obj() const { return _obj; } bool do_load() const { return _do_load; } virtual void emit_code(LIR_Assembler* e); virtual void visit(LIR_OpVisitState* visitor) { + visitor->do_slow_case(); if (_do_load) { - // don't pass in the code emit info since it's processed in the fast - // path - if (_info != nullptr) - visitor->do_slow_case(_info); - else - visitor->do_slow_case(); - visitor->do_input(_addr); - visitor->do_temp(_pre_val); + visitor->do_temp(_addr); + visitor->do_temp(_obj); } else { - visitor->do_slow_case(); - visitor->do_input(_pre_val); + visitor->do_input(_obj); } } #ifndef PRODUCT - virtual void print_name(outputStream* out) const { out->print("ShenandoahPreBarrierStub"); } + virtual void print_name(outputStream* out) const { out->print("ShenandoahKeepaliveBarrierStub"); } #endif // PRODUCT }; @@ -94,29 +79,21 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { private: LIR_Opr _obj; LIR_Opr _addr; - LIR_Opr _result; - LIR_Opr _tmp1; - LIR_Opr _tmp2; + LIR_Opr _slow_result; DecoratorSet _decorators; public: - ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr addr, LIR_Opr result, LIR_Opr tmp1, LIR_Opr tmp2, DecoratorSet decorators) : - _obj(obj), _addr(addr), _result(result), _tmp1(tmp1), _tmp2(tmp2), _decorators(decorators) + ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr addr, LIR_Opr slow_result, DecoratorSet decorators) : + _obj(obj), _addr(addr), _slow_result(slow_result), _decorators(decorators) { assert(_obj->is_register(), "should be register"); assert(_addr->is_register(), "should be register"); - assert(_result->is_register(), "should be register"); - assert(_tmp1->is_register(), "should be register"); - assert(_tmp2->is_register(), "should be register"); - FrameMap* f = Compilation::current()->frame_map(); f->update_reserved_argument_area_size(2 * BytesPerWord); } LIR_Opr obj() const { return _obj; } LIR_Opr addr() const { return _addr; } - LIR_Opr result() const { return _result; } - LIR_Opr tmp1() const { return _tmp1; } - LIR_Opr tmp2() const { return _tmp2; } + LIR_Opr slow_result() const { return _slow_result; } DecoratorSet decorators() const { return _decorators; } virtual void emit_code(LIR_Assembler* e); @@ -124,12 +101,10 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { visitor->do_slow_case(); visitor->do_input(_obj); visitor->do_temp(_obj); + visitor->do_output(_obj); visitor->do_input(_addr); visitor->do_temp(_addr); - visitor->do_temp(_result); - visitor->do_output(_result); - visitor->do_temp(_tmp1); - visitor->do_temp(_tmp2); + visitor->do_temp(_slow_result); } #ifndef PRODUCT virtual void print_name(outputStream* out) const { out->print("ShenandoahLoadReferenceBarrierStub"); } @@ -138,63 +113,35 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { class ShenandoahBarrierSetC1 : public BarrierSetC1 { private: - CodeBlob* _pre_barrier_c1_runtime_code_blob; + CodeBlob* _keepalive_barrier_c1_runtime_code_blob; CodeBlob* _load_reference_barrier_strong_rt_code_blob; CodeBlob* _load_reference_barrier_strong_native_rt_code_blob; CodeBlob* _load_reference_barrier_weak_rt_code_blob; CodeBlob* _load_reference_barrier_phantom_rt_code_blob; - void pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val); - - LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void enter_if_gc_state(LIRGenerator* gen, int flags, CodeStub* slow_stub); - LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void keepalive_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); + void card_barrier(LIRGenerator* gen, LIR_Opr addr, DecoratorSet decorators); LIR_Opr ensure_in_register(LIRGenerator* gen, LIR_Opr obj, BasicType type); public: ShenandoahBarrierSetC1(); - CodeBlob* pre_barrier_c1_runtime_code_blob() { - assert(_pre_barrier_c1_runtime_code_blob != nullptr, ""); - return _pre_barrier_c1_runtime_code_blob; - } - - CodeBlob* load_reference_barrier_strong_rt_code_blob() { - assert(_load_reference_barrier_strong_rt_code_blob != nullptr, ""); - return _load_reference_barrier_strong_rt_code_blob; - } - - CodeBlob* load_reference_barrier_strong_native_rt_code_blob() { - assert(_load_reference_barrier_strong_native_rt_code_blob != nullptr, ""); - return _load_reference_barrier_strong_native_rt_code_blob; - } - - CodeBlob* load_reference_barrier_weak_rt_code_blob() { - assert(_load_reference_barrier_weak_rt_code_blob != nullptr, ""); - return _load_reference_barrier_weak_rt_code_blob; - } + address keepalive_barrier_stub(); + address load_reference_barrier_stub(DecoratorSet decorators); - CodeBlob* load_reference_barrier_phantom_rt_code_blob() { - assert(_load_reference_barrier_phantom_rt_code_blob != nullptr, ""); - return _load_reference_barrier_phantom_rt_code_blob; - } + virtual bool generate_c1_runtime_stubs(BufferBlob* buffer_blob); protected: - virtual void store_at_resolved(LIRAccess& access, LIR_Opr value); virtual LIR_Opr resolve_address(LIRAccess& access, bool resolve_in_register); virtual void load_at_resolved(LIRAccess& access, LIR_Opr result); virtual LIR_Opr atomic_cmpxchg_at_resolved(LIRAccess& access, LIRItem& cmp_value, LIRItem& new_value); - virtual LIR_Opr atomic_xchg_at_resolved(LIRAccess& access, LIRItem& value); - - void post_barrier(LIRAccess& access, LIR_Opr addr); - -public: - - virtual bool generate_c1_runtime_stubs(BufferBlob* buffer_blob); }; #endif // SHARE_GC_SHENANDOAH_C1_SHENANDOAHBARRIERSETC1_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp index 976a505c7139..eb40dfbd31d6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.hpp @@ -95,14 +95,18 @@ template class ShenandoahMarkRefsClosure : public ShenandoahMarkRefsSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + ALWAYSINLINE + void do_oop_work(T* p) { work(p); } public: ShenandoahMarkRefsClosure(ShenandoahObjToScanQueue* q, ShenandoahReferenceProcessor* rp, ShenandoahObjToScanQueue* old_q) : ShenandoahMarkRefsSuperClosure(q, rp, old_q) {}; - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } + ALWAYSINLINE + void do_oop(narrowOop* p) override { do_oop_work(p); } + + ALWAYSINLINE + void do_oop(oop* p) override { do_oop_work(p); } }; class ShenandoahForwardedIsAliveClosure : public BoolObjectClosure { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp index 83aede5b7d9b..f57a9b209578 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp @@ -77,7 +77,8 @@ ShenandoahMarkRefsSuperClosure::ShenandoahMarkRefsSuperClosure(ShenandoahObjToSc _weak(false) {} template -inline void ShenandoahMarkRefsSuperClosure::work(T* p) { +ALWAYSINLINE +void ShenandoahMarkRefsSuperClosure::work(T* p) { ShenandoahMark::mark_through_ref(p, _queue, _old_queue, _mark_context, _weak); } @@ -143,7 +144,7 @@ void ShenandoahEvacuateUpdateRootClosureBase::do_oop( template template void ShenandoahEvacuateUpdateRootClosureBase::do_oop_work(T* p) { - assert(_heap->is_concurrent_weak_root_in_progress() || + assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || _heap->is_concurrent_strong_root_in_progress(), "Only do this in root processing phase"); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index 3116ec30665c..d1c25eb49b4f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -111,7 +111,7 @@ class ShenandoahNMethodUnlinkClosure : public NMethodClosure { return; } - { + if (_heap->is_evacuation_in_progress()) { ShenandoahNMethodLocker locker(nm_data->lock()); // Heal oops diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index 24748bdaab38..28f04de2f868 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, 2022, Red Hat, Inc. All rights reserved. * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -183,7 +183,7 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { assert(heap->is_concurrent_weak_root_in_progress(), "Must be doing weak roots now"); - // Concurrent stack processing + // Finish all thread/stack roots if needed. This completes stack watermark processing. if (heap->is_evacuation_in_progress()) { entry_thread_roots(); } @@ -204,8 +204,6 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { // we will not age young-gen objects in the case that we skip evacuation. entry_cleanup_early(); - heap->free_set()->log_status_under_lock(); - // Processing strong roots // This may be skipped if there is nothing to update/evacuate. // If so, strong_root_in_progress would be unset. @@ -213,6 +211,9 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { entry_strong_roots(); } + // Roots processing is complete, put the weak roots flag down. + entry_final_roots(); + // Continue the cycle with evacuation and optional update-refs. // This may be skipped if there is nothing to evacuate. // If so, evac_in_progress would be unset by collection set preparation code. @@ -251,9 +252,18 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { entry_cleanup_complete(); } else { _abbreviated = true; - if (!entry_final_roots()) { - assert(_degen_point != _degenerated_unset, "Need to know where to start degenerated cycle"); - return false; + + if (heap->mode()->is_generational()) { + entry_complete_abbreviated_cycle(); + + // If the promote-in-place operation was cancelled, we can have the degenerated + // cycle complete the operation. It will see that no evacuations are in progress, + // and that there are regions wanting promotion. The risk with not handling the + // cancellation would be failing to restore top for these regions and leaving + // them unable to serve allocations for the old generation. + if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_evac)) { + return false; + } } // In normal cycle, final-update-refs would verify at the end of the cycle. @@ -277,34 +287,34 @@ bool ShenandoahConcurrentGC::collect(GCCause::Cause cause) { return true; } -bool ShenandoahConcurrentGC::complete_abbreviated_cycle() { +void ShenandoahConcurrentGC::entry_complete_abbreviated_cycle() { shenandoah_assert_generational(); ShenandoahGenerationalHeap* const heap = ShenandoahGenerationalHeap::heap(); + TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); + static const char* msg = "Concurrent complete abbreviated cycle"; + ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::complete_abbreviated); + EventMark em("%s", msg); + + ShenandoahWorkerScope scope(heap->workers(), + ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), + msg); + // We chose not to evacuate because we found sufficient immediate garbage. // However, there may still be regions to promote in place, so do that now. if (heap->old_generation()->has_in_place_promotions()) { - entry_promote_in_place(); - - // If the promote-in-place operation was cancelled, we can have the degenerated - // cycle complete the operation. It will see that no evacuations are in progress, - // and that there are regions wanting promotion. The risk with not handling the - // cancellation would be failing to restore top for these regions and leaving - // them unable to serve allocations for the old generation.This will leave the weak - // roots flag set (the degenerated cycle will unset it). - if (check_cancellation_and_abort(ShenandoahDegenPoint::_degenerated_evac)) { - return false; - } + ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::complete_abbreviated_promote_in_place); + ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::complete_abbreviated_promote_in_place); + heap->promote_regions_in_place(_generation, true); } // At this point, the cycle is effectively complete. If the cycle has been cancelled here, // the control thread will detect it on its next iteration and run a degenerated young cycle. - if (!_generation->is_old()) { + if (!heap->cancelled_gc() && !_generation->is_old()) { + ShenandoahTimingsTracker tracker(ShenandoahPhaseTimings::complete_abbreviated_update_region_ages); heap->update_region_ages(_generation->complete_marking_context()); } - - return true; } void ShenandoahConcurrentGC::vmop_entry_init_mark() { @@ -584,16 +594,6 @@ void ShenandoahConcurrentGC::entry_evacuate() { op_evacuate(); } -void ShenandoahConcurrentGC::entry_promote_in_place() const { - shenandoah_assert_generational(); - - ShenandoahTimingsTracker timing(ShenandoahPhaseTimings::promote_in_place); - ShenandoahGCWorkerPhase worker_phase(ShenandoahPhaseTimings::promote_in_place); - EventMark em("%s", "Promote in place"); - - ShenandoahGenerationalHeap::heap()->promote_regions_in_place(_generation, true); -} - void ShenandoahConcurrentGC::entry_update_thread_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); @@ -1229,26 +1229,14 @@ void ShenandoahConcurrentGC::op_final_update_refs() { } } -bool ShenandoahConcurrentGC::entry_final_roots() { +void ShenandoahConcurrentGC::entry_final_roots() { ShenandoahHeap* const heap = ShenandoahHeap::heap(); TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters()); - - const char* msg = conc_final_roots_event_message(); ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_final_roots); EventMark em("%s", msg); - ShenandoahWorkerScope scope(heap->workers(), - ShenandoahWorkerPolicy::calc_workers_for_conc_evac(), - msg); - - if (heap->mode()->is_generational()) { - if (!complete_abbreviated_cycle()) { - return false; - } - } heap->concurrent_final_roots(); - return true; } void ShenandoahConcurrentGC::op_verify_final() { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp index fde585b4aa9c..e763d1853e3f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp @@ -91,6 +91,8 @@ class ShenandoahConcurrentGC : public ShenandoahGC { void entry_class_unloading(); void entry_strong_roots(); void entry_cleanup_early(); + void entry_complete_abbreviated_cycle(); + void entry_final_roots(); void entry_evacuate(); void entry_update_thread_roots(); void entry_update_card_table(); @@ -98,12 +100,6 @@ class ShenandoahConcurrentGC : public ShenandoahGC { void entry_update_refs(); void entry_cleanup_complete(); - // This is the last phase of a cycle which performs no evacuations - bool entry_final_roots(); - - // Called when the collection set is empty, but the generational mode has regions to promote in place - void entry_promote_in_place() const; - // Actual work for the phases void op_reset(); void op_init_mark(); @@ -135,8 +131,6 @@ class ShenandoahConcurrentGC : public ShenandoahGC { private: void start_mark(); - bool complete_abbreviated_cycle(); - static bool has_in_place_promotions(ShenandoahHeap* heap); // Messages for GC trace events, they have to be immortal for diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp index 750022b274e2..54ab4c27038b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalEvacuationTask.cpp @@ -62,10 +62,12 @@ ShenandoahGenerationalEvacuationTask::ShenandoahGenerationalEvacuationTask(Shena void ShenandoahGenerationalEvacuationTask::work(uint worker_id) { if (_concurrent) { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::conc_evac, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahConcurrentWorkerSession worker_session(worker_id); SuspendibleThreadSetJoiner stsj; do_work(); } else { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::degen_gc_evac, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahParallelWorkerSession worker_session(worker_id); do_work(); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp index e7638ed15c79..31129182380e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahGenerationalHeap.cpp @@ -722,10 +722,12 @@ class ShenandoahGenerationalUpdateHeapRefsTask : public WorkerTask { void work(uint worker_id) override { if (CONCURRENT) { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::conc_update_refs, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahConcurrentWorkerSession worker_session(worker_id); SuspendibleThreadSetJoiner stsj; do_work(worker_id); } else { + ShenandoahWorkerTimingsTracker timer(ShenandoahPhaseTimings::degen_gc_update_refs, ShenandoahPhaseTimings::Work, worker_id, true); ShenandoahParallelWorkerSession worker_session(worker_id); do_work(worker_id); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index e60db88974a8..ae0c873fa589 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -1236,7 +1236,6 @@ void ShenandoahHeap::concurrent_prepare_for_update_refs() { // A cancellation at this point means the degenerated cycle must resume from update-refs. set_gc_state_concurrent(EVACUATION, false); - set_gc_state_concurrent(WEAK_ROOTS, false); set_gc_state_concurrent(UPDATE_REFS, true); } @@ -1252,35 +1251,24 @@ void ShenandoahHeap::concurrent_prepare_for_update_refs() { _update_refs_iterator.reset(); } -class ShenandoahCompositeHandshakeClosure : public HandshakeClosure { - HandshakeClosure* _handshake_1; - HandshakeClosure* _handshake_2; - public: - ShenandoahCompositeHandshakeClosure(HandshakeClosure* handshake_1, HandshakeClosure* handshake_2) : - HandshakeClosure(handshake_2->name()), - _handshake_1(handshake_1), _handshake_2(handshake_2) {} +void ShenandoahHeap::concurrent_final_roots() { + { + MutexLocker lock(Threads_lock); - void do_thread(Thread* thread) override { - _handshake_1->do_thread(thread); - _handshake_2->do_thread(thread); +#ifdef ASSERT + for (JavaThreadIteratorWithHandle jtiwh; JavaThread* jt = jtiwh.next();) { + StackWatermark* sw = StackWatermarkSet::get(jt, StackWatermarkKind::gc); + assert(sw == nullptr || sw->processing_completed(), + "Cannot turn off weak roots before stack watermark processing is complete"); } -}; +#endif -void ShenandoahHeap::concurrent_final_roots(HandshakeClosure* handshake_closure) { - { - assert(!is_evacuation_in_progress(), "Should not evacuate for abbreviated or old cycles"); - MutexLocker lock(Threads_lock); set_gc_state_concurrent(WEAK_ROOTS, false); } ShenandoahGCStatePropagatorHandshakeClosure propagator(_gc_state.raw_value()); Threads::non_java_threads_do(&propagator); - if (handshake_closure == nullptr) { - Handshake::execute(&propagator); - } else { - ShenandoahCompositeHandshakeClosure composite(&propagator, handshake_closure); - Handshake::execute(&composite); - } + Handshake::execute(&propagator); } oop ShenandoahHeap::evacuate_object(oop p, Thread* thread) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp index 86707c7e8310..9810b316c217 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp @@ -493,8 +493,8 @@ class ShenandoahHeap : public CollectedHeap { // Retires LABs used for evacuation void concurrent_prepare_for_update_refs(); - // Turn off weak roots flag, purge old satb buffers in generational mode - void concurrent_final_roots(HandshakeClosure* handshake_closure = nullptr); + // Turn off weak roots flag + void concurrent_final_roots(); virtual void update_heap_references(ShenandoahGeneration* generation, bool concurrent); // Final update region states diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp index ee29c76dcaf8..1ba2cd067b64 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.hpp @@ -51,7 +51,8 @@ class ShenandoahMark: public StackObj { public: template - static inline void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak); + ALWAYSINLINE + static void mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak); // Loom support void start_mark(); @@ -72,34 +73,42 @@ class ShenandoahMark: public StackObj { // ---------- Marking loop and tasks template - inline void do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id); + ALWAYSINLINE + static void do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task, uint worker_id); template - inline void do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop array, Klass* klass, bool weak); + ALWAYSINLINE + static void do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop array, Klass* klass, bool weak); template - inline void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, int chunk, int pow, bool weak); + ALWAYSINLINE + static void do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop array, int chunk, int pow, bool weak); template - inline void count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id); - - template - void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t, StringDedup::Requests* const req); - - template - void mark_loop_prework(uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req, bool update_refs); + ALWAYSINLINE + static void count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id); template + ALWAYSINLINE static bool in_generation(ShenandoahHeap* const heap, oop obj); template + ALWAYSINLINE static void mark_non_generational_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak); - static void mark_ref(ShenandoahObjToScanQueue* q, - ShenandoahMarkingContext* const mark_context, - bool weak, oop obj); + ALWAYSINLINE + static void mark_ref(ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak, oop obj); + + ALWAYSINLINE + static void dedup_string(oop obj, StringDedup::Requests* const req); + + template + void mark_loop_prework(uint worker_id, TaskTerminator *terminator, StringDedup::Requests* const req, bool update_refs); + + template + NOINLINE // Main hot loop, start inlining from here + void mark_loop_work(T* cl, ShenandoahLiveData* live_data, uint worker_id, TaskTerminator *t, StringDedup::Requests* const req); - static inline void dedup_string(oop obj, StringDedup::Requests* const req); protected: template void mark_loop(uint worker_id, TaskTerminator* terminator, ShenandoahGenerationType generation_type, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp index 71ef99b17ac4..72129ff9e14a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp @@ -94,7 +94,7 @@ void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveD } } -void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { +inline void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { assert(req != nullptr, "Should be available if dedup is enabled"); // Skip if already requested or dedup is forbidden. @@ -111,7 +111,7 @@ void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { } template -inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id) { +void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj, Klass* klass, uint worker_id) { const ShenandoahHeap* const heap = ShenandoahHeap::heap(); const size_t region_idx = heap->heap_region_index_containing(obj); ShenandoahHeapRegion* const region = heap->get_region(region_idx); @@ -155,7 +155,7 @@ inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop ob } template -inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj, Klass* klass, bool weak) { +void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj, Klass* klass, bool weak) { assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); int len = array->length(); @@ -222,7 +222,7 @@ inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, } template -inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, int chunk, int pow, bool weak) { +void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, int chunk, int pow, bool weak) { assert(obj->is_objArray(), "expect object array"); objArrayOop array = objArrayOop(obj); @@ -291,7 +291,7 @@ bool ShenandoahMark::in_generation(ShenandoahHeap* const heap, oop obj) { } template -inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { +void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { // Note: This is a very hot code path, so the code should be conditional on GENERATION template // parameter where possible, in order to generate the most efficient code. @@ -327,17 +327,19 @@ inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, } template<> -inline void ShenandoahMark::mark_through_ref(oop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { +ALWAYSINLINE +void ShenandoahMark::mark_through_ref(oop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { mark_non_generational_ref(p, q, mark_context, weak); } template<> -inline void ShenandoahMark::mark_through_ref(narrowOop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { +ALWAYSINLINE +void ShenandoahMark::mark_through_ref(narrowOop *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old_q, ShenandoahMarkingContext* const mark_context, bool weak) { mark_non_generational_ref(p, q, mark_context, weak); } template -inline void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQueue* q, +void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak) { oop o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { @@ -353,8 +355,8 @@ inline void ShenandoahMark::mark_non_generational_ref(T* p, ShenandoahObjToScanQ } inline void ShenandoahMark::mark_ref(ShenandoahObjToScanQueue* q, - ShenandoahMarkingContext* const mark_context, - bool weak, oop obj) { + ShenandoahMarkingContext* const mark_context, + bool weak, oop obj) { bool skip_live = false; bool marked; if (weak) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp index 73bf3ecbeea9..177d953d4f3b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp @@ -160,13 +160,15 @@ class ShenandoahMarkBitMap { // strong. // Words that have been marked final before or by a concurrent thread will be // upgraded to strong. In this case, this method also returns true. - inline bool mark_strong(HeapWord* w, bool& was_upgraded); + ALWAYSINLINE + bool mark_strong(HeapWord* w, bool& was_upgraded); // Mark word as 'weak' if it hasn't been marked weak or strong yet. // Return true if the word has been marked weak, false if it has already been // marked strong or weak or if another thread has beat us by marking it // strong or weak. - inline bool mark_weak(HeapWord* heap_addr); + ALWAYSINLINE + bool mark_weak(HeapWord* heap_addr); inline bool is_marked(HeapWord* addr) const; inline bool is_marked_strong(HeapWord* w) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp index d8e0c74ea4e9..870a81e4588c 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkingContext.hpp @@ -55,8 +55,11 @@ class ShenandoahMarkingContext : public CHeapObj { * been marked by this thread. Returns false if the object has already been marked, * or if a competing thread succeeded in marking this object. */ - inline bool mark_strong(oop obj, bool& was_upgraded); - inline bool mark_weak(oop obj); + ALWAYSINLINE + bool mark_strong(oop obj, bool& was_upgraded); + + ALWAYSINLINE + bool mark_weak(oop obj); // Simple versions of marking accessors, to be used outside of marking (e.g. no possible concurrent updates) inline bool is_marked(oop obj) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index 5b24cfc979ae..b0573c3f677f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -124,7 +124,7 @@ void ShenandoahNMethod::heal_nmethod(nmethod* nm) { assert(data->lock()->owned_by_self(), "Must hold the lock"); ShenandoahHeap* const heap = ShenandoahHeap::heap(); - if (heap->is_concurrent_weak_root_in_progress() || + if ((heap->is_concurrent_weak_root_in_progress() && heap->is_evacuation_in_progress()) || heap->is_concurrent_strong_root_in_progress()) { heal_nmethod_metadata(data); } else if (heap->is_concurrent_mark_in_progress()) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp index ff441a0c8682..df41069d922d 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGC.cpp @@ -134,7 +134,7 @@ bool ShenandoahOldGC::collect(GCCause::Cause cause) { // return from here with weak roots in progress. This is not a valid gc state // for any young collections (or allocation failures) that interrupt the old // collection. - heap->concurrent_final_roots(); + entry_final_roots(); // After concurrent old marking finishes, we reclaim immediate garbage. Further, we may also want to expand OLD in order // to make room for anticipated promotions and/or for mixed evacuations. Mixed evacuations are especially likely to diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp index 0a0beaaffeef..c92f74364fbc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.cpp @@ -132,16 +132,18 @@ ShenandoahOldGeneration::ShenandoahOldGeneration(uint max_queues) void ShenandoahOldGeneration::set_promoted_reserve(size_t new_val) { shenandoah_assert_heaplocked_or_safepoint(); - _promoted_reserve = new_val; + _promoted_reserve.store_relaxed(new_val); } size_t ShenandoahOldGeneration::get_promoted_reserve() const { - return _promoted_reserve; + return _promoted_reserve.load_relaxed(); } void ShenandoahOldGeneration::augment_promoted_reserve(size_t increment) { shenandoah_assert_heaplocked_or_safepoint(); - _promoted_reserve += increment; + // Writers are serialized by the heap lock, so relaxed ordering is sufficient; the atomic RMW + // only guards against tearing the concurrent lock-free reader (get_promoted_reserve). + _promoted_reserve.fetch_then_add(increment, memory_order_relaxed); } void ShenandoahOldGeneration::reset_promoted_expended() { @@ -194,6 +196,8 @@ void ShenandoahOldGeneration::maybe_log_promotion_failure_stats(bool concurrent) } bool ShenandoahOldGeneration::try_expend_promoted(size_t increment) { + // The promote reserve rarely changes during evacuation(only when there is PIP region), so snapshot it once; + // only _promoted_expended is contended and re-read on CAS failure. const size_t reserve = get_promoted_reserve(); size_t cur = _promoted_expended.load_relaxed(); while (cur + increment <= reserve) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp index 43151af4c87a..61a3114f906b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahOldGeneration.hpp @@ -58,7 +58,7 @@ class ShenandoahOldGeneration : public ShenandoahGeneration { // and in addition to the evacuation reserve for intra-generation evacuations (ShenandoahGeneration::_evacuation_reserve). // If there is more data ready to be promoted than can fit within this reserve, the promotion of some objects will be // deferred until a subsequent evacuation pass. - size_t _promoted_reserve; + Atomic _promoted_reserve; // Bytes of old-gen memory expended on promotions. This may be modified concurrently // by mutators and gc workers when promotion LABs are retired during evacuation. It diff --git a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp index bc52d7551392..dfb42e0b76f0 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp @@ -106,8 +106,10 @@ class outputStream; " CE: ") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_update_card_table, "Concurrent Update Cards") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, conc_final_roots, "Concurrent Final Roots") \ - SHENANDOAH_WORKER_PHASE_DEF(f, promote_in_place, " Promote Regions", \ + SHENANDOAH_SIMPLE_PHASE_DEF(f, complete_abbreviated, "Complete Abbreviated Cycle") \ + SHENANDOAH_WORKER_PHASE_DEF(f, complete_abbreviated_promote_in_place, " Promote Regions", \ " PIP: ") \ + SHENANDOAH_SIMPLE_PHASE_DEF(f, complete_abbreviated_update_region_ages, " Update Region Ages") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, final_verify_gross, "Pause Final Verify (G)") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, final_verify, "Pause Final Verify (N)") \ SHENANDOAH_SIMPLE_PHASE_DEF(f, init_update_refs_gross, "Pause Init Update Refs (G)") \ diff --git a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp index 81c584dfa37a..8df2449f8b6b 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahStackWatermark.cpp @@ -67,14 +67,13 @@ ShenandoahStackWatermark::ShenandoahStackWatermark(JavaThread* jt) : OopClosure* ShenandoahStackWatermark::closure_from_context(void* context) { if (context != nullptr) { - assert(_heap->is_concurrent_weak_root_in_progress() || + assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || _heap->is_concurrent_mark_in_progress(), "Only these two phases"); assert(Thread::current()->is_Worker_thread(), "Unexpected thread passing in context: " PTR_FORMAT, p2i(context)); return reinterpret_cast(context); } else { - if (_heap->is_concurrent_weak_root_in_progress()) { - assert(_heap->is_evacuation_in_progress(), "Nothing to evacuate"); + if (_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) { return &_evac_update_oop_cl; } else if (_heap->is_concurrent_mark_in_progress()) { return &_keep_alive_cl; @@ -87,11 +86,9 @@ OopClosure* ShenandoahStackWatermark::closure_from_context(void* context) { void ShenandoahStackWatermark::start_processing_impl(void* context) { NoSafepointVerifier nsv; - ShenandoahHeap* const heap = ShenandoahHeap::heap(); // Process the non-frame part of the thread - if (heap->is_concurrent_weak_root_in_progress()) { - assert(heap->is_evacuation_in_progress(), "Should not be armed"); + if (_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) { // Retire the TLABs, which will force threads to reacquire their TLABs. // This is needed for two reasons. Strong one: new allocations would be with new freeset, // which would be outside the collection set, so no cset writes would happen there. @@ -100,7 +97,7 @@ void ShenandoahStackWatermark::start_processing_impl(void* context) { retire_tlab(); _jt->oops_do_no_frames(closure_from_context(context), &_nm_cl); - } else if (heap->is_concurrent_mark_in_progress()) { + } else if (_heap->is_concurrent_mark_in_progress()) { // We need to reset all TLABs because they might be below the TAMS, and we need to mark // the objects in them. Do not let mutators allocate any new objects in their current TLABs. // It is also a good place to resize the TLAB sizes for future allocations. @@ -129,9 +126,8 @@ void ShenandoahStackWatermark::retire_tlab() { void ShenandoahStackWatermark::process(const frame& fr, RegisterMap& register_map, void* context) { OopClosure* oops = closure_from_context(context); assert(oops != nullptr, "Should not get to here"); - ShenandoahHeap* const heap = ShenandoahHeap::heap(); - assert((heap->is_concurrent_weak_root_in_progress() && heap->is_evacuation_in_progress()) || - heap->is_concurrent_mark_in_progress(), + assert((_heap->is_concurrent_weak_root_in_progress() && _heap->is_evacuation_in_progress()) || + _heap->is_concurrent_mark_in_progress(), "Only these two phases"); fr.oops_do(oops, &_nm_cl, ®ister_map, DerivedPointerIterationMode::_directly); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp index ad4e29a5cc2b..7bdf2d273498 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.hpp @@ -49,10 +49,12 @@ class BufferedOverflowTaskQueue: public OverflowTaskQueue TASKQUEUE_STATS_ONLY(using taskqueue_t::stats;) // Push task t into the queue. Returns true on success. - inline bool push(E t); + ALWAYSINLINE + bool push(E t); // Attempt to pop from the queue. Returns true on success. - inline bool pop(E &t); + ALWAYSINLINE + bool pop(E &t); inline void clear(); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp index 9fa4fabc1c7b..0f01425f3e9f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahTaskqueue.inline.hpp @@ -47,7 +47,7 @@ bool BufferedOverflowTaskQueue::pop(E &t) { } template -inline bool BufferedOverflowTaskQueue::push(E t) { +bool BufferedOverflowTaskQueue::push(E t) { if (_buf_empty) { _elem = t; _buf_empty = false; diff --git a/src/hotspot/share/opto/arraycopynode.cpp b/src/hotspot/share/opto/arraycopynode.cpp index 2f64482f55b5..07b9e907b8f4 100644 --- a/src/hotspot/share/opto/arraycopynode.cpp +++ b/src/hotspot/share/opto/arraycopynode.cpp @@ -180,6 +180,12 @@ Node* ArrayCopyNode::try_clone_instance(PhaseGVN *phase, bool can_reshape, int c return nullptr; } + Node* out_mem = proj_out_or_null(TypeFunc::Memory); + if (can_reshape && out_mem == nullptr) { // dead node? + return NodeSentinel; + } + + Node* base_src = in(ArrayCopyNode::Src); Node* base_dest = in(ArrayCopyNode::Dest); Node* ctl = in(TypeFunc::Control); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 7033dad211cb..4d06e20875ab 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -268,6 +268,8 @@ macro(MulD) macro(MulF) macro(MulHiL) macro(UMulHiL) +macro(MulHiLoL) +macro(UMulHiLoL) macro(MulI) macro(MulL) macro(Multi) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index e7dc57524ebd..e5f91875516e 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -2721,13 +2721,11 @@ static uint collect_unique_inputs(Node* n, Unique_Node_List& inputs) { if (is_vector_bitwise_op(n)) { uint inp_cnt = n->is_predicated_vector() ? n->req()-1 : n->req(); if (VectorNode::is_vector_bitwise_not_pattern(n)) { - for (uint i = 1; i < inp_cnt; i++) { - Node* in = n->in(i); - bool skip = VectorNode::is_all_ones_vector(in); - if (!skip && !inputs.member(in)) { - inputs.push(in); - cnt++; - } + assert(n->req() == (n->is_predicated_vector() ? 4 : 3), "must have 2 data inputs"); + Node* opnd = VectorNode::is_all_ones_vector(n->in(1)) ? n->in(2) : n->in(1); + if (!inputs.member(opnd)) { + inputs.push(opnd); + cnt++; } assert(cnt <= 1, "not unary"); } else { @@ -3278,8 +3276,8 @@ void Compile::handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned) { // DivMod node so the dependency is not lost. divmod->add_prec_from(n); divmod->add_prec_from(d); - d->subsume_by(divmod->div_proj(), this); - n->subsume_by(divmod->mod_proj(), this); + d->subsume_by(divmod->first_proj(), this); + n->subsume_by(divmod->second_proj(), this); } else { // Replace "a % b" with "a - ((a / b) * b)" Node* mult = MulNode::make(d, d->in(2), bt); @@ -3288,6 +3286,24 @@ void Compile::handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned) { } } +void Compile::handle_mulhi_mul_op(Node* n, bool is_unsigned) { + const int fused_opcode = is_unsigned ? Op_UMulHiLoL : Op_MulHiLoL; + if (!Matcher::has_match_rule(fused_opcode)) { + return; + } + + Node* mul = n->find_similar(Op_MulL, true); + + if (mul == nullptr) { + return; + } + + MulHiLoLNode* mul_hi_lo = is_unsigned ? static_cast(UMulHiLoLNode::make(n)) + : MulHiLoLNode::make(n); + mul->subsume_by(mul_hi_lo->first_proj(), this); + n->subsume_by(mul_hi_lo->second_proj(), this); +} + void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) { switch( nop ) { case Op_Opaque1: // Remove Opaque Nodes before matching @@ -3481,22 +3497,38 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f ResourceMark rm; Unique_Node_List wq; wq.push(n); + + + // When we remove a CastPP, we need to pin all of its transitive users under the control of + // the removed node. The simplest approach is to pin all of the uses of the removed CastPP, + // but it is overly conservative, as an AddP does not really need pinning. As a result, we + // look through those nodes that do not need pinning and only pin memory access nodes under + // n->in(0). for (uint next = 0; next < wq.size(); ++next) { Node *m = wq.at(next); for (DUIterator_Fast imax, i = m->fast_outs(imax); i < imax; i++) { Node* use = m->fast_out(i); - if (use->is_Mem() || use->is_EncodeNarrowPtr()) { + int use_op = use->Opcode(); + if (use->is_CFG() || use->pinned() || // already pinned at the exact control + use->is_Cmp() || use_op == Op_CastP2X || use_op == Op_Conv2B) { // pure computations + continue; + } else if (use->is_EncodeNarrowPtr() || // EncodeP remembers whether its input is nullable, so it must be pinned + use_op == Op_PartialSubtypeCheck || // This accesses its pointer inputs, so it must depend on them being not-null + use->is_Mem() || use->is_memory_access_intrinsic()) { use->ensure_control_or_add_prec(n->in(0)); + } else if (use_op == Op_AddP || + use_op == Op_CastPP || use_op == Op_CheckCastPP || + use_op == Op_CMoveP || use_op == Op_CMoveN || + use_op == Op_DecodeN || use_op == Op_DecodeNKlass || + use_op == Op_VerifyVectorAlignment) { + // Look through use to find memory accesses if use does not need pinning + wq.push(use); } else { - switch(use->Opcode()) { - case Op_AddP: - case Op_DecodeN: - case Op_DecodeNKlass: - case Op_CheckCastPP: - case Op_CastPP: - wq.push(use); - break; - } + // Should have handled all kinds of nodes, verify that we do not unexpectedly arrive + // here + assert(false, "unexpected node %s", use->Name()); + // Be conservative in product and pin the unexpected use + use->ensure_control_or_add_prec(n->in(0)); } } } @@ -3723,6 +3755,14 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f handle_div_mod_op(n, T_LONG, true); break; + case Op_MulHiL: + handle_mulhi_mul_op(n, false); + break; + + case Op_UMulHiL: + handle_mulhi_mul_op(n, true); + break; + case Op_LoadVector: case Op_StoreVector: #ifdef ASSERT diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 3c2e1c641195..ab36f59a28fc 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -1257,6 +1257,7 @@ class Compile : public Phase { void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes); void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes); void handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned); + void handle_mulhi_mul_op(Node* n, bool is_unsigned); // Logic cone optimization. void optimize_logic_cones(PhaseIterGVN &igvn); diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index b398ec27b809..1687ff2cade4 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -1614,12 +1614,6 @@ const Type* ModFloatingNode::Value(PhaseGVN* phase) const { //============================================================================= -DivModNode::DivModNode( Node *c, Node *dividend, Node *divisor ) : MultiNode(3) { - init_req(0, c); - init_req(1, dividend); - init_req(2, divisor); -} - DivModNode* DivModNode::make(Node* div_or_mod, BasicType bt, bool is_unsigned) { assert(bt == T_INT || bt == T_LONG, "only int or long input pattern accepted"); @@ -1645,8 +1639,8 @@ DivModINode* DivModINode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); DivModINode* divmod = new DivModINode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1657,8 +1651,8 @@ DivModLNode* DivModLNode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); DivModLNode* divmod = new DivModLNode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1667,11 +1661,11 @@ DivModLNode* DivModLNode::make(Node* div_or_mod) { Node *DivModINode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divI_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstI_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modI_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondI_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1682,11 +1676,11 @@ Node *DivModINode::match( const ProjNode *proj, const Matcher *match ) { Node *DivModLNode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divL_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstL_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modL_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondL_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1698,8 +1692,8 @@ UDivModINode* UDivModINode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); UDivModINode* divmod = new UDivModINode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1710,8 +1704,8 @@ UDivModLNode* UDivModLNode::make(Node* div_or_mod) { "only div or mod input pattern accepted"); UDivModLNode* divmod = new UDivModLNode(n->in(0), n->in(1), n->in(2)); - Node* dproj = new ProjNode(divmod, DivModNode::div_proj_num); - Node* mproj = new ProjNode(divmod, DivModNode::mod_proj_num); + Node* dproj = new ProjNode(divmod, DivModNode::first_proj_num); + Node* mproj = new ProjNode(divmod, DivModNode::second_proj_num); return divmod; } @@ -1720,11 +1714,11 @@ UDivModLNode* UDivModLNode::make(Node* div_or_mod) { Node* UDivModINode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divI_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstI_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modI_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondI_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } @@ -1735,11 +1729,11 @@ Node* UDivModINode::match( const ProjNode *proj, const Matcher *match ) { Node* UDivModLNode::match( const ProjNode *proj, const Matcher *match ) { uint ideal_reg = proj->ideal_reg(); RegMask rm; - if (proj->_con == div_proj_num) { - rm.assignFrom(match->divL_proj_mask()); + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstL_proj_mask()); } else { - assert(proj->_con == mod_proj_num, "must be div or mod projection"); - rm.assignFrom(match->modL_proj_mask()); + assert(proj->_con == second_proj_num, "must be div or mod projection"); + rm.assignFrom(match->secondL_proj_mask()); } return new MachProjNode(this, proj->_con, rm, ideal_reg); } diff --git a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp index 2598429716fd..366e3fb882db 100644 --- a/src/hotspot/share/opto/divnode.hpp +++ b/src/hotspot/share/opto/divnode.hpp @@ -239,36 +239,20 @@ class UModLNode : public DivModIntegerNode { //------------------------------DivModNode--------------------------------------- // Division with remainder result. -class DivModNode : public MultiNode { +class DivModNode : public BinaryMultiNode { protected: - DivModNode( Node *c, Node *dividend, Node *divisor ); + DivModNode(Node* ctrl, Node* dividend, Node* divisor) : BinaryMultiNode(ctrl, dividend, divisor) {} public: - enum { - div_proj_num = 0, // quotient - mod_proj_num = 1 // remainder - }; virtual int Opcode() const; - virtual Node* Identity(PhaseGVN* phase) { return this; } - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape) { return nullptr; } - virtual const Type* Value(PhaseGVN* phase) const { return bottom_type(); } - virtual uint hash() const { return Node::hash(); } - virtual bool is_CFG() const { return false; } - virtual uint ideal_reg() const { return NotAMachineReg; } static DivModNode* make(Node* div_or_mod, BasicType bt, bool is_unsigned); - - ProjNode* div_proj() { return proj_out_or_null(div_proj_num); } - ProjNode* mod_proj() { return proj_out_or_null(mod_proj_num); } - -private: - virtual bool depends_only_on_test() const { return false; } }; //------------------------------DivModINode--------------------------------------- // Integer division with remainder result. class DivModINode : public DivModNode { public: - DivModINode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + DivModINode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::INT_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); @@ -281,7 +265,7 @@ class DivModINode : public DivModNode { // Long division with remainder result. class DivModLNode : public DivModNode { public: - DivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + DivModLNode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::LONG_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); @@ -295,7 +279,7 @@ class DivModLNode : public DivModNode { // Unsigend integer division with remainder result. class UDivModINode : public DivModNode { public: - UDivModINode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + UDivModINode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::INT_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); @@ -308,7 +292,7 @@ class UDivModINode : public DivModNode { // Unsigned long division with remainder result. class UDivModLNode : public DivModNode { public: - UDivModLNode( Node *c, Node *dividend, Node *divisor ) : DivModNode(c, dividend, divisor) {} + UDivModLNode(Node* ctrl, Node* dividend, Node* divisor) : DivModNode(ctrl, dividend, divisor) {} virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeTuple::LONG_PAIR; } virtual Node *match( const ProjNode *proj, const Matcher *m ); diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 3112bb6b1690..4f5251f39e1f 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -3240,9 +3240,10 @@ Node* GraphKit::maybe_cast_profiled_obj(Node* obj, Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replace) { kill_dead_locals(); // Benefit all the uncommon traps assert( !stopped(), "dead parse path should be checked in callers" ); - assert(!TypePtr::NULL_PTR->higher_equal(_gvn.type(superklass)->is_klassptr()), + const TypeKlassPtr* klass_ptr_type = _gvn.type(superklass)->isa_klassptr(); + assert(klass_ptr_type != nullptr && !TypePtr::NULL_PTR->higher_equal(klass_ptr_type), "must check for not-null not-dead klass in callers"); - + const TypeKlassPtr* improved_klass_ptr_type = klass_ptr_type->try_improve(); // Make the merge point enum { _obj_path = 1, _fail_path, _null_path, PATH_LIMIT }; RegionNode* region = new RegionNode(PATH_LIMIT); @@ -3278,11 +3279,10 @@ Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replac // Do we know the type check always succeed? bool known_statically = false; - if (_gvn.type(superklass)->singleton()) { - const TypeKlassPtr* superk = _gvn.type(superklass)->is_klassptr(); + if (improved_klass_ptr_type->singleton()) { const TypeKlassPtr* subk = _gvn.type(obj)->is_oopptr()->as_klass_type(); if (subk->is_loaded()) { - int static_res = C->static_subtype_check(superk, subk); + int static_res = C->static_subtype_check(improved_klass_ptr_type, subk); known_statically = (static_res == Compile::SSC_always_true || static_res == Compile::SSC_always_false); } } @@ -3305,7 +3305,11 @@ Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replac } // Generate the subtype check - Node* not_subtype_ctrl = gen_subtype_check(not_null_obj, superklass); + Node* improved_superklass = superklass; + if (improved_klass_ptr_type != klass_ptr_type && improved_klass_ptr_type->singleton()) { + improved_superklass = makecon(improved_klass_ptr_type); + } + Node* not_subtype_ctrl = gen_subtype_check(not_null_obj, improved_superklass); // Plug in the success path to the general merge in slot 1. region->init_req(_obj_path, control()); diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 31f4a7822477..2453a7ece4e1 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -418,15 +418,15 @@ class Matcher : public PhaseTransform { static OptoReg::Name inline_cache_reg(); static int inline_cache_reg_encode(); - // Register for DIVI projection of divmodI - static const RegMask& divI_proj_mask(); - // Register for MODI projection of divmodI - static const RegMask& modI_proj_mask(); - - // Register for DIVL projection of divmodL - static const RegMask& divL_proj_mask(); - // Register for MODL projection of divmodL - static const RegMask& modL_proj_mask(); + // Register for the first projection of an int pair + static const RegMask& firstI_proj_mask(); + // Register for the second projection of an int pair + static const RegMask& secondI_proj_mask(); + + // Register for the first projection of a long pair + static const RegMask& firstL_proj_mask(); + // Register for the second projection of a long pair + static const RegMask& secondL_proj_mask(); // Java-Interpreter calling convention // (what you use when calling between compiled-Java and Interpreted-Java diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index e48acd23b873..eb24e31eee2c 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -26,6 +26,8 @@ #include "opto/addnode.hpp" #include "opto/connode.hpp" #include "opto/convertnode.hpp" +#include "opto/machnode.hpp" +#include "opto/matcher.hpp" #include "opto/memnode.hpp" #include "opto/mulnode.hpp" #include "opto/phaseX.hpp" @@ -606,6 +608,36 @@ const Type* UMulHiLNode::Value(PhaseGVN* phase) const { return MulHiValue(t1, t2, bot); } +MulHiLoLNode* MulHiLoLNode::make(Node* mul_hi) { + assert(mul_hi->Opcode() == Op_MulHiL, "expected MulHiL"); + + MulHiLoLNode* mul_hi_lo = new MulHiLoLNode(mul_hi->in(0), mul_hi->in(1), mul_hi->in(2)); + [[maybe_unused]] Node* lo_proj = new ProjNode(mul_hi_lo, MulHiLoLNode::first_proj_num); + [[maybe_unused]] Node* hi_proj = new ProjNode(mul_hi_lo, MulHiLoLNode::second_proj_num); + return mul_hi_lo; +} + +UMulHiLoLNode* UMulHiLoLNode::make(Node* umul_hi) { + assert(umul_hi->Opcode() == Op_UMulHiL, "expected UMulHiL"); + + UMulHiLoLNode* umul_hi_lo = new UMulHiLoLNode(umul_hi->in(0), umul_hi->in(1), umul_hi->in(2)); + [[maybe_unused]] Node* lo_proj = new ProjNode(umul_hi_lo, MulHiLoLNode::first_proj_num); + [[maybe_unused]] Node* hi_proj = new ProjNode(umul_hi_lo, MulHiLoLNode::second_proj_num); + return umul_hi_lo; +} + +Node* MulHiLoLNode::match(const ProjNode* proj, const Matcher* match) { + uint ideal_reg = proj->ideal_reg(); + RegMask rm; + if (proj->_con == first_proj_num) { + rm.assignFrom(match->firstL_proj_mask()); + } else { + assert(proj->_con == second_proj_num, "must be lo or hi projection"); + rm.assignFrom(match->secondL_proj_mask()); + } + return new MachProjNode(this, proj->_con, rm, ideal_reg); +} + // A common routine used by UMulHiLNode and MulHiLNode const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot) { // Either input is TOP ==> the result is TOP diff --git a/src/hotspot/share/opto/mulnode.hpp b/src/hotspot/share/opto/mulnode.hpp index 1e19e8ec5cd7..f26137dfe491 100644 --- a/src/hotspot/share/opto/mulnode.hpp +++ b/src/hotspot/share/opto/mulnode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved. * 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,6 +25,7 @@ #ifndef SHARE_OPTO_MULNODE_HPP #define SHARE_OPTO_MULNODE_HPP +#include "opto/multnode.hpp" #include "opto/node.hpp" #include "opto/opcodes.hpp" #include "opto/type.hpp" @@ -32,6 +33,7 @@ // Portions of code courtesy of Clifford Click class PhaseTransform; +class Matcher; //------------------------------MulNode---------------------------------------- // Classic MULTIPLY functionality. This covers all the usual 'multiply' @@ -205,6 +207,31 @@ class UMulHiLNode : public Node { friend const Type* MulHiValue(const Type *t1, const Type *t2, const Type *bot); }; +//------------------------------MulHiLoLNode----------------------------------- +// Lower and upper 64-bit results of a signed 64x64->128 multiply. +class MulHiLoLNode : public BinaryMultiNode { +protected: + MulHiLoLNode(Node* ctrl, Node* in1, Node* in2) : BinaryMultiNode(ctrl, in1, in2) {} + +public: + virtual int Opcode() const; + virtual const Type* bottom_type() const { return TypeTuple::LONG_PAIR; } + + virtual Node* match(const ProjNode* proj, const Matcher* m); + + static MulHiLoLNode* make(Node* mul_hi); +}; + +//------------------------------UMulHiLoLNode---------------------------------- +// Lower and upper 64-bit results of an unsigned 64x64->128 multiply. +class UMulHiLoLNode : public MulHiLoLNode { +public: + UMulHiLoLNode(Node* ctrl, Node* in1, Node* in2) : MulHiLoLNode(ctrl, in1, in2) {} + virtual int Opcode() const; + + static UMulHiLoLNode* make(Node* umul_hi); +}; + //------------------------------AndINode--------------------------------------- // Logically AND 2 integers. Included with the MUL nodes because it inherits // all the behavior of multiplication on a ring. diff --git a/src/hotspot/share/opto/multnode.hpp b/src/hotspot/share/opto/multnode.hpp index b63d418b7423..6a69eafb7ed4 100644 --- a/src/hotspot/share/opto/multnode.hpp +++ b/src/hotspot/share/opto/multnode.hpp @@ -149,6 +149,34 @@ class MultiNode : public Node { ProjNode* find_first(uint which_proj, bool is_io_use) const; }; +class BinaryMultiNode : public MultiNode { +protected: + BinaryMultiNode(Node* ctrl, Node* in1, Node* in2) : MultiNode(3) { + init_req(0, ctrl); + init_req(1, in1); + init_req(2, in2); + } + +public: + enum { + first_proj_num = 0, + second_proj_num = 1 + }; + + virtual Node* Identity(PhaseGVN* phase) { return this; } + virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) { return nullptr; } + virtual const Type* Value(PhaseGVN* phase) const { return bottom_type(); } + virtual uint hash() const { return Node::hash(); } + virtual bool is_CFG() const { return false; } + virtual uint ideal_reg() const { return NotAMachineReg; } + + ProjNode* first_proj() const { return proj_out_or_null(first_proj_num); } + ProjNode* second_proj() const { return proj_out_or_null(second_proj_num); } + +private: + virtual bool depends_only_on_test() const { return false; } +}; + //------------------------------ProjNode--------------------------------------- // This class defines a Projection node. Projections project a single element // out of a tuple (or Signature) type. Only MultiNodes produce TypeTuple diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 997ce92fe1cf..726a3ea1b555 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -2882,7 +2882,7 @@ bool Node::is_iteratively_computed() { //--------------------------find_similar------------------------------ // Return a node with opcode "opc" and same inputs as "this" if one can // be found; Otherwise return null; -Node* Node::find_similar(int opc) { +Node* Node::find_similar(int opc, bool is_commutative) { if (req() >= 2) { Node* def = in(1); if (def && def->outcnt() >= 2) { @@ -2890,9 +2890,26 @@ Node* Node::find_similar(int opc) { Node* use = def->fast_out(i); if (use != this && use->Opcode() == opc && - use->req() == req() && - has_same_inputs_as(use)) { - return use; + use->req() == req()) { + bool same = false; + if (!is_commutative || req() < 3) { + same = use->has_same_inputs_as(this); + } else { + if (use->in(0) == in(0) && + ((use->in(1) == in(1) && use->in(2) == in(2)) || + (use->in(1) == in(2) && use->in(2) == in(1)))) { + same = true; + for (uint j = 3; j < req(); j++) { + if (use->in(j) != in(j)) { + same = false; + break; + } + } + } + } + if (same) { + return use; + } } } } @@ -3001,6 +3018,27 @@ bool Node::is_data_proj_of_pure_function(const Node* maybe_pure_function) const return Opcode() == Op_Proj && as_Proj()->_con == TypeFunc::Parms && maybe_pure_function->is_CallLeafPure(); } +// Whether this is an intrinsic node that accesses memory and has a memory input, such as array +// equal intrinsic. Some nodes do access memory but do not have a memory input, such as +// PartialSubTypeCheck, they are not included here. +bool Node::is_memory_access_intrinsic() const { + switch (Opcode()) { + case Op_StrComp: + case Op_StrEquals: + case Op_StrIndexOf: + case Op_StrIndexOfChar: + case Op_StrCompressedCopy: + case Op_StrInflatedCopy: + case Op_AryEq: + case Op_CountPositives: + case Op_VectorizedHashCode: + case Op_EncodeISOArray: + return true; + default: + return false; + } +} + //--------------------------has_non_debug_uses------------------------------ // Checks whether the node has any non-debug uses or not. bool Node::has_non_debug_uses() const { diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index 1ef4b5a51b68..b3de7498e50c 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1069,6 +1069,7 @@ class Node { uint is_Copy() const { return (_flags & Flag_is_Copy); } virtual bool is_CFG() const { return false; } + bool is_memory_access_intrinsic() const; // If this node is control-dependent on a test, can it be rerouted to a dominating equivalent // test? This means that the node can be executed safely as long as it happens after the test @@ -1315,7 +1316,7 @@ class Node { // Return a node with opcode "opc" and same inputs as "this" if one can // be found; Otherwise return null; - Node* find_similar(int opc); + Node* find_similar(int opc, bool is_commutative = false); bool has_same_inputs_as(const Node* other) const; // Return the unique control out if only one. Null if none or more than one. diff --git a/src/hotspot/share/runtime/cpuTimeCounters.cpp b/src/hotspot/share/runtime/cpuTimeCounters.cpp index e174407089cd..3374a1c5db3a 100644 --- a/src/hotspot/share/runtime/cpuTimeCounters.cpp +++ b/src/hotspot/share/runtime/cpuTimeCounters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023 Google LLC. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -25,6 +25,7 @@ #include "runtime/atomicAccess.hpp" #include "runtime/cpuTimeCounters.hpp" +#include "utilities/globalCounter.inline.hpp" const char* CPUTimeGroups::to_string(CPUTimeType val) { switch (val) { @@ -77,6 +78,10 @@ void CPUTimeCounters::inc_gc_total_cpu_time(jlong diff) { } void CPUTimeCounters::publish_gc_total_cpu_time() { + GlobalCounter::CriticalSection cs(Thread::current()); + if (!UsePerfData || !PerfDataManager::has_PerfData()) { + return; + } CPUTimeCounters* instance = CPUTimeCounters::get_instance(); // Atomically fetch the current _gc_total_cpu_time_diff and reset it to zero. jlong new_value = 0; @@ -103,6 +108,10 @@ PerfCounter* CPUTimeCounters::get_counter(CPUTimeGroups::CPUTimeType name) { } void CPUTimeCounters::update_counter(CPUTimeGroups::CPUTimeType name, jlong total) { + GlobalCounter::CriticalSection cs(Thread::current()); + if (!UsePerfData || !PerfDataManager::has_PerfData()) { + return; + } CPUTimeCounters* instance = CPUTimeCounters::get_instance(); PerfCounter* counter = instance->get_counter(name); jlong prev_value = counter->get_value(); diff --git a/src/hotspot/share/runtime/cpuTimeCounters.hpp b/src/hotspot/share/runtime/cpuTimeCounters.hpp index c2e636bdb1d4..15f680c06e13 100644 --- a/src/hotspot/share/runtime/cpuTimeCounters.hpp +++ b/src/hotspot/share/runtime/cpuTimeCounters.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023 Google LLC. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -79,6 +79,8 @@ class CPUTimeCounters: public CHeapObj { static void inc_gc_total_cpu_time(jlong diff); + static PerfCounter* get_counter(CPUTimeGroups::CPUTimeType name); + public: static void initialize() { assert(_instance == nullptr, "we can only allocate one CPUTimeCounters object"); @@ -91,7 +93,6 @@ class CPUTimeCounters: public CHeapObj { } static void create_counter(CPUTimeGroups::CPUTimeType name); - static PerfCounter* get_counter(CPUTimeGroups::CPUTimeType name); static void update_counter(CPUTimeGroups::CPUTimeType name, jlong total); static void publish_gc_total_cpu_time(); diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index d5dccc820f3e..e9143a3c4e30 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -673,7 +673,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread // as interpreted so the skeleton frame will be walkable // The correct pc will be set when the skeleton frame is completely filled out // The final pc we store in the loop is wrong and will be overwritten below - frame_pcs[number_of_frames - 1 - index ] = Interpreter::deopt_entry(vtos, 0) - frame::pc_return_offset; + frame_pcs[number_of_frames - 1 - index ] = Interpreter::deopt_entry(vtos, 0); callee_parameters = array->element(index)->method()->size_of_parameters(); callee_locals = array->element(index)->method()->max_locals(); diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index d99d36571ad4..ae04d398043c 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -206,9 +206,9 @@ address frame::raw_pc() const { if (is_deoptimized_frame()) { nmethod* nm = cb()->as_nmethod_or_null(); assert(nm != nullptr, "only nmethod is expected here"); - return nm->deopt_handler_entry() - pc_return_offset; + return nm->deopt_handler_entry(); } else { - return (pc() - pc_return_offset); + return pc(); } } diff --git a/src/hotspot/share/runtime/hotCodeSampler.cpp b/src/hotspot/share/runtime/hotCodeSampler.cpp index 94242b718a5e..e033765c1f20 100644 --- a/src/hotspot/share/runtime/hotCodeSampler.cpp +++ b/src/hotspot/share/runtime/hotCodeSampler.cpp @@ -61,15 +61,13 @@ bool ThreadSampler::sample_all_java_threads() { continue; } - if (CodeCache::contains(pc)) { - nmethod* nm = CodeCache::find_blob(pc)->as_nmethod_or_null(); - if (nm != nullptr) { - bool created = false; - int *count = _samples.put_if_absent(nm, 0, &created); - (*count)++; - if (created) { - _samples.maybe_grow(); - } + CodeBlob* cb = CodeCache::find_blob(pc); + if (cb != nullptr && cb->is_nmethod()) { + bool created = false; + int *count = _samples.put_if_absent(cb->as_nmethod(), 0, &created); + (*count)++; + if (created) { + _samples.maybe_grow(); } } } diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index e2473bdfb04f..c9fa936f203b 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -104,8 +104,8 @@ Mutex* G1MarkStackChunkList_lock = nullptr; Mutex* G1MarkStackFreeList_lock = nullptr; Monitor* G1OldGCCount_lock = nullptr; Mutex* G1OldSets_lock = nullptr; -Mutex* G1ReviseYoungLength_lock = nullptr; Mutex* G1RareEvent_lock = nullptr; +Mutex* G1ReviseNumYoungRegions_lock = nullptr; Mutex* G1Uncommit_lock = nullptr; #endif @@ -335,7 +335,7 @@ void mutex_init() { if (UseG1GC) { MUTEX_DEFL(G1OldGCCount_lock , PaddedMonitor, Threads_lock, true); MUTEX_DEFL(G1RareEvent_lock , PaddedMutex , Threads_lock, true); - MUTEX_DEFL(G1ReviseYoungLength_lock , PaddedMutex , Threads_lock, true); + MUTEX_DEFL(G1ReviseNumYoungRegions_lock , PaddedMutex , Threads_lock, true); } #endif diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index aeee000b377d..ae9c5e8a1f1a 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -100,7 +100,7 @@ extern Mutex* G1MarkStackFreeList_lock; // Protects access to the G1 gl extern Monitor* G1OldGCCount_lock; // in support of "concurrent" full gc extern Mutex* G1OldSets_lock; // protects the G1 old region sets extern Mutex* G1RareEvent_lock; // Synchronizes (rare) parallel GC operations. -extern Mutex* G1ReviseYoungLength_lock; // Protects access to young gen length revising operations. +extern Mutex* G1ReviseNumYoungRegions_lock; // Protects access to number of young regions revising operations. extern Mutex* G1Uncommit_lock; // protects the G1 uncommit list when not at safepoints #endif diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index b799063d58e3..5489735da39b 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -1810,7 +1810,7 @@ JRT_LEAF(void, SharedRuntime::fixup_callers_callsite(Method* method, address cal nmethod* caller = cb->as_nmethod(); // Get the return PC for the passed caller PC. - address return_pc = caller_pc + frame::pc_return_offset; + address return_pc = caller_pc; if (!caller->is_in_use() || !NativeCall::is_call_before(return_pc)) { return; diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp index e22d11cf1a81..af9aa68c7ec4 100644 --- a/src/hotspot/share/runtime/vmOperation.hpp +++ b/src/hotspot/share/runtime/vmOperation.hpp @@ -59,6 +59,7 @@ template(G1PauseCleanup) \ template(G1TryInitiateConcMark) \ template(G1RendezvousGCThreads) \ + template(G1StopMarking) \ template(ZMarkEndOld) \ template(ZMarkEndYoung) \ template(ZMarkFlushOperation) \ diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 856ff947dc4b..3868510691ac 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -1709,8 +1709,6 @@ /**********************/ \ NOT_ZERO(PPC64_ONLY(declare_constant(frame::entry_frame_locals_size))) \ \ - declare_constant(frame::pc_return_offset) \ - \ /*************/ \ /* vmSymbols */ \ /*************/ \ diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 40691de518e6..5e5a57c37809 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -1157,8 +1157,8 @@ inline T clamp(T value, T min, T max) { return MIN2(MAX2(value, min), max); } -inline bool is_odd (intx x) { return x & 1; } -inline bool is_even(intx x) { return !is_odd(x); } +constexpr bool is_odd (intx x) { return x & 1; } +constexpr bool is_even(intx x) { return !is_odd(x); } // abs methods which cannot overflow and so are well-defined across // the entire domain of integer types. diff --git a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java index 96a1eb686cc9..5335357f8b97 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/ML_KEM.java @@ -858,7 +858,7 @@ private short[][][] generateA(byte[] rho, Boolean transposed) { allDone = false; while (!allDone) { allDone = true; - parXof.squeezeBlock(); + parXof.squeezeBlock(parInd); for (int k = 0; k < parInd; k++) { int parsedOfs = 0; int tmp; diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java index 760f3ebc2550..9f56ceb445af 100644 --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -671,14 +671,6 @@ private static String ascii(byte[] bytes, int offset, int length) { } private static String decode(Charset charset, byte[] bytes, int offset, int length) { - // (1)We never cache the "external" cs, the only benefit of creating - // an additional StringDe/Encoder object to wrap it is to share the - // de/encode() method. These SD/E objects are short-lived, the young-gen - // gc should be able to take care of them well. But the best approach - // is still not to generate them if not really necessary. - // (2)The defensive copy of the input byte/char[] has a big performance - // impact, as well as the outgoing result byte/char[]. Need to do the - // optimization check of (sm==null && classLoader0==null) for both. CharsetDecoder cd = charset.newDecoder(); // ArrayDecoder fastpaths if (cd instanceof ArrayDecoder ad) { diff --git a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java index e4b2886404f9..922ac651f7e8 100644 --- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java @@ -479,12 +479,7 @@ private static List> split(MethodHandle[] getters) { * {@link java.lang.Record#toString()}. * * - * @param lookup Every bootstrap method is expected to have a {@code lookup} - * which usually represents a lookup context with the - * accessibility privileges of the caller. This is because - * {@code invokedynamic} call sites always provide a {@code lookup} - * to the corresponding bootstrap method, but this method just - * ignores the {@code lookup} parameter + * @param lookup the full-privilege lookup context of the caller * @param methodName the name of the method to generate, which must be one of * {@code "equals"}, {@code "hashCode"}, or {@code "toString"} * @param type a {@link MethodType} corresponding the descriptor type @@ -503,8 +498,6 @@ private static List> split(MethodHandle[] getters) { * if invoked by a condy * @throws IllegalArgumentException if the bootstrap arguments are invalid * or inconsistent - * @throws NullPointerException if any argument is {@code null} or if any element - * in the {@code getters} array is {@code null} * @throws Throwable if any exception is thrown during call site construction */ public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type, @@ -518,6 +511,9 @@ public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, T requireNonNull(names); List getterList = List.of(getters); // deep null check + if (!lookup.hasFullPrivilegeAccess()) + throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString())); + MethodType methodType; if (type instanceof MethodType mt) methodType = mt; 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 d15e701b94d1..c52fc9ec75c8 100644 --- a/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java +++ b/src/java.base/share/classes/java/lang/runtime/SwitchBootstraps.java @@ -169,16 +169,13 @@ private static class StaticHolders { * the length of the {@code labels} array (inclusive), * both or an {@link IndexOutOfBoundsException} is thrown. * - * @param lookup Represents a lookup context with the accessibility - * privileges of the caller. When used with {@code invokedynamic}, - * this is stacked automatically by the VM. + * @param lookup the full-privilege lookup context of the caller * @param invocationName unused, {@code null} is permitted * @param invocationType The invocation type of the {@code CallSite} with two parameters, * a target type, an {@code int}, and {@code int} as a return type. * @param labels case labels as described above * @return a {@code CallSite} returning the first matching element as described above * - * @throws NullPointerException if any argument is {@code null}, unless noted otherwise * @throws IllegalArgumentException if any element in the labels array is null * @throws IllegalArgumentException if the invocation type is not a method type of first parameter of a target type, * second parameter of type {@code int} and with {@code int} as its return type @@ -198,6 +195,9 @@ public static CallSite typeSwitch(MethodHandles.Lookup lookup, requireNonNull(invocationType); requireNonNull(labels); + if (!lookup.hasFullPrivilegeAccess()) + throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString())); + Class selectorType = invocationType.parameterType(0); if (invocationType.parameterCount() != 2 || (!invocationType.returnType().equals(int.class)) @@ -275,9 +275,7 @@ private static void verifyLabel(Object label, Class selectorType) { * @apiNote It is permissible for the {@code labels} array to contain {@code String} * values that do not represent any enum constants at runtime. * - * @param lookup Represents a lookup context with the accessibility - * privileges of the caller. When used with {@code invokedynamic}, - * this is stacked automatically by the VM. + * @param lookup the full-privilege lookup context of the caller * @param invocationName unused, {@code null} is permitted * @param invocationType The invocation type of the {@code CallSite} with two parameters, * an enum type, an {@code int}, and {@code int} as a return type. @@ -285,7 +283,6 @@ private static void verifyLabel(Object label, Class selectorType) { * in any combination * @return a {@code CallSite} returning the first matching element as described above * - * @throws NullPointerException if any argument is {@code null}, unless noted otherwise * @throws IllegalArgumentException if any element in the labels array is null * @throws IllegalArgumentException if any element in the labels array is an empty {@code String} * @throws IllegalArgumentException if the invocation type is not a method type @@ -305,6 +302,9 @@ public static CallSite enumSwitch(MethodHandles.Lookup lookup, requireNonNull(invocationType); requireNonNull(labels); + if (!lookup.hasFullPrivilegeAccess()) + throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString())); + if (invocationType.parameterCount() != 2 || (!invocationType.returnType().equals(int.class)) || invocationType.parameterType(0).isPrimitive() diff --git a/src/java.base/share/classes/java/lang/runtime/package-info.java b/src/java.base/share/classes/java/lang/runtime/package-info.java index 9e19ef9bd7e7..e2597e45c344 100644 --- a/src/java.base/share/classes/java/lang/runtime/package-info.java +++ b/src/java.base/share/classes/java/lang/runtime/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,8 +26,20 @@ /** * The {@code java.lang.runtime} package provides low-level runtime support * for the Java language. + *

+ * Unless otherwise specified:

    + *
  • Methods and constructors in this package throw a {@link + * NullPointerException} when they are called with {@code null} or an array + * that contains {@code null} as an argument. + *
  • {@linkplain java.lang.invoke##bsm Bootstrap methods} in this package + * throw an {@link IllegalArgumentException} when they are called with a + * {@link Lookup Lookup} that does not have {@linkplain + * Lookup#hasFullPrivilegeAccess() full privilege access}. + *
* * @since 14 */ package java.lang.runtime; + +import java.lang.invoke.MethodHandles.Lookup; diff --git a/src/java.base/share/classes/java/util/Locale.java b/src/java.base/share/classes/java/util/Locale.java index f600afbb007a..462cd5755c28 100644 --- a/src/java.base/share/classes/java/util/Locale.java +++ b/src/java.base/share/classes/java/util/Locale.java @@ -120,9 +120,13 @@ * {@code Locale} always canonicalizes to lower case. * *
Syntax: Well-formed {@code language} values have the form {@code [a-zA-Z]{2,8}}.
- *
BCP 47 deviation: this is not the full BCP 47 language production, since it excludes + *
BCP 47 deviation: {@code Locale} does not retain the * extlang - * (as modern three-letter language codes are preferred).
+ * subtag. This is because three-letter language codes are preferred over extlang + * subtags. When a {@code Locale} is created from a language tag containing an + * extlang subtag, the first extlang subtag is interpreted as the language + * field. The primary language subtag and any subsequent extlang subtags + * are ignored. * *
Example: "en" (English), "ja" (Japanese), "kok" (Konkani)
* diff --git a/src/java.base/share/classes/java/util/PriorityQueue.java b/src/java.base/share/classes/java/util/PriorityQueue.java index bacce5ef97ef..b9ef312d66d5 100644 --- a/src/java.base/share/classes/java/util/PriorityQueue.java +++ b/src/java.base/share/classes/java/util/PriorityQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -209,6 +209,32 @@ else if (c instanceof PriorityQueue) { } } + /** + * Creates a {@code PriorityQueue} containing the elements in the + * specified collection. The elements of the new {@code PriorityQueue} + * will be ordered according to the specified comparator. + * + * @param c the collection whose elements are to be placed + * into this priority queue + * @param comparator the comparator that will be used to order this + * priority queue. If {@code null}, the {@linkplain Comparable + * natural ordering} of the elements will be used. + * @throws NullPointerException if the specified collection or any + * of its elements are null + * @since 28 + */ + public PriorityQueue(Collection c, + Comparator comparator) { + this.comparator = comparator; + if (c instanceof SortedSet ss && comparator == ss.comparator()) { + initElementsFromCollection(ss); + } else if (c instanceof PriorityQueue pq && comparator == pq.comparator()) { + initFromPriorityQueue(pq); + } else { + initFromCollection(c); + } + } + /** * Creates a {@code PriorityQueue} containing the elements in the * specified priority queue. This priority queue will be diff --git a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java index 88d08386e8c0..8586dc7f63b7 100644 --- a/src/java.base/share/classes/java/util/zip/GZIPInputStream.java +++ b/src/java.base/share/classes/java/util/zip/GZIPInputStream.java @@ -58,6 +58,19 @@ * The {@link #close} method should be called to release resources used by this * stream, either directly, or with the {@code try}-with-resources statement. * + * @implNote + * After reading a member trailer, the {@linkplain #read(byte[], int, int) read} method calls + * {@link InputStream#available()} on the underlying stream to determine whether additional + * bytes are available that may represent a subsequent member. If the + * {@systemProperty jdk.util.gzip.tryReadAheadAfterTrailer} system property is set + * to {@code true}, then the call to {@code InputStream.available()} is skipped and the + * implementation instead attempts to read a subsequent member in the stream. + * {@code GZIPInputStream} depends on the return value of {@code InputStream.available()} + * to reliably process a stream with a series of members. Consequently, it may be necessary + * to set this property in environments that process streams with a series of members. By default, + * the {@code jdk.util.gzip.tryReadAheadAfterTrailer} system property is not set, and + * {@code InputStream.available()} gets called. + * * @spec https://www.rfc-editor.org/info/rfc1952 * RFC 1952: GZIP file format specification version 4.3 * @@ -66,6 +79,12 @@ * @since 1.1 */ public class GZIPInputStream extends InflaterInputStream { + + // system property which configures whether we skip the call to InputStream.available() + // when checking for additional GZIP members in a stream + private static final boolean alwaysReadNextMember = + Boolean.getBoolean("jdk.util.gzip.tryReadAheadAfterTrailer"); + /** * GZIP header magic number. */ @@ -119,7 +138,11 @@ public GZIPInputStream(InputStream in, int size) throws IOException { super(in, createInflater(in, size), size); usesDefaultInflater = true; try { - readHeader(in); + // we don't expect the stream to be at EOF + // and if it is, then we want readHeader to + // raise an exception, so we pass "true" for + // the "failOnEOF" param. + readHeader(in, true); } catch (IOException ioe) { this.inf.end(); throw ioe; @@ -194,10 +217,15 @@ public int read(byte[] buf, int off, int len) throws IOException { } int n = super.read(buf, off, len); if (n == -1) { - if (readTrailer()) + if (hasNoMoreMembers()) { eos = true; - else + } else { + // When a next member is available, hasNoMoreMembers() will read + // its header and will position the stream at the next member's + // deflated data. We now decompress and return that member's + // decompressed data. return this.read(buf, off, len); + } } else { crc.update(buf, off, n); } @@ -221,12 +249,40 @@ public void close() throws IOException { /* * Reads GZIP member header and returns the total byte number * of this member header. + * If failOnEOF is false and if the given InputStream has already + * reached EOF when this method was invoked, then this method returns + * -1 (indicating that there's no GZIP member header). + * In all other cases of malformed header or EOF being detected + * when reading the header, this method will throw an IOException. */ - private int readHeader(InputStream this_in) throws IOException { - CheckedInputStream in = new CheckedInputStream(this_in, crc); + private int readHeader(InputStream stream, boolean failOnEOF) throws IOException { + CheckedInputStream in = new CheckedInputStream(stream, crc); crc.reset(); + + int magic; + if (!failOnEOF) { + // read an unsigned short value representing the GZIP magic header. + // this is the same as calling readUShort(in), except that here, + // when reading the first byte, we don't raise an EOFException + // if the stream has already reached EOF. + + // read unsigned byte + int b = in.read(); + if (b == -1) { // EOF + crc.reset(); + return -1; // represents no header bytes available + } + checkUnexpectedByte(b); + // read the next unsigned byte to form the unsigned + // short. we throw the usual EOFException/ZipException + // from this point on if there is no more data or + // the data doesn't represent a header. + magic = (readUByte(in) << 8) | b; + } else { + magic = readUShort(in); + } // Check header magic - if (readUShort(in) != GZIP_MAGIC) { + if (magic != GZIP_MAGIC) { throw new ZipException("Not in GZIP format"); } // Check compression method @@ -268,44 +324,66 @@ private int readHeader(InputStream this_in) throws IOException { return n; } - /* - * Reads GZIP member trailer and returns true if the eos - * reached, false if there are more (concatenated gzip - * data set) + /** + * Reads the current GZIP member's trailer and returns true if the end-of-stream is + * reached. After reading the current member's trailer, if the stream has a subsequent + * GZIP member, then this method reads that member's header and returns false indicating + * that there is another member in the stream. */ - private boolean readTrailer() throws IOException { - InputStream in = this.in; - int n = inf.getRemaining(); - if (n > 0) { - in = new SequenceInputStream( - new ByteArrayInputStream(buf, len - n, n), - new FilterInputStream(in) { - public void close() throws IOException {} - }); + private boolean hasNoMoreMembers() throws IOException { + final int numRemainingInInflater = inf.getRemaining(); + InputStream stream = this.in; + if (numRemainingInInflater > 0) { + stream = new SequenceInputStream( + new ByteArrayInputStream(buf, len - numRemainingInInflater, numRemainingInInflater), + new FilterInputStream(stream) { + public void close() {} + }); } + // first read the current member's trailer + readTrailer(stream); + // decide whether to read next member's header + final boolean readNextMember = alwaysReadNextMember + || this.in.available() > 0 + || numRemainingInInflater > 26; // current member's trailer == 8 bytes + // + minimum of 10 bytes header for next member + // + mandatory 8 bytes from next member's trailer + // == at least 26 bytes needed for next member to + // be present + if (!readNextMember) { + return true; // no need to read next member + } + // read next member's header + int m = 8; // this.trailer + try { + int numNextHeaderBytes = readHeader(stream, false); // next.header (if available) + if (numNextHeaderBytes == -1) { + return true; // end of stream reached, no more members + } + m += numNextHeaderBytes; + } catch (IOException ze) { + return true; // ignore any malformed, consider it as no more members in the stream + } + inf.reset(); // reset the inflater for fresh input data from the next member + if (numRemainingInInflater > m) { + // position the inflater's input buffer to the start of next member's deflated data + inf.setInput(buf, len - numRemainingInInflater + m, numRemainingInInflater - m); + } + return false; // next member exists + } + + /** + * Reads the current member's trailer + * + * @param stream the InputStream containing the trailer + */ + private void readTrailer(final InputStream stream) throws IOException { // Uses left-to-right evaluation order - if ((readUInt(in) != crc.getValue()) || - // rfc1952; ISIZE is the input size modulo 2^32 - (readUInt(in) != (inf.getBytesWritten() & 0xffffffffL))) + if ((readUInt(stream) != crc.getValue()) || + // rfc1952; ISIZE is the input size modulo 2^32 + (readUInt(stream) != (inf.getBytesWritten() & 0xffffffffL))) { throw new ZipException("Corrupt GZIP trailer"); - - // If there are more bytes available in "in" or - // the leftover in the "inf" is > 26 bytes: - // this.trailer(8) + next.header.min(10) + next.trailer(8) - // try concatenated case - if (this.in.available() > 0 || n > 26) { - int m = 8; // this.trailer - try { - m += readHeader(in); // next.header - } catch (IOException ze) { - return true; // ignore any malformed, do nothing - } - inf.reset(); - if (n > m) - inf.setInput(buf, len - n + m, n - m); - return false; } - return true; } /* @@ -332,12 +410,16 @@ private int readUByte(InputStream in) throws IOException { if (b == -1) { throw new EOFException(); } + checkUnexpectedByte(b); + return b; + } + + private void checkUnexpectedByte(final int b) throws IOException { if (b < -1 || b > 255) { - // Report on this.in, not argument in; see read{Header, Trailer}. + // report the InputStream type which returned this unexpected byte throw new IOException(this.in.getClass().getName() - + ".read() returned value out of range -1..255: " + b); + + ".read() returned value out of range -1..255: " + b); } - return b; } /* diff --git a/src/java.base/share/classes/sun/security/provider/ML_DSA.java b/src/java.base/share/classes/sun/security/provider/ML_DSA.java index 9c4e2c898b68..e1b418174350 100644 --- a/src/java.base/share/classes/sun/security/provider/ML_DSA.java +++ b/src/java.base/share/classes/sun/security/provider/ML_DSA.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1184,7 +1184,7 @@ private int[][][] generateA(byte[] seed) { allDone = false; while (!allDone) { allDone = true; - parXof.squeezeBlock(); + parXof.squeezeBlock(parInd); for (int k = 0; k < parInd; k++) { int parsedOfs = 0; int tmp; diff --git a/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java b/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java index caf6a7a2899d..0fcc91542fa6 100644 --- a/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java +++ b/src/java.base/share/classes/sun/security/provider/SHA3Parallel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,9 +80,28 @@ public void reset(byte[][] buffers) throws InvalidAlgorithmParameterException { } } - public int squeezeBlock() { - int retVal = quadKeccak(lanesArr[0], lanesArr[1], lanesArr[2], lanesArr[3]); - for (int i = 0; i < NRPAR; i++) { + public int squeezeBlock(int nr) throws InvalidAlgorithmParameterException { + int retVal = 0; + switch (nr) { + case 1: + // until we enable single keccak intrinsic, use the better + // doubleKeccak + case 2: + retVal = doubleKeccak(lanesArr[0], lanesArr[1]); + break; + case 3: + // until we enable single keccak intrinsic, use the better + // doubleKeccak/quadKeccak + case 4: + retVal = quadKeccak(lanesArr[0], lanesArr[1], lanesArr[2], + lanesArr[3]); + break; + default: + throw new InvalidAlgorithmParameterException( + "Bad parallel parameter."); + } + + for (int i = 0; i < nr; i++) { l2bLittle(lanesArr[i], 0, buffers[i], 0, blockSize); } return retVal; diff --git a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java index 565ed8f6128a..61b1236e9bc2 100644 --- a/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java +++ b/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmDecomposer.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2026, IBM Corporation. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -172,9 +173,18 @@ private Set decomposes(SSLCipher bulkCipher) { case B_AES_128_GCM: components.add("AES_128_GCM"); break; + case B_AES_128_GCM_IV: + components.add("AES_128_GCM"); + break; case B_AES_256_GCM: components.add("AES_256_GCM"); break; + case B_AES_256_GCM_IV: + components.add("AES_256_GCM"); + break; + case B_CC20_P1305: + components.add("CHACHA20_POLY1305"); + break; } return components; diff --git a/src/java.base/share/classes/sun/util/locale/LanguageTag.java b/src/java.base/share/classes/sun/util/locale/LanguageTag.java index bdbbc5eec2d5..5ce62a275cc8 100644 --- a/src/java.base/share/classes/sun/util/locale/LanguageTag.java +++ b/src/java.base/share/classes/sun/util/locale/LanguageTag.java @@ -120,7 +120,7 @@ public static LanguageTag parse(String languageTag, ParsePosition pp, List extensions; // langtag must start with either language or privateuse if (!language.isEmpty()) { - extlangs = parseExtlangs(itr, pp); + extlangs = parseExtlangs(itr, pp, language); script = parseScript(itr, pp); region = parseRegion(itr, pp); variants = parseVariants(itr, pp); @@ -170,8 +170,11 @@ private static String parseLanguage(StringTokenIterator itr, ParsePosition pp) { return EMPTY_SUBTAG; } - private static List parseExtlangs(StringTokenIterator itr, ParsePosition pp) { - if (itr.isDone() || pp.getErrorIndex() != -1) { + private static List parseExtlangs(StringTokenIterator itr, ParsePosition pp, String lang) { + var langLen = lang.length(); + if (itr.isDone() || pp.getErrorIndex() != -1 + // Extlangs only accepted after 2*3ALPHA lang + || (langLen != 2 && langLen != 3)) { return EMPTY_SUBTAGS; } List extlangs = null; diff --git a/src/java.base/share/data/cacerts/luxtrustglobalrootca b/src/java.base/share/data/cacerts/luxtrustglobalrootca deleted file mode 100644 index 7fb3d818f807..000000000000 --- a/src/java.base/share/data/cacerts/luxtrustglobalrootca +++ /dev/null @@ -1,28 +0,0 @@ -Owner: CN=LuxTrust Global Root, O=LuxTrust s.a., C=LU -Issuer: CN=LuxTrust Global Root, O=LuxTrust s.a., C=LU -Serial number: bb8 -Valid from: Thu Mar 17 09:51:37 GMT 2011 until: Wed Mar 17 09:51:37 GMT 2021 -Signature algorithm name: SHA256withRSA -Subject Public Key Algorithm: 2048-bit RSA key -Version: 3 ------BEGIN CERTIFICATE----- -MIIDZDCCAkygAwIBAgICC7gwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCTFUx -FjAUBgNVBAoTDUx1eFRydXN0IHMuYS4xHTAbBgNVBAMTFEx1eFRydXN0IEdsb2Jh -bCBSb290MB4XDTExMDMxNzA5NTEzN1oXDTIxMDMxNzA5NTEzN1owRDELMAkGA1UE -BhMCTFUxFjAUBgNVBAoTDUx1eFRydXN0IHMuYS4xHTAbBgNVBAMTFEx1eFRydXN0 -IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsn+n -QPAiygz267Hxyw6VV0B1r6A/Ps7sqjJX5hmxZ0OYWmt8s7j6eJyqpoSyYBuAQc5j -zR8XCJmk9e8+EsdMsFeaXHhAePxFjdqRZ9w6Ubltc+a3OY52OrQfBfVpVfmTz3iI -Sr6qm9d7R1tGBEyCFqY19vx039a0r9jitScRdFmiwmYsaArhmIiIPIoFdRTjuK7z -CISbasE/MRivJ6VLm6T9eTHemD0OYcqHmMH4ijCc+j4z1aXEAwfh95Z0GAAnOCfR -K6qq4UFFi2/xJcLcopeVx0IUM115hCNq52XAV6DYXaljAeew5Ivo+MVjuOVsdJA9 -x3f8K7p56aTGEnin/wIDAQABo2AwXjAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjAfBgNVHSMEGDAWgBQXFYWJCS8kh28/HRvk8pZ5g0gTzjAdBgNVHQ4EFgQU -FxWFiQkvJIdvPx0b5PKWeYNIE84wDQYJKoZIhvcNAQELBQADggEBAFrwHNDUUM9B -fua4nX3DcNBeNv9ujnov3kgR1TQuPLdFwlQlp+HBHjeDtpSutkVIA+qVvuucarQ3 -XB8u02uCgUNbCj8RVWOs+nwIAjegPDkEM/6XMshS5dklTbDG7mgfcKpzzlcD3H0K -DTPy0lrfCmw7zBFRlxqkIaKFNQLXgCLShLL4wKpov9XrqsMLq6F8K/f1O4fhVFfs -BSTveUJO84ton+Ruy4KZycwq3FPCH3CDqyEPVrRI/98HIrOM+R2mBN8tAza53W/+ -MYhm/2xtRDSvCHc+JtJy9LtHVpM8mGPhM7uZI5K1g3noHZ9nrWLWidb2/CfeMifL -hNp3hSGhEiE= ------END CERTIFICATE----- diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java index 2d72aeb2ee93..ed28e1a1fe7b 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java +++ b/src/java.base/unix/classes/sun/nio/fs/UnixNativeDispatcher.java @@ -555,9 +555,10 @@ static native int flistxattr(int filedes, long listAddress, int size) /** * Capabilities */ - private static final int SUPPORTS_OPENAT = 1 << 1; // syscalls - private static final int SUPPORTS_XATTR = 1 << 3; - private static final int SUPPORTS_BIRTHTIME = 1 << 16; // other features + private static final int SUPPORTS_OPENAT = 1 << 1; // syscalls + private static final int SUPPORTS_FCHMODAT_NOFOLLOW = 1 << 2; + private static final int SUPPORTS_XATTR = 1 << 3; + private static final int SUPPORTS_BIRTHTIME = 1 << 16; // other features private static final int capabilities; /** @@ -585,9 +586,8 @@ static boolean xattrSupported() { * Supports fchmodat with AT_SYMLINK_NOFOLLOW flag */ static boolean fchmodatNoFollowSupported() { - return fchmodatNoFollowSupported0(); + return (capabilities & SUPPORTS_FCHMODAT_NOFOLLOW) != 0; } - private static native boolean fchmodatNoFollowSupported0(); private static native int init(); static { diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c index 4b5cfabebfbc..aba16118988a 100644 --- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c +++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -388,17 +388,16 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_XATTR; #endif - return capabilities; -} - -JNIEXPORT jboolean JNICALL -Java_sun_nio_fs_UnixNativeDispatcher_fchmodatNoFollowSupported0(JNIEnv* env, jclass this) { #if defined(__linux__) - // Linux recognizes but does not support the AT_SYMLINK_NOFOLLOW flag - return JNI_FALSE; + // Linux 6.6+ supports AT_SYMLINK_NOFOLLOW. glibc 2.32+ also provides emulation for older kernels. + if (fchmodat(AT_FDCWD, "", 0, AT_SYMLINK_NOFOLLOW) == 0 || errno != ENOTSUP) { + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FCHMODAT_NOFOLLOW; + } #else - return JNI_TRUE; + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FCHMODAT_NOFOLLOW; #endif + + return capabilities; } JNIEXPORT jbyteArray JNICALL diff --git a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java index 1e0a924f12c2..3bbebf5f9d7a 100644 --- a/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java +++ b/src/java.naming/share/classes/com/sun/jndi/ldap/Connection.java @@ -1173,8 +1173,14 @@ public void handshakeCompleted(HandshakeCompletedEvent event) { tlsHandshakeCompleted.complete(tlsServerCert); } catch (SSLPeerUnverifiedException ex) { CommunicationException ce = new CommunicationException(); - ce.setRootCause(closureReason); - tlsHandshakeCompleted.completeExceptionally(ex); + IOException priorFailure = closureReason; + if (priorFailure != null) { + ce.setRootCause(priorFailure); + ce.addSuppressed(ex); + } else { + ce.setRootCause(ex); + } + tlsHandshakeCompleted.completeExceptionally(ce); } } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java index 487a8a186f6a..f2991c0738ef 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/PacketSpaceManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -645,22 +645,15 @@ private synchronized boolean shouldLogWhenNewDeadline() { return false; } - boolean hasNoDeadline() { - return Deadline.MAX.equals(nextDeadline); - } - // reschedule this task void reschedule() { Deadline deadline = computeNextDeadline(); - Deadline nextDeadline = this.nextDeadline; if (Deadline.MAX.equals(deadline)) { - debug.log("no deadline, don't reschedule"); - } else if (deadline.equals(nextDeadline)) { - debug.log("deadline unchanged, don't reschedule"); - } else { - packetEmitter.reschedule(this, deadline); - debug.log("retransmission task: rescheduled"); + if (debug.on()) debug.log("no deadline, don't reschedule"); + return; } + if (debug.on()) debug.log("retransmission task: rescheduled"); + packetEmitter.reschedule(this, deadline); } @Override @@ -1304,7 +1297,7 @@ public void packetSent(QuicPacket packet, long previousPacketNumber, long packet } finally { transferLock.unlock(); } - if (found && packetTransmissionTask.hasNoDeadline()) { + if (found) { packetTransmissionTask.reschedule(); } if (!found) { @@ -1340,9 +1333,7 @@ public void packetSent(QuicPacket packet, long previousPacketNumber, long packet return; } addAcknowledgement(pending); - if (packetTransmissionTask.hasNoDeadline()) { - packetTransmissionTask.reschedule(); - } + packetTransmissionTask.reschedule(); } finally { transferLock.unlock(); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java index 3dee814e1f1a..18fd7717d6d7 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java @@ -1788,9 +1788,10 @@ public final void processIncoming(SocketAddress source, ByteBuffer destConnId, H if (more > 16) { // the server doesn't seem to take into account our // connection close frame. Just stop responding - updatedDeadline = Deadline.MIN; + updated = updatedDeadline = Deadline.MIN; } else { - updatedDeadline = updated.plusMillis(maxIdleTimeMs); + updated = updatedDeadline = timeSource().instant() + .plusMillis(maxIdleTimeMs); } handleIncoming(source, destConnId, headersType, buffer); } else { @@ -1798,7 +1799,7 @@ public final void processIncoming(SocketAddress source, ByteBuffer destConnId, H dropIncoming(source, destConnId, headersType, buffer); } - timer().reschedule(this, updatedDeadline); + timer().reschedule(this, updated); } protected void handleIncoming(SocketAddress source, ByteBuffer idbytes, @@ -1821,8 +1822,8 @@ public final void onWriteError(Throwable t) { } public final void startTimer() { - deadline = updatedDeadline = timeSource().instant().plusMillis(maxIdleTimeMs); - timer().offer(this); + Deadline deadline = updatedDeadline = timeSource().instant().plusMillis(maxIdleTimeMs); + timer().reschedule(this, deadline); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java index 830415593cbe..bbb88cf1c45b 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,6 +74,9 @@ public final class QuicTimerQueue { private volatile Deadline scheduledDeadline = Deadline.MAX; private volatile Deadline returnedDeadline = Deadline.MAX; + // Not volatile: never accessed without holding monitor + private Deadline notifiedDeadline = Deadline.MAX; + /** * Creates a new timer queue with the given notifier. * A notifier is used to notify the timer thread that @@ -113,33 +116,8 @@ private Deadline debugNow() { * @param event an event to be scheduled */ public void offer(QuicTimedEvent event) { - if (event instanceof Marker marker) - throw new IllegalArgumentException(marker.name()); - assert QuicTimedEvent.COMPARATOR.compare(event, FLOOR) > 0; - assert QuicTimedEvent.COMPARATOR.compare(event, CEILING) < 0; - Deadline deadline = event.deadline(); - scheduled.add(event); - scheduled(deadline); if (debug.on()) debug.log("QuicTimerQueue: event %s offered", event); - if (notify(deadline)) { - if (debug.on()) debug.log("QuicTimerQueue: event %s will be rescheduled", event); - if (Log.quicTimer()) { - var now = debugNow(); - Log.logQuic(String.format("%s: QuicTimerQueue: event %s will be scheduled" + - " at %s (returned deadline: %s, nextDeadline: %s)", - Thread.currentThread().getName(), event, d(now, deadline), - d(now, returnedDeadline), d(now, nextDeadline()))); - } - notifier.run(); - } else { - if (Log.quicTimer()) { - var now = debugNow(); - Log.logQuic(String.format("%s: QuicTimerQueue: event %s will not be scheduled" + - " at %s (returned deadline: %s, nextDeadline: %s)", - Thread.currentThread().getName(), event, d(now, deadline), - d(now, returnedDeadline), d(now, nextDeadline()))); - } - } + reschedule(event, event.deadline()); } /** @@ -181,7 +159,7 @@ public Deadline processEventsAndReturnNextDeadline(Deadline now, Executor execut int drained = 0; int dues; synchronized (this) { - scheduledDeadline = Deadline.MAX; + scheduledDeadline = returnedDeadline = notifiedDeadline = Deadline.MAX; } // moved scheduled / rescheduled tasks to due, until // nothing else is due. Then process dues. @@ -347,7 +325,16 @@ private boolean notify(Deadline deadline) { synchronized (this) { if (deadline.isBefore(nextDeadline()) || deadline.isBefore(returnedDeadline)) { - return true; + // notifiedDeadline will be reset to MAX first thing in + // processEventAndReturnNextDeadline; We do not want + // to call the notifier (wake the selector) again if it's + // been already called for a notifiedDeadline <= to deadline; + // On the other hand, if deadline < notifiedDeadline, we + // need to call the notifier to force an additional wakeup + if (deadline.isBefore(notifiedDeadline)) { + notifiedDeadline = deadline; + return true; + } } } return false; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java index 58beee78af2c..9a0b75a3aa43 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java @@ -32,7 +32,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -278,12 +277,8 @@ public void init(String ownName) { */ public Set getFileObjects() { if (fileObjects == null) { - fileObjects = new LinkedHashSet<>(); - } - if (files != null) { - JavacFileManager jfm = (JavacFileManager) getFileManager(); - for (JavaFileObject fo: jfm.getJavaFileObjectsFromPaths(files)) - fileObjects.add(fo); + // see Arguments::validate + throw new IllegalStateException("file objects have not been initialized"); } return fileObjects; } @@ -421,6 +416,9 @@ private boolean doProcessArgs(Iterable args, */ public boolean validate() { JavaFileManager fm = getFileManager(); + if (fileObjects == null) { + fileObjects = new LinkedHashSet<>(); + } if (options.isSet(Option.MODULE)) { if (!fm.hasLocation(StandardLocation.CLASS_OUTPUT)) { log.error(Errors.OutputDirMustBeSpecifiedWithDashMOption); @@ -433,19 +431,10 @@ public boolean validate() { Location sourceLoc = fm.getLocationForModule(StandardLocation.MODULE_SOURCE_PATH, module); if (sourceLoc == null) { log.error(Errors.ModuleNotFoundInModuleSourcePath(module)); - } else { - Location classLoc = fm.getLocationForModule(StandardLocation.CLASS_OUTPUT, module); - - for (JavaFileObject file : fm.list(sourceLoc, "", EnumSet.of(JavaFileObject.Kind.SOURCE), true)) { - String className = fm.inferBinaryName(sourceLoc, file); - JavaFileObject classFile = fm.getJavaFileForInput(classLoc, className, Kind.CLASS); - - if (classFile == null || classFile.getLastModified() < file.getLastModified()) { - if (fileObjects == null) - fileObjects = new HashSet<>(); - fileObjects.add(file); - } - } + return false; + } + for (JavaFileObject file : fm.list(sourceLoc, "", EnumSet.of(Kind.SOURCE), true)) { + fileObjects.add(file); } } } catch (IOException ex) { @@ -455,6 +444,12 @@ public boolean validate() { } } } + if (files != null) { + JavacFileManager jfm = (JavacFileManager) getFileManager(); + for (JavaFileObject fo : jfm.getJavaFileObjectsFromPaths(files)){ + fileObjects.add(fo); + } + } if (isEmpty()) { // It is allowed to compile nothing if just asking for help or version info. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java index 11fa3a5aebf0..ede75a73824b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java @@ -1547,7 +1547,8 @@ public void visitClassDef(JCClassDecl node) { } public void visitMethodDef(JCMethodDecl node) { // remove super constructor call that may have been added during attribution: - if (TreeInfo.isConstructor(node) && node.sym != null && node.sym.owner.isEnum() && + if (TreeInfo.isConstructor(node) && node.sym != null && + (node.sym.owner.isEnum() || TreeInfo.isCanonicalConstructor(node)) && node.body != null && node.body.stats.nonEmpty() && TreeInfo.isSuperCall(node.body.stats.head) && node.body.stats.head.pos == node.body.pos) { node.body.stats = node.body.stats.tail; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties index 7824772b1f3e..d835c6398275 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties @@ -48,7 +48,7 @@ javac.opt.modulepath=\ javac.opt.sourcepath=\ Specify where to find input source files javac.opt.m=\ - Compile only the specified module(s), check timestamps + Compile only the specified module(s) javac.opt.modulesourcepath=\ Specify where to find input source files for multiple modules javac.opt.bootclasspath=\ diff --git a/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt b/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt index 9853f8c70fdd..bd9b9177e232 100644 --- a/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt +++ b/src/jdk.compiler/share/data/symbols/java.base-R.sym.txt @@ -27,7 +27,7 @@ # ########################################################## # module name java.base -header exports java/io,java/lang,java/lang/annotation,java/lang/classfile,java/lang/classfile/attribute,java/lang/classfile/constantpool,java/lang/classfile/instruction,java/lang/constant,java/lang/foreign,java/lang/invoke,java/lang/module,java/lang/ref,java/lang/reflect,java/lang/runtime,java/math,java/net,java/net/spi,java/nio,java/nio/channels,java/nio/channels/spi,java/nio/charset,java/nio/charset/spi,java/nio/file,java/nio/file/attribute,java/nio/file/spi,java/security,java/security/cert,java/security/interfaces,java/security/spec,java/text,java/text/spi,java/time,java/time/chrono,java/time/format,java/time/temporal,java/time/zone,java/util,java/util/concurrent,java/util/concurrent/atomic,java/util/concurrent/locks,java/util/function,java/util/jar,java/util/random,java/util/regex,java/util/spi,java/util/stream,java/util/zip,javax/crypto,javax/crypto/interfaces,javax/crypto/spec,javax/net,javax/net/ssl,javax/security/auth,javax/security/auth/callback,javax/security/auth/login,javax/security/auth/spi,javax/security/auth/x500,javax/security/cert,jdk/internal/event[jdk.jfr],jdk/internal/javac[java.compiler\u005C;u002C;jdk.compiler],jdk/internal/vm/vector[jdk.incubator.vector] extraModulePackages jdk/internal/access/foreign,jdk/internal/classfile/impl,jdk/internal/constant,jdk/internal/foreign/abi,jdk/internal/foreign/abi/aarch64/linux,jdk/internal/foreign/abi/aarch64/macos,jdk/internal/foreign/abi/aarch64/windows,jdk/internal/foreign/abi/fallback,jdk/internal/foreign/abi/ppc64/aix,jdk/internal/foreign/abi/ppc64/linux,jdk/internal/foreign/abi/riscv64/linux,jdk/internal/foreign/abi/s390/linux,jdk/internal/foreign/abi/x64/sysv,jdk/internal/foreign/abi/x64/windows,jdk/internal/foreign/layout,jdk/internal/lang,sun/nio,sun/nio/ch,sun/net,jdk/internal/foreign,jdk/internal/foreign,sun/net,sun/nio/ch uses java/lang/System$LoggerFinder,java/net/ContentHandlerFactory,java/net/spi/InetAddressResolverProvider,java/net/spi/URLStreamHandlerProvider,java/nio/channels/spi/AsynchronousChannelProvider,java/nio/channels/spi/SelectorProvider,java/nio/charset/spi/CharsetProvider,java/nio/file/spi/FileSystemProvider,java/nio/file/spi/FileTypeDetector,java/security/Provider,java/text/spi/BreakIteratorProvider,java/text/spi/CollatorProvider,java/text/spi/DateFormatProvider,java/text/spi/DateFormatSymbolsProvider,java/text/spi/DecimalFormatSymbolsProvider,java/text/spi/NumberFormatProvider,java/time/chrono/AbstractChronology,java/time/chrono/Chronology,java/time/format/DateTimeFormatterPatternProvider,java/time/zone/ZoneRulesProvider,java/util/spi/CalendarDataProvider,java/util/spi/CalendarNameProvider,java/util/spi/CurrencyNameProvider,java/util/spi/LocaleNameProvider,java/util/spi/ResourceBundleControlProvider,java/util/spi/ResourceBundleProvider,java/util/spi/TimeZoneNameProvider,java/util/spi/ToolProvider,javax/security/auth/spi/LoginModule,jdk/internal/io/JdkConsoleProvider,jdk/internal/logger/DefaultLoggerFinder,sun/util/locale/provider/LocaleDataMetaInfo,sun/util/resources/LocaleData$LocaleDataResourceBundleProvider,sun/util/spi/CalendarProvider provides interface\u0020;java/nio/file/spi/FileSystemProvider\u0020;impls\u0020;jdk/internal/jrtfs/JrtFileSystemProvider target macos-aarch64 flags 8000 +header exports java/io,java/lang,java/lang/annotation,java/lang/classfile,java/lang/classfile/attribute,java/lang/classfile/constantpool,java/lang/classfile/instruction,java/lang/constant,java/lang/foreign,java/lang/invoke,java/lang/module,java/lang/ref,java/lang/reflect,java/lang/runtime,java/math,java/net,java/net/spi,java/nio,java/nio/channels,java/nio/channels/spi,java/nio/charset,java/nio/charset/spi,java/nio/file,java/nio/file/attribute,java/nio/file/spi,java/security,java/security/cert,java/security/interfaces,java/security/spec,java/text,java/text/spi,java/time,java/time/chrono,java/time/format,java/time/temporal,java/time/zone,java/util,java/util/concurrent,java/util/concurrent/atomic,java/util/concurrent/locks,java/util/function,java/util/jar,java/util/random,java/util/regex,java/util/spi,java/util/stream,java/util/zip,javax/crypto,javax/crypto/interfaces,javax/crypto/spec,javax/net,javax/net/ssl,javax/security/auth,javax/security/auth/callback,javax/security/auth/login,javax/security/auth/spi,javax/security/auth/x500,javax/security/cert,jdk/internal/event[jdk.jfr],jdk/internal/javac[java.compiler\u005C;u002C;jdk.compiler],jdk/internal/vm/vector[jdk.incubator.vector] extraModulePackages jdk/internal/access/foreign,jdk/internal/classfile/impl,jdk/internal/constant,jdk/internal/foreign/abi,jdk/internal/foreign/abi/aarch64/linux,jdk/internal/foreign/abi/aarch64/macos,jdk/internal/foreign/abi/aarch64/windows,jdk/internal/foreign/abi/fallback,jdk/internal/foreign/abi/ppc64/aix,jdk/internal/foreign/abi/ppc64/linux,jdk/internal/foreign/abi/riscv64/linux,jdk/internal/foreign/abi/s390/linux,jdk/internal/foreign/abi/x64/sysv,jdk/internal/foreign/abi/x64/windows,jdk/internal/foreign/layout,jdk/internal/lang,sun/nio,sun/security/internal,sun/nio/ch,sun/net,jdk/internal/foreign,jdk/internal/foreign,sun/net,sun/nio/ch uses java/lang/System$LoggerFinder,java/net/ContentHandlerFactory,java/net/spi/InetAddressResolverProvider,java/net/spi/URLStreamHandlerProvider,java/nio/channels/spi/AsynchronousChannelProvider,java/nio/channels/spi/SelectorProvider,java/nio/charset/spi/CharsetProvider,java/nio/file/spi/FileSystemProvider,java/nio/file/spi/FileTypeDetector,java/security/Provider,java/text/spi/BreakIteratorProvider,java/text/spi/CollatorProvider,java/text/spi/DateFormatProvider,java/text/spi/DateFormatSymbolsProvider,java/text/spi/DecimalFormatSymbolsProvider,java/text/spi/NumberFormatProvider,java/time/chrono/AbstractChronology,java/time/chrono/Chronology,java/time/format/DateTimeFormatterPatternProvider,java/time/zone/ZoneRulesProvider,java/util/spi/CalendarDataProvider,java/util/spi/CalendarNameProvider,java/util/spi/CurrencyNameProvider,java/util/spi/LocaleNameProvider,java/util/spi/ResourceBundleControlProvider,java/util/spi/ResourceBundleProvider,java/util/spi/TimeZoneNameProvider,java/util/spi/ToolProvider,javax/security/auth/spi/LoginModule,jdk/internal/io/JdkConsoleProvider,jdk/internal/logger/DefaultLoggerFinder,sun/util/locale/provider/LocaleDataMetaInfo,sun/util/resources/LocaleData$LocaleDataResourceBundleProvider,sun/util/spi/CalendarProvider provides interface\u0020;java/nio/file/spi/FileSystemProvider\u0020;impls\u0020;jdk/internal/jrtfs/JrtFileSystemProvider target macos-aarch64 flags 8000 class name java/io/ProxyingConsole header extends java/io/Console flags 30 runtimeAnnotations @Ljdk/internal/ValueBased; @@ -124,7 +124,7 @@ class name java/security/AsymmetricKey header extends java/lang/Object implements java/security/Key,java/security/BinaryEncodable flags 601 class name java/security/BinaryEncodable -header extends java/lang/Object sealed true permittedSubclasses java/security/AsymmetricKey,java/security/KeyPair,java/security/spec/PKCS8EncodedKeySpec,java/security/spec/X509EncodedKeySpec,javax/crypto/EncryptedPrivateKeyInfo,java/security/cert/X509Certificate,java/security/cert/X509CRL,java/security/PEM flags 601 classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;PEM_API;) +header extends java/lang/Object sealed true permittedSubclasses java/security/AsymmetricKey,java/security/KeyPair,java/security/spec/PKCS8EncodedKeySpec,java/security/spec/X509EncodedKeySpec,javax/crypto/EncryptedPrivateKeyInfo,java/security/cert/X509Certificate,java/security/cert/X509CRL,java/security/PEM,sun/security/internal/InternalBinaryEncodable flags 601 classAnnotations @Ljdk/internal/javac/PreviewFeature;(feature=eLjdk/internal/javac/PreviewFeature$Feature;PEM_API;) -class name java/security/DEREncodable @@ -483,3 +483,6 @@ method name convert descriptor (ILjava/lang/Class;IILjava/lang/Class;IILjdk/inte method name compressExpandOp descriptor (ILjava/lang/Class;Ljava/lang/Class;IILjdk/internal/vm/vector/VectorSupport$Vector;Ljdk/internal/vm/vector/VectorSupport$VectorMask;Ljdk/internal/vm/vector/VectorSupport$CompressExpandOperation;)Ljdk/internal/vm/vector/VectorSupport$VectorPayload; flags 9 signature ;M:Ljdk/internal/vm/vector/VectorSupport$VectorMask;E:Ljava/lang/Object;>(ILjava/lang/Class<+TV;>;Ljava/lang/Class<+TM;>;IITV;TM;Ljdk/internal/vm/vector/VectorSupport$CompressExpandOperation;)Ljdk/internal/vm/vector/VectorSupport$VectorPayload; runtimeAnnotations @Ljdk/internal/vm/annotation/IntrinsicCandidate; method name maskReductionCoerced descriptor (ILjava/lang/Class;IILjdk/internal/vm/vector/VectorSupport$VectorMask;Ljdk/internal/vm/vector/VectorSupport$VectorMaskOp;)J flags 9 signature ;E:Ljava/lang/Object;>(ILjava/lang/Class<+TM;>;IITM;Ljdk/internal/vm/vector/VectorSupport$VectorMaskOp;)J runtimeAnnotations @Ljdk/internal/vm/annotation/IntrinsicCandidate; +class name sun/security/internal/InternalBinaryEncodable +header extends java/lang/Object implements java/security/BinaryEncodable flags 31 + diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java index 978fb39ad1ce..0258fea68085 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Frame.java @@ -75,12 +75,6 @@ public void update(Observable o, Object data) { /** Size of ConstMethod for computing BCI from BCP (FIXME: hack) */ private static long ConstMethodSize; - private static int pcReturnOffset; - - public static int pcReturnOffset() { - return pcReturnOffset; - } - protected void adjustForDeopt() { if (pc != null) { // Look for a deopt pc and if it is deopted convert to original pc @@ -104,8 +98,6 @@ private static synchronized void initialize(TypeDataBase db) { // FIXME: not sure whether alignment here is correct or how to // force it (round up to address size?) ConstMethodSize = ConstMethodType.getSize(); - - pcReturnOffset = db.lookupIntConstant("frame::pc_return_offset").intValue(); } protected int bcpToBci(Address bcp, ConstMethod cm) { diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java index 94fe78b9c647..3d77a61c0be1 100644 --- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java +++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java @@ -538,6 +538,8 @@ public void run() { if (MAX_CONNECTIONS > 0 && allConnections.size() >= MAX_CONNECTIONS) { // we've hit max limit of current open connections, so we go // ahead and close this connection without processing it + logger.log(Level.DEBUG, "connection limit reached, " + + "closing accepted connection " + chan); try { chan.close(); } catch (IOException ignore) { diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java index cf7eae5dd6a4..ce3a67357f92 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/Float16Vector.java @@ -2861,6 +2861,17 @@ public final short[] toArray() { return a; } + // Returns the lane values boxed as Float16 elements. + @ForceInline + final Float16[] toFloat16Array() { + short[] bits = vec(); + Float16[] a = new Float16[bits.length]; + for (int i = 0; i < bits.length; i++) { + a[i] = Float16.shortBitsToFloat16(bits[i]); + } + return a; + } + /** {@inheritDoc} */ @ForceInline @@ -3734,8 +3745,9 @@ boolean equals(Object obj) { @ForceInline public final int hashCode() { - // now that toArray is strongly typed, we can define this - return Objects.hash(species(), Arrays.hashCode(toArray())); + // Hash the lanes as Float16 values; Float16.hashCode canonicalizes NaN + // so that all NaN representations contribute the same hash code. + return Objects.hash(species(), Arrays.hashCode(toFloat16Array())); } // ================================================ diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template index 7c6fb3bcfb2b..f11c62836858 100644 --- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template +++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/X-Vector.java.template @@ -3705,6 +3705,19 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp return a; } +#if[FP16] + // Returns the lane values boxed as Float16 elements. + @ForceInline + final Float16[] toFloat16Array() { + short[] bits = vec(); + Float16[] a = new Float16[bits.length]; + for (int i = 0; i < bits.length; i++) { + a[i] = Float16.shortBitsToFloat16(bits[i]); + } + return a; + } + +#end[FP16] #if[int] /** * {@inheritDoc} @@ -5749,8 +5762,14 @@ public abstract sealed class $abstractvectortype$ extends AbstractVector<$Boxtyp @ForceInline public final int hashCode() { +#if[FP16] + // Hash the lanes as Float16 values; Float16.hashCode canonicalizes NaN + // so that all NaN representations contribute the same hash code. + return Objects.hash(species(), Arrays.hashCode(toFloat16Array())); +#else[FP16] // now that toArray is strongly typed, we can define this return Objects.hash(species(), Arrays.hashCode(toArray())); +#end[FP16] } // ================================================ diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CACertsPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CACertsPlugin.java new file mode 100644 index 000000000000..3f6639838286 --- /dev/null +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/CACertsPlugin.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.tools.jlink.internal.plugins; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.util.HashMap; +import java.util.Map; + +import jdk.tools.jlink.internal.ResourcePoolEntryFactory; +import jdk.tools.jlink.plugin.PluginException; +import jdk.tools.jlink.plugin.ResourcePool; +import jdk.tools.jlink.plugin.ResourcePoolBuilder; +import jdk.tools.jlink.plugin.ResourcePoolEntry; + +/** + * Creates the cacerts keystore in the output image with the certificates of + * the specified aliases only. + */ +public class CACertsPlugin extends AbstractPlugin { + + private static final String RES = "/java.base/lib/security/cacerts"; + + // cacerts keystore aliases + private String[] aliases; + + public CACertsPlugin() { + super("cacerts"); + } + + @Override + public boolean hasArguments() { + return true; + } + + @Override + public void configure(Map config) { + String option = config.get(getName()); + if (option == null) { + throw new AssertionError(); + } + // If alias has a comma in it, this won't work, but no cacerts + // aliases have commas. + aliases = option.split(","); + } + + @Override + public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) { + in.transformAndCopy(res -> { + if (res.type() == ResourcePoolEntry.Type.NATIVE_LIB && + res.path().equals(RES)) { + byte[] cacerts = transformCACerts(res.content()); + return ResourcePoolEntryFactory.create(res, cacerts); + } + return res; + }, out); + return out.build(); + } + + /** + * Creates a keystore containing only the certificates of the specified + * aliases. + */ + private byte[] transformCACerts(InputStream content) { + try { + var ks = KeyStore.getInstance("PKCS12"); + ks.load(content, null); + Map certs = new HashMap<>(aliases.length); + for (var alias : aliases) { + var cert = ks.getCertificate(alias); + if (cert == null) { + throw new PluginException( + "alias " + alias + " does not exist"); + } + certs.put(alias, cert); + } + ks.load(null, null); + for (var entry : certs.entrySet()) { + ks.setCertificateEntry(entry.getKey(), entry.getValue()); + } + var baos = new ByteArrayOutputStream(); + ks.store(baos, null); + return baos.toByteArray(); + } catch (PluginException pe) { + throw pe; + } catch (Exception ex) { + throw new PluginException(ex); + } + } +} diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties index 75a43bc09cc6..6ea8508ef59e 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -53,6 +53,20 @@ release-info.usage=\ \ Any number of = pairs can be passed.\n\ \ del: is to delete the list of keys in release file. +cacerts.argument=[,]* + +cacerts.description=\ +Create the cacerts keystore in the output image with only the certificates\n\ +of the specified aliases. is the name of an alias in the cacerts\n\ +keystore in the java.base module. + +cacerts.usage=\ +\ --cacerts [,]*\n\ +\ Create the cacerts keystore in the output image\n\ +\ with only the certificates of the specified\n\ +\ aliases. is the name of an alias in the\n\ +\ cacerts keystore in the java.base module. + class-for-name.argument= class-for-name.description=\ diff --git a/src/jdk.jlink/share/classes/module-info.java b/src/jdk.jlink/share/classes/module-info.java index 5e876c879c28..51c5c3ab8c82 100644 --- a/src/jdk.jlink/share/classes/module-info.java +++ b/src/jdk.jlink/share/classes/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -81,7 +81,8 @@ jdk.tools.jlink.internal.plugins.VendorVMBugURLPlugin, jdk.tools.jlink.internal.plugins.VendorVersionPlugin, jdk.tools.jlink.internal.plugins.CDSPlugin, + jdk.tools.jlink.internal.plugins.SaveJlinkArgfilesPlugin, + jdk.tools.jlink.internal.plugins.CACertsPlugin, // SapMachine 2025-01-09: SapMachine tools plugin - jdk.tools.jlink.internal.plugins.AddSapMachineTools, - jdk.tools.jlink.internal.plugins.SaveJlinkArgfilesPlugin; + jdk.tools.jlink.internal.plugins.AddSapMachineTools; } diff --git a/src/jdk.jlink/share/man/jlink.md b/src/jdk.jlink/share/man/jlink.md index b95424fdde92..1ee4d08646d9 100644 --- a/src/jdk.jlink/share/man/jlink.md +++ b/src/jdk.jlink/share/man/jlink.md @@ -235,6 +235,16 @@ Options Description : Generate CDS archive if the runtime image supports the CDS feature. +### Plugin `cacerts` + +Options +: `--cacerts=`*alias*\[`,`*alias*\]\* + +Description +: Create the `cacerts` keystore in the output image with only the + certificates of the specified aliases. *alias* is the name of an alias + in the `cacerts` keystore in the java.base module. + ## jlink Examples The following command creates a runtime image in the directory `greetingsapp`. diff --git a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java index a0ff70066b97..404185ce8323 100644 --- a/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java +++ b/src/jdk.jpackage/windows/classes/jdk/jpackage/internal/RtfConverter.java @@ -46,11 +46,11 @@ static boolean isRtfFile(Path path) throws IOException { } try (InputStream fin = Files.newInputStream(path)) { - byte[] firstBits = new byte[7]; + byte[] firstBits = fin.readNBytes(Details.RTF_HEADER.length()); - if (fin.read(firstBits) == firstBits.length) { + if (Details.RTF_HEADER.length() == firstBits.length) { String header = new String(firstBits); - return "{\\rtf1\\".equals(header); + return Details.RTF_HEADER.equals(header); } } @@ -136,5 +136,6 @@ private void convert(Stream textFile, Appendable sink) throws IOExceptio } } + private static final String RTF_HEADER = "{\\rtf1\\"; } } diff --git a/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c b/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c index 6c0554a5c323..5a01e3ad7386 100644 --- a/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c +++ b/src/jdk.management/share/native/libmanagement_ext/DiagnosticCommandImpl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -151,7 +151,7 @@ Java_com_sun_management_internal_DiagnosticCommandImpl_getDiagnosticCommandInfo jobjectArray args; jobject obj; jmmOptionalSupport mos; - jint ret = jmm_interface_management_ext->GetOptionalSupport(env, &mos); + jmm_interface_management_ext->GetOptionalSupport(env, &mos); jsize num_commands; dcmdInfo* dcmd_info_array; jstring jname, jdesc, jimpact, cmd; diff --git a/test/docs/TEST.ROOT b/test/docs/TEST.ROOT index 11cba9c1c884..a42f6c99aa1b 100644 --- a/test/docs/TEST.ROOT +++ b/test/docs/TEST.ROOT @@ -38,7 +38,7 @@ groups=TEST.groups # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index 705cded007ae..b30a09a7710a 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -29,6 +29,11 @@ serviceability/AsyncGetCallTrace/MyPackage/ASGCTBaseTest.java 8308026 generic-al serviceability/jvmti/Heap/IterateHeapWithEscapeAnalysisEnabled.java 8264699 generic-all vmTestbase/vm/mlvm/indy/func/jvmti/mergeCP_indy2manyDiff_a/TestDescription.java 8308367 generic-all +vmTestbase/nsk/jvmti/unit/functions/Dispose/JvmtiTest/TestDescription.java 8387429 generic-all +vmTestbase/nsk/jvmti/scenarios/capability/CM02/cm02t001/TestDescription.java 8299217 generic-all +vmTestbase/nsk/jdi/EventRequestManager/threadStartRequests/thrstartreq002/TestDescription.java 8327967 generic-all + + #### ## Classes not unloaded as expected (TODO, need to check if FJ keeps a reference) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 05e1b9ddeeb9..09f3cc789627 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -70,6 +70,8 @@ compiler/c2/aarch64/TestStaticCallStub.java 8359963 generic-aarch64 compiler/unsafe/AlignmentGapAccess.java 8373487 generic-all +compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java 8387392 windows-aarch64 + ############################################################################# # :hotspot_gc @@ -167,6 +169,7 @@ vmTestbase/metaspace/gc/firstGC_default/TestDescription.java 8208250 generic-all vmTestbase/nsk/jvmti/scenarios/capability/CM03/cm03t001/TestDescription.java 8073470 linux-all vmTestbase/nsk/jvmti/scenarios/events/EM02/em02t006/TestDescription.java 8372206 generic-all vmTestbase/nsk/jvmti/InterruptThread/intrpthrd003/TestDescription.java 8288911 macosx-all +vmTestbase/nsk/jvmti/unit/timers/JvmtiTest/TestDescription.java 8235348 windows-x64 vmTestbase/jit/escape/LockCoarsening/LockCoarsening001.java 8148743 generic-all vmTestbase/jit/escape/LockCoarsening/LockCoarsening002.java 8208259 generic-all diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index d61d0d3c5d4c..52f4dcd87e0c 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -104,7 +104,7 @@ requires.properties= \ jdk.static # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../../ notation to reach them diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index ae328cc4447a..f53dce4dcff9 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -314,7 +314,8 @@ tier1_gc_shenandoah = \ gc/shenandoah/compiler/ \ gc/shenandoah/mxbeans/ \ gc/shenandoah/TestSmallHeap.java \ - gc/shenandoah/oom/ + gc/shenandoah/oom/ \ + gtest/ShenandoahGtests.java tier2_gc_shenandoah = \ runtime/MemberName/MemberNameLeak.java \ diff --git a/test/hotspot/jtreg/compiler/arraycopy/TestDeadCloneMem.java b/test/hotspot/jtreg/compiler/arraycopy/TestDeadCloneMem.java new file mode 100644 index 000000000000..807f45f11f7a --- /dev/null +++ b/test/hotspot/jtreg/compiler/arraycopy/TestDeadCloneMem.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026 IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug JDK-8387015 + * @summary C2: crash with "named projection 2 not found" from ArrayCopyNode::finish_transform() for clone + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:CompileOnly=${test.main.class}::test1 + * -XX:CompileCommand=dontinline,${test.main.class}::notInlined -XX:+StressIGVN + * -XX:StressSeed=1324432947 ${test.main.class} + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:CompileOnly=${test.main.class}::test1 + * -XX:CompileCommand=dontinline,${test.main.class}::notInlined -XX:+StressIGVN + * ${test.main.class} + */ + +package compiler.arraycopy; + +public class TestDeadCloneMem { + private static int field; + + public static void main(String[] args) { + int[] array = new int[10]; + array.clone(); + Object o = new Object(); + test1(42, false); + } + + private static int test1(int flag, boolean flag2) { + int len; + if (flag != 42) { + if (flag2) { + field = 42; + } + int[] array2; + if (flag != 42) { + len = -1; + array2 = new int[4]; + } else { + len = 42; + array2 = new int[100]; + } + int[] array = new int[len]; + int length = array.length; + int i = 0; + do { + synchronized (new Object()) {} + notInlined(); + array2.clone(); + i++; + } while (i < 10); + return length; + } + return 0; + } + + private static void notInlined() { + + } +} diff --git a/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java b/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java index d0bce0246966..14c4f7b5a48e 100644 --- a/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java +++ b/test/hotspot/jtreg/compiler/c2/ReachabilityFenceTest.java @@ -38,7 +38,7 @@ * @summary Tests to ensure that reachabilityFence() correctly keeps objects from being collected prematurely. * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/othervm -Xbatch compiler.c2.ReachabilityFenceTest + * @run driver ${test.main.class} */ public class ReachabilityFenceTest { private static final int SIZE = 100; diff --git a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java index 5e6a757dd5f8..99143f04dcd3 100644 --- a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java +++ b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,7 @@ * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/timeout=480 compiler.c2.TestMergeStores aligned + * @run driver/timeout=480 ${test.main.class} aligned */ /* @@ -48,7 +48,7 @@ * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/timeout=480 compiler.c2.TestMergeStores unaligned + * @run driver/timeout=480 ${test.main.class} unaligned */ /* @@ -58,7 +58,7 @@ * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / - * @run main/timeout=480 compiler.c2.TestMergeStores StressIGVN + * @run driver/timeout=480 ${test.main.class} StressIGVN */ public class TestMergeStores { diff --git a/test/hotspot/jtreg/compiler/c2/TestMultiplyHighLowFusion.java b/test/hotspot/jtreg/compiler/c2/TestMultiplyHighLowFusion.java new file mode 100644 index 000000000000..31dd52bd3f64 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestMultiplyHighLowFusion.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8379327 + * @summary Verify correctness for combined low/high 64-bit multiplication patterns. + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.c2; + +import compiler.lib.generators.Generator; +import compiler.lib.generators.Generators; +import compiler.lib.ir_framework.*; +import java.math.BigInteger; + +public class TestMultiplyHighLowFusion { + private static final BigInteger MASK_64 = BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE); + private static final Generator LONG_GEN = Generators.G.longs(); + + public static void main(String[] args) { + TestFramework.run(); + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bMulHiLoL\\b", "1"}) + public static long doMath(long a, long b) { + long low = a * b; + long high = Math.multiplyHigh(a, b); + return low + high; + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bMulHiLoL\\b", "1"}) + public static long doMathSwapped(long a, long b) { + long low = b * a; + long high = Math.multiplyHigh(b, a); + return low + high; + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bUMulHiLoL\\b", "1"}) + public static long doUnsignedMath(long a, long b) { + long low = a * b; + long high = Math.unsignedMultiplyHigh(a, b); + return low + high; + } + + @Test + @IR(applyIfPlatform = {"x64", "true"}, phase = CompilePhase.PRINT_IDEAL, counts = {"\\bUMulHiLoL\\b", "1"}) + public static long doUnsignedMathSwapped(long a, long b) { + long low = b * a; + long high = Math.unsignedMultiplyHigh(b, a); + return low + high; + } + + @Run(test = {"doMath", "doMathSwapped", "doUnsignedMath", "doUnsignedMathSwapped"}) + public void runTests() { + verifyPair(LONG_GEN.next(), LONG_GEN.next()); + } + + private void verifyPair(long a, long b) { + long expectedSigned = expectedSigned(a, b); + long expectedUnsigned = expectedUnsigned(a, b); + + if (doMath(a, b) != expectedSigned) { + throw new RuntimeException("Signed mismatch for a=" + a + ", b=" + b); + } + if (doMathSwapped(a, b) != expectedSigned) { + throw new RuntimeException("Signed swapped mismatch for a=" + a + ", b=" + b); + } + if (doUnsignedMath(a, b) != expectedUnsigned) { + throw new RuntimeException("Unsigned mismatch for a=" + a + ", b=" + b); + } + if (doUnsignedMathSwapped(a, b) != expectedUnsigned) { + throw new RuntimeException("Unsigned swapped mismatch for a=" + a + ", b=" + b); + } + } + + private static long expectedSigned(long a, long b) { + BigInteger product = BigInteger.valueOf(a).multiply(BigInteger.valueOf(b)); + long low = product.longValue(); + long high = product.shiftRight(64).longValue(); + return low + high; + } + + private static long expectedUnsigned(long a, long b) { + BigInteger ua = BigInteger.valueOf(a).and(MASK_64); + BigInteger ub = BigInteger.valueOf(b).and(MASK_64); + BigInteger product = ua.multiply(ub); + long low = product.longValue(); + long high = product.shiftRight(64).longValue(); + return low + high; + } +} diff --git a/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java b/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java index 114f7f9bfab5..084f63279bfa 100644 --- a/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java +++ b/test/hotspot/jtreg/compiler/c2/aarch64/TestTrampoline.java @@ -89,15 +89,19 @@ private static void checkOutput(OutputAnalyzer output) { } static class Test { - private static void test(String s, int i) { + // Use a StringBuilder to avoid issues with String.charAt() not being + // inlined on Windows because its UTF-16 path was executed at startup + // but not enough for C2 to inline it. + private static void test(StringBuilder s, int i) { if (s.charAt(i) > 128) throw new RuntimeException(); } public static void main(String[] args) { - String s = "Returns the char value at the specified index."; + var sb = new StringBuilder(); + sb.append("Returns the char value at the specified index."); for (int i = 0; i < ITERATIONS_TO_HEAT_LOOP; ++i) { - test(s, i % s.length()); + test(sb, i % sb.length()); } } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java b/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java index ba7e7d851b08..66dabcebf808 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ConstructorBarriers.java @@ -31,7 +31,7 @@ * @summary Test barriers emitted in constructors * @library /test/lib / * @requires os.arch=="aarch64" | os.arch=="riscv64" | os.arch=="x86_64" | os.arch=="amd64" - * @run main compiler.c2.irTests.ConstructorBarriers + * @run driver ${test.main.class} */ public class ConstructorBarriers { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java index 5524b5d7b6c0..9556fce988d8 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestVectorizationMismatchedAccess.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2023, Red Hat, Inc. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,6 @@ import compiler.lib.ir_framework.*; import jdk.test.lib.Utils; -import jdk.test.whitebox.WhiteBox; import jdk.internal.misc.Unsafe; import java.util.Random; import java.util.Arrays; @@ -40,15 +39,12 @@ * @summary C2: vectorization fails on simple ByteBuffer loop * @modules java.base/jdk.internal.misc * @library /test/lib / - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.c2.irTests.TestVectorizationMismatchedAccess + * @run driver ${test.main.class} */ public class TestVectorizationMismatchedAccess { private static final Unsafe UNSAFE = Unsafe.getUnsafe(); private static final Random RANDOM = Utils.getRandomInstance(); - private final static WhiteBox wb = WhiteBox.getWhiteBox(); public static void main(String[] args) { TestFramework framework = new TestFramework(); diff --git a/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java b/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java index 8b3abbb03009..82bb79d3c1fa 100644 --- a/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java +++ b/test/hotspot/jtreg/compiler/c2/riscv64/TestIntegerReverse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,7 +30,7 @@ * * @library /test/lib / * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*zbkb.*" - * @run main/othervm compiler.c2.riscv64.TestIntegerReverse + * @run driver ${test.main.class} */ package compiler.c2.riscv64; diff --git a/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java b/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java index 01c3b871ffa2..807a58a18f37 100644 --- a/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java +++ b/test/hotspot/jtreg/compiler/c2/riscv64/TestLongReverse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,7 +30,7 @@ * * @library /test/lib / * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*zbkb.*" - * @run main/othervm compiler.c2.riscv64.TestLongReverse + * @run driver ${test.main.class} */ package compiler.c2.riscv64; diff --git a/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java b/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java new file mode 100644 index 000000000000..3d752cc74f52 --- /dev/null +++ b/test/hotspot/jtreg/compiler/controldependency/TestRemoveCastPPWithCMoveUse.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.controldependency; + +/* + * @test + * @bug 8385420 + * @summary C2 correctly handles the case when the removed CastPPNode has a CMove use. + * @run main ${test.main.class} + * @run main/othervm -Xbatch -XX:CompileOnly=${test.main.class}::test + * -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM ${test.main.class} + * + */ +public class TestRemoveCastPPWithCMoveUse { + public static void main(String[] args) { + for (int i = 0; i < 10_000; i++) { + test(null, false); + test(null, true); + test("", false); + test("", true); + } + } + + static int test(String a, boolean flag) { + StringBuilder sb = new StringBuilder(); + if (a == null) { + sb.append(""); + } else { + sb.append(flag ? a : ""); + } + return sb.length(); + } +} diff --git a/test/hotspot/jtreg/compiler/cpuflags/TestUseCountTrailingZerosInstruction.java b/test/hotspot/jtreg/compiler/cpuflags/TestUseCountTrailingZerosInstruction.java new file mode 100644 index 000000000000..0c7d04486f29 --- /dev/null +++ b/test/hotspot/jtreg/compiler/cpuflags/TestUseCountTrailingZerosInstruction.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8386656 + * @summary Verify no assertions when running with -XX:-UseCountTrailingZerosInstruction + * @requires os.simpleArch == "x64" + * @run main/othervm -Xbatch -XX:-UseCountTrailingZerosInstruction ${test.main.class} + */ + +/** + * @test + * @bug 8386656 + * @summary Verify no assertions when running with -XX:+UseCountTrailingZerosInstruction + * @requires os.simpleArch == "x64" + * @run main/othervm -Xbatch -XX:+UseCountTrailingZerosInstruction ${test.main.class} + */ + +package compiler.cpuflags; + +import java.util.Arrays; + +public class TestUseCountTrailingZerosInstruction { + public static void main(String[] args) { + byte[] a = new byte[32]; + byte[] b = new byte[32]; + for (int i = 0; i < 20_000; i++) { + Arrays.mismatch(a, b); + } + } +} + diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java new file mode 100644 index 000000000000..f33a16785d19 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestBCEscapeAnalyzerOverflow.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8216486 + * @summary Verify BCEscapeAnalyzer handles methods where + * (numblocks+1)*(max_stack+max_locals) overflows a 32-bit int. + * On a UBSAN build the signed overflow would be caught as UB; + * on a normal build the test verifies no crash from the bogus + * allocation size that resulted from the overflow. + * + * @requires vm.compiler2.enabled + * + * @run main/othervm -Xcomp -XX:-TieredCompilation + * compiler.escapeAnalysis.TestBCEscapeAnalyzerOverflow + */ + +package compiler.escapeAnalysis; + +import java.lang.classfile.ClassFile; +import java.lang.classfile.Label; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class TestBCEscapeAnalyzerOverflow { + + // Number of goto instructions in the generated method. + // Creates NUM_GOTOS + 1 basic blocks. With max_stack = 0xFFFF and + // max_locals = 0xFFFF the product (numblocks+1)*(max_stack+max_locals) + // is 16386 * 131070 = 2,147,713,020 which exceeds Integer.MAX_VALUE. + static final int NUM_GOTOS = 16384; + static final int TARGET_MAX_STACK = 0xFFFF; + static final int TARGET_MAX_LOCALS = 0xFFFF; + + static final ClassDesc CD_HELPER = + ClassDesc.of("compiler.escapeAnalysis.BCEscapeOverflowHelper"); + + public static void main(String[] args) throws Throwable { + byte[] classBytes = buildClass(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class cls = lookup.defineClass(classBytes); + + // caller() allocates an Object and passes it to bigMethod() via + // invokestatic. Under -Xcomp -XX:-TieredCompilation, C2 compiles + // caller() and invokes BCEscapeAnalyzer on bigMethod to determine + // whether the argument escapes. Without the fix the 32-bit + // overflow in iterate_blocks leads to undefined behavior. + var mh = lookup.findStatic(cls, "caller", + MethodType.methodType(void.class)); + mh.invoke(); + } + + /** + * Builds a minimal class (version 50, no StackMapTable needed) with: + * public static void bigMethod(Object o) -- pathological method + * public static void caller() -- calls bigMethod + * + * The ClassFile API generates the bytecode; max_stack and max_locals + * of bigMethod are then patched to the target overflow-triggering values. + */ + static byte[] buildClass() { + var mtd_Obj_void = MethodTypeDesc.of(ConstantDescs.CD_void, + ConstantDescs.CD_Object); + var mtd_void = MethodTypeDesc.of(ConstantDescs.CD_void); + + byte[] bytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS) + .build(CD_HELPER, cb -> { + cb.withVersion(50, 0); + cb.withFlags(ClassFile.ACC_PUBLIC | ClassFile.ACC_SUPER); + + // bigMethod(Object o): aload_0, pop, , return + cb.withMethod("bigMethod", mtd_Obj_void, + ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC, + mb -> mb.withCode(code -> { + code.aload(0); + code.pop(); + for (int i = 0; i < NUM_GOTOS; i++) { + Label next = code.newLabel(); + code.goto_(next); + code.labelBinding(next); + } + code.return_(); + })); + + // caller(): new Object → dup → invokespecial → + // invokestatic bigMethod → return + cb.withMethod("caller", mtd_void, + ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC, + mb -> mb.withCode(code -> { + code.new_(ConstantDescs.CD_Object); + code.dup(); + code.invokespecial(ConstantDescs.CD_Object, + "", mtd_void); + code.invokestatic(CD_HELPER, + "bigMethod", mtd_Obj_void); + code.return_(); + })); + }); + + patchBigMethodMaxes(bytes); + return bytes; + } + + /** + * Locates bigMethod's Code attribute and patches max_stack/max_locals + * to TARGET_MAX_STACK/TARGET_MAX_LOCALS. The ClassFile API computes + * small values (max_stack=1, max_locals=1); we inflate them to create + * the pathological overflow case. + * + * The Code attribute layout is: + * attribute_name_index(u2), attribute_length(u4), + * max_stack(u2), max_locals(u2), code_length(u4), code[...]... + * + * We search for bigMethod's unique code_length and patch the two u2 + * fields immediately before it. + */ + static void patchBigMethodMaxes(byte[] b) { + int expectedCodeLen = NUM_GOTOS * 3 + 3; + for (int i = 4; i <= b.length - 4; i++) { + int codeLen = ((b[i] & 0xFF) << 24) | ((b[i + 1] & 0xFF) << 16) + | ((b[i + 2] & 0xFF) << 8) | (b[i + 3] & 0xFF); + if (codeLen == expectedCodeLen) { + int ms = ((b[i - 4] & 0xFF) << 8) | (b[i - 3] & 0xFF); + int ml = ((b[i - 2] & 0xFF) << 8) | (b[i - 1] & 0xFF); + if (ms <= 2 && ml <= 2) { + b[i - 4] = (byte)(TARGET_MAX_STACK >>> 8); + b[i - 3] = (byte)(TARGET_MAX_STACK); + b[i - 2] = (byte)(TARGET_MAX_LOCALS >>> 8); + b[i - 1] = (byte)(TARGET_MAX_LOCALS); + return; + } + } + } + throw new RuntimeException("Could not find bigMethod Code attribute"); + } +} diff --git a/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java b/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java index 9be192d1f55c..026b2d15b771 100644 --- a/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java +++ b/test/hotspot/jtreg/compiler/exceptions/TestDebugDuringExceptionCatching.java @@ -43,7 +43,7 @@ * @library /test/lib /test/jdk/java/lang/invoke/common / * @build test.java.lang.invoke.lib.InstructionHelper * - * @run main/othervm ${test.main.class} + * @run driver ${test.main.class} */ public class TestDebugDuringExceptionCatching { diff --git a/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java b/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java index 5677ca88eb28..2b93c24e2552 100644 --- a/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java +++ b/test/hotspot/jtreg/compiler/hotcode/HotCodeCollectorMoveFunction.java @@ -79,6 +79,8 @@ static class Runner { private static final int C2_LEVEL = 4; private static final int FUNC_RUN_MILLIS = 60_000; + private static volatile int blackholeCount = 0; + static { try { method = Runner.class.getMethod("func"); @@ -111,7 +113,15 @@ private static void compileFunc() { public static void func() { long start = System.currentTimeMillis(); - while (System.currentTimeMillis() - start < FUNC_RUN_MILLIS) {} + while (System.currentTimeMillis() - start < FUNC_RUN_MILLIS) { + // Perform multiplicative LCG to ensure the compiler does not optimize away the code. + // Integer overflow is used for the modulus so the loop terminates after (2^32)/4 iterations + int num = 1; + do { + blackholeCount++; + num *= 69069; + } while (num != 1); + } } } } diff --git a/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java b/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java index c5ef16407211..ab40a2ea234d 100644 --- a/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java +++ b/test/hotspot/jtreg/compiler/igvn/CmpDisjointButNonOrderedRangesLong.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ * @summary Ranges can be proven to be disjoint but not orderable (thanks to unsigned range) * Comparing such values in such range with != should always be true. * @library /test/lib / - * @run main compiler.igvn.CmpDisjointButNonOrderedRangesLong + * @run driver ${test.main.class} */ package compiler.igvn; diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index becda83a0297..3dffa0965253 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -264,6 +264,10 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(BOOLEANS, "Boolean.logicalOr(", BOOLEANS, ", ", BOOLEANS, ")")); ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")")); + // ------------ Math ------------- + ops.add(Expression.make(LONGS, "Math.multiplyHigh(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(LONGS, "Math.unsignedMultiplyHigh(", LONGS, ", ", LONGS, ")")); + // TODO: Math and other classes. // Note: Math.copySign is non-deterministic because of NaN having encoding with sign bit set and unset. diff --git a/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java b/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java index 7e55353e0f75..ed65deb6c854 100644 --- a/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java +++ b/test/hotspot/jtreg/compiler/longcountedloops/TestShortRunningLongCountedLoop.java @@ -32,14 +32,11 @@ * @bug 8342692 * @summary C2: long counted loop/long range checks: don't create loop-nest for short running loops * @library /test/lib / - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.longcountedloops.TestShortRunningLongCountedLoop + * @run driver ${test.main.class} */ public class TestShortRunningLongCountedLoop { private static volatile int volatileField; - private final static WhiteBox wb = WhiteBox.getWhiteBox(); public static void main(String[] args) { // IR rules expect a single loop so disable unrolling @@ -351,8 +348,9 @@ public static void testLongLoopUnknownBoundsShortLoopFailedSpeculation_runner(Ru throw new RuntimeException("incorrect result: " + res); } } - wb.enqueueMethodForCompilation(info.getTest(), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); - if (!wb.isMethodCompiled(info.getTest())) { + WhiteBox whitebox = WhiteBox.getWhiteBox(); + whitebox.enqueueMethodForCompilation(info.getTest(), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!whitebox.isMethodCompiled(info.getTest())) { throw new RuntimeException("Should be compiled now"); } for (int i = 0; i < 10; i++) { diff --git a/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java b/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java index 9a68a2fcb771..143933ed6ea9 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestHasTruncationWrap.java @@ -27,14 +27,14 @@ * @summary Test CountedLoopConverter::has_truncation_wrap logic that checks if * a truncated iv (e.g. byte or char iv) is still a valid counted loop. * @library /test/lib / - * @run main ${test.main.class} + * @run driver ${test.main.class} */ /* * @test id=Xcomp * @bug 8385855 * @library /test/lib / - * @run main ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* + * @run driver ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* */ package compiler.loopopts; diff --git a/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java b/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java index 69f86a2bf1dd..f557a4911608 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestRedundantSafepointElimination.java @@ -30,7 +30,7 @@ * @bug 8347499 * @summary Tests that redundant safepoints can be eliminated in loops. * @library /test/lib / - * @run main compiler.loopopts.TestRedundantSafepointElimination + * @run driver ${test.main.class} */ public class TestRedundantSafepointElimination { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/compiler/parsing/TestInstanceOfImprovedKlassPtrType.java b/test/hotspot/jtreg/compiler/parsing/TestInstanceOfImprovedKlassPtrType.java new file mode 100644 index 000000000000..19214738dd60 --- /dev/null +++ b/test/hotspot/jtreg/compiler/parsing/TestInstanceOfImprovedKlassPtrType.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8387197 + * @summary Verify that improving klass_ptr_type in GraphKit::gen_instanceof() allows + * eliminating SubTypeCheckNode when the receiver implements an interface + * unrelated to the checked class. + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.parsing; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; + +public class TestInstanceOfImprovedKlassPtrType { + static abstract class B {} + static final class C extends B {} + + interface I {} + static class D implements I {} + static class E implements I {} + + public static void main(String[] args) { + TestFramework.run(); + } + + @DontInline + int testHelper2(Object o) { + return 1; + } + + @Test + @IR(counts = {IRNode.SUBTYPE_CHECK, "1"}, + phase = CompilePhase.AFTER_PARSING) + int test1(Object o) { + Object o1 = (I) o; + if (o1 instanceof B) { + return testHelper2(o1); + } else { + return 2; + } + } + + @Run(test = "test1") + @Warmup(0) + void runTest() { + int sum = 0; + Object[] arr = new Object[] {new C(), new D(), new E()}; + for (int i = 0; i < 3; i++){ + Object o = arr[i]; + if (o instanceof I) { + sum += test1(o); + } else { + sum += 3; + } + } + Asserts.assertEquals(sum, 7); + return; + } +} diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java b/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java index bec3e4424039..b0df68b209ad 100644 --- a/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java +++ b/test/hotspot/jtreg/compiler/rangechecks/TestFoldCompares.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,14 +27,14 @@ * @summary Test logic in IfNode::fold_compares, which folds 2 signed comparisons * into a single comparison. * @library /test/lib / - * @run main ${test.main.class} + * @run driver ${test.main.class} */ /* * @test id=Xcomp * @bug 8346420 * @library /test/lib / - * @run main ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* + * @run driver ${test.main.class} -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,${test.main.class}::test* */ package compiler.rangechecks; diff --git a/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java b/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java index b9f9343dd393..8f967fae5609 100644 --- a/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java +++ b/test/hotspot/jtreg/compiler/stable/LazyConstantsIrTest.java @@ -27,7 +27,7 @@ * @modules java.base/jdk.internal.lang * @library /test/lib / * @enablePreview - * @run main ${test.main.class} + * @run driver ${test.main.class} */ package compiler.stable; diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestMaskedNotAllOnes.java b/test/hotspot/jtreg/compiler/vectorapi/TestMaskedNotAllOnes.java new file mode 100644 index 000000000000..6abf88ed06f6 --- /dev/null +++ b/test/hotspot/jtreg/compiler/vectorapi/TestMaskedNotAllOnes.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8386163 + * @summary Checks there is no assertion failure with macro logic optimization when both inputs of not patterns are all one vectors + * @modules jdk.incubator.vector + * @library /test/lib / + * @run driver ${test.main.class} + */ + +package compiler.vectorapi; + +import compiler.lib.ir_framework.*; +import compiler.lib.verify.Verify; +import jdk.incubator.vector.IntVector; +import jdk.incubator.vector.VectorMask; +import jdk.incubator.vector.VectorOperators; +import jdk.incubator.vector.VectorSpecies; + +public class TestMaskedNotAllOnes { + + private static final VectorSpecies ISP = IntVector.SPECIES_PREFERRED; + private static final int VALUE = 1234567; + + public static void main(String[] args) { + TestFramework.runWithFlags("--add-modules=jdk.incubator.vector"); + } + + @Test + @Warmup(10000) + static int[] testMaskedDivNegOne() { + IntVector v = IntVector.broadcast(ISP, VALUE); + VectorMask mask = VectorMask.fromLong(ISP, -1L); + int[] out = new int[ISP.length()]; + v.div(-1, mask).intoArray(out, 0); + return out; + } + + static final int[] GOLD_DIV = testMaskedDivNegOne(); + + @Check(test = "testMaskedDivNegOne") + static void checkMaskedDivNegOne(int[] out) { + Verify.checkEQ(GOLD_DIV, out); + } + + @Test + @Warmup(10000) + static int[] testMaskedNotAllOnesVector() { + IntVector allOnes = IntVector.broadcast(ISP, -1); + VectorMask mask = VectorMask.fromLong(ISP, -1L); + int[] out = new int[ISP.length()]; + allOnes.lanewise(VectorOperators.NOT, mask).intoArray(out, 0); + return out; + } + + static final int[] GOLD_NOT = testMaskedNotAllOnesVector(); + + @Check(test = "testMaskedNotAllOnesVector") + static void checkMaskedNotAllOnesVector(int[] out) { + Verify.checkEQ(GOLD_NOT, out); + } +} diff --git a/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java b/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java index 79cde2f0d260..4c448564a877 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestRotateByteAndShortVector.java @@ -29,7 +29,7 @@ * @key randomness * @summary Test vectorization of rotate byte and short * @library /test/lib / - * @run main/othervm TestRotateByteAndShortVector + * @run driver ${test.main.class} */ import java.util.Random; diff --git a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java index 78dd4f50a064..e5a6966cdcf3 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorDoubleRandom.java @@ -31,7 +31,7 @@ * @library /test/lib / * @modules java.base/jdk.internal.math * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*rvv.*" - * @run main compiler.vectorization.TestRoundVectorDoubleRandom + * @run driver ${test.main.class} */ package compiler.vectorization; diff --git a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java index 474601346e89..92b6d3b98402 100644 --- a/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java +++ b/test/hotspot/jtreg/compiler/vectorization/TestRoundVectorFloatRandom.java @@ -31,7 +31,7 @@ * @library /test/lib / * @modules java.base/jdk.internal.math * @requires os.arch == "riscv64" & vm.cpu.features ~= ".*rvv.*" - * @run main compiler.vectorization.TestRoundVectorFloatRandom + * @run driver ${test.main.class} */ package compiler.vectorization; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java index 48b2ff754ad2..f1140533d25b 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayCopyTest.java @@ -24,18 +24,10 @@ /* * @test * @summary Vectorization test on array copy + * @requires vm.compiler2.enabled * @library /test/lib / * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayCopyTest - * - * @requires vm.compiler2.enabled + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java index 8d0ba2be589e..3708fc87f290 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayIndexFillTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,18 +26,10 @@ * @test * @summary Vectorization test on array index fill * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayIndexFillTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java index b7044b1c79da..90e4955bee3f 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayInvariantFillTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * 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,24 +26,11 @@ * @test * @summary Vectorization test on array invariant fill * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:-OptimizeFill - * compiler.vectorization.runner.ArrayInvariantFillTest - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:+OptimizeFill - * compiler.vectorization.runner.ArrayInvariantFillTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} NoOptimizeFill + * @run driver ${test.main.class} OptimizeFill */ package compiler.vectorization.runner; @@ -68,11 +56,22 @@ public ArrayInvariantFillTest() { doubleInv = ran.nextDouble(); } + // We must pass the flags directly to the Test VM, and not the Driver VM in the @run above. + @Override + protected String[] testVMFlags(String[] args) { + return switch (args[0]) { + case "NoOptimizeFill" -> new String[]{"-XX:-OptimizeFill"}; + case "OptimizeFill" -> new String[]{"-XX:+OptimizeFill"}; + default -> throw new RuntimeException("Test argument not recognized: " + args[0]); + }; + } + // ---------------- Simple Fill ---------------- @Test - @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, - applyIf = {"OptimizeFill", "false"}, - counts = {IRNode.REPLICATE_B, ">0"}) + // TODO 8387402 + //@IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, + // applyIf = {"OptimizeFill", "false"}, + // counts = {IRNode.REPLICATE_B, ">0"}) @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, applyIf = {"OptimizeFill", "true"}, counts = {IRNode.REPLICATE_B, "0"}) diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java index e2d28cbf0832..2699afda5ccf 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayShiftOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -28,18 +28,10 @@ * @bug 8183390 8332905 * @summary Vectorization test on bug-prone shift operation * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayShiftOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java index f9c5f6199f1b..d6f2febb06f2 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayTypeConvertTest.java @@ -27,33 +27,12 @@ * @bug 8183390 8340010 8342095 * @summary Vectorization test on array type conversions * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * * @requires vm.compiler2.enabled * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest nCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest nCOH_yAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest yCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayTypeConvertTest yCOH_yAV + * @run driver ${test.main.class} nCOH_nAV + * @run driver ${test.main.class} nCOH_yAV + * @run driver ${test.main.class} yCOH_nAV + * @run driver ${test.main.class} yCOH_yAV */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java index 8b4513b8490e..f6874a03ffbc 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/ArrayUnsafeOpTest.java @@ -25,17 +25,9 @@ * @test * @summary Vectorization test on array unsafe operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.ArrayUnsafeOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java index ba82013e1826..3a61b3658007 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicBooleanOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,17 +27,9 @@ * @summary Vectorization test on basic boolean operations * @requires vm.opt.StressUnstableIfTraps == null | !vm.opt.StressUnstableIfTraps * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicBooleanOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java index a336b32f7b9f..acbf44c471cc 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicByteOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,19 +26,9 @@ * @test * @summary Vectorization test on basic byte operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:CompileCommand=CompileOnly,compiler.vectorization.runner.BasicByteOpTest::* - * -XX:LoopUnrollLimit=1000 - * compiler.vectorization.runner.BasicByteOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; @@ -64,6 +54,12 @@ public BasicByteOpTest() { } } + // We must pass the flags directly to the test-VM, and not the driver vm in the @run above. + @Override + protected String[] testVMFlags(String[] args) { + return new String[]{"-XX:CompileCommand=CompileOnly,compiler.vectorization.runner.BasicByteOpTest::*", "-XX:LoopUnrollLimit=1000"}; + } + // ---------------- Arithmetic ---------------- @Test @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java index 4211d5eec5e8..be462f0be16e 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicCharOpTest.java @@ -25,17 +25,9 @@ * @test * @summary Vectorization test on basic char operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicCharOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java index 8d5925ec8c6c..1adb89591a50 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicDoubleOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -27,18 +27,10 @@ * @test * @summary Vectorization test on basic double operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicDoubleOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java index b89d068d8af1..870b8746bafa 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicFloatOpTest.java @@ -25,18 +25,10 @@ * @test * @summary Vectorization test on basic float operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicFloatOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java index e096f7878aba..8849418e6099 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicIntOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,17 +26,9 @@ * @test * @summary Vectorization test on basic int operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicIntOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java index a67670549589..5404d943bbc6 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicLongOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,18 +26,10 @@ * @test * @summary Vectorization test on basic long operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicLongOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java index b957a00278a2..4c7221dea527 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/BasicShortOpTest.java @@ -27,17 +27,9 @@ * @bug 8183390 8342095 * @summary Vectorization test on basic short operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.BasicShortOpTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java index c8a3c71bdee6..27058012f361 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopArrayIndexComputeTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,26 +26,13 @@ * @test * @summary Vectorization test on loop array index computation * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest nAV_ySAC - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest yAV_ySAC - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest nAV_nSAC - * - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopArrayIndexComputeTest yAV_nSAC - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} nAV_ySAC + * @run driver ${test.main.class} yAV_ySAC + * @run driver ${test.main.class} nAV_nSAC + * @run driver ${test.main.class} yAV_nSAC */ package compiler.vectorization.runner; @@ -60,10 +47,10 @@ public class LoopArrayIndexComputeTest extends VectorizationTestRunner { @Override protected String[] testVMFlags(String[] args) { return switch (args[0]) { - case "nAV_ySAC" -> new String[]{"-XX:-AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; - case "yAV_ySAC" -> new String[]{"-XX:+AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; - case "nAV_nSAC" -> new String[]{"-XX:-AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; - case "yAV_nSAC" -> new String[]{"-XX:+AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; + case "nAV_ySAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:-AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; + case "yAV_ySAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:+AlignVector", "-XX:+UseAutoVectorizationSpeculativeAliasingChecks"}; + case "nAV_nSAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:-AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; + case "yAV_nSAC" -> new String[]{"-XX:+UnlockDiagnosticVMOptions", "-XX:+AlignVector", "-XX:-UseAutoVectorizationSpeculativeAliasingChecks"}; default -> { throw new RuntimeException("Test argument not recognized: " + args[0]); } }; } diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java index c46b2e116128..714de5b3c6b2 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopCombinedOpTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,33 +26,12 @@ * @test * @summary Vectorization test on combined operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * * @requires vm.compiler2.enabled * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest nCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest nCOH_yAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest yCOH_nAV - * - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopCombinedOpTest yCOH_yAV + * @run driver ${test.main.class} nCOH_nAV + * @run driver ${test.main.class} nCOH_yAV + * @run driver ${test.main.class} yCOH_nAV + * @run driver ${test.main.class} yCOH_yAV */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java index e36e4097813c..513269569836 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopControlFlowTest.java @@ -25,17 +25,9 @@ * @test * @summary Vectorization test on simple control flow in loop * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopControlFlowTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java index 06a3eb33bc32..cad2af04a9be 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopLiveOutNodesTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,17 +26,9 @@ * @test * @summary Vectorization test on loops with live out nodes * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopLiveOutNodesTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java index 2db565461ac5..a36d11198e73 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopRangeStrideTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,18 +26,10 @@ * @test * @summary Vectorization test on different loop ranges and strides * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopRangeStrideTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java index 546d99f5cce3..9b9dcb03f6ef 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/LoopReductionOpTest.java @@ -25,19 +25,10 @@ * @test * @summary Vectorization test on reduction operations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.LoopReductionOpTest - * * @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64") | (os.simpleArch == "riscv64") * @requires vm.compiler2.enabled * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java index 4dbfba02a430..4be74d207331 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/MultipleLoopsTest.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. - * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,17 +26,9 @@ * @test * @summary Vectorization test on multiple loops in a method * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * compiler.vectorization.runner.MultipleLoopsTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java b/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java index dbc999647ad0..347571fc95b5 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/StripMinedLoopTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * 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,18 +26,9 @@ * @test * @summary Vectorization test with small strip mining iterations * @library /test/lib / - * - * @build jdk.test.whitebox.WhiteBox - * compiler.vectorization.runner.VectorizationTestRunner - * - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions - * -XX:+WhiteBoxAPI - * -XX:LoopStripMiningIter=10 - * compiler.vectorization.runner.StripMinedLoopTest - * * @requires vm.compiler2.enabled + * + * @run driver ${test.main.class} */ package compiler.vectorization.runner; @@ -59,6 +51,12 @@ public StripMinedLoopTest() { } } + // We must pass the flags directly to the Test VM, and not the Driver VM in the @run above. + @Override + protected String[] testVMFlags(String[] args) { + return new String[]{"-XX:LoopStripMiningIter=10"}; + } + @Test @IR(applyIfCPUFeatureOr = {"asimd", "true", "sse2", "true", "rvv", "true"}, counts = {IRNode.STORE_VECTOR, ">0"}) diff --git a/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java b/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java index 7f8e4ec3b395..9adebf30d311 100644 --- a/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java +++ b/test/hotspot/jtreg/compiler/vectorization/runner/VectorizationTestRunner.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, 2023, Arm Limited. All rights reserved. + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,14 +30,23 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import jdk.test.lib.Utils; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.ProcessTools; import jdk.test.whitebox.WhiteBox; public class VectorizationTestRunner { - private static final WhiteBox WB = WhiteBox.getWhiteBox(); + private static final String VERIFY_CORRECTNESS_ARG = "--verify-vectorization-correctness"; + + private static class Flags { + private static final WhiteBox WHITEBOX = WhiteBox.getWhiteBox(); + } private static final int COMP_LEVEL_INTP = 0; private static final int COMP_LEVEL_C2 = 4; @@ -52,6 +62,35 @@ protected void run(String[] args) { // invokes it twice - first time in the interpreter and second time compiled // by C2. Then this runner compares the two return values. Hence we require // each test method returning a primitive value or an array of primitive type. + runCorrectnessTestsInTestVM(args); + + // 2) Vectorization ability test + // To test vectorizability, invoke the IR test framework to check existence of + // expected C2 IR node. + TestFramework irTest = new TestFramework(klass); + irTest.addFlags(testVMFlags(args)); + irTest.start(); + } + + private void runCorrectnessTestsInTestVM(String[] args) { + List command = new ArrayList<>(); + command.addAll(Arrays.asList(testVMFlags(args))); + command.add("-Xbootclasspath/a:."); + command.add("-XX:+UnlockDiagnosticVMOptions"); + command.add("-XX:+WhiteBoxAPI"); + command.add(getClass().getName()); + command.add(VERIFY_CORRECTNESS_ARG); + command.add(getClass().getName()); + try { + ClassFileInstaller.main("jdk.test.whitebox.WhiteBox"); + ProcessTools.executeTestJava(command).shouldHaveExitValue(0); + } catch (Exception e) { + throw new RuntimeException("Vectorization correctness test failed", e); + } + } + + private void runCorrectnessTests() { + Class klass = getClass(); for (Method method : klass.getDeclaredMethods()) { try { if (method.isAnnotationPresent(Test.class)) { @@ -63,13 +102,6 @@ protected void run(String[] args) { "." + method.getName() + ": " + e.getMessage()); } } - - // 2) Vectorization ability test - // To test vectorizability, invoke the IR test framework to check existence of - // expected C2 IR node. - TestFramework irTest = new TestFramework(klass); - irTest.addFlags(testVMFlags(args)); - irTest.start(); } // Override this to add extra flags. @@ -111,20 +143,20 @@ private void runTestOnMethod(Method method) throws InterruptedException { // Temporarily disable the compiler and invoke the method to get reference // result from the interpreter - WB.setBooleanVMFlag("UseCompiler", false); + Flags.WHITEBOX.setBooleanVMFlag("UseCompiler", false); try { expected = method.invoke(this); } catch (Exception e) { e.printStackTrace(); fail("Exception is thrown in test method invocation (interpreter)."); } - assert(WB.getMethodCompilationLevel(method) == COMP_LEVEL_INTP); - WB.setBooleanVMFlag("UseCompiler", true); + assert(Flags.WHITEBOX.getMethodCompilationLevel(method) == COMP_LEVEL_INTP); + Flags.WHITEBOX.setBooleanVMFlag("UseCompiler", true); // Compile the method and invoke it again long enqueueTime = System.currentTimeMillis(); - WB.enqueueMethodForCompilation(method, COMP_LEVEL_C2); - while (WB.getMethodCompilationLevel(method) != COMP_LEVEL_C2) { + Flags.WHITEBOX.enqueueMethodForCompilation(method, COMP_LEVEL_C2); + while (Flags.WHITEBOX.getMethodCompilationLevel(method) != COMP_LEVEL_C2) { Thread.sleep(100 /*ms*/); } try { @@ -133,7 +165,7 @@ private void runTestOnMethod(Method method) throws InterruptedException { e.printStackTrace(); fail("Exception is thrown in test method invocation (C2)."); } - assert(WB.getMethodCompilationLevel(method) == COMP_LEVEL_C2); + assert(Flags.WHITEBOX.getMethodCompilationLevel(method) == COMP_LEVEL_C2); // Check if two invocations return the same Class retType = method.getReturnType(); @@ -172,11 +204,10 @@ private void runTestOnMethod(Method method) throws InterruptedException { } private static VectorizationTestRunner createTestInstance(String testName) { - if (!testName.toLowerCase().endsWith(".java")) { - fail("Invalid test file name " + testName); + if (testName.toLowerCase().endsWith(".java")) { + testName = testName.substring(0, testName.length() - 5); + testName = testName.replace('/', '.'); } - testName = testName.substring(0, testName.length() - 5); - testName = testName.replace('/', '.'); VectorizationTestRunner instance = null; try { @@ -196,7 +227,13 @@ private static void fail(String reason) { } public static void main(String[] args) { - VectorizationTestRunner testObj = createTestInstance(Utils.TEST_NAME); + VectorizationTestRunner testObj; + if (args.length > 0 && args[0].equals(VERIFY_CORRECTNESS_ARG)) { + testObj = createTestInstance(args[1]); + testObj.runCorrectnessTests(); + return; + } + testObj = createTestInstance(Utils.TEST_NAME); testObj.run(args); } } diff --git a/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java b/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java index 014181b2fce1..6be05def1080 100644 --- a/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java +++ b/test/hotspot/jtreg/gc/arguments/TestNewRatioFlag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -167,13 +167,13 @@ public static void verifyG1NewRatio(int expectedRatio) { long maxOld = HeapRegionUsageTool.getOldUsage().getMax(); int regionSize = wb.g1RegionSize(); - int youngListLength = (int) ((initEden + initSurv) / regionSize); + int numYoungRegions = (int) ((initEden + initSurv) / regionSize); int maxRegions = (int) (maxOld / regionSize); - int expectedYoungListLength = (int) (maxRegions / (double) (expectedRatio + 1)); + int expectedNumYoungRegions = (int) (maxRegions / (double) (expectedRatio + 1)); - if (youngListLength != expectedYoungListLength) { - throw new RuntimeException("Expected G1 young list length is: " + expectedYoungListLength - + ", but observed young list length is: " + youngListLength); + if (numYoungRegions != expectedNumYoungRegions) { + throw new RuntimeException("Expected G1 number of young regions is: " + expectedNumYoungRegions + + ", but observed number of young regions is: " + numYoungRegions); } } } diff --git a/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java b/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java index bdf8186bfaa9..36b85e8c09de 100644 --- a/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java +++ b/test/hotspot/jtreg/gc/arguments/TestSurvivorRatioFlag.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -116,7 +116,7 @@ public static void main(String args[]) throws Exception { * Depending on selected young GC we verify that: * - for DefNew and ParNew: eden_size / survivor_size is close to expectedRatio; * - for PSNew: survivor_size equal to young_gen_size / expectedRatio; - * - for G1: survivor_regions <= young_list_length / expectedRatio. + * - for G1: survivor_regions <= num_young_regions / expectedRatio. */ public static Void verifySurvivorRatio(int expectedRatio) { GCTypes.YoungGCType type = GCTypes.YoungGCType.getYoungGCType(); @@ -166,8 +166,8 @@ private static void verifyG1SurvivorRatio(int expectedRatio) { MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage(); int regionSize = wb.g1RegionSize(); - int youngListLength = (int) Math.max(NEW_SIZE / regionSize, 1); - int expectedSurvivorRegions = (int) Math.ceil(youngListLength / (double) expectedRatio); + int numYoungRegions = (int) Math.max(NEW_SIZE / regionSize, 1); + int expectedSurvivorRegions = (int) Math.ceil(numYoungRegions / (double) expectedRatio); int observedSurvivorRegions = (int) (survivorUsage.getCommitted() / regionSize); if (expectedSurvivorRegions < observedSurvivorRegions) { diff --git a/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java b/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java index 34a221b10255..ca34707bf12b 100644 --- a/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java +++ b/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java @@ -310,8 +310,8 @@ public static void allocateMemory(double ratio, long maxSize) throws Exception { */ public static long getMaxSurvivorSize() { if (GCTypes.YoungGCType.getYoungGCType() == GCTypes.YoungGCType.G1) { - int youngLength = (int) Math.max(MAX_NEW_SIZE / wb.g1RegionSize(), 1); - return (long) Math.ceil(youngLength / (double) SURVIVOR_RATIO) * wb.g1RegionSize(); + int numYoungRegions = (int) Math.max(MAX_NEW_SIZE / wb.g1RegionSize(), 1); + return (long) Math.ceil(numYoungRegions / (double) SURVIVOR_RATIO) * wb.g1RegionSize(); } else { return HeapRegionUsageTool.getSurvivorUsage().getMax(); } diff --git a/test/hotspot/jtreg/gtest/ShenandoahGtests.java b/test/hotspot/jtreg/gtest/ShenandoahGtests.java new file mode 100644 index 000000000000..1e8c404fc129 --- /dev/null +++ b/test/hotspot/jtreg/gtest/ShenandoahGtests.java @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/* @test + * @summary Run Shenandoah gtests + * @library /test/lib + * @requires vm.gc.Shenandoah + * @requires vm.debug + * @run main/native GTestWrapper --gtest_filter=Shenandoah* + */ diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java b/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java new file mode 100644 index 000000000000..1f2fec74feef --- /dev/null +++ b/test/hotspot/jtreg/runtime/ErrorHandling/TestAVXRegisterDump.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test that YMM and ZMM registers are correctly dumped in hs_err for different UseAVX settings + * @library /test/lib + * @requires os.family == "linux" & os.arch == "amd64" + * @requires vm.debug == true + * @modules java.base/jdk.internal.misc + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run driver TestAVXRegisterDump + */ + +// Note: this test can only run on debug since it relies on VMError::controlled_crash() which +// only exists in debug builds. + +import java.io.File; +import java.util.regex.Pattern; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.whitebox.WhiteBox; + +public class TestAVXRegisterDump { + + public static void main(String[] args) throws Exception { + + if (args.length > 0 && args[0].equals("crash")) { + WhiteBox.getWhiteBox().controlledCrash(2); + throw new RuntimeException("Still alive?"); + } + + // Test UseAVX=1 (XMM only) + testWithUseAVX(1); + + // Test UseAVX=2 (YMM) + testWithUseAVX(2); + + // Test UseAVX=3 (ZMM + K masks if available) + testWithUseAVX(3); + } + + static void testWithUseAVX(int useAVX) throws Exception { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:UseAVX=" + useAVX, + "-XX:-CreateCoredumpOnCrash", + "-Xmx100M", + TestAVXRegisterDump.class.getName(), "crash"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*"); + + File hsErrFile = HsErrFileUtils.openHsErrFileFromOutput(output); + validateRegisterContent(hsErrFile, useAVX); + } + + static Pattern[] createRegisterPatterns(String regType, int count) { + Pattern[] patterns = new Pattern[count]; + for (int i = 0; i < count; i++) { + // Create regex pattern to match entire register line (e.g., "XMM[0]=0xHEX 0xHEX") + // Used with Matcher.matches() which requires matching the entire line + patterns[i] = Pattern.compile(regType + "\\[" + i + "\\]=.*"); + } + return patterns; + } + + static void validateRegisterContent(File hsErrFile, int useAVX) throws Exception { + if (useAVX == 1) { + validateRegistersUseAVX1(hsErrFile); + } else if (useAVX == 2) { + validateRegistersUseAVX2(hsErrFile); + } else if (useAVX == 3) { + validateRegistersUseAVX3(hsErrFile); + } + } + + static void validateRegistersUseAVX1(File hsErrFile) throws Exception { + // UseAVX=1: XMM registers only (0-15) + Pattern[] positivePatterns = createRegisterPatterns("XMM", 16); + Pattern[] negativePatterns = new Pattern[] { + Pattern.compile("YMM\\[.*\\]=.*"), + Pattern.compile("ZMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, positivePatterns, negativePatterns, false, false); + } + + static void validateRegistersUseAVX2(File hsErrFile) throws Exception { + // UseAVX=2: YMM registers only (0-15) + Pattern[] positivePatterns = createRegisterPatterns("YMM", 16); + Pattern[] negativePatterns = new Pattern[] { + Pattern.compile("XMM\\[.*\\]=.*"), + Pattern.compile("ZMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, positivePatterns, negativePatterns, false, false); + } + + static void validateRegistersUseAVX3(File hsErrFile) throws Exception { + // UseAVX=3: ZMM + K masks (if available) or fallback to YMM + // Try ZMM first, then fallback to YMM if CPU doesn't support AVX-512 + try { + Pattern[] zmmPatterns = createRegisterPatterns("ZMM", 32); + Pattern[] zmmNegativePatterns = new Pattern[] { + Pattern.compile("XMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, zmmPatterns, zmmNegativePatterns, false, false); + + Pattern[] kPatterns = createRegisterPatterns("K", 8); + HsErrFileUtils.checkHsErrFileContent(hsErrFile, kPatterns, null, false, false); + } catch (RuntimeException e) { + // If ZMM not found, try YMM + Pattern[] ymmPatterns = createRegisterPatterns("YMM", 16); + Pattern[] ymmNegativePatterns = new Pattern[] { + Pattern.compile("XMM\\[.*\\]=.*"), + }; + HsErrFileUtils.checkHsErrFileContent(hsErrFile, ymmPatterns, ymmNegativePatterns, false, false); + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java b/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java index eacc508ac1cd..9c5108ba82e3 100644 --- a/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java +++ b/test/hotspot/jtreg/runtime/cds/TestCDSVMCrash.java @@ -64,7 +64,7 @@ public static void main(String[] args) throws Exception { throw new Error("Expected VM to crash"); } catch(RuntimeException e) { if (!e.getMessage().contains("A fatal error has been detected")) { - throw new Error("Expected message: A fatal error has been detected"); + throw new Error("Expected message: A fatal error has been detected. Instead message is: " + e.getMessage()); } } System.out.println("PASSED"); diff --git a/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java b/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java index 11e43b8d6309..f93bfb8b3a3e 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/compiler/PerfMapTest.java @@ -75,6 +75,7 @@ public void run(CommandExecutor executor, String cmd, Path path) { // Sanity check the file contents boolean sawCallStub = false; + boolean sawVtableStub = false; try { for (String entry : Files.readAllLines(path)) { Matcher m = LINE_PATTERN.matcher(entry); @@ -82,12 +83,17 @@ public void run(CommandExecutor executor, String cmd, Path path) { if (m.group(3).contains("StubRoutines call_stub")) { sawCallStub = true; } + if (m.group(3).contains("vtable stub [")) { + sawVtableStub = true; + } } } catch (IOException e) { Assert.fail(e.toString()); } Assert.assertTrue(sawCallStub, "Expected StubRoutines call_stub entry in " + path); + Assert.assertTrue(sawVtableStub, + "Expected vtable stub entry in " + path); } @Test diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java index 7330f4c061d3..115326567d33 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadTest/VThreadTest.java @@ -33,13 +33,13 @@ import java.util.concurrent.*; public class VThreadTest { - private static final String agentLib = "VThreadTest"; - static final int MSG_COUNT = 10*1000; static final SynchronousQueue QUEUE = new SynchronousQueue<>(); static native boolean check(); + static void log(String msg) { System.out.println(msg); } + static void producer(String msg) throws InterruptedException { int ii = 1; long ll = 2*(long)ii; @@ -54,7 +54,11 @@ static void producer(String msg) throws InterruptedException { for (int i = 0; i < MSG_COUNT; i++) { producer("msg: "); } - } catch (InterruptedException e) { } + } catch (Throwable t) { + t.printStackTrace(System.out); + log("VThreadTest failed: PRODUCER caught a throwable: " + t); + System.exit(1); + } }; static final Runnable CONSUMER = () -> { @@ -62,7 +66,11 @@ static void producer(String msg) throws InterruptedException { for (int i = 0; i < MSG_COUNT; i++) { String s = QUEUE.take(); } - } catch (InterruptedException e) { } + } catch (Throwable t) { + t.printStackTrace(System.out); + log("VThreadTest failed: CONSUMER caught a throwable: " + t); + System.exit(1); + } }; public static void test1() throws Exception { @@ -80,14 +88,6 @@ void runTest() throws Exception { } public static void main(String[] args) throws Exception { - try { - System.loadLibrary(agentLib); - } catch (UnsatisfiedLinkError ex) { - System.err.println("Failed to load " + agentLib + " lib"); - System.err.println("java.library.path: " + System.getProperty("java.library.path")); - throw ex; - } - VThreadTest obj = new VThreadTest(); obj.runTest(); } diff --git a/test/jaxp/TEST.ROOT b/test/jaxp/TEST.ROOT index ddf29839e205..695645315d83 100644 --- a/test/jaxp/TEST.ROOT +++ b/test/jaxp/TEST.ROOT @@ -23,7 +23,7 @@ modules=java.xml groups=TEST.groups # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 2fe601815b0e..79a3830447be 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -655,6 +655,8 @@ jdk/jfr/event/oldobject/TestZ.java 8375615 generic- # jdk_foreign +java/foreign/normalize/TestNormalize.java 8386848 generic-all + ############################################################################ # Client manual tests diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT index 7048aafc638f..08bc31ffdb8c 100644 --- a/test/jdk/TEST.ROOT +++ b/test/jdk/TEST.ROOT @@ -120,7 +120,7 @@ requires.properties= \ jdk.static # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them diff --git a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh index f2412a92a43f..af2f3f8b3f10 100644 --- a/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh +++ b/test/jdk/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -52,7 +52,7 @@ case "$OS" in OS="Windows" FS="\\" ;; - CYGWIN* | MSYS* | MINGW*) + CYGWIN*) PS=";" OS="Windows" FS="\\" diff --git a/test/jdk/java/lang/runtime/ObjectMethodsTest.java b/test/jdk/java/lang/runtime/ObjectMethodsTest.java index d7ca5912273b..56be7008f410 100644 --- a/test/jdk/java/lang/runtime/ObjectMethodsTest.java +++ b/test/jdk/java/lang/runtime/ObjectMethodsTest.java @@ -79,6 +79,7 @@ static class Empty { } static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + static final MethodHandles.Lookup UNPRIVILEGED_LOOKUP = LOOKUP.dropLookupMode(MethodHandles.Lookup.PRIVATE); @Test public void testEqualsC() throws Throwable { @@ -184,6 +185,9 @@ void commonExceptions(NamePlusType npt) { assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null, type, C.class, "x;y", C.ACCESSORS)); assertThrows(NPE, () -> ObjectMethods.bootstrap(null, name, type, C.class, "x;y", C.ACCESSORS)); + // Unprivileged lookup + assertThrows(IAE, () -> ObjectMethods.bootstrap(UNPRIVILEGED_LOOKUP, name, type, C.class, "x;y", C.ACCESSORS)); + // Bad indy call receiver type - change C to this test class assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type.changeParameterType(0, this.getClass()), C.class, "x;y", C.ACCESSORS)); diff --git a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java index 061ce2ae2412..76bfe7616f69 100644 --- a/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java +++ b/test/jdk/java/lang/runtime/SwitchBootstrapsTest.java @@ -42,40 +42,25 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -/** +/* * @test * @bug 8318144 * @enablePreview * @compile SwitchBootstrapsTest.java - * @run junit/othervm SwitchBootstrapsTest + * @run junit SwitchBootstrapsTest */ public class SwitchBootstrapsTest { - public static final MethodHandle BSM_TYPE_SWITCH; - public static final MethodHandle BSM_ENUM_SWITCH; - - static { - try { - BSM_TYPE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "typeSwitch", - MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class)); - BSM_ENUM_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "enumSwitch", - MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class)); - } - catch (ReflectiveOperationException e) { - throw new AssertionError("Should not happen", e); - } - } - private void testType(Object target, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); - MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker(); assertEquals(result, (int) indy.invoke(target, start)); assertEquals(-1, (int) indy.invoke(null, start)); } private void testPrimitiveType(Object target, Class targetType, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, targetType, int.class); - MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker(); assertEquals(result, (int) indy.invoke(target, start)); } @@ -85,7 +70,7 @@ private void testEnum(Enum target, int start, int result, Object... labels) t private void testEnum(Class targetClass, Enum target, int start, int result, Object... labels) throws Throwable { MethodType switchType = MethodType.methodType(int.class, targetClass, int.class); - MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker(); assertEquals(result, (int) indy.invoke(target, start)); assertEquals(-1, (int) indy.invoke(null, start)); } @@ -188,7 +173,7 @@ public void testEnums() throws Throwable { //null invocation name: MethodType switchType = MethodType.methodType(int.class, E1.class, int.class); - MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), null, switchType)).dynamicInvoker(); + MethodHandle indy = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), null, switchType).dynamicInvoker(); assertEquals(0, (int) indy.invoke(E1.A, 0)); } @@ -229,7 +214,7 @@ public void testWrongSwitchTypes() throws Throwable { }; for (MethodType switchType : switchTypes) { assertThrows(IllegalArgumentException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType) + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType) ); } MethodType[] enumSwitchTypes = new MethodType[] { @@ -240,7 +225,7 @@ public void testWrongSwitchTypes() throws Throwable { }; for (MethodType enumSwitchType : enumSwitchTypes) { assertThrows(IllegalArgumentException.class, () -> - BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType) + SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", enumSwitchType) ); } } @@ -270,23 +255,23 @@ enum E {A, B, C} public void testNullLabels() throws Throwable { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); assertThrows(NullPointerException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, (Object[]) null) + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, (Object[]) null) ); assertThrows(IllegalArgumentException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, new Object[] {1, null, String.class}) ); MethodType enumSwitchType = MethodType.methodType(int.class, E1.class, int.class); assertThrows(NullPointerException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null) + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null) ); assertThrows(IllegalArgumentException.class, () -> - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", enumSwitchType, new Object[] {1, null, String.class}) ); //null invocationName is OK: - BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), null, switchType, - new Object[] {Object.class}); + SwitchBootstraps.typeSwitch(MethodHandles.lookup(), null, switchType, + Object.class); } private static AtomicBoolean enumInitialized = new AtomicBoolean(); @@ -304,7 +289,7 @@ enum E { MethodType enumSwitchType = MethodType.methodType(int.class, E.class, int.class); - CallSite invocation = (CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, new Object[] {"A"}); + CallSite invocation = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", enumSwitchType, "A"); assertFalse(enumInitialized.get()); assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0)); assertFalse(enumInitialized.get()); @@ -330,7 +315,7 @@ enum E { EnumDesc.of(ClassDesc.of(E.class.getName()), "A"), "test" }; - CallSite invocation = (CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels); + CallSite invocation = (CallSite) SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels); assertFalse(enumInitialized.get()); assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0)); assertFalse(enumInitialized.get()); @@ -402,21 +387,37 @@ private static byte[] createClass() { } @Test - public void testNullLookup() throws Throwable { + public void testNullLookup() { assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); - BSM_TYPE_SWITCH.invoke(null, "", switchType, Object.class); + SwitchBootstraps.typeSwitch(null, "", switchType, Object.class); }); enum E {} assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, E.class, int.class); - BSM_ENUM_SWITCH.invoke(null, "", switchType, - new Object[] {}); + SwitchBootstraps.enumSwitch(null, "", switchType); }); assertThrows(NullPointerException.class, () -> { MethodType switchType = MethodType.methodType(int.class, E.class, int.class); - BSM_ENUM_SWITCH.invoke(null, "", switchType, - new Object[] {"A"}); + SwitchBootstraps.enumSwitch(null, "", switchType, "A"); + }); + } + + @Test + public void testUnprivilegedLookup() { + var lookup = MethodHandles.lookup().dropLookupMode(MethodHandles.Lookup.PRIVATE); + assertThrows(IllegalArgumentException.class, () -> { + MethodType switchType = MethodType.methodType(int.class, Object.class, int.class); + SwitchBootstraps.typeSwitch(lookup, "", switchType, Object.class); + }); + enum E {} + assertThrows(IllegalArgumentException.class, () -> { + MethodType switchType = MethodType.methodType(int.class, E.class, int.class); + SwitchBootstraps.enumSwitch(lookup, "", switchType); + }); + assertThrows(IllegalArgumentException.class, () -> { + MethodType switchType = MethodType.methodType(int.class, E.class, int.class); + SwitchBootstraps.enumSwitch(lookup, "", switchType, "A"); }); } } diff --git a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java index c38671e65b84..ff8e3804996f 100644 --- a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java +++ b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java @@ -77,7 +77,7 @@ */ /* * @test id=useNioSelector - * @bug 8087112 8372409 + * @bug 8087112 8372409 8386989 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer diff --git a/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java b/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java index 0a363e104ae1..3a33bbcf95d2 100644 --- a/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java +++ b/test/jdk/java/net/httpclient/quic/PacketSpaceManagerTest.java @@ -80,16 +80,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; /* * @test + * @bug 8349910 8386985 * @summary tests the logic to build an AckFrame * @library /test/lib * @library ../debug @@ -102,6 +105,7 @@ * @run junit/othervm -Dseed=-4159871071396382784 ${test.main.class} * @run junit/othervm -Dseed=2252276218459363615 ${test.main.class} * @run junit/othervm -Dseed=-5130588140709404919 ${test.main.class} + * @run junit/othervm -Dseed=4257295716830862528 ${test.main.class} */ // -Djdk.internal.httpclient.debug=true public class PacketSpaceManagerTest { @@ -766,6 +770,26 @@ boolean isDue(Deadline now) { } } + // Sends a trivial INITIAL packet, with a CRYPTO frame containing + // a payload of length 1 for the provided offset. If ackFrameToSend + // is not null it will be included in the packet. + public List sendPacket(long offset, AckFrame ackFrameToSend, Packet packet, long largestReceivedAckedPN) { + // add a crypto frame and build the packet + CryptoFrame crypto = new CryptoFrame(offset, 1, + ByteBuffer.wrap(new byte[] {nextByte(offset)})); + List frames = ackFrameToSend == null ? + List.of(crypto) : List.of(crypto, ackFrameToSend); + QuicPacket newPacket = codingContext.encoder + .newInitialPacket(localId, peerId, + null, + packet.packetNumber, + largestReceivedAckedPN, + frames, codingContext); + // pretend that we sent a packet + manager.packetSent(newPacket, -1, packet.packetNumber); + return frames; + } + /** * Drives the test by pretending to emit each packet in order, * then pretending to receive ack frames (as soon as possible @@ -830,19 +854,8 @@ public void run() throws Exception { debug.log("largestAckSent is: " + largestAckAcked); } - // add a crypto frame and build the packet - CryptoFrame crypto = new CryptoFrame(offset, 1, - ByteBuffer.wrap(new byte[] {nextByte(offset)})); - List frames = ackFrameToSend == null ? - List.of(crypto) : List.of(crypto, ackFrameToSend); - QuicPacket newPacket = codingContext.encoder - .newInitialPacket(localId, peerId, - null, - packet.packetNumber, - largestReceivedAckedPN, - frames, codingContext); - // pretend that we sent a packet - manager.packetSent(newPacket, -1, packet.packetNumber); + // send a packet + List frames = sendPacket(offset, ackFrameToSend, packet, largestReceivedAckedPN); // compute next deadline var nextDeadline = timerQueue.nextDeadline(); @@ -1125,4 +1138,53 @@ public void testPacketSpaceManager(TestCase testCase) throws Exception { driver.check(); } + @Test + public void testPacketSent() throws Exception { + // this test case is specifically for JDK-8386985 + System.out.printf("%n ------- testPacketSent ------- %n"); + + // create a minimal SynchronousTestDriver + TestCase testCase = new TestCase(List.of(new Acknowledged(1, 3), new Acknowledged(4,4)), + List.of(new Packet(3, 0), new Packet(4, 0))); + SynchronousTestDriver driver = new SynchronousTestDriver(testCase); + + // send a first ack-eliciting packet, and move the timeline past PTO + driver.sendPacket(0, null, new Packet(1, 0), -1); + Deadline pto = driver.manager.nextScheduledDeadline(); // should be PTO + driver.timeSource.advance(driver.timeSource.instant().until(pto, ChronoUnit.MILLIS) + 250, ChronoUnit.MILLIS); + + // start processing events, but delay the task that will run the transmitter + ArrayList tasks = new ArrayList<>(); + Executor executor = new Executor() { + @Override + public void execute(Runnable command) { + tasks.add(command); + } + }; + driver.timerQueue.processEventsAndReturnNextDeadline(driver.timeSource.instant(), executor); + + // acknowledge the first packet so that it's no longer pending retransmission + driver.manager.processAckFrame(new AckFrameBuilder().addAck(1).build()); + + // send a second packet, and examine the timerQueue next deadline + // if sending the second packet didn't cause the task to be rescheduled, we + // will observe Deadline.MAX, or a deadline before now: that's the bug. + driver.sendPacket(1, null, new Packet(2, 0), -1); + + Deadline next = driver.timerQueue.nextDeadline(); + assertNotEquals(next, Deadline.MAX); + assertTrue(next.isAfter(driver.timeSource.instant())); + + // now finish running the task and ack the second packet, + // so that we leave the packet space manager in a clean state + // for running the driver with the next two packets. + for (Runnable task : tasks) { + task.run(); + } + driver.manager.processAckFrame(new AckFrameBuilder() + .addAck(1).addAck(2).build()); + driver.run(); + driver.check(); + } + } diff --git a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java index 870a84a89273..f3321a8c04dc 100644 --- a/test/jdk/java/nio/file/DirectoryStream/SecureDS.java +++ b/test/jdk/java/nio/file/DirectoryStream/SecureDS.java @@ -212,6 +212,13 @@ static void doSetPermissions(Path dir) throws IOException { Path link = createSymbolicLink(aDir.resolve(linkEntry), fileEntry); Set permsLink = getPosixFilePermissions(link, NOFOLLOW_LINKS); + // Test setting permissions on a regular file through the no-follow view + view = stream.getFileAttributeView(fileEntry, PosixFileAttributeView.class, NOFOLLOW_LINKS); + view.setPermissions(noperms); + assertEquals(noperms, getPosixFilePermissions(file)); + view.setPermissions(permsFile); + assertEquals(permsFile, getPosixFilePermissions(file)); + // Test following link to file view = stream.getFileAttributeView(link, PosixFileAttributeView.class); view.setPermissions(noperms); @@ -220,14 +227,19 @@ static void doSetPermissions(Path dir) throws IOException { view.setPermissions(permsFile); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); - // Symbolic link permissions do not apply on Linux - if (!Platform.isLinux()) { - // Test not following link to file - view = stream.getFileAttributeView(link, PosixFileAttributeView.class, NOFOLLOW_LINKS); - view.setPermissions(noperms); + + // Test not following link to file + var linkView = stream.getFileAttributeView(link, PosixFileAttributeView.class, NOFOLLOW_LINKS); + if (Platform.isLinux()) { + // Symbolic link permissions do not apply on Linux + assertThrows(IOException.class, () -> linkView.setPermissions(noperms)); + assertEquals(permsFile, getPosixFilePermissions(file)); + assertThrows(IOException.class, () -> linkView.setPermissions(permsLink)); + } else { + linkView.setPermissions(noperms); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(noperms, getPosixFilePermissions(link, NOFOLLOW_LINKS)); - view.setPermissions(permsLink); + linkView.setPermissions(permsLink); assertEquals(permsFile, getPosixFilePermissions(file)); assertEquals(permsLink, getPosixFilePermissions(link, NOFOLLOW_LINKS)); } diff --git a/test/jdk/java/util/Locale/LocaleEnhanceTest.java b/test/jdk/java/util/Locale/LocaleEnhanceTest.java index 1e38f0b887ab..3fe3745034d6 100644 --- a/test/jdk/java/util/Locale/LocaleEnhanceTest.java +++ b/test/jdk/java/util/Locale/LocaleEnhanceTest.java @@ -44,6 +44,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -57,7 +58,7 @@ * @test * @bug 6875847 6992272 7002320 7015500 7023613 7032820 7033504 7004603 * 7044019 8008577 8176853 8255086 8263202 8287868 8174269 8369452 - * 8369590 8387185 + * 8369590 8387185 8387253 * @summary test API changes to Locale * @modules jdk.localedata * @run junit/othervm -esa LocaleEnhanceTest @@ -1396,6 +1397,19 @@ public void numericSingletonRoundTripTest() { assertEquals(tag, locale.toLanguageTag()); } + // Ensure that extlang is only accepted after a 2*3ALPHA language subtag + // That is, the 4 ALPHA and 5*8 ALPHA language subtags should not accept extlangs + @ParameterizedTest + @ValueSource(strings = {"quux", "foobar"}) + public void testExtlangAfterReservedLanguage(String lang) { + String tag = lang + "-baz"; + // Locale.forLanguageTag is lenient and truncates the extlang + assertEquals(lang, Locale.forLanguageTag(tag).toLanguageTag()); + // Locale.Builder is strict and should throw + assertThrows(IllformedLocaleException.class, + () -> new Builder().setLanguageTag(tag)); + } + private void checkCalendar(Locale loc, String expected) { Calendar cal = Calendar.getInstance(loc); assertEquals(expected, cal.getClass().getName(), "Wrong calendar"); diff --git a/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java b/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java index 690c056e23b1..706816f4880d 100644 --- a/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java +++ b/test/jdk/java/util/concurrent/tck/PriorityQueueTest.java @@ -40,6 +40,7 @@ import java.util.NoSuchElementException; import java.util.PriorityQueue; import java.util.Queue; +import java.util.TreeSet; import junit.framework.Test; @@ -168,6 +169,101 @@ public void testConstructor7() { mustEqual(items[i], q.poll()); } + /** + * Queue contains all elements of collection used to initialize and + * uses the custom comparator provided to order its elements + */ + public void testConstructor8() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + PriorityQueue q = new PriorityQueue<>(Arrays.asList(items), cmp); + assertEquals(cmp, q.comparator()); + for (int i = SIZE - 1; i >= 0; --i) + mustEqual(items[i], q.poll()); + } + + /** + * Initializing from Collection with a comparator has the order + * of its elements the same as the queue initialized with a comparator + * and populated with Collection after initialization + */ + public void testConstructor9() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + PriorityQueue q1 = new PriorityQueue<>(Arrays.asList(items), cmp); + @SuppressWarnings("unchecked") + PriorityQueue q2 = new PriorityQueue<>(SIZE, cmp); + q2.addAll(Arrays.asList(items)); + for (int i = 0; i < SIZE; ++i) + mustEqual(q1.poll(), q2.poll()); + } + + /** + * Initializing from null Collection throws NPE + */ + public void testConstructor10() { + try { + new PriorityQueue((Collection)null, new MyReverseComparator()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from Collection of null elements throws NPE + */ + public void testConstructor11() { + try { + new PriorityQueue(Arrays.asList(new Item[SIZE]), new MyReverseComparator()); + shouldThrow(); + } catch (NullPointerException success) {} + } + + /** + * Initializing from PriorityQueue and its comparator + */ + public void testConstructor12() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + PriorityQueue q1 = new PriorityQueue<>(cmp); + q1.addAll(Arrays.asList(items)); + @SuppressWarnings("unchecked") + PriorityQueue q2 = new PriorityQueue<>(q1, q1.comparator()); + for (int i = 0; i < SIZE; ++i) + mustEqual(q1.poll(), q2.poll()); + } + + /** + * Initializing from SortedSet and its comparator + */ + public void testConstructor13() { + Item[] items = seqItems(SIZE); + MyReverseComparator cmp = new MyReverseComparator(); + @SuppressWarnings("unchecked") + TreeSet s = new TreeSet<>(cmp); + s.addAll(Arrays.asList(items)); + @SuppressWarnings("unchecked") + PriorityQueue q = new PriorityQueue<>(s, s.comparator()); + for (int i = 0; i < SIZE; ++i) + mustEqual(q.poll(), s.removeFirst()); + } + + /** + * Initializing with null comparator + */ + public void testConstructor14() { + Item[] items = seqItems(SIZE); + @SuppressWarnings("unchecked") + PriorityQueue q1 = new PriorityQueue<>((Comparator) null); + q1.addAll(Arrays.asList(items)); + @SuppressWarnings("unchecked") + PriorityQueue q2 = new PriorityQueue<>(Arrays.asList(items), null); + for (int i = 0; i < SIZE; ++i) + mustEqual(q1.poll(), q2.poll()); + } + /** * isEmpty is true before add, false after */ diff --git a/test/jdk/java/util/zip/GZIP/GZIPInputStreamCallsAvailable.java b/test/jdk/java/util/zip/GZIP/GZIPInputStreamCallsAvailable.java new file mode 100644 index 000000000000..e39b47dfc8ea --- /dev/null +++ b/test/jdk/java/util/zip/GZIP/GZIPInputStreamCallsAvailable.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Random; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import jdk.test.lib.RandomFactory; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +/* + * @test + * @summary Verify the behaviour of GZIPInputStream when dealing with InputStream.available() + * on the underlying stream and the jdk.util.gzip.tryReadAheadAfterTrailer + * system property being enabled/disabled + * @key randomness + * @library /test/lib + * @build jdk.test.lib.RandomFactory + * @run junit/othervm -Djdk.util.gzip.tryReadAheadAfterTrailer=true GZIPInputStreamCallsAvailable + * @run junit/othervm -Djdk.util.gzip.tryReadAheadAfterTrailer=false GZIPInputStreamCallsAvailable + * @run junit GZIPInputStreamCallsAvailable + */ +class GZIPInputStreamCallsAvailable { + + private static final boolean AVAILABLE_METHOD_INVOCATION_SKIPPED = + Boolean.getBoolean("jdk.util.gzip.tryReadAheadAfterTrailer"); + private static final Random random = RandomFactory.getRandom(); + + private record TestData(byte[] uncompressed, byte[] compressed) { + } + + static List numGZIPMembers() { + return List.of(1, + 33, + random.nextInt(2, 1001) // a reasonably large number of members + ); + } + + /* + * Verify that GZIPInputStream reads and returns the correct decompressed data when: + * - the underlying InputStream.available() returns an accurate value + * - and when the GZIPInputStream isn't expected to call the underlying InputStream.available() + * method + */ + @ParameterizedTest + @MethodSource("numGZIPMembers") + void testMultipleMembers(final int numMembers) throws IOException { + final TestData testData = createGZIPStream(numMembers); + final InputStream underlyingStream = AVAILABLE_METHOD_INVOCATION_SKIPPED + // stream whose available() method isn't expected to be invoked + ? new AlwaysThrowFromAvailable(new ByteArrayInputStream(testData.compressed)) + // stream whose available() will be invoked and returns an accurate value + : new ByteArrayInputStream(testData.compressed); + try (GZIPInputStream gzip = new GZIPInputStream(underlyingStream)) { + final byte[] decompressed = gzip.readAllBytes(); + assertArrayEquals(testData.uncompressed, decompressed, "unexpected decompressed data"); + } + } + + /* + * Creates and returns bytes representing a GZIP stream consisting of the given number of + * members. + */ + private static TestData createGZIPStream(final int numMembers) throws IOException { + final String content = "foo bar hello world from " + GZIPInputStreamCallsAvailable.class; + final ByteArrayOutputStream uncompressed = new ByteArrayOutputStream(); + final ByteArrayOutputStream gzipped = new ByteArrayOutputStream(); + for (int i = 1; i <= numMembers; i++) { + final ByteArrayOutputStream member = new ByteArrayOutputStream(); + try (final OutputStream gzip = new GZIPOutputStream(member)) { + final byte[] memberRawBytes = ("member-" + i + " " + content).getBytes(US_ASCII); + gzip.write(memberRawBytes); + // keep track of the uncompressed content too so that it can be compared for + // equality with the decompressed content + uncompressed.write(memberRawBytes); + } + // write out the GZIP member to the stream which accumulates all the members + gzipped.write(member.toByteArray()); + } + return new TestData(uncompressed.toByteArray(), gzipped.toByteArray()); + } + + private static class AlwaysThrowFromAvailable extends FilterInputStream { + public AlwaysThrowFromAvailable(InputStream in) { + super(in); + } + + @Override + public int available() { + throw new AssertionError(this.getClass().getName() + + ".available() wasn't expected to be invoked"); + } + } +} diff --git a/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java b/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java index 81f55f2f0dd6..b6dea98c28da 100644 --- a/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java +++ b/test/jdk/java/util/zip/GZIP/GZIPOverBlockingStreams.java @@ -63,6 +63,9 @@ * @library /test/lib * @build jdk.test.lib.net.URIBuilder jdk.test.lib.RandomFactory * @run junit GZIPOverBlockingStreams + * @comment verify it behaves the same when jdk.util.gzip.tryReadAheadAfterTrailer system property + * is set to false + * @run junit/othervm -Djdk.util.gzip.tryReadAheadAfterTrailer=false GZIPOverBlockingStreams */ class GZIPOverBlockingStreams { diff --git a/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java new file mode 100644 index 000000000000..11f3efb1518f --- /dev/null +++ b/test/jdk/javax/net/ssl/ciphersuites/BulkCipherDisabledAlgorithms.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2026, IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8387124 + * @summary Test TLS cipher suite disabling via jdk.tls.disabledAlgorithms, + * including matching on bulk cipher components, covering both + * visibility and handshake behavior. + * @library /test/lib + * /javax/net/ssl/TLSCommon + * /javax/net/ssl/templates + * @run main/othervm BulkCipherDisabledAlgorithms visibility + * @run main/othervm BulkCipherDisabledAlgorithms handshake + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.*; + +import jdk.test.lib.process.Proc; + +import java.security.NoSuchAlgorithmException; +import java.security.Security; + +public class BulkCipherDisabledAlgorithms { + + public static void main(String[] args) throws Exception { + if (args.length == 0) { + throw new RuntimeException("Missing mode argument"); + } + + String mode = args[0]; + boolean isVisibilityTest = "visibility".equals(mode); + boolean isHandshakeTest = "handshake".equals(mode); + + if (args.length == 1) { + List tests = buildTests(isVisibilityTest); + + for (String[] test : tests) { + String suite = test[0]; + String disabled = test[1]; + String expected = test[2]; + + System.out.println("================================================="); + System.out.println("Testing: " + mode + + ", suite=" + suite + + ", disabled=" + disabled + + ", expected=" + expected); + + Proc p = Proc.create( + BulkCipherDisabledAlgorithms.class.getName()) + .args(mode, suite, expected) + .secprop("jdk.tls.disabledAlgorithms", disabled) + .inheritIO(); + + p.start().waitFor(0); + } + + System.out.println("TEST PASS - OK"); + return; + } + + String suite = args[1]; + String expected = args[2]; + boolean expectedDisabled = "disabled".equals(expected); + + if (isVisibilityTest) { + testCipherSuiteVisibility(suite, expectedDisabled); + } + + if (isHandshakeTest) { + testHandshake(suite, expectedDisabled); + } + } + + // Returns cipher suites for testing. + // - true: use all supported suites (independent of disabledAlgorithms) + // - false: use default enabled suites (candidates for handshake) + private static CipherSuite[] getCipherSuites(boolean useSupportedSuites) + throws NoSuchAlgorithmException { + SSLEngine engine = SSLContext.getDefault().createSSLEngine(); + String[] suites = useSupportedSuites + ? engine.getSupportedCipherSuites() + : engine.getEnabledCipherSuites(); + + return Arrays.stream(suites) + .map(CipherSuite::cipherSuite) + .filter(cs -> cs != CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + .toArray(CipherSuite[]::new); + } + + private static List buildTests(boolean useSupportedSuites) + throws NoSuchAlgorithmException { + if (useSupportedSuites) { + // disabledAlgorithms limits supported suites; clear to list all + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + } + + List tests = new ArrayList<>(); + CipherSuite[] suites = getCipherSuites(useSupportedSuites); + + for (CipherSuite suite : suites) { + String suiteName = suite.name(); + String bulk = extractBulkCipher(suiteName); + + tests.add(new String[] { suiteName, suiteName, "disabled" }); + tests.add(new String[] { suiteName, bulk, "disabled" }); + + for (CipherSuite other : suites) { + // Negative test case: disable a different bulk cipher than the one + // used by the current suite. This ensures that the suite remains + // enabled and a successful TLS handshake can still be negotiated. + if (other == suite) { + continue; + } + + String otherBulk = extractBulkCipher(other.name()); + + if (!bulk.equals(otherBulk) + && !suiteName.contains(otherBulk)) { + tests.add(new String[] { suiteName, otherBulk, "enabled" }); + break; + } + } + } + + return tests; + } + + /** + * Separator used in TLS cipher suite names to mark the start of + * the bulk cipher component (e.g. TLS_RSA_WITH_AES_128_CBC_SHA). + */ + private static final String WITH = "_WITH_"; + + private static String extractBulkCipher(String suite) { + if (suite.contains(WITH)) { + String after = suite.substring(suite.indexOf(WITH) + WITH.length()); + int last = after.lastIndexOf('_'); + return after.substring(0, last); + } else { + int first = suite.indexOf('_'); + int last = suite.lastIndexOf('_'); + return suite.substring(first + 1, last); + } + } + + private static void testCipherSuiteVisibility(String suite, boolean expectedDisabled) + throws NoSuchAlgorithmException { + boolean visible = Arrays.asList(getCipherSuites(true)) + .contains(CipherSuite.cipherSuite(suite)); + + if (!expectedDisabled && !visible) { + throw new RuntimeException( + "Cipher suite '" + suite + "' not visible but expected to be enabled"); + } else if (expectedDisabled && visible) { + throw new RuntimeException( + "Cipher suite '" + suite + "' visible but expected to be disabled"); + } + } + + private static void testHandshake(String suite, boolean expectedDisabled) throws Exception { + try { + new TLSHandshakeTest(suite).run(); + + if (expectedDisabled) { + throw new RuntimeException( + "Handshake succeeded but should fail: " + suite); + } + } catch (SSLHandshakeException e) { + if (!expectedDisabled) { + throw new RuntimeException( + "Handshake failed unexpectedly: " + suite, e); + } + } + } + + private static class TLSHandshakeTest extends SSLSocketTemplate { + private final String suite; + + TLSHandshakeTest(String suite) { + this.suite = suite; + } + + @Override + protected void configureClientSocket(SSLSocket socket) { + socket.setEnabledCipherSuites(new String[] { suite }); + } + + @Override + protected void configureServerSocket(SSLServerSocket socket) { + socket.setEnabledCipherSuites(new String[] { suite }); + } + } +} diff --git a/test/jdk/javax/script/CommonSetup.sh b/test/jdk/javax/script/CommonSetup.sh index 4b59f9cbbc71..363d679df542 100644 --- a/test/jdk/javax/script/CommonSetup.sh +++ b/test/jdk/javax/script/CommonSetup.sh @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,7 @@ case "$OS" in OS="Windows" FS="\\" ;; - CYGWIN* | MSYS* | MINGW* ) + CYGWIN* ) PS=";" OS="Windows" FS="\\" diff --git a/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java index ad971e9b9bf2..a33e83d14eac 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector128Tests.java @@ -1561,14 +1561,16 @@ static short[] fill(short[] a, ToFloat16F f) { } static short cornerCaseValue(int i) { - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits(Float16.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; } @@ -5423,11 +5425,19 @@ static void hashCodeFloat16Vector128TestsSmokeTest(IntFunction fa) { int hash = av.hashCode(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + static long ADDReduceLong(short[] a, int idx) { short res = 0; diff --git a/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java index a946e0d85858..99b167d40242 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector256Tests.java @@ -1561,14 +1561,16 @@ static short[] fill(short[] a, ToFloat16F f) { } static short cornerCaseValue(int i) { - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits(Float16.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; } @@ -5423,11 +5425,19 @@ static void hashCodeFloat16Vector256TestsSmokeTest(IntFunction fa) { int hash = av.hashCode(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + static long ADDReduceLong(short[] a, int idx) { short res = 0; diff --git a/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java index 0e70b4c85ecb..1c3914970153 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector512Tests.java @@ -1561,14 +1561,16 @@ static short[] fill(short[] a, ToFloat16F f) { } static short cornerCaseValue(int i) { - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits(Float16.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; } @@ -5423,11 +5425,19 @@ static void hashCodeFloat16Vector512TestsSmokeTest(IntFunction fa) { int hash = av.hashCode(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + static long ADDReduceLong(short[] a, int idx) { short res = 0; diff --git a/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java b/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java index 94017042b7b5..6ef651860ad0 100644 --- a/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java +++ b/test/jdk/jdk/incubator/vector/Float16Vector64Tests.java @@ -1561,14 +1561,16 @@ static short[] fill(short[] a, ToFloat16F f) { } static short cornerCaseValue(int i) { - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits(Float16.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; } @@ -5423,11 +5425,19 @@ static void hashCodeFloat16Vector64TestsSmokeTest(IntFunction fa) { int hash = av.hashCode(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + static long ADDReduceLong(short[] a, int idx) { short res = 0; diff --git a/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java b/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java index d8649c838ee3..61efa3de9a0a 100644 --- a/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java +++ b/test/jdk/jdk/incubator/vector/Float16VectorMaxTests.java @@ -1567,14 +1567,16 @@ static short[] fill(short[] a, ToFloat16F f) { } static short cornerCaseValue(int i) { - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits(Float16.MAX_VALUE); case 1 -> float16ToRawShortBits(Float16.MIN_VALUE); case 2 -> float16ToRawShortBits(Float16.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits(Float16.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits(Float16.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; } @@ -5429,11 +5431,19 @@ static void hashCodeFloat16VectorMaxTestsSmokeTest(IntFunction fa) { int hash = av.hashCode(); short subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); - int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + static long ADDReduceLong(short[] a, int idx) { short res = 0; diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template b/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template index 8606b9ba5984..0ae9342539fc 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-Miscellaneous.template @@ -100,11 +100,25 @@ int hash = av.hashCode(); $type$ subarr[] = Arrays.copyOfRange(a, i, i + SPECIES.length()); +#if[FP16] + int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(toFloat16Array(subarr))); +#else[FP16] int expectedHash = Objects.hash(SPECIES, Arrays.hashCode(subarr)); +#end[FP16] Assert.assertTrue(hash == expectedHash, "at index " + i + ", hash should be = " + expectedHash + ", but is = " + hash); } } +#if[FP16] + static Float16[] toFloat16Array(short[] bits) { + Float16[] a = new Float16[bits.length]; + for (int j = 0; j < bits.length; j++) { + a[j] = shortBitsToFloat16(bits[j]); + } + return a; + } + +#end[FP16] #if[byte] @Test(dataProvider = "$type$UnaryOpProvider") static void reinterpretAsBytes$vectorteststype$SmokeTest(IntFunction<$type$[]> fa) { diff --git a/test/jdk/jdk/incubator/vector/templates/Unit-header.template b/test/jdk/jdk/incubator/vector/templates/Unit-header.template index eac7edbbb3f2..7047e27b7970 100644 --- a/test/jdk/jdk/incubator/vector/templates/Unit-header.template +++ b/test/jdk/jdk/incubator/vector/templates/Unit-header.template @@ -2013,14 +2013,16 @@ relativeError)); static $type$ cornerCaseValue(int i) { #if[FP] #if[FP16] - return switch(i % 8) { + return switch(i % 10) { case 0 -> float16ToRawShortBits($Wideboxtype$.MAX_VALUE); case 1 -> float16ToRawShortBits($Wideboxtype$.MIN_VALUE); case 2 -> float16ToRawShortBits($Wideboxtype$.NEGATIVE_INFINITY); case 3 -> float16ToRawShortBits($Wideboxtype$.POSITIVE_INFINITY); case 4 -> float16ToRawShortBits($Wideboxtype$.NaN); case 5 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7FFA)); - case 6 -> float16ToShortBits(Float16.valueOf(0.0f)); + case 6 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7c01)); // signaling NaN + case 7 -> float16ToRawShortBits(shortBitsToFloat16((short)0x7e00)); // quiet NaN + case 8 -> float16ToShortBits(Float16.valueOf(0.0f)); default -> float16ToShortBits(Float16.valueOf(-0.0f)); }; #else[FP16] diff --git a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java index bf21194e432c..84e44b1ab697 100644 --- a/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java +++ b/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java @@ -28,7 +28,7 @@ * 8223499 8225392 8232019 8234245 8233223 8225068 8225069 8243321 8243320 * 8243559 8225072 8258630 8259312 8256421 8225081 8225082 8225083 8245654 * 8305975 8304760 8307134 8295894 8314960 8317373 8317374 8318759 8319187 - * 8321408 8316138 8341057 8303770 8350498 8359170 8361212 8372351 + * 8321408 8316138 8341057 8303770 8350498 8359170 8361212 8372351 8387123 * @summary Check root CA entries in cacerts file */ import java.io.ByteArrayInputStream; @@ -48,13 +48,13 @@ public class VerifyCACerts { // The numbers of certs now. // SapMachine 2021-09-23: Additional certificate for SAP - private static final int COUNT = 112; + private static final int COUNT = 111; // SHA-256 of cacerts, can be generated with // shasum -a 256 cacerts | sed -e 's/../&:/g' | tr '[:lower:]' '[:upper:]' | cut -c1-95 // SapMachine 2021-09-23: Additional certificate for SAP private static final String CHECKSUM - = "46:B4:9E:42:70:3A:8A:AA:28:88:21:EE:DA:1B:4A:9B:6F:0C:C6:5F:D4:58:31:F7:A5:DB:9E:1B:5E:43:A8:9D"; + = "A6:7C:8E:66:E1:30:1E:DB:A0:AF:2A:1E:63:44:EB:D3:E5:B5:4A:75:F8:AE:A2:85:3A:1F:20:09:E4:A5:D7:33"; // Hex formatter to upper case with ":" delimiter private static final HexFormat HEX = HexFormat.ofDelimiter(":").withUpperCase(); @@ -148,8 +148,6 @@ public class VerifyCACerts { "96:BC:EC:06:26:49:76:F3:74:60:77:9A:CF:28:C5:A7:CF:E8:A3:C0:AA:E1:1A:8F:FC:EE:05:C0:BD:DF:08:C6"); put("letsencryptisrgx2 [jdk]", "69:72:9B:8E:15:A8:6E:FC:17:7A:57:AF:B7:17:1D:FC:64:AD:D2:8C:2F:CA:8C:F1:50:7E:34:45:3C:CB:14:70"); - put("luxtrustglobalrootca [jdk]", - "A1:B2:DB:EB:64:E7:06:C6:16:9E:3C:41:18:B2:3B:AA:09:01:8A:84:27:66:6D:8B:F0:E2:88:91:EC:05:19:50"); put("quovadisrootca [jdk]", "A4:5E:DE:3B:BB:F0:9C:8A:E1:5C:72:EF:C0:72:68:D6:93:A2:1C:99:6F:D5:1E:67:CA:07:94:60:FD:6D:88:73"); put("quovadisrootca1g3 [jdk]", @@ -301,8 +299,6 @@ public class VerifyCACerts { add("addtrustexternalca [jdk]"); // Valid until: Sat May 30 10:44:50 GMT 2020 add("addtrustqualifiedca [jdk]"); - // Valid until: Wed Mar 17 02:51:37 PDT 2021 - add("luxtrustglobalrootca [jdk]"); // Valid until: Wed Mar 17 11:33:33 PDT 2021 add("quovadisrootca [jdk]"); // Valid until: Sat May 21 04:00:00 GMT 2022 diff --git a/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java b/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java new file mode 100644 index 000000000000..87a6f156152b --- /dev/null +++ b/test/jdk/sun/security/ssl/CipherSuite/TLS13BulkCipherDisabledCipherSuite.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026, IBM Corporation. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8387124 + * @summary Test disabling TLS 1.3 cipher suites with bulk ciphers names + * @run testng/othervm TLS13BulkCipherDisabledCipherSuite + */ + +import static org.testng.AssertJUnit.assertTrue; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.security.Security; +import java.util.List; + +public class TLS13BulkCipherDisabledCipherSuite extends AbstractDisableCipherSuites { + + private static final String SECURITY_PROPERTY = "jdk.tls.disabledAlgorithms"; + private static final String TEST_ALGORITHMS = "AES_256_GCM," + + " AES_128_GCM," + + " CHACHA20_POLY1305"; + private static final String[] CIPHER_SUITES = new String[] { + "TLS_AES_256_GCM_SHA384", + "TLS_AES_128_GCM_SHA256", + "TLS_CHACHA20_POLY1305_SHA256" + }; + static final List CIPHER_SUITES_IDS = List.of( + 0x1301, + 0x1302, + 0x1303); + + @Override + protected String getProtocol() { + return "TLSv1.3"; + } + + @BeforeTest + void setUp() throws Exception { + Security.setProperty(SECURITY_PROPERTY, TEST_ALGORITHMS); + } + + @Test + public void testDefault() throws Exception { + assertTrue(testDefaultCase(CIPHER_SUITES_IDS)); + } + + @Test + public void testAddDisabled() throws Exception { + assertTrue(testEngAddDisabled(CIPHER_SUITES, CIPHER_SUITES_IDS)); + } + + @Test + public void testOnlyDisabled() throws Exception { + assertTrue(testEngOnlyDisabled(CIPHER_SUITES)); + } +} diff --git a/test/jdk/tools/jlink/plugins/CACertsPluginTest.java b/test/jdk/tools/jlink/plugins/CACertsPluginTest.java new file mode 100644 index 000000000000..8ca720be6393 --- /dev/null +++ b/test/jdk/tools/jlink/plugins/CACertsPluginTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.Path; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.util.Enumeration; + +import jtreg.SkippedException; +import jdk.test.lib.Asserts; +import jdk.test.lib.security.SecurityUtils; +import jdk.tools.jlink.internal.LinkableRuntimeImage; +import tests.Helper; + +/* @test + * @bug 8377102 + * @summary Test the --cacerts plugin + * @library ../../lib /test/lib + * @modules java.base/jdk.internal.jimage + * jdk.jlink/jdk.tools.jimage + * jdk.jlink/jdk.tools.jlink.internal + * @build tests.* + * @run main/othervm CACertsPluginTest + */ + +public class CACertsPluginTest { + + private static Helper helper; + + private static final String CACERTS_PATH = "lib/security/cacerts"; + private static final boolean LINKABLE_RUNTIME = + LinkableRuntimeImage.isLinkableRuntime(); + + public static void main(String[] args) throws Throwable { + + helper = Helper.newHelper(LINKABLE_RUNTIME); + if (helper == null) { + throw new SkippedException("Test not run"); + } + + KeyStore jdkCacerts = SecurityUtils.getCacertsKeyStore(); + Enumeration aliases = jdkCacerts.aliases(); + String alias1 = aliases.nextElement(); + String alias2 = aliases.nextElement(); + + // test one alias + test("testOne", jdkCacerts, alias1); + + // test two aliases + test("testTwo", jdkCacerts, alias1, alias2); + + // test illegal/bad options + testBadOptions(); + } + + private static void test(String module, KeyStore jdkCacerts, + String... aliases) throws Exception { + + helper.generateDefaultJModule(module); + + String option = toOption(aliases); + Path image = helper.generateDefaultImage( + new String[] { "--cacerts", option }, module).assertSuccess(); + helper.checkImage(image, module, null, null, + new String[] { CACERTS_PATH }); + + KeyStore imageCacerts = KeyStore.getInstance( + image.resolve(CACERTS_PATH).toFile(), (char[]) null); + + Asserts.assertEquals(imageCacerts.size(), aliases.length); + for (String alias : aliases) { + Asserts.assertTrue(imageCacerts.isCertificateEntry(alias)); + Asserts.assertEquals( + jdkCacerts.getCertificate(alias), + imageCacerts.getCertificate(alias)); + } + } + + private static void testBadOptions() throws Exception { + + String module = "testBad"; + helper.generateDefaultJModule(module); + helper.generateDefaultImage(new String[] + { "--cacerts", "bogus-alias" }, module) + .assertFailure("alias bogus-alias does not exist"); + } + + private static String toOption(String... aliases) { + int max = aliases.length - 1; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; ; i++) { + sb.append(aliases[i]); + if (i == max) { + return sb.toString(); + } + sb.append(","); + } + } +} diff --git a/test/langtools/TEST.ROOT b/test/langtools/TEST.ROOT index c76f99d1396a..8319e724e89a 100644 --- a/test/langtools/TEST.ROOT +++ b/test/langtools/TEST.ROOT @@ -15,7 +15,7 @@ keys=intermittent randomness needs-src needs-src-jdk_javadoc groups=TEST.groups # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Path to libraries in the topmost test directory. This is needed so @library diff --git a/test/langtools/tools/javac/IncrementalComp/TestIncrementalComp.java b/test/langtools/tools/javac/IncrementalComp/TestIncrementalComp.java new file mode 100644 index 000000000000..2be04e98c2e7 --- /dev/null +++ b/test/langtools/tools/javac/IncrementalComp/TestIncrementalComp.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test javac incremental compilation with modules + * @run junit TestIncrementalComp + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.spi.ToolProvider; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.nio.file.StandardOpenOption.CREATE_NEW; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TestIncrementalComp { + + static final ToolProvider JAVAC = ToolProvider.findFirst("javac") + .orElseThrow(); + + record TestCase(String srcDir, Map sources, Set modules, String mainModule, Map addReadsEdges) { + TestCase(String srcDir, Map sources, Set modules, String mainModule) { + this(srcDir, sources, modules, mainModule, Map.of()); + } + } + + @ParameterizedTest + @MethodSource("cases") + public void test(TestCase testCase) throws Throwable { + Path workDir = Path.of(testCase.srcDir()); + // set up test sources + Path localTestModules = workDir.resolve("test_modules"); + Path outDir = workDir.resolve("mods"); + for (Map.Entry sourceFile : testCase.sources().entrySet()) { + Path filePath = localTestModules.resolve(sourceFile.getKey()); + Files.createDirectories(filePath.getParent()); + Files.writeString(filePath, sourceFile.getValue(), CREATE_NEW); + } + + Path libPath = localTestModules.resolve(LIB_PATH); + Files.createDirectories(libPath.getParent()); + Files.writeString(libPath, ALT_LIB_INT, CREATE_NEW); + + List javacCommand = new ArrayList<>(List.of( + "-d", outDir.toString(), + "--module-source-path=" + localTestModules, + "--module", String.join(",", testCase.modules()) + )); + for (Map.Entry addReads : testCase.addReadsEdges().entrySet()) { + String reader = addReads.getKey(); + String read = addReads.getValue(); + javacCommand.add(String.format("--add-reads=%s=%s", reader, read)); + } + // compile both modules + compile(javacCommand); + + String mainClass = testCase.mainModule() + ".app.Main"; + invokeMainMethod(outDir, testCase.mainModule(), mainClass, testCase.addReadsEdges()); + + // modify sources. Dep is not modified + Files.writeString(libPath, ALT_LIB_LONG, TRUNCATE_EXISTING); + + // recompile. Any dependency on the changed file should be recompiled as well + compile(javacCommand); + + // should work + // if this fails because of incremental compilation issues, we can expect to see a NoSuchMethodError + invokeMainMethod(outDir, testCase.mainModule(), mainClass, testCase.addReadsEdges()); + } + + private static void invokeMainMethod(Path modulePath, String moduleName, String mainClassName, + Map addReadsEdges) + throws ReflectiveOperationException { + // define module layer + // note that we need to explicitly add any read module to the set of roots + ModuleLayer boot = ModuleLayer.boot(); + Set allRoots = Stream.concat(Stream.of(moduleName), addReadsEdges.values().stream()) + .collect(Collectors.toSet()); + Configuration config = boot.configuration() + .resolve(ModuleFinder.of(modulePath), ModuleFinder.of(), allRoots); + ModuleLayer.Controller controller = ModuleLayer.defineModulesWithOneLoader( + config, List.of(boot), ClassLoader.getSystemClassLoader()); + + // add extra reads edges + for (Map.Entry addReads : addReadsEdges.entrySet()) { + Module reader = controller.layer().findModule(addReads.getKey()).orElseThrow(); + Module read = controller.layer().findModule(addReads.getValue()).orElseThrow(); + controller.addReads(reader, read); + } + + // invoke main + Class main1 = controller.layer().findLoader(moduleName).loadClass(mainClassName); + Method m = main1.getMethod("main", String[].class); + m.invoke(null, new Object[]{ new String[0] }); + } + + private static void compile(List args) { + System.err.println("compile: " + args); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + int rc = JAVAC.run(pw, pw, args.toArray(String[]::new)); + pw.close(); + System.err.println(sw); + assertEquals(0, rc); + } + + private static final Path LIB_PATH = Path.of("org.moda/org/moda/lib/Lib.java"); + + private static final String ALT_LIB_INT = """ + package org.moda.lib; + public class Lib { + public static int getVal() { + return 42; + } + } + """; + + private static final String ALT_LIB_LONG = """ + package org.moda.lib; + public class Lib { + public static long getVal() { + return 42; + } + } + """; + + static Stream cases() { + return Stream.of( + new TestCase("single", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + // for reflective access + exports org.moda.app; + } + """, + Path.of("org.moda/org/moda/lib/Dep.java"), + """ + package org.moda.lib; + + public class Dep { + public static long getVal() { + return Lib.getVal(); + } + } + """, + Path.of("org.moda/org/moda/app/Main.java"), + """ + package org.moda.app; + + import org.moda.lib.Dep; + + public class Main { + public static void main(String[] args) { + System.out.println(Dep.getVal()); + } + } + """ + ), Set.of("org.moda"), "org.moda"), + new TestCase("multi", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + exports org.moda.lib; + } + """, + Path.of("org.modb/module-info.java"), + """ + module org.modb { + requires org.moda; + + // for reflective access + exports org.modb.app; + } + """, + Path.of("org.modb/org/modb/app/Main.java"), + """ + package org.modb.app; + + import org.moda.lib.Lib; + + public class Main { + public static void main(String[] args) { + System.out.println(Lib.getVal()); + } + } + """ + ), Set.of("org.moda", "org.modb"), "org.modb"), + new TestCase("transitive", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + exports org.moda.lib; + } + + """, + Path.of("org.modb/module-info.java"), + """ + module org.modb { + // for org.modc + requires transitive org.moda; + } + """, + Path.of("org.modc/module-info.java"), + """ + module org.modc { + requires org.modb; + + // for reflective access + exports org.modc.app; + } + """, + Path.of("org.modc/org/modc/app/Main.java"), + """ + package org.modc.app; + + import org.moda.lib.Lib; + + public class Main { + public static void main(String[] args) { + System.out.println(Lib.getVal()); + } + } + """ + ), Set.of("org.moda", "org.modb", "org.modc"), "org.modc"), + new TestCase("add_reads", Map.of( + Path.of("org.moda/module-info.java"), + """ + module org.moda { + exports org.moda.lib; + } + """, + Path.of("org.modb/module-info.java"), + """ + module org.modb { + // no explicit requires + + // for reflective access + exports org.modb.app; + } + """, + Path.of("org.modb/org/modb/app/Main.java"), + """ + package org.modb.app; + + import org.moda.lib.Lib; + + public class Main { + public static void main(String[] args) { + System.out.println(Lib.getVal()); + } + } + """ + ), Set.of("org.moda", "org.modb"), "org.modb", Map.of("org.modb", "org.moda")) + ); + } + } diff --git a/test/langtools/tools/javac/modules/MOptionTest.java b/test/langtools/tools/javac/modules/MOptionTest.java index ff08960b0bc7..470815a9493b 100644 --- a/test/langtools/tools/javac/modules/MOptionTest.java +++ b/test/langtools/tools/javac/modules/MOptionTest.java @@ -84,12 +84,12 @@ public void testOneModule(Path base) throws Exception { .run(Task.Expect.SUCCESS) .writeAll(); - if (!moduleInfoTimeStamp.equals(Files.getLastModifiedTime(moduleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(moduleInfoClass).compareTo(moduleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!testTestTimeStamp.equals(Files.getLastModifiedTime(testTestClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(testTestClass).compareTo(Files.getLastModifiedTime(testTest)) < 0) { + throw new AssertionError("Classfiles too old!"); } // Date back the source file by one second compared to the current time. @@ -102,8 +102,8 @@ public void testOneModule(Path base) throws Exception { .run(Task.Expect.SUCCESS) .writeAll(); - if (!moduleInfoTimeStamp.equals(Files.getLastModifiedTime(moduleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(moduleInfoClass).compareTo(moduleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } if (Files.getLastModifiedTime(testTestClass).compareTo(Files.getLastModifiedTime(testTest)) < 0) { @@ -219,20 +219,20 @@ public void testMultiModule(Path base) throws Exception { .run(Task.Expect.SUCCESS) .writeAll(); - if (!m1ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m1ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m1ModuleInfoClass).compareTo(m1ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!m2ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m2ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m2ModuleInfoClass).compareTo(m2ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!C1TimeStamp.equals(Files.getLastModifiedTime(classC1))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(classC1).compareTo(Files.getLastModifiedTime(C1Source)) < 0) { + throw new AssertionError("Classfiles too old!"); } - if (!C2TimeStamp.equals(Files.getLastModifiedTime(classC2))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(classC2).compareTo(Files.getLastModifiedTime(C2Source)) < 0) { + throw new AssertionError("Classfiles too old!"); } // Date back the source file by one second compared to the current time. @@ -246,12 +246,12 @@ public void testMultiModule(Path base) throws Exception { .run(Task.Expect.SUCCESS) .writeAll(); - if (!m1ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m1ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m1ModuleInfoClass).compareTo(m1ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } - if (!m2ModuleInfoTimeStamp.equals(Files.getLastModifiedTime(m2ModuleInfoClass))) { - throw new AssertionError("Classfile update!"); + if (Files.getLastModifiedTime(m2ModuleInfoClass).compareTo(m2ModuleInfoTimeStamp) <= 0) { + throw new AssertionError("Classfile too old!"); } if (Files.getLastModifiedTime(classC1).compareTo(Files.getLastModifiedTime(C1Source)) < 0) { diff --git a/test/langtools/tools/javac/processing/model/trees/OnDemandAttributionRecordConstructor.java b/test/langtools/tools/javac/processing/model/trees/OnDemandAttributionRecordConstructor.java new file mode 100644 index 000000000000..37d1541f18a8 --- /dev/null +++ b/test/langtools/tools/javac/processing/model/trees/OnDemandAttributionRecordConstructor.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8387215 + * @summary Check that javac does not report invalid errors when compiling a valid + * compact record constructor when an on-demand attribution is triggered + * by an annotation processor calling Trees.getElement(...) for identifiers + * inside the constructor. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run junit ${test.main.class} + */ + +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedOptions; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import toolbox.JavacTask; +import toolbox.ToolBox; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import toolbox.Task; + +public class OnDemandAttributionRecordConstructor { + + Path base; + ToolBox tb = new ToolBox(); + + @Test + void testCompactRecordConstructorWithGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + record Repro(String name) { + Repro { + name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testCompactRecordConstructorWithoutGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString(), "-AskipGetElement=true") + .sources(""" + record Repro(String name) { + Repro { + name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testCanonicalRecordConstructorWithGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .options("-d", classes.toString()) + .sources(""" + record Repro(String name) { + Repro(String name) { + this.name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run() + .writeAll(); + } + + @Test + void testBrokenRecordConstructorWithGetElementCall() throws Exception { + Path classes = base.resolve("classes"); + Files.createDirectories(classes); + List out = new JavacTask(tb) + .options("-d", classes.toString(), "-XDrawDiagnostics", "-nowarn") + .sources(""" + record Repro(String name) { + Repro(String name) { + super(); //illegal + this.name = name.trim(); + } + } + """) + .processors(new ProcessorImpl()) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + tb.checkEqual(out, List.of( + "Repro.java:2:5: compiler.err.invalid.canonical.constructor.in.record: (compiler.misc.canonical), Repro, (compiler.misc.canonical.must.not.contain.explicit.constructor.invocation)", + "1 error")); + } + + @SupportedAnnotationTypes("*") + @SupportedOptions(ProcessorImpl.SKIP_GET_ELEMENT) + private static class ProcessorImpl extends AbstractProcessor { + + private static final String SKIP_GET_ELEMENT = "skipGetElement"; + private Trees trees; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + trees = Trees.instance(processingEnv); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return false; + } + for (Element rootElement : roundEnv.getRootElements()) { + TreePath rootPath = trees.getPath(rootElement); + if (rootPath == null) { + continue; + } + new TreePathScanner() { + @Override + public Void visitIdentifier(IdentifierTree node, Void unused) { + TreePath currentPath = getCurrentPath(); + if (!skipGetElement() && insideRecordConstructor(currentPath)) { + processingEnv.getMessager() + .printMessage(Diagnostic.Kind.NOTE, + "Calling Trees.getElement for identifier '" + node.getName() + + "' inside a record constructor"); + trees.getElement(currentPath); + } + return super.visitIdentifier(node, unused); + } + }.scan(rootPath, null); + } + return false; + } + + private boolean skipGetElement() { + return Boolean.parseBoolean(processingEnv.getOptions().get(SKIP_GET_ELEMENT)); + } + + private static boolean insideRecordConstructor(TreePath path) { + TreePath current = path; + while (current != null) { + if (current.getLeaf() instanceof MethodTree method + && method.getReturnType() == null + && current.getParentPath() != null + && current.getParentPath().getLeaf().getKind() == Tree.Kind.RECORD) { + return true; + } + current = current.getParentPath(); + } + return false; + } + } + + @BeforeEach + public void setUp(TestInfo info) { + base = Paths.get(".") + .resolve(info.getTestMethod() + .orElseThrow() + .getName()); + } +} diff --git a/test/lib-test/TEST.ROOT b/test/lib-test/TEST.ROOT index 33c9a9c2a43b..9c9db2998a57 100644 --- a/test/lib-test/TEST.ROOT +++ b/test/lib-test/TEST.ROOT @@ -29,7 +29,7 @@ keys=randomness # Minimum jtreg version -requiredVersion=8.2.1+1 +requiredVersion=8.3+1 # Prevent TestNG-based tests under this root, use @run junit actions instead disallowedActions=testng diff --git a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java index 59e4a1bbbde5..8060eb92a871 100644 --- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java @@ -703,7 +703,7 @@ public static OutputAnalyzer executeAndLog(Process process, String logName) thro static String getCrashMessage(String stdOut) { int start = stdOut.indexOf("# A fatal error has been detected by the Java Runtime Environment:"); - int end = stdOut.indexOf(".log", start) + 4; + int end = stdOut.indexOf("# JRE version", start); return stdOut.substring(start, end); } diff --git a/test/micro/org/openjdk/bench/vm/compiler/MultiplyHighLowFusion.java b/test/micro/org/openjdk/bench/vm/compiler/MultiplyHighLowFusion.java new file mode 100644 index 000000000000..c4f4e5224096 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/MultiplyHighLowFusion.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** + * Benchmarks patterns that may fuse low/high 64-bit multiply operations. + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS) +@Fork(value = 3) +public class MultiplyHighLowFusion { + + @Param("1024") + private int arraySize; + + private long[] lhs; + private long[] rhs; + + @Setup + public void setup() { + Random random = new Random(0x5EED); + lhs = new long[arraySize]; + rhs = new long[arraySize]; + for (int i = 0; i < arraySize; i++) { + lhs[i] = random.nextLong(); + rhs[i] = random.nextLong(); + } + } + + @Benchmark + public long signedLowOnly() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + sum += lhs[i] * rhs[i]; + } + return sum; + } + + @Benchmark + public long signedHighOnly() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + sum += Math.multiplyHigh(lhs[i], rhs[i]); + } + return sum; + } + + @Benchmark + public long signedLowPlusHigh() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + long a = lhs[i]; + long b = rhs[i]; + sum += (a * b) + Math.multiplyHigh(a, b); + } + return sum; + } + + @Benchmark + public long unsignedHighOnly() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + sum += Math.unsignedMultiplyHigh(lhs[i], rhs[i]); + } + return sum; + } + + @Benchmark + public long unsignedLowPlusHigh() { + long sum = 0; + for (int i = 0; i < arraySize; i++) { + long a = lhs[i]; + long b = rhs[i]; + sum += (a * b) + Math.unsignedMultiplyHigh(a, b); + } + return sum; + } +}