From 48c84947b96178f1661b65fcd9592cdc2dddb8c5 Mon Sep 17 00:00:00 2001
From: Volkan Yazici
Date: Thu, 13 Aug 2026 07:46:25 +0000
Subject: [PATCH 01/88] 8387853: HttpServer unexpectedly closes the connection
after first response to empty requests if "drainAmount <= 0"
Reviewed-by: jpai, dfuchs
---
.../httpserver/FixedLengthInputStream.java | 3 +-
.../property/DrainAmountPropertyTest.java | 219 ++++++++++++++++++
2 files changed, 221 insertions(+), 1 deletion(-)
create mode 100644 test/jdk/com/sun/net/httpserver/property/DrainAmountPropertyTest.java
diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthInputStream.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthInputStream.java
index 100ba9e1b3bb..9c5717874177 100644
--- a/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthInputStream.java
+++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2025, 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,6 +45,7 @@ class FixedLengthInputStream extends LeftOverInputStream {
throw new IllegalArgumentException("Content-Length: " + len);
}
this.remaining = len;
+ this.eof = len == 0;
}
protected int readImpl(byte[] b, int off, int len) throws IOException {
diff --git a/test/jdk/com/sun/net/httpserver/property/DrainAmountPropertyTest.java b/test/jdk/com/sun/net/httpserver/property/DrainAmountPropertyTest.java
new file mode 100644
index 000000000000..0b060178b6c9
--- /dev/null
+++ b/test/jdk/com/sun/net/httpserver/property/DrainAmountPropertyTest.java
@@ -0,0 +1,219 @@
+/*
+ * 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 com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import jdk.test.lib.format.Format;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/*
+ * @test id=default
+ * @bug 8387853
+ * @summary Tests the default value of `sun.net.httpserver.drainAmount`,
+ * which is 65,536.
+ *
+ * @library /test/lib
+ *
+ * @run junit/othervm
+ * ${test.main.class}
+ */
+
+/*
+ * @test id=negative
+ * @bug 8387853
+ * @summary Verifies that configuring `sun.net.httpserver.drainAmount` to a
+ * negative value (-1, in this case) results in server closing the
+ * connection if the handler leaves behind an unconsumed request body
+ * of length bigger than 0.
+ *
+ * @library /test/lib
+ *
+ * @run junit/othervm
+ * -Dsun.net.httpserver.drainAmount=-1
+ * ${test.main.class}
+ */
+
+/*
+ * @test id=0
+ * @bug 8387853
+ * @summary Verifies that configuring `sun.net.httpserver.drainAmount` to 0
+ * results in server closing the connection if the handler leaves
+ * behind an unconsumed request body of length bigger than 0.
+ *
+ * @library /test/lib
+ *
+ * @run junit/othervm
+ * -Dsun.net.httpserver.drainAmount=0
+ * ${test.main.class}
+ */
+
+/*
+ * @test id=1
+ * @bug 8387853
+ * @summary Verifies that configuring `sun.net.httpserver.drainAmount` to 1
+ * results in server closing the connection if the handler leaves
+ * behind an unconsumed request body of length greater than or equal
+ * to 1.
+ *
+ * @library /test/lib
+ *
+ * @run junit/othervm
+ * -Dsun.net.httpserver.drainAmount=1
+ * ${test.main.class}
+ */
+
+class DrainAmountPropertyTest {
+
+ /**
+ * The {@code com.sun.net.httpserver} logger anchor to avoid getting it garbage-collected.
+ */
+ private static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
+
+ static {
+ boolean enableLogging = System.getProperty("test.enableLogging") != null;
+ if (enableLogging) {
+ LOGGER.setLevel(Level.ALL); // 0. Set `HttpServer`'s logger to `ALL`
+ Logger.getLogger("") // 1. Get the root logger
+ .getHandlers()[0] // 2. Get its first handler (by default it's a `ConsoleHandler`)
+ .setLevel(Level.ALL); // 3. Sets its level to `ALL` (by default it's `INFO`)
+ }
+ }
+
+ @Test
+ void test() throws Exception {
+
+ // Read the drain amount (i.e., the maximum allowed unconsumed request body length)
+ var drainAmount = Integer.getInteger("sun.net.httpserver.drainAmount", 65536);
+
+ // Create the HTTP server
+ var server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
+ server.createContext("/", new NoContentReturningHandler());
+ server.start();
+
+ // Create the client
+ var serverAddress = server.getAddress();
+ try (var clientSocket = new Socket(serverAddress.getAddress(), serverAddress.getPort())) {
+
+ if (drainAmount > 0) {
+
+ // Send the 1st request containing a body matching the drain amount, that is, no excess.
+ // This should leave the connection in a reusable state.
+ sendRequest(clientSocket, "/?request=reusable", drainAmount - 1);
+
+ // Send the 2nd (empty) request and verify that the connection is still usable
+ sendRequest(clientSocket, "/?request=still-usable", 0);
+
+ }
+
+ // Send requests with zero-length bodies in various forms and verify that the connection is still usable
+ sendRequest(clientSocket, "GET", "/?request=get-no-content-length", null);
+ sendRequest(clientSocket, "GET", "/?request=get-content-length-zero", 0);
+ sendRequest(clientSocket, "POST", "/?request=post-no-content-length", null);
+ sendRequest(clientSocket, "POST", "/?request=post-content-length-zero", 0);
+
+ // Send the 3rd request containing a body exceeding the drain amount and verify the server disconnect
+ assertThrows(SocketException.class, () -> {
+ sendRequest(clientSocket, "/?request=closing", Math.max(1, drainAmount));
+ // Above request might still successfully consume the response before reading the server disconnect.
+ // Hence, send a 4th (empty) request to ensure to observe the socket close.
+ sendRequest(clientSocket, "/?request=after-close", 0);
+ });
+
+ } finally {
+ server.stop(0);
+ }
+
+ }
+
+ private static void sendRequest(
+ Socket clientSocket, String requestTarget, int requestBodyLength)
+ throws IOException {
+ sendRequest(clientSocket, "POST", requestTarget, requestBodyLength);
+ }
+
+ private static void sendRequest(
+ Socket clientSocket, String requestMethod, String requestTarget, Integer requestBodyLength)
+ throws IOException {
+ LOGGER.info("Sending request (target=%s, bodyLength=%s)".formatted(requestTarget, requestBodyLength));
+ var socketOutput = clientSocket.getOutputStream();
+ var request = "%s %s HTTP/1.1%s\r\n\r\n".formatted(
+ requestMethod, requestTarget,
+ requestBodyLength == null ? "" : "\r\nContent-Length: " + requestBodyLength);
+ socketOutput.write(request.getBytes(US_ASCII));
+ if (requestBodyLength != null && requestBodyLength > 0) {
+ socketOutput.write(new byte[requestBodyLength]);
+ }
+ socketOutput.flush();
+ var inputStream = clientSocket.getInputStream();
+ assertEquals("HTTP/1.1 204 No Content", readUntilCrLf(inputStream));
+ // Consume headers, including the terminating empty line
+ while (!readUntilCrLf(inputStream).isEmpty());
+ }
+
+ private static String readUntilCrLf(InputStream inputStream) throws IOException {
+ var buffer = new StringBuilder();
+ var prevChar = -1;
+ while (true) {
+ int nextChar = inputStream.read();
+ if (nextChar < 0) {
+ // Peer disconnect is not expected, escalate it
+ throw new SocketException("EOF after reading: " + Format.asLiteral(buffer));
+ }
+ buffer.append((char) nextChar);
+ if (prevChar == '\r' && nextChar == '\n') {
+ break;
+ }
+ prevChar = nextChar;
+ }
+ // Drop CRLF
+ buffer.setLength(buffer.length() - 2);
+ return buffer.toString();
+ }
+
+ private static final class NoContentReturningHandler implements HttpHandler {
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ LOGGER.info("Received request (target=%s)".formatted(exchange.getRequestURI()));
+ try (exchange) {
+ exchange.sendResponseHeaders(204, HttpExchange.RSPBODY_EMPTY);
+ }
+ }
+
+ }
+
+}
From 3b1f2030f25929ea0dac257385fb32a02af6dd29 Mon Sep 17 00:00:00 2001
From: Matthias Baesken
Date: Thu, 13 Aug 2026 08:25:32 +0000
Subject: [PATCH 02/88] 8390136: ResourceMark needed for external_name calls in
continuationFreezeThaw.cpp
Reviewed-by: pchilanomate, shade
---
src/hotspot/share/runtime/continuationFreezeThaw.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
index 15d994d02b7e..7916d4423d2f 100644
--- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp
+++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
@@ -1747,6 +1747,8 @@ static void verify_frame_kind(frame& top, Continuation::preempt_kind preempt_kin
Method* m;
const char* code_name;
int bci;
+ ResourceMark rm;
+
if (preempt_kind == Continuation::monitorenter) {
assert(top.is_interpreted_frame() || top.is_runtime_frame(), "unexpected %sframe",
top.is_compiled_frame() ? "compiled " : top.is_native_frame() ? "native " : "");
@@ -1763,7 +1765,6 @@ static void verify_frame_kind(frame& top, Continuation::preempt_kind preempt_kin
bci = at_sync_method ? -1 : top.interpreter_frame_bci();
} else {
JavaThread* current = JavaThread::current();
- ResourceMark rm(current);
CodeBlob* cb = top.cb();
RegisterMap reg_map(current,
RegisterMap::UpdateMap::skip,
From 0a2e196bead63468d647476f096e1259eeba2472 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Manuel=20H=C3=A4ssig?=
Date: Thu, 13 Aug 2026 08:33:01 +0000
Subject: [PATCH 03/88] 8390190: [IR-Framework] Integrate valhalla IR nodes
into IRNode.java
Reviewed-by: thartmann, chagedorn, mchevalier
---
.../compiler/lib/ir_framework/IRNode.java | 75 +++++++++++--
.../inlinetypes/InlineTypeIRNode.java | 100 ------------------
.../inlinetypes/InlineTypeRegexes.java | 37 -------
.../valhalla/inlinetypes/TestArrays.java | 20 ++--
.../inlinetypes/TestBasicFunctionality.java | 8 +-
.../inlinetypes/TestCallingConvention.java | 6 +-
.../valhalla/inlinetypes/TestIntrinsics.java | 4 +-
.../valhalla/inlinetypes/TestLWorld.java | 12 +--
.../inlinetypes/TestLWorldProfiling.java | 2 -
.../inlinetypes/TestMethodHandles.java | 9 +-
.../inlinetypes/TestNullableArrays.java | 12 +--
.../inlinetypes/TestNullableInlineTypes.java | 6 +-
.../inlinetypes/TestOnStackReplacement.java | 7 +-
.../inlinetypes/TestValueClasses.java | 4 +-
14 files changed, 113 insertions(+), 189 deletions(-)
delete mode 100644 test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeIRNode.java
delete mode 100644 test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeRegexes.java
diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
index b2961b8ba2bf..6fad572fd37f 100644
--- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
+++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java
@@ -28,7 +28,6 @@
import compiler.lib.ir_framework.shared.CheckedTestFrameworkException;
import compiler.lib.ir_framework.shared.TestFormat;
import compiler.lib.ir_framework.shared.TestFormatException;
-import compiler.valhalla.inlinetypes.InlineTypeIRNode;
import jdk.test.lib.Platform;
import jdk.test.whitebox.WhiteBox;
@@ -152,12 +151,6 @@ public class IRNode {
* }
*/
- // Valhalla: Make sure that all Valhalla specific IR nodes are also properly initialized. Doing it here also
- // ensures that the Flag VM is able to pick up the correct compile phases.
- static {
- InlineTypeIRNode.forceStaticInitialization();
- }
-
public static final String ABS_D = PREFIX + "ABS_D" + POSTFIX;
static {
beforeMatchingNameRegex(ABS_D, "AbsD");
@@ -3362,6 +3355,74 @@ public static void anyStoreOfNodes(String irNodePlaceholder, String fieldHolder)
beforeMatchingNameRegex(OPAQUE_CONSTANT_BOOL, "OpaqueConstantBool");
}
+ /*
+ * Inline type nodes.
+ */
+
+ public static final String CALL_UNSAFE = PREFIX + "CALL_UNSAFE" + POSTFIX;
+ static {
+ staticCallOfMethodNodes(CALL_UNSAFE, "# Static jdk.internal.misc.Unsafe::");
+ }
+
+ public static final String STORE_INLINE_FIELDS = PREFIX + "STORE_INLINE_FIELDS" + POSTFIX;
+ static {
+ staticCallOfMethodNodes(STORE_INLINE_FIELDS, "store_inline_type_fields");
+ }
+
+ public static final String LOAD_UNKNOWN_INLINE = PREFIX + "LOAD_UNKNOWN_INLINE" + POSTFIX;
+ static {
+ staticCallOfMethodNodes(LOAD_UNKNOWN_INLINE, "load_unknown_inline_blob \\(C2 runtime\\)");
+ }
+
+ public static final String STORE_UNKNOWN_INLINE = PREFIX + "STORE_UNKNOWN_INLINE" + POSTFIX;
+ static {
+ staticCallOfMethodNodes(STORE_UNKNOWN_INLINE, "store_unknown_inline_blob \\(C2 runtime\\)");
+ }
+
+ public static final String INLINE_ARRAY_NULL_GUARD = PREFIX + "INLINE_ARRAY_NULL_GUARD" + POSTFIX;
+ static {
+ staticCallOfMethodNodes(INLINE_ARRAY_NULL_GUARD, "null_check' action='none'");
+ }
+
+ public static final String CLONE_INTRINSIC_SLOW_PATH = PREFIX + "CLONE_INTRINSIC_SLOW_PATH" + POSTFIX;
+ static {
+ staticCallOfMethodNodes(CLONE_INTRINSIC_SLOW_PATH, "java.lang.Object::clone");
+ }
+
+ public static final String JLONG_ARRAYCOPY = PREFIX + "JLONG_ARRAYCOPY" + POSTFIX;
+ static {
+ callLeafNoFpOfMethodNodes(JLONG_ARRAYCOPY, "jlong_disjoint_arraycopy");
+ }
+
+ // The following nodes are specific to tests in in compiler/valhalla/inlinetypes using one of the MyValue classes.
+ private static final String MYVALUE_KLASS = "compiler/valhalla/inlinetypes/.*MyValue\\w*";
+ public static final String ALLOC_OF_MYVALUE_KLASS = PREFIX + "ALLOC_OF_MYVALUE_KLASS" + POSTFIX;
+ static {
+ allocateOfNodes(ALLOC_OF_MYVALUE_KLASS, MYVALUE_KLASS);
+ }
+
+ public static final String ALLOC_ARRAY_OF_MYVALUE_KLASS = PREFIX + "ALLOC_ARRAY_OF_MYVALUE_KLASS" + POSTFIX;
+ static {
+ allocateArrayOfNodes(ALLOC_ARRAY_OF_MYVALUE_KLASS, MYVALUE_KLASS);
+ }
+
+ private static final String ANY_KLASS = "compiler/valhalla/inlinetypes/[\\w/]*";
+
+ // TODO: Revisit with JDK-8380875
+ public static final String LOAD_OF_ANY_KLASS = PREFIX + "LOAD_OF_ANY_KLASS" + POSTFIX;
+ static {
+ String loadNode = "Load(B|UB|S|US|I|L|F|D|P|N)";
+ String valueClass = "@instptr:" + ANY_KLASS;
+ String regex = START + loadNode + MID + valueClass + END;
+ beforeMatching(LOAD_OF_ANY_KLASS, regex);
+ }
+
+ // TODO: Revisit with JDK-8380875
+ public static final String STORE_OF_ANY_KLASS = PREFIX + "STORE_OF_ANY_KLASS" + POSTFIX;
+ static {
+ anyStoreOfNodes(STORE_OF_ANY_KLASS, ANY_KLASS);
+ }
+
/*
* Utility methods to set up IR_NODE_MAPPINGS.
*/
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeIRNode.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeIRNode.java
deleted file mode 100644
index b55622fc7da1..000000000000
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeIRNode.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
- * 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.valhalla.inlinetypes;
-
-import compiler.lib.ir_framework.IRNode;
-
-import static compiler.lib.ir_framework.IRNode.*;
-
-public class InlineTypeIRNode {
- private static final String POSTFIX = "#I_";
-
- public static final String CALL_UNSAFE = PREFIX + "CALL_UNSAFE" + POSTFIX;
- static {
- IRNode.staticCallOfMethodNodes(CALL_UNSAFE, InlineTypeRegexes.JDK_INTERNAL_MISC_UNSAFE);
- }
-
- public static final String STORE_INLINE_FIELDS = PREFIX + "STORE_INLINE_FIELDS" + POSTFIX;
- static {
- IRNode.staticCallOfMethodNodes(STORE_INLINE_FIELDS, InlineTypeRegexes.STORE_INLINE_TYPE_FIELDS);
- }
-
- public static final String LOAD_UNKNOWN_INLINE = PREFIX + "LOAD_UNKNOWN_INLINE" + POSTFIX;
- static {
- IRNode.staticCallOfMethodNodes(LOAD_UNKNOWN_INLINE, InlineTypeRegexes.LOAD_UNKNOWN_INLINE);
- }
-
- public static final String STORE_UNKNOWN_INLINE = PREFIX + "STORE_UNKNOWN_INLINE" + POSTFIX;
- static {
- IRNode.staticCallOfMethodNodes(STORE_UNKNOWN_INLINE, InlineTypeRegexes.STORE_UNKNOWN_INLINE);
- }
-
- public static final String INLINE_ARRAY_NULL_GUARD = PREFIX + "INLINE_ARRAY_NULL_GUARD" + POSTFIX;
- static {
- IRNode.staticCallOfMethodNodes(INLINE_ARRAY_NULL_GUARD, InlineTypeRegexes.INLINE_ARRAY_NULL_GUARD);
- }
-
- public static final String CLONE_INTRINSIC_SLOW_PATH = PREFIX + "CLONE_INTRINSIC_SLOW_PATH" + POSTFIX;
- static {
- IRNode.staticCallOfMethodNodes(CLONE_INTRINSIC_SLOW_PATH, InlineTypeRegexes.JAVA_LANG_OBJECT_CLONE);
- }
-
- public static final String CHECKCAST_ARRAYCOPY = PREFIX + "CHECKCAST_ARRAYCOPY" + POSTFIX;
- static {
- IRNode.callLeafNoFpOfMethodNodes(CHECKCAST_ARRAYCOPY, InlineTypeRegexes.CHECKCAST_ARRAYCOPY);
- }
-
- public static final String JLONG_ARRAYCOPY = PREFIX + "JLONG_ARRAYCOPY" + POSTFIX;
- static {
- IRNode.callLeafNoFpOfMethodNodes(JLONG_ARRAYCOPY, InlineTypeRegexes.JLONG_DISJOINT_ARRAYCOPY);
- }
-
- public static final String ALLOC_OF_MYVALUE_KLASS = PREFIX + "ALLOC_OF_MYVALUE_KLASS" + POSTFIX;
- static {
- IRNode.allocateOfNodes(ALLOC_OF_MYVALUE_KLASS, InlineTypeRegexes.MYVALUE_KLASS);
- }
-
- public static final String ALLOC_ARRAY_OF_MYVALUE_KLASS = PREFIX + "ALLOC_ARRAY_OF_MYVALUE_KLASS" + POSTFIX;
- static {
- IRNode.allocateArrayOfNodes(ALLOC_ARRAY_OF_MYVALUE_KLASS, InlineTypeRegexes.MYVALUE_KLASS);
- }
-
- // TODO: Revisit with JDK-8380875
- public static final String LOAD_OF_ANY_KLASS = PREFIX + "LOAD_OF_ANY_KLASS" + POSTFIX;
- static {
- String loadNode = "Load(B|UB|S|US|I|L|F|D|P|N)";
- String valueClass = "@instptr:compiler/valhalla/inlinetypes/[\\w/]*";
- String regex = START + loadNode + MID + valueClass + END;
- IRNode.beforeMatching(LOAD_OF_ANY_KLASS, regex);
- }
-
- // TODO: Revisit with JDK-8380875
- public static final String STORE_OF_ANY_KLASS = PREFIX + "STORE_OF_ANY_KLASS" + POSTFIX;
- static {
- IRNode.anyStoreOfNodes(STORE_OF_ANY_KLASS, InlineTypeRegexes.ANY_KLASS);
- }
-
- // Dummy method to call to force the static initializer blocks to be run before starting the IR framework.
- public static void forceStaticInitialization() {}
-}
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeRegexes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeRegexes.java
deleted file mode 100644
index ee447bf7e11f..000000000000
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/InlineTypeRegexes.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
- * 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.valhalla.inlinetypes;
-
-public class InlineTypeRegexes {
- public static final String MYVALUE_KLASS = "compiler/valhalla/inlinetypes/.*MyValue\\w*";
- public static final String ANY_KLASS = "compiler/valhalla/inlinetypes/[\\w/]*";
- public static final String STORE_INLINE_TYPE_FIELDS = "store_inline_type_fields";
- public static final String JDK_INTERNAL_MISC_UNSAFE = "# Static jdk.internal.misc.Unsafe::";
- public static final String LOAD_UNKNOWN_INLINE = "load_unknown_inline_blob \\(C2 runtime\\)";
- public static final String STORE_UNKNOWN_INLINE = "store_unknown_inline_blob \\(C2 runtime\\)";
- public static final String INLINE_ARRAY_NULL_GUARD = "null_check' action='none'";
- public static final String JLONG_DISJOINT_ARRAYCOPY = "jlong_disjoint_arraycopy";
- public static final String CHECKCAST_ARRAYCOPY = "checkcast_arraycopy";
- public static final String JAVA_LANG_OBJECT_CLONE = "java.lang.Object::clone";
-}
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java
index 2f49b5fe09cc..4966465665c4 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArrays.java
@@ -36,24 +36,24 @@
import java.lang.reflect.Method;
import java.util.Arrays;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.CHECKCAST_ARRAYCOPY;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.CLONE_INTRINSIC_SLOW_PATH;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.INLINE_ARRAY_NULL_GUARD;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.JLONG_ARRAYCOPY;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_UNKNOWN_INLINE;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_UNKNOWN_INLINE;
import static compiler.valhalla.inlinetypes.InlineTypes.*;
import static compiler.lib.ir_framework.IRNode.ALLOC;
import static compiler.lib.ir_framework.IRNode.ALLOC_ARRAY;
+import static compiler.lib.ir_framework.IRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS;
+import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS;
+import static compiler.lib.ir_framework.IRNode.CHECKCAST_ARRAYCOPY;
import static compiler.lib.ir_framework.IRNode.CLASS_CHECK_TRAP;
+import static compiler.lib.ir_framework.IRNode.CLONE_INTRINSIC_SLOW_PATH;
+import static compiler.lib.ir_framework.IRNode.INLINE_ARRAY_NULL_GUARD;
import static compiler.lib.ir_framework.IRNode.INTRINSIC_TRAP;
+import static compiler.lib.ir_framework.IRNode.JLONG_ARRAYCOPY;
+import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS;
+import static compiler.lib.ir_framework.IRNode.LOAD_UNKNOWN_INLINE;
import static compiler.lib.ir_framework.IRNode.LOOP;
import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP;
+import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS;
+import static compiler.lib.ir_framework.IRNode.STORE_UNKNOWN_INLINE;
import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP;
/*
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java
index 6519a4191bd5..848570c04a6c 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestBasicFunctionality.java
@@ -33,15 +33,15 @@
import jdk.internal.vm.annotation.NullRestricted;
import jdk.test.whitebox.WhiteBox;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS;
import static compiler.valhalla.inlinetypes.InlineTypes.*;
+import static compiler.lib.ir_framework.IRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS;
+import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS;
+import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS;
import static compiler.lib.ir_framework.IRNode.LOOP;
import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP;
import static compiler.lib.ir_framework.IRNode.SCOPE_OBJECT;
+import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS;
import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP;
/*
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java
index 7e38e3e0092f..4453515dd959 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestCallingConvention.java
@@ -32,14 +32,14 @@
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS;
import static compiler.valhalla.inlinetypes.InlineTypes.*;
import static compiler.lib.ir_framework.IRNode.ALLOC;
+import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS;
import static compiler.lib.ir_framework.IRNode.CALL_OF_METHOD;
+import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS;
import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP;
+import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS;
import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP;
import jdk.internal.value.ValueClass;
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java
index b2b81b92eceb..23b42bad191b 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestIntrinsics.java
@@ -36,13 +36,13 @@
import jdk.internal.vm.annotation.NullRestricted;
import jdk.test.whitebox.WhiteBox;
-import static compiler.lib.ir_framework.IRNode.STATIC_CALL_OF_METHOD;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.CALL_UNSAFE;
import static compiler.valhalla.inlinetypes.InlineTypes.rI;
import static compiler.valhalla.inlinetypes.InlineTypes.rL;
+import static compiler.lib.ir_framework.IRNode.CALL_UNSAFE;
import static compiler.lib.ir_framework.IRNode.LOAD;
import static compiler.lib.ir_framework.IRNode.LOAD_KLASS;
+import static compiler.lib.ir_framework.IRNode.STATIC_CALL_OF_METHOD;
/*
* @test
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java
index c049a8548fe8..513ee0c20644 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorld.java
@@ -39,27 +39,27 @@
import jdk.internal.vm.annotation.LooselyConsistentValue;
import jdk.internal.vm.annotation.NullRestricted;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.INLINE_ARRAY_NULL_GUARD;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_UNKNOWN_INLINE;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_UNKNOWN_INLINE;
import static compiler.valhalla.inlinetypes.InlineTypes.*;
import static compiler.lib.ir_framework.IRNode.ALLOC;
+import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS;
import static compiler.lib.ir_framework.IRNode.CLASS_CHECK_TRAP;
import static compiler.lib.ir_framework.IRNode.COUNTED_LOOP;
import static compiler.lib.ir_framework.IRNode.COUNTED_LOOP_MAIN;
import static compiler.lib.ir_framework.IRNode.DYNAMIC_CALL_OF_METHOD;
import static compiler.lib.ir_framework.IRNode.FIELD_ACCESS;
+import static compiler.lib.ir_framework.IRNode.INLINE_ARRAY_NULL_GUARD;
import static compiler.lib.ir_framework.IRNode.LOAD;
+import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS;
import static compiler.lib.ir_framework.IRNode.LOAD_P;
+import static compiler.lib.ir_framework.IRNode.LOAD_UNKNOWN_INLINE;
import static compiler.lib.ir_framework.IRNode.LOOP;
import static compiler.lib.ir_framework.IRNode.MEMBAR;
import static compiler.lib.ir_framework.IRNode.NULL_CHECK_TRAP;
import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP;
import static compiler.lib.ir_framework.IRNode.STATIC_CALL_OF_METHOD;
+import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS;
+import static compiler.lib.ir_framework.IRNode.STORE_UNKNOWN_INLINE;
import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP;
/*
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java
index 3ff5a0c86f59..a45ab06e49c8 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestLWorldProfiling.java
@@ -34,8 +34,6 @@
import jdk.internal.vm.annotation.NullRestricted;
import static compiler.lib.ir_framework.IRNode.*;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_UNKNOWN_INLINE;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_UNKNOWN_INLINE;
import static compiler.valhalla.inlinetypes.InlineTypes.*;
/*
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java
index 9ad94fc5b728..150e0baaedec 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestMethodHandles.java
@@ -34,10 +34,11 @@
import jdk.internal.vm.annotation.NullRestricted;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_INLINE_FIELDS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS;
+import static compiler.lib.ir_framework.IRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS;
+import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS;
+import static compiler.lib.ir_framework.IRNode.STORE_INLINE_FIELDS;
+import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS;
+
import static compiler.valhalla.inlinetypes.InlineTypes.*;
import static compiler.lib.ir_framework.IRNode.STATIC_CALL;
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java
index 4faf637f99f7..9f8242cdd9e9 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableArrays.java
@@ -26,19 +26,19 @@
import jdk.test.lib.Asserts;
import compiler.lib.ir_framework.*;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_UNKNOWN_INLINE;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_UNKNOWN_INLINE;
import static compiler.valhalla.inlinetypes.InlineTypes.rI;
import static compiler.valhalla.inlinetypes.InlineTypes.rL;
import static compiler.valhalla.inlinetypes.InlineTypes.rD;
import static compiler.lib.ir_framework.IRNode.ALLOC;
+import static compiler.lib.ir_framework.IRNode.ALLOC_ARRAY_OF_MYVALUE_KLASS;
+import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS;
+import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS;
+import static compiler.lib.ir_framework.IRNode.LOAD_UNKNOWN_INLINE;
import static compiler.lib.ir_framework.IRNode.LOOP;
import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP;
+import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS;
+import static compiler.lib.ir_framework.IRNode.STORE_UNKNOWN_INLINE;
import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP;
import jdk.internal.value.ValueClass;
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java
index 61d923ed629b..43446ec688d2 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestNullableInlineTypes.java
@@ -38,15 +38,15 @@
import jdk.internal.vm.annotation.LooselyConsistentValue;
import jdk.internal.vm.annotation.NullRestricted;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS;
import static compiler.valhalla.inlinetypes.InlineTypes.*;
import static compiler.lib.ir_framework.IRNode.ALLOC;
+import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS;
import static compiler.lib.ir_framework.IRNode.CMP_N;
import static compiler.lib.ir_framework.IRNode.CMP_P;
+import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS;
import static compiler.lib.ir_framework.IRNode.PREDICATE_TRAP;
+import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS;
import static compiler.lib.ir_framework.IRNode.UNSTABLE_IF_TRAP;
/*
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java
index e6b647b6dfba..cdc1bc09e54f 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestOnStackReplacement.java
@@ -30,9 +30,10 @@
import jdk.internal.vm.annotation.LooselyConsistentValue;
import jdk.internal.vm.annotation.NullRestricted;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.LOAD_OF_ANY_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS;
+import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS;
+import static compiler.lib.ir_framework.IRNode.LOAD_OF_ANY_KLASS;
+import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS;
+
import static compiler.valhalla.inlinetypes.InlineTypes.rI;
import static compiler.valhalla.inlinetypes.InlineTypes.rL;
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java
index 7ab9f1b710be..ebf246cd7d60 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestValueClasses.java
@@ -28,11 +28,11 @@
import java.lang.reflect.Method;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.ALLOC_OF_MYVALUE_KLASS;
-import static compiler.valhalla.inlinetypes.InlineTypeIRNode.STORE_OF_ANY_KLASS;
import static compiler.valhalla.inlinetypes.InlineTypes.*;
import static compiler.lib.ir_framework.IRNode.ALLOC;
+import static compiler.lib.ir_framework.IRNode.ALLOC_OF_MYVALUE_KLASS;
+import static compiler.lib.ir_framework.IRNode.STORE_OF_ANY_KLASS;
import jdk.internal.vm.annotation.LooselyConsistentValue;
import jdk.internal.vm.annotation.NullRestricted;
From 34dda4ecd89e07d4530f9c26e88f9968788d695f Mon Sep 17 00:00:00 2001
From: Fredrik Bredberg
Date: Thu, 13 Aug 2026 08:40:01 +0000
Subject: [PATCH 04/88] 8389325: Remove the UseObjectMonitorTable flag and
related code
Reviewed-by: stefank, coleenp
---
.../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 106 +++---
.../cpu/aarch64/macroAssembler_aarch64.cpp | 16 +-
src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 151 +++-----
src/hotspot/cpu/ppc/ppc.ad | 19 +-
src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 3 +-
.../cpu/riscv/c2_MacroAssembler_riscv.cpp | 103 +++---
.../cpu/riscv/macroAssembler_riscv.cpp | 16 +-
src/hotspot/cpu/s390/macroAssembler_s390.cpp | 137 +++-----
src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 139 +++-----
src/hotspot/cpu/x86/macroAssembler_x86.cpp | 18 +-
src/hotspot/cpu/x86/sharedRuntime_x86.cpp | 8 +-
.../gc/shenandoah/shenandoahArguments.cpp | 12 +-
.../gc/shenandoah/shenandoahHeap.inline.hpp | 11 +-
src/hotspot/share/oops/markWord.cpp | 8 -
src/hotspot/share/oops/markWord.hpp | 22 +-
src/hotspot/share/opto/library_call.cpp | 13 -
src/hotspot/share/runtime/arguments.cpp | 13 -
src/hotspot/share/runtime/arguments.hpp | 1 -
src/hotspot/share/runtime/basicLock.cpp | 25 +-
.../share/runtime/basicLock.inline.hpp | 5 +-
src/hotspot/share/runtime/deoptimization.cpp | 19 +-
src/hotspot/share/runtime/globals.hpp | 4 -
.../share/runtime/javaThread.inline.hpp | 5 +-
src/hotspot/share/runtime/objectMonitor.cpp | 78 +---
src/hotspot/share/runtime/objectMonitor.hpp | 11 +-
.../share/runtime/objectMonitor.inline.hpp | 11 +-
src/hotspot/share/runtime/sharedRuntime.cpp | 10 +-
src/hotspot/share/runtime/synchronizer.cpp | 332 ++----------------
src/hotspot/share/runtime/synchronizer.hpp | 2 -
.../classes/sun/jvm/hotspot/oops/Mark.java | 19 +-
.../hotspot/runtime/ObjectSynchronizer.java | 9 +-
.../runtime/CommandLine/VMOptionWarning.java | 34 --
...CompressedClassPointersEncodingScheme.java | 1 -
.../Monitor/UseObjectMonitorTableTest.java | 7 +-
34 files changed, 348 insertions(+), 1020 deletions(-)
diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
index 4af590d51323..55cfd0756f6f 100644
--- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
@@ -182,10 +182,8 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1,
// Finish fast lock unsuccessfully. MUST branch to with flag == NE
Label slow_path;
- if (UseObjectMonitorTable) {
- // Clear cache in case fast locking succeeds or we need to take the slow-path.
- str(zr, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
- }
+ // Clear cache in case fast locking succeeds or we need to take the slow-path.
+ str(zr, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(t1, obj, rscratch2);
@@ -245,60 +243,55 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1,
const ByteSize omc_monitor_offset = OMCache::monitor_offset();
const ByteSize omc_obj_offset = OMCache::obj_offset();
- if (!UseObjectMonitorTable) {
- assert(t1_monitor == t1_mark, "should be the same here");
- } else {
- const Register t1_hash = t1;
- Label monitor_found;
+ const Register t1_hash = t1;
+ Label monitor_found;
- // Save the mark, we might need it to extract the hash.
- mov(t3, t1_mark);
+ // Save the mark, we might need it to extract the hash.
+ mov(t3, t1_mark);
- // Look for the monitor in the current thread's object monitor cache (omc).
+ // Look for the monitor in the current thread's object monitor cache (omc).
- ldr(t1_monitor, Address(rthread, thr_omc_offset + omc_monitor_offset));
- ldr(t2, Address(rthread, thr_omc_offset + omc_obj_offset));
- cmp(obj, t2);
- br(Assembler::EQ, monitor_found);
+ ldr(t1_monitor, Address(rthread, thr_omc_offset + omc_monitor_offset));
+ ldr(t2, Address(rthread, thr_omc_offset + omc_obj_offset));
+ cmp(obj, t2);
+ br(Assembler::EQ, monitor_found);
- // Look for the monitor in the table.
+ // Look for the monitor in the table.
- // Get the hash code.
- ubfx(t1_hash, t3, markWord::hash_shift, markWord::hash_bits);
+ // Get the hash code.
+ ubfx(t1_hash, t3, markWord::hash_shift, markWord::hash_bits);
- // Get the table and calculate the bucket's address
- lea(t3, ExternalAddress(ObjectMonitorTable::current_table_address()));
- ldr(t3, Address(t3));
- ldr(t2, Address(t3, ObjectMonitorTable::table_capacity_mask_offset()));
- ands(t1_hash, t1_hash, t2);
- ldr(t3, Address(t3, ObjectMonitorTable::table_buckets_offset()));
+ // Get the table and calculate the bucket's address
+ lea(t3, ExternalAddress(ObjectMonitorTable::current_table_address()));
+ ldr(t3, Address(t3));
+ ldr(t2, Address(t3, ObjectMonitorTable::table_capacity_mask_offset()));
+ ands(t1_hash, t1_hash, t2);
+ ldr(t3, Address(t3, ObjectMonitorTable::table_buckets_offset()));
- // Read the monitor from the bucket.
- ldr(t1_monitor, Address(t3, t1_hash, Address::lsl(LogBytesPerWord)));
+ // Read the monitor from the bucket.
+ ldr(t1_monitor, Address(t3, t1_hash, Address::lsl(LogBytesPerWord)));
- // Check if the monitor in the bucket is special (empty, tombstone or removed).
- cmp(t1_monitor, (unsigned char)ObjectMonitorTable::SpecialPointerValues::below_is_special);
- br(Assembler::LO, slow_path);
+ // Check if the monitor in the bucket is special (empty, tombstone or removed).
+ cmp(t1_monitor, (unsigned char)ObjectMonitorTable::SpecialPointerValues::below_is_special);
+ br(Assembler::LO, slow_path);
- // Check if object matches.
- ldr(t3, Address(t1_monitor, ObjectMonitor::object_offset()));
- BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
- bs_asm->try_peek_weak_handle_in_nmethod(this, t3, t3, t2, slow_path);
- cmp(t3, obj);
- br(Assembler::NE, slow_path);
+ // Check if object matches.
+ ldr(t3, Address(t1_monitor, ObjectMonitor::object_offset()));
+ BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs_asm->try_peek_weak_handle_in_nmethod(this, t3, t3, t2, slow_path);
+ cmp(t3, obj);
+ br(Assembler::NE, slow_path);
- // Store the monitor in the current thread's object monitor cache (omc).
- str(t1_monitor, Address(rthread, thr_omc_offset + omc_monitor_offset));
- str(obj, Address(rthread, thr_omc_offset + omc_obj_offset));
+ // Store the monitor in the current thread's object monitor cache (omc).
+ str(t1_monitor, Address(rthread, thr_omc_offset + omc_monitor_offset));
+ str(obj, Address(rthread, thr_omc_offset + omc_obj_offset));
- bind(monitor_found);
- }
+ bind(monitor_found);
const Register t2_owner_addr = t2;
const Register t3_owner = t3;
- const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value));
- const Address owner_address(t1_monitor, ObjectMonitor::owner_offset() - monitor_tag);
- const Address recursions_address(t1_monitor, ObjectMonitor::recursions_offset() - monitor_tag);
+ const Address owner_address(t1_monitor, ObjectMonitor::owner_offset());
+ const Address recursions_address(t1_monitor, ObjectMonitor::recursions_offset());
Label monitor_locked;
@@ -318,10 +311,8 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register t1,
increment(recursions_address, 1);
bind(monitor_locked);
- if (UseObjectMonitorTable) {
- // Cache the monitor for unlock.
- str(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
- }
+ // Cache the monitor for unlock.
+ str(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
}
bind(locked);
@@ -388,7 +379,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box, Register t1,
// Because we got here by popping (meaning we pushed in locked)
// there will be no monitor in the box. So we need to push back the obj
// so that the runtime can fix any potential anonymous owner.
- tbnz(t1_mark, exact_log2(markWord::monitor_value), UseObjectMonitorTable ? push_and_slow_path : inflated);
+ tbnz(t1_mark, exact_log2(markWord::monitor_value), push_and_slow_path);
// Try to unlock. Transition lock bits 0b00 => 0b01
assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea");
@@ -430,17 +421,10 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box, Register t1,
const Register t1_monitor = t1;
- if (!UseObjectMonitorTable) {
- assert(t1_monitor == t1_mark, "should be the same here");
-
- // Untag the monitor.
- add(t1_monitor, t1_mark, -(int)markWord::monitor_value);
- } else {
- ldr(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
- // null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*)
- cmp(t1_monitor, checked_cast(alignof(ObjectMonitor*)));
- br(Assembler::LO, slow_path);
- }
+ ldr(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
+ // null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*)
+ cmp(t1_monitor, checked_cast(alignof(ObjectMonitor*)));
+ br(Assembler::LO, slow_path);
const Register t2_recursions = t2;
Label not_recursive;
@@ -3032,4 +3016,4 @@ void C2_MacroAssembler::sve_sdiv_short(FloatRegister dst_src1, FloatRegister src
sve_sdiv(src1, S, ptrue, vtmp2);
// Narrow the two INT result halves back to SHORT.
sve_uzp1(dst_src1, H, vtmp1, src1);
-}
\ No newline at end of file
+}
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
index cf5ccb2c2863..78f8a86dcf34 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
@@ -2412,16 +2412,6 @@ void MacroAssembler::test_field_is_flat(Register flags, Register temp_reg, Label
void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int32_t test_bit, bool jmp_set, Label& jmp_label) {
// load mark word
ldr(temp_reg, Address(oop, oopDesc::mark_offset_in_bytes()));
- if (!UseObjectMonitorTable) {
- Label test_mark_word;
- // check displaced
- tst(temp_reg, markWord::unlocked_value);
- br(Assembler::NE, test_mark_word);
- // slow path use klass prototype
- load_prototype_header(temp_reg, oop);
-
- bind(test_mark_word);
- }
andr(temp_reg, temp_reg, test_bit);
if (jmp_set) {
cbnz(temp_reg, jmp_label);
@@ -7901,10 +7891,8 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register t1, R
// instruction emitted as it is part of C1's null check semantics.
ldr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
- if (UseObjectMonitorTable) {
- // Clear cache in case fast locking succeeds or we need to take the slow-path.
- str(zr, Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))));
- }
+ // Clear cache in case fast locking succeeds or we need to take the slow-path.
+ str(zr, Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))));
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(t1, obj, rscratch1);
diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
index fe0ac25f58c3..e7bf14dad340 100644
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
@@ -2688,7 +2688,6 @@ void MacroAssembler::tlab_allocate(
void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register obj, Register box,
Register tmp1, Register tmp2, Register tmp3) {
assert_different_registers(obj, box, tmp1, tmp2, tmp3);
- assert(UseObjectMonitorTable || tmp3 == noreg, "tmp3 not needed");
assert(flag == CR0, "bad condition register");
// Handle inflated monitor.
@@ -2698,11 +2697,9 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register
// Finish fast lock unsuccessfully. MUST branch to with flag == EQ
Label slow_path;
- if (UseObjectMonitorTable) {
- // Clear cache in case fast locking succeeds or we need to take the slow-path.
- li(tmp1, 0);
- std(tmp1, in_bytes(BasicObjectLock::lock_offset()) + BasicLock::object_monitor_cache_offset_in_bytes(), box);
- }
+ // Clear cache in case fast locking succeeds or we need to take the slow-path.
+ li(tmp1, 0);
+ std(tmp1, in_bytes(BasicObjectLock::lock_offset()) + BasicLock::object_monitor_cache_offset_in_bytes(), box);
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(tmp1, obj);
@@ -2760,9 +2757,9 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register
// mark contains the tagged ObjectMonitor*.
const uintptr_t monitor_tag = markWord::monitor_value;
- const Register monitor = UseObjectMonitorTable ? tmp1 : noreg;
+ const Register monitor = tmp1;
const Register owner_addr = tmp2;
- const Register thread_id = UseObjectMonitorTable ? tmp3 : tmp1;
+ const Register thread_id = tmp3;
// Offsets into the current thread's object monitor cache (omc).
const ByteSize thr_omc_offset = JavaThread::om_cache_offset();
const ByteSize omc_monitor_offset = OMCache::monitor_offset();
@@ -2770,61 +2767,55 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register
Label monitor_locked;
- if (!UseObjectMonitorTable) {
- // Compute owner address.
- addi(owner_addr, mark, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag);
- mark = noreg;
- } else {
- const Register tmp3_bucket = tmp3;
- const Register tmp2_hash = tmp2;
- Label monitor_found;
+ const Register tmp3_bucket = tmp3;
+ const Register tmp2_hash = tmp2;
+ Label monitor_found;
- // Save the mark, we might need it to extract the hash.
- mr(tmp2_hash, mark);
+ // Save the mark, we might need it to extract the hash.
+ mr(tmp2_hash, mark);
- // Look for the monitor in the current thread's object monitor cache (omc).
+ // Look for the monitor in the current thread's object monitor cache (omc).
- ld(R0, in_bytes(thr_omc_offset + omc_obj_offset), R16_thread);
- ld(monitor, in_bytes(thr_omc_offset + omc_monitor_offset), R16_thread);
- cmpd(CR0, R0, obj);
- beq(CR0, monitor_found);
+ ld(R0, in_bytes(thr_omc_offset + omc_obj_offset), R16_thread);
+ ld(monitor, in_bytes(thr_omc_offset + omc_monitor_offset), R16_thread);
+ cmpd(CR0, R0, obj);
+ beq(CR0, monitor_found);
- // Look for the monitor in the table.
+ // Look for the monitor in the table.
- // Get the hash code.
- srdi(tmp2_hash, tmp2_hash, markWord::hash_shift);
+ // Get the hash code.
+ srdi(tmp2_hash, tmp2_hash, markWord::hash_shift);
- // Get the table and calculate the bucket's address
- int simm16_rest = load_const_optimized(tmp3, ObjectMonitorTable::current_table_address(), R0, true);
- ld_ptr(tmp3, simm16_rest, tmp3);
- ld(tmp1, in_bytes(ObjectMonitorTable::table_capacity_mask_offset()), tmp3);
- andr(tmp2_hash, tmp2_hash, tmp1);
- ld(tmp3_bucket, in_bytes(ObjectMonitorTable::table_buckets_offset()), tmp3);
+ // Get the table and calculate the bucket's address
+ int simm16_rest = load_const_optimized(tmp3, ObjectMonitorTable::current_table_address(), R0, true);
+ ld_ptr(tmp3, simm16_rest, tmp3);
+ ld(tmp1, in_bytes(ObjectMonitorTable::table_capacity_mask_offset()), tmp3);
+ andr(tmp2_hash, tmp2_hash, tmp1);
+ ld(tmp3_bucket, in_bytes(ObjectMonitorTable::table_buckets_offset()), tmp3);
- // Read the monitor from the bucket.
- sldi(tmp2_hash, tmp2_hash, LogBytesPerWord);
- ldx(monitor, tmp3_bucket, tmp2_hash);
+ // Read the monitor from the bucket.
+ sldi(tmp2_hash, tmp2_hash, LogBytesPerWord);
+ ldx(monitor, tmp3_bucket, tmp2_hash);
- // Check if the monitor in the bucket is special (empty, tombstone or removed).
- cmpldi(CR0, monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special);
- blt(CR0, slow_path);
+ // Check if the monitor in the bucket is special (empty, tombstone or removed).
+ cmpldi(CR0, monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special);
+ blt(CR0, slow_path);
- // Check if object matches.
- ld(tmp3, in_bytes(ObjectMonitor::object_offset()), monitor);
- BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
- bs_asm->try_peek_weak_handle_in_nmethod(this, tmp3, tmp3, tmp2, slow_path);
- cmpd(CR0, tmp3, obj);
- bne(CR0, slow_path);
+ // Check if object matches.
+ ld(tmp3, in_bytes(ObjectMonitor::object_offset()), monitor);
+ BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs_asm->try_peek_weak_handle_in_nmethod(this, tmp3, tmp3, tmp2, slow_path);
+ cmpd(CR0, tmp3, obj);
+ bne(CR0, slow_path);
- // Store the monitor in the current thread's object monitor cache (omc).
- std(monitor, in_bytes(thr_omc_offset + omc_monitor_offset), R16_thread);
- std(obj, in_bytes(thr_omc_offset + omc_obj_offset), R16_thread);
+ // Store the monitor in the current thread's object monitor cache (omc).
+ std(monitor, in_bytes(thr_omc_offset + omc_monitor_offset), R16_thread);
+ std(obj, in_bytes(thr_omc_offset + omc_obj_offset), R16_thread);
- bind(monitor_found);
+ bind(monitor_found);
- // Compute owner address.
- addi(owner_addr, monitor, in_bytes(ObjectMonitor::owner_offset()));
- }
+ // Compute owner address.
+ addi(owner_addr, monitor, in_bytes(ObjectMonitor::owner_offset()));
// Try to CAS owner (no owner => current thread's _monitor_owner_id).
assert_different_registers(thread_id, monitor, owner_addr, box, R0);
@@ -2843,23 +2834,14 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register
bne(CR0, slow_path);
// Recursive.
- if (!UseObjectMonitorTable) {
- assert_different_registers(tmp1, owner_addr);
- ld(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr);
- addi(tmp1, tmp1, 1);
- std(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr);
- } else {
- assert_different_registers(tmp2, monitor);
- ld(tmp2, in_bytes(ObjectMonitor::recursions_offset()), monitor);
- addi(tmp2, tmp2, 1);
- std(tmp2, in_bytes(ObjectMonitor::recursions_offset()), monitor);
- }
+ assert_different_registers(tmp2, monitor);
+ ld(tmp2, in_bytes(ObjectMonitor::recursions_offset()), monitor);
+ addi(tmp2, tmp2, 1);
+ std(tmp2, in_bytes(ObjectMonitor::recursions_offset()), monitor);
bind(monitor_locked);
- if (UseObjectMonitorTable) {
- // Cache the monitor for unlock.
- std(monitor, BasicLock::object_monitor_cache_offset_in_bytes(), box);
- }
+ // Cache the monitor for unlock.
+ std(monitor, BasicLock::object_monitor_cache_offset_in_bytes(), box);
}
bind(locked);
@@ -2926,11 +2908,7 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe
// Check for monitor (0b10).
ld(mark, oopDesc::mark_offset_in_bytes(), obj);
andi_(t, mark, markWord::monitor_value);
- if (!UseObjectMonitorTable) {
- bne(CR0, inflated);
- } else {
- bne(CR0, push_and_slow);
- }
+ bne(CR0, push_and_slow);
#ifdef ASSERT
// Check header not unlocked (0b01).
@@ -2980,15 +2958,10 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe
const Register monitor = mark;
const uintptr_t monitor_tag = markWord::monitor_value;
- if (!UseObjectMonitorTable) {
- // Untag the monitor.
- subi(monitor, mark, monitor_tag);
- } else {
- ld(monitor, BasicLock::object_monitor_cache_offset_in_bytes(), box);
- // null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*)
- cmpldi(CR0, monitor, checked_cast(alignof(ObjectMonitor*)));
- blt(CR0, slow_path);
- }
+ ld(monitor, BasicLock::object_monitor_cache_offset_in_bytes(), box);
+ // null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*)
+ cmpldi(CR0, monitor, checked_cast(alignof(ObjectMonitor*)));
+ blt(CR0, slow_path);
const Register recursions = tmp2;
Label not_recursive;
@@ -3359,16 +3332,6 @@ void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int
Label& jmp_label, bool maybe_far) {
// load mark word
ld(temp_reg, oopDesc::mark_offset_in_bytes(), oop);
- if (!UseObjectMonitorTable) {
- Label test_mark_word;
- // if unlocked bit is set we can directly use the mark word
- andi_(R0, temp_reg, markWord::unlocked_value);
- bne(CR0, test_mark_word);
- // slow path use klass prototype
- load_prototype_header(temp_reg, oop);
-
- bind(test_mark_word);
- }
andi_(R0, temp_reg, test_bit);
if (maybe_far) {
bc_far_optimized(jmp_set ? Assembler::bcondCRbiIs0 : Assembler::bcondCRbiIs1,
@@ -4907,11 +4870,9 @@ void MacroAssembler::fast_lock(Register box, Register obj, Register t1, Register
Label push;
const Register t = R0;
- if (UseObjectMonitorTable) {
- // Clear cache in case fast locking succeeds or we need to take the slow-path.
- li(t, 0);
- std(t, in_bytes(BasicObjectLock::lock_offset()) + BasicLock::object_monitor_cache_offset_in_bytes(), box);
- }
+ // Clear cache in case fast locking succeeds or we need to take the slow-path.
+ li(t, 0);
+ std(t, in_bytes(BasicObjectLock::lock_offset()) + BasicLock::object_monitor_cache_offset_in_bytes(), box);
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(t1, obj);
diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad
index 8d9bf3ab0b2e..3681165812a5 100644
--- a/src/hotspot/cpu/ppc/ppc.ad
+++ b/src/hotspot/cpu/ppc/ppc.ad
@@ -10985,24 +10985,7 @@ instruct partialSubtypeCheckConstSuper(rarg3RegP sub, rarg2RegP super_reg, immP
// inlined locking and unlocking
-instruct cmpFastLock(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2) %{
- predicate(!UseObjectMonitorTable);
- match(Set crx (FastLock oop box));
- effect(TEMP tmp1, TEMP tmp2);
-
- format %{ "FASTLOCK $oop, $box, $tmp1, $tmp2" %}
- ins_encode %{
- __ fast_lock($crx$$CondRegister, $oop$$Register, $box$$Register,
- $tmp1$$Register, $tmp2$$Register, noreg /*tmp3*/);
- // If locking was successful, crx should indicate 'EQ'.
- // The compiler generates a branch to the runtime call to
- // _complete_monitor_locking_Java for the case where crx is 'NE'.
- %}
- ins_pipe(pipe_class_compare);
-%}
-
-instruct cmpFastLockMonitorTable(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3, flagsRegCR1 cr1) %{
- predicate(UseObjectMonitorTable);
+instruct cmpFastLock(flagsRegCR0 crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3, flagsRegCR1 cr1) %{
match(Set crx (FastLock oop box));
effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr1);
diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
index 6b5f1bc70a74..364ed7de3b07 100644
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
@@ -2525,8 +2525,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm,
// Try fastpath for locking.
// fast_lock kills r_temp_1, r_temp_2, r_temp_3.
- Register r_temp_3_or_noreg = UseObjectMonitorTable ? r_temp_3 : noreg;
- __ compiler_fast_lock_object(CR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3_or_noreg);
+ __ compiler_fast_lock_object(CR0, r_oop, r_box, r_temp_1, r_temp_2, r_temp_3);
__ beq(CR0, locked);
// None of the above fast optimizations worked so we have to get into the
diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
index 011d20e2f9bf..e70840e9e52b 100644
--- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
@@ -84,10 +84,8 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box,
// Finish fast lock unsuccessfully. slow_path MUST branch to with flag != 0
Label slow_path;
- if (UseObjectMonitorTable) {
- // Clear cache in case fast locking succeeds or we need to take the slow-path.
- sd(zr, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
- }
+ // Clear cache in case fast locking succeeds or we need to take the slow-path.
+ sd(zr, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(tmp1, obj);
@@ -149,61 +147,56 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box,
const ByteSize omc_monitor_offset = OMCache::monitor_offset();
const ByteSize omc_obj_offset = OMCache::obj_offset();
- if (!UseObjectMonitorTable) {
- assert(tmp1_monitor == tmp1_mark, "should be the same here");
- } else {
- const Register tmp2_hash = tmp2;
- const Register tmp3_bucket = tmp3;
- Label monitor_found;
+ const Register tmp2_hash = tmp2;
+ const Register tmp3_bucket = tmp3;
+ Label monitor_found;
- // Save the mark, we might need it to extract the hash.
- mv(tmp2_hash, tmp1_mark);
+ // Save the mark, we might need it to extract the hash.
+ mv(tmp2_hash, tmp1_mark);
- // Look for the monitor in the current thread's object monitor cache (omc).
+ // Look for the monitor in the current thread's object monitor cache (omc).
- ld(tmp1_monitor, Address(xthread, thr_omc_offset + omc_monitor_offset));
- ld(tmp4, Address(xthread, thr_omc_offset + omc_obj_offset));
- beq(obj, tmp4, monitor_found);
+ ld(tmp1_monitor, Address(xthread, thr_omc_offset + omc_monitor_offset));
+ ld(tmp4, Address(xthread, thr_omc_offset + omc_obj_offset));
+ beq(obj, tmp4, monitor_found);
- // Look for the monitor in the table.
+ // Look for the monitor in the table.
- // Get the hash code.
- srli(tmp2_hash, tmp2_hash, markWord::hash_shift);
+ // Get the hash code.
+ srli(tmp2_hash, tmp2_hash, markWord::hash_shift);
- // Get the table and calculate the bucket's address.
- la(tmp3_t, ExternalAddress(ObjectMonitorTable::current_table_address()));
- ld(tmp3_t, Address(tmp3_t));
- ld(tmp1, Address(tmp3_t, ObjectMonitorTable::table_capacity_mask_offset()));
- andr(tmp2_hash, tmp2_hash, tmp1);
- ld(tmp3_t, Address(tmp3_t, ObjectMonitorTable::table_buckets_offset()));
+ // Get the table and calculate the bucket's address.
+ la(tmp3_t, ExternalAddress(ObjectMonitorTable::current_table_address()));
+ ld(tmp3_t, Address(tmp3_t));
+ ld(tmp1, Address(tmp3_t, ObjectMonitorTable::table_capacity_mask_offset()));
+ andr(tmp2_hash, tmp2_hash, tmp1);
+ ld(tmp3_t, Address(tmp3_t, ObjectMonitorTable::table_buckets_offset()));
- // Read the monitor from the bucket.
- shadd(tmp3_bucket, tmp2_hash, tmp3_t, tmp4, LogBytesPerWord);
- ld(tmp1_monitor, Address(tmp3_bucket));
+ // Read the monitor from the bucket.
+ shadd(tmp3_bucket, tmp2_hash, tmp3_t, tmp4, LogBytesPerWord);
+ ld(tmp1_monitor, Address(tmp3_bucket));
- // Check if the monitor in the bucket is special (empty, tombstone or removed).
- mv(tmp2, ObjectMonitorTable::SpecialPointerValues::below_is_special);
- bltu(tmp1_monitor, tmp2, slow_path);
+ // Check if the monitor in the bucket is special (empty, tombstone or removed).
+ mv(tmp2, ObjectMonitorTable::SpecialPointerValues::below_is_special);
+ bltu(tmp1_monitor, tmp2, slow_path);
- // Check if object matches.
- ld(tmp3, Address(tmp1_monitor, ObjectMonitor::object_offset()));
- BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
- bs_asm->try_peek_weak_handle_in_nmethod(this, tmp3, tmp3, tmp2, slow_path);
- bne(tmp3, obj, slow_path);
+ // Check if object matches.
+ ld(tmp3, Address(tmp1_monitor, ObjectMonitor::object_offset()));
+ BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs_asm->try_peek_weak_handle_in_nmethod(this, tmp3, tmp3, tmp2, slow_path);
+ bne(tmp3, obj, slow_path);
- // Store the monitor in the current thread's object monitor cache (omc).
- sd(tmp1_monitor, Address(xthread, thr_omc_offset + omc_monitor_offset));
- sd(obj, Address(xthread, thr_omc_offset + omc_obj_offset));
+ // Store the monitor in the current thread's object monitor cache (omc).
+ sd(tmp1_monitor, Address(xthread, thr_omc_offset + omc_monitor_offset));
+ sd(obj, Address(xthread, thr_omc_offset + omc_obj_offset));
- bind(monitor_found);
- }
+ bind(monitor_found);
const Register tmp2_owner_addr = tmp2;
const Register tmp3_owner = tmp3;
- const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value));
- const Address owner_address(tmp1_monitor, ObjectMonitor::owner_offset() - monitor_tag);
- const Address recursions_address(tmp1_monitor, ObjectMonitor::recursions_offset() - monitor_tag);
+ const Address owner_address(tmp1_monitor, ObjectMonitor::owner_offset());
+ const Address recursions_address(tmp1_monitor, ObjectMonitor::recursions_offset());
Label monitor_locked;
@@ -224,10 +217,8 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box,
increment(recursions_address, 1, tmp2, tmp3);
bind(monitor_locked);
- if (UseObjectMonitorTable) {
- // Cache the monitor for unlock.
- sd(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
- }
+ // Cache the monitor for unlock.
+ sd(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
}
bind(locked);
@@ -300,7 +291,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box,
// there will be no monitor in the box. So we need to push back the obj
// so that the runtime can fix any potential anonymous owner.
test_bit(tmp3_t, tmp1_mark, exact_log2(markWord::monitor_value));
- bnez(tmp3_t, UseObjectMonitorTable ? push_and_slow_path : inflated);
+ bnez(tmp3_t, push_and_slow_path);
// Try to unlock. Transition lock bits 0b00 => 0b01
assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea");
@@ -344,16 +335,10 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register box,
const Register tmp1_monitor = tmp1;
- if (!UseObjectMonitorTable) {
- assert(tmp1_monitor == tmp1_mark, "should be the same here");
- // Untag the monitor.
- subi(tmp1_monitor, tmp1_mark, (int)markWord::monitor_value);
- } else {
- ld(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
- // No valid pointer below alignof(ObjectMonitor*). Take the slow path.
- mv(tmp3_t, alignof(ObjectMonitor*));
- bltu(tmp1_monitor, tmp3_t, slow_path);
- }
+ ld(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
+ // No valid pointer below alignof(ObjectMonitor*). Take the slow path.
+ mv(tmp3_t, alignof(ObjectMonitor*));
+ bltu(tmp1_monitor, tmp3_t, slow_path);
const Register tmp2_recursions = tmp2;
Label not_recursive;
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
index 61b9633a4ef7..b6ac430a6033 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
@@ -3765,16 +3765,6 @@ void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int
assert_different_registers(temp_reg, t0);
// load mark word
ld(temp_reg, Address(oop, oopDesc::mark_offset_in_bytes()));
- if (!UseObjectMonitorTable) {
- Label test_mark_word;
- // check displaced
- test_bit(t0, temp_reg, exact_log2(markWord::unlocked_value));
- bnez(t0, test_mark_word);
- // slow path use klass prototype
- load_prototype_header(temp_reg, oop);
-
- bind(test_mark_word);
- }
andi(temp_reg, temp_reg, tst_bit);
if (jmp_set) {
bnez(temp_reg, jmp_label, /* is_far */ true);
@@ -7035,10 +7025,8 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register tmp1,
// instruction emitted as it is part of C1's null check semantics.
ld(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
- if (UseObjectMonitorTable) {
- // Clear cache in case fast locking succeeds or we need to take the slow-path.
- sd(zr, Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))));
- }
+ // Clear cache in case fast locking succeeds or we need to take the slow-path.
+ sd(zr, Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))));
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(tmp1, obj);
diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp
index b434b3bd9caa..e8971e7630e6 100644
--- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp
+++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp
@@ -4265,15 +4265,6 @@ void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int
assert(test_bit <= 0xFFFF, "must fit in low 16 bits for z_tmll");
// Load mark word
z_lg(temp_reg, oopDesc::mark_offset_in_bytes(), oop);
- if (!UseObjectMonitorTable) {
- Label test_mark_word;
- // If unlocked bit is set we can directly use the mark word
- z_tmll(temp_reg, markWord::unlocked_value);
- z_brnaz(test_mark_word);
- // Slow path: use klass prototype
- load_prototype_header(temp_reg, oop);
- bind(test_mark_word);
- }
z_tmll(temp_reg, test_bit);
// Use branch_optimized to handle both near and far branches automatically
branch_optimized(jmp_set ? Assembler::bcondNotAllZero : Assembler::bcondAllZero, jmp_label);
@@ -6357,11 +6348,9 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register temp1
// instruction emitted as it is part of C1's null check semantics.
z_lg(mark, Address(obj, mark_offset));
- if (UseObjectMonitorTable) {
- // Clear cache in case fast locking succeeds or we need to take the slow-path.
- const Address om_cache_addr = Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes())));
- z_mvghi(om_cache_addr, 0);
- }
+ // Clear cache in case fast locking succeeds or we need to take the slow-path.
+ const Address om_cache_addr = Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes())));
+ z_mvghi(om_cache_addr, 0);
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(temp1, obj);
@@ -6506,10 +6495,8 @@ void MacroAssembler::compiler_fast_lock_object(Register obj, Register box, Regis
// Finish fast lock unsuccessfully. MUST branch to with flag == EQ
NearLabel slow_path;
- if (UseObjectMonitorTable) {
- // Clear cache in case fast locking succeeds or we need to take the slow-path.
- z_mvghi(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0);
- }
+ // Clear cache in case fast locking succeeds or we need to take the slow-path.
+ z_mvghi(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0);
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(tmp1, obj);
@@ -6587,61 +6574,57 @@ void MacroAssembler::compiler_fast_lock_object(Register obj, Register box, Regis
const ByteSize omc_monitor_offset = OMCache::monitor_offset();
const ByteSize omc_obj_offset = OMCache::obj_offset();
- if (!UseObjectMonitorTable) {
- assert(tmp1_monitor == mark, "should be the same here");
- } else {
- const Register tmp1_bucket = tmp1;
- const Register hash = Z_R0_scratch;
- NearLabel monitor_found;
+ const Register tmp1_bucket = tmp1;
+ const Register hash = Z_R0_scratch;
+ NearLabel monitor_found;
- // Save the mark, we might need it to extract the hash.
- z_lgr(hash, mark);
+ // Save the mark, we might need it to extract the hash.
+ z_lgr(hash, mark);
- // Look for the monitor in the current thread's object monitor cache (omc).
+ // Look for the monitor in the current thread's object monitor cache (omc).
- z_lg(tmp1_monitor, Address(Z_thread, thr_omc_offset + omc_monitor_offset));
- z_cg(obj, Address(Z_thread, thr_omc_offset + omc_obj_offset));
- z_bre(monitor_found);
+ z_lg(tmp1_monitor, Address(Z_thread, thr_omc_offset + omc_monitor_offset));
+ z_cg(obj, Address(Z_thread, thr_omc_offset + omc_obj_offset));
+ z_bre(monitor_found);
- // Get the hash code.
- z_srlg(hash, hash, markWord::hash_shift);
+ // Get the hash code.
+ z_srlg(hash, hash, markWord::hash_shift);
- // Get the table and calculate the bucket's address.
- load_const_optimized(tmp2, ObjectMonitorTable::current_table_address());
- z_lg(tmp2, Address(tmp2));
- z_ng(hash, Address(tmp2, ObjectMonitorTable::table_capacity_mask_offset()));
- z_lg(tmp1_bucket, Address(tmp2, ObjectMonitorTable::table_buckets_offset()));
- z_sllg(hash, hash, LogBytesPerWord);
- z_agr(tmp1_bucket, hash);
+ // Get the table and calculate the bucket's address.
+ load_const_optimized(tmp2, ObjectMonitorTable::current_table_address());
+ z_lg(tmp2, Address(tmp2));
+ z_ng(hash, Address(tmp2, ObjectMonitorTable::table_capacity_mask_offset()));
+ z_lg(tmp1_bucket, Address(tmp2, ObjectMonitorTable::table_buckets_offset()));
+ z_sllg(hash, hash, LogBytesPerWord);
+ z_agr(tmp1_bucket, hash);
- // Read the monitor from the bucket.
- z_lg(tmp1_monitor, Address(tmp1_bucket));
+ // Read the monitor from the bucket.
+ z_lg(tmp1_monitor, Address(tmp1_bucket));
- // Check if the monitor in the bucket is special (empty, tombstone or removed).
- z_clgfi(tmp1_monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special);
- z_brl(slow_path);
+ // Check if the monitor in the bucket is special (empty, tombstone or removed).
+ z_clgfi(tmp1_monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special);
+ z_brl(slow_path);
- // Check if object matches.
- z_lg(tmp2, Address(tmp1_monitor, ObjectMonitor::object_offset()));
- BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
- bs_asm->try_peek_weak_handle_in_nmethod(this, tmp2, tmp2, Z_R0_scratch, slow_path);
- z_cgr(obj, tmp2);
- z_brne(slow_path);
+ // Check if object matches.
+ z_lg(tmp2, Address(tmp1_monitor, ObjectMonitor::object_offset()));
+ BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs_asm->try_peek_weak_handle_in_nmethod(this, tmp2, tmp2, Z_R0_scratch, slow_path);
+ z_cgr(obj, tmp2);
+ z_brne(slow_path);
- // Store the monitor in the current thread's object monitor cache (omc).
- z_stg(tmp1_monitor, Address(Z_thread, thr_omc_offset + omc_monitor_offset));
- z_stg(obj, Address(Z_thread, thr_omc_offset + omc_obj_offset));
+ // Store the monitor in the current thread's object monitor cache (omc).
+ z_stg(tmp1_monitor, Address(Z_thread, thr_omc_offset + omc_monitor_offset));
+ z_stg(obj, Address(Z_thread, thr_omc_offset + omc_obj_offset));
+
+ bind(monitor_found);
- bind(monitor_found);
- }
NearLabel monitor_locked;
// lock the monitor
const Register zero = tmp2;
- const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value));
- const Address owner_address(tmp1_monitor, ObjectMonitor::owner_offset() - monitor_tag);
- const Address recursions_address(tmp1_monitor, ObjectMonitor::recursions_offset() - monitor_tag);
+ const Address owner_address(tmp1_monitor, ObjectMonitor::owner_offset());
+ const Address recursions_address(tmp1_monitor, ObjectMonitor::recursions_offset());
// Try to CAS owner (no owner => current thread's _monitor_owner_id).
// If csg succeeds then CR=EQ, otherwise, register zero is filled
@@ -6659,10 +6642,8 @@ void MacroAssembler::compiler_fast_lock_object(Register obj, Register box, Regis
z_agsi(recursions_address, 1ll);
bind(monitor_locked);
- if (UseObjectMonitorTable) {
- // Cache the monitor for unlock.
- z_stg(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
- }
+ // Cache the monitor for unlock.
+ z_stg(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
// set the CC now
z_cgr(obj, obj);
}
@@ -6739,11 +6720,7 @@ void MacroAssembler::compiler_fast_unlock_object(Register obj, Register box, Reg
// so that the runtime can fix any potential anonymous owner.
z_lg(mark, Address(obj, mark_offset));
z_tmll(mark, markWord::monitor_value);
- if (!UseObjectMonitorTable) {
- z_brnaz(inflated);
- } else {
- z_brnaz(push_and_slow_path);
- }
+ z_brnaz(push_and_slow_path);
#ifdef ASSERT
// Check header not unlocked (0b01).
@@ -6802,25 +6779,20 @@ void MacroAssembler::compiler_fast_unlock_object(Register obj, Register box, Reg
const Register tmp1_monitor = tmp1;
- if (!UseObjectMonitorTable) {
- assert(tmp1_monitor == mark, "should be the same here");
- } else {
- // Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack.
- z_lg(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
- // null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*)
- z_cghi(tmp1_monitor, alignof(ObjectMonitor*));
+ // Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack.
+ z_lg(tmp1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
+ // null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*)
+ z_cghi(tmp1_monitor, alignof(ObjectMonitor*));
- z_brl(slow_path);
- }
+ z_brl(slow_path);
// mark contains the tagged ObjectMonitor*.
const Register monitor = mark;
- const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value));
- const Address recursions_address{monitor, ObjectMonitor::recursions_offset() - monitor_tag};
- const Address succ_address{monitor, ObjectMonitor::succ_offset() - monitor_tag};
- const Address entry_list_address{monitor, ObjectMonitor::entry_list_offset() - monitor_tag};
- const Address owner_address{monitor, ObjectMonitor::owner_offset() - monitor_tag};
+ const Address recursions_address{monitor, ObjectMonitor::recursions_offset()};
+ const Address succ_address{monitor, ObjectMonitor::succ_offset()};
+ const Address entry_list_address{monitor, ObjectMonitor::entry_list_offset()};
+ const Address owner_address{monitor, ObjectMonitor::owner_offset()};
NearLabel not_recursive;
const Register recursions = tmp2;
@@ -6856,9 +6828,6 @@ void MacroAssembler::compiler_fast_unlock_object(Register obj, Register box, Reg
// Save the monitor pointer in the current thread, so we can try to
// reacquire the lock in SharedRuntime::monitor_exit_helper().
- if (!UseObjectMonitorTable) {
- z_xilf(monitor, markWord::monitor_value);
- }
z_stg(monitor, Address(Z_thread, JavaThread::unlocked_inflated_monitor_offset()));
z_ltgr(obj, obj); // Set flag = NE
diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
index d503edce992a..6e08f438a4af 100644
--- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp
@@ -276,10 +276,8 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg,
// Finish fast lock unsuccessfully. MUST jump with ZF == 0
Label slow_path;
- if (UseObjectMonitorTable) {
- // Clear cache in case fast locking succeeds or we need to take the slow-path.
- movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0);
- }
+ // Clear cache in case fast locking succeeds or we need to take the slow-path.
+ movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0);
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(rax_reg, obj, t);
@@ -293,7 +291,7 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg,
Label push;
- const Register top = UseObjectMonitorTable ? rax_reg : box;
+ const Register top = rax_reg;
// Load the mark.
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
@@ -320,10 +318,9 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg,
lock(); cmpxchgptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
jcc(Assembler::notEqual, slow_path);
- if (UseObjectMonitorTable) {
- // Need to reload top, clobbered by CAS.
- movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
- }
+ // Need to reload top, clobbered by CAS.
+ movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
+
bind(push);
// After successful lock, push object on lock-stack.
movptr(Address(thread, top), obj);
@@ -340,63 +337,57 @@ void C2_MacroAssembler::fast_lock(Register obj, Register box, Register rax_reg,
const ByteSize omc_monitor_offset = OMCache::monitor_offset();
const ByteSize omc_obj_offset = OMCache::obj_offset();
- if (!UseObjectMonitorTable) {
- assert(mark == monitor, "should be the same here");
- } else {
- const Register hash = t;
- Label monitor_found;
+ const Register hash = t;
+ Label monitor_found;
- // Look for the monitor in the current thread's object monitor cache (omc).
+ // Look for the monitor in the current thread's object monitor cache (omc).
- movptr(monitor, Address(thread, thr_omc_offset + omc_monitor_offset));
- cmpptr(obj, Address(thread, thr_omc_offset + omc_obj_offset));
- jccb(Assembler::equal, monitor_found);
+ movptr(monitor, Address(thread, thr_omc_offset + omc_monitor_offset));
+ cmpptr(obj, Address(thread, thr_omc_offset + omc_obj_offset));
+ jccb(Assembler::equal, monitor_found);
- // Look for the monitor in the table.
+ // Look for the monitor in the table.
- // Get the hash code.
- movptr(hash, Address(obj, oopDesc::mark_offset_in_bytes()));
- shrq(hash, markWord::hash_shift);
- andq(hash, markWord::hash_mask);
+ // Get the hash code.
+ movptr(hash, Address(obj, oopDesc::mark_offset_in_bytes()));
+ shrq(hash, markWord::hash_shift);
+ andq(hash, markWord::hash_mask);
- // Get the table and calculate the bucket's address.
- lea(rax_reg, ExternalAddress(ObjectMonitorTable::current_table_address()));
- movptr(rax_reg, Address(rax_reg));
- andq(hash, Address(rax_reg, ObjectMonitorTable::table_capacity_mask_offset()));
- movptr(rax_reg, Address(rax_reg, ObjectMonitorTable::table_buckets_offset()));
+ // Get the table and calculate the bucket's address.
+ lea(rax_reg, ExternalAddress(ObjectMonitorTable::current_table_address()));
+ movptr(rax_reg, Address(rax_reg));
+ andq(hash, Address(rax_reg, ObjectMonitorTable::table_capacity_mask_offset()));
+ movptr(rax_reg, Address(rax_reg, ObjectMonitorTable::table_buckets_offset()));
- // Read the monitor from the bucket.
- movptr(monitor, Address(rax_reg, hash, Address::times_ptr));
+ // Read the monitor from the bucket.
+ movptr(monitor, Address(rax_reg, hash, Address::times_ptr));
- // Check if the monitor in the bucket is special (empty, tombstone or removed)
- cmpptr(monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special);
- jcc(Assembler::below, slow_path);
+ // Check if the monitor in the bucket is special (empty, tombstone or removed)
+ cmpptr(monitor, ObjectMonitorTable::SpecialPointerValues::below_is_special);
+ jcc(Assembler::below, slow_path);
- // Check if object matches.
- movptr(rax_reg, Address(monitor, ObjectMonitor::object_offset()));
- BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
- bs_asm->try_peek_weak_handle_in_nmethod(this, rax_reg, rax_reg, slow_path);
- cmpptr(rax_reg, obj);
- jcc(Assembler::notEqual, slow_path);
+ // Check if object matches.
+ movptr(rax_reg, Address(monitor, ObjectMonitor::object_offset()));
+ BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
+ bs_asm->try_peek_weak_handle_in_nmethod(this, rax_reg, rax_reg, slow_path);
+ cmpptr(rax_reg, obj);
+ jcc(Assembler::notEqual, slow_path);
- // Store the monitor in the current thread's object monitor cache (omc).
- movptr(Address(thread, thr_omc_offset + omc_monitor_offset), monitor);
- movptr(Address(thread, thr_omc_offset + omc_obj_offset), obj);
+ // Store the monitor in the current thread's object monitor cache (omc).
+ movptr(Address(thread, thr_omc_offset + omc_monitor_offset), monitor);
+ movptr(Address(thread, thr_omc_offset + omc_obj_offset), obj);
- bind(monitor_found);
- }
- const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value));
- const Address recursions_address(monitor, ObjectMonitor::recursions_offset() - monitor_tag);
- const Address owner_address(monitor, ObjectMonitor::owner_offset() - monitor_tag);
+ bind(monitor_found);
+
+ const Address recursions_address(monitor, ObjectMonitor::recursions_offset());
+ const Address owner_address(monitor, ObjectMonitor::owner_offset());
Label monitor_locked;
// Lock the monitor.
- if (UseObjectMonitorTable) {
- // Cache the monitor for unlock before trashing box. On failure to acquire
- // the lock, the slow path will reset the entry accordingly (see CacheSetter).
- movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), monitor);
- }
+ // Cache the monitor for unlock before trashing box. On failure to acquire
+ // the lock, the slow path will reset the entry accordingly (see CacheSetter).
+ movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), monitor);
// Try to CAS owner (no owner => current thread's _monitor_owner_id).
xorptr(rax_reg, rax_reg);
@@ -481,7 +472,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t,
const Register mark = t;
const Register monitor = t;
- const Register top = UseObjectMonitorTable ? t : reg_rax;
+ const Register top = t;
const Register box = reg_rax;
Label dummy;
@@ -499,11 +490,6 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t,
// Load top.
movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
- if (!UseObjectMonitorTable) {
- // Prefetch mark.
- movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
- }
-
// Check if obj is top of lock-stack.
cmpptr(obj, Address(thread, top, Address::times_1, -oopSize));
// Top of lock stack was not obj. Must be monitor.
@@ -519,10 +505,8 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t,
// We elide the monitor check, let the CAS fail instead.
- if (UseObjectMonitorTable) {
- // Load mark.
- movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
- }
+ // Load mark.
+ movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
// Try to unlock. Transition lock bits 0b00 => 0b01
movptr(reg_rax, mark);
@@ -545,9 +529,7 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t,
jcc(Assembler::notEqual, inflated_check_lock_stack);
stop("Fast Unlock lock on stack");
bind(check_done);
- if (UseObjectMonitorTable) {
- movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
- }
+ movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
testptr(mark, markWord::monitor_value);
jcc(Assembler::notZero, inflated);
stop("Fast Unlock not monitor");
@@ -555,20 +537,16 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t,
bind(inflated);
- if (!UseObjectMonitorTable) {
- assert(mark == monitor, "should be the same here");
- } else {
- // Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack.
- movptr(monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
- // null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*)
- cmpptr(monitor, alignof(ObjectMonitor*));
- jcc(Assembler::below, slow_path);
- }
- const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast(markWord::monitor_value));
- const Address recursions_address{monitor, ObjectMonitor::recursions_offset() - monitor_tag};
- const Address succ_address{monitor, ObjectMonitor::succ_offset() - monitor_tag};
- const Address entry_list_address{monitor, ObjectMonitor::entry_list_offset() - monitor_tag};
- const Address owner_address{monitor, ObjectMonitor::owner_offset() - monitor_tag};
+ // Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack.
+ movptr(monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
+ // null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*)
+ cmpptr(monitor, alignof(ObjectMonitor*));
+ jcc(Assembler::below, slow_path);
+
+ const Address recursions_address{monitor, ObjectMonitor::recursions_offset()};
+ const Address succ_address{monitor, ObjectMonitor::succ_offset()};
+ const Address entry_list_address{monitor, ObjectMonitor::entry_list_offset()};
+ const Address owner_address{monitor, ObjectMonitor::owner_offset()};
Label recursive;
@@ -593,9 +571,6 @@ void C2_MacroAssembler::fast_unlock(Register obj, Register reg_rax, Register t,
// Save the monitor pointer in the current thread, so we can try to
// reacquire the lock in SharedRuntime::monitor_exit_helper().
- if (!UseObjectMonitorTable) {
- andptr(monitor, ~(int32_t)markWord::monitor_value);
- }
movptr(Address(thread, JavaThread::unlocked_inflated_monitor_offset()), monitor);
orl(t, 1); // Fast Unlock ZF = 0
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
index cedf086ef36e..d5b39f8b0eb8 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
@@ -2444,18 +2444,6 @@ void MacroAssembler::test_field_is_flat(Register flags, Register temp_reg, Label
void MacroAssembler::test_oop_prototype_bit(Register oop, Register temp_reg, int32_t test_bit, bool jmp_set, Label& jmp_label) {
// load mark word
movptr(temp_reg, Address(oop, oopDesc::mark_offset_in_bytes()));
- if (!UseObjectMonitorTable) {
- Label test_mark_word;
- // check displaced
- testl(temp_reg, markWord::unlocked_value);
- jccb(Assembler::notZero, test_mark_word);
- // slow path use klass prototype
- push(rscratch1);
- load_prototype_header(temp_reg, oop, rscratch1);
- pop(rscratch1);
-
- bind(test_mark_word);
- }
testl(temp_reg, test_bit);
jcc((jmp_set) ? Assembler::notZero : Assembler::zero, jmp_label);
}
@@ -10611,10 +10599,8 @@ void MacroAssembler::fast_lock(Register basic_lock, Register obj, Register reg_r
// instruction emitted as it is part of C1's null check semantics.
movptr(reg_rax, Address(obj, oopDesc::mark_offset_in_bytes()));
- if (UseObjectMonitorTable) {
- // Clear cache in case fast locking succeeds or we need to take the slow-path.
- movptr(Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))), 0);
- }
+ // Clear cache in case fast locking succeeds or we need to take the slow-path.
+ movptr(Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))), 0);
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(tmp, obj, rscratch1);
diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86.cpp
index 17fdfa61d366..478283bc77a6 100644
--- a/src/hotspot/cpu/x86/sharedRuntime_x86.cpp
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86.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
@@ -59,12 +59,6 @@ void SharedRuntime::inline_check_hashcode_from_object_header(MacroAssembler* mas
__ movptr(result, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
- if (!UseObjectMonitorTable) {
- // check if monitor
- __ testptr(result, markWord::monitor_value);
- __ jcc(Assembler::notZero, slowCase);
- }
-
// get hash
// Read the header and build a mask to get its hash field.
// Depend on hash_mask being at most 32 bits and avoid the use of hash_mask_in_place
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
index ac5c4d0c8a73..e14c9201c389 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2018, 2022, Red Hat, Inc. All rights reserved.
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
- * 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
@@ -44,16 +44,6 @@ void ShenandoahArguments::initialize() {
vm_exit_during_initialization("Shenandoah GC is not supported on this platform.");
#endif
- // Shenandoah relies on the object header bits (including the self-forwarded bit
- // at markWord::self_fwd_mask_in_place) being preserved across monitor inflation,
- // which only holds with UseObjectMonitorTable.
- if (!UseObjectMonitorTable) {
- if (FLAG_IS_CMDLINE(UseObjectMonitorTable)) {
- vm_exit_during_initialization("Shenandoah requires UseObjectMonitorTable");
- }
- FLAG_SET_DEFAULT(UseObjectMonitorTable, true);
- }
-
#if 0 // leave this block as stepping stone for future platforms
log_warning(gc)("Shenandoah GC is not fully supported on this platform:");
log_warning(gc)(" concurrent modes are not supported, only STW cycles are enabled;");
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
index b3c847cadaf6..a34a91c6d867 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2015, 2020, Red Hat, Inc. All rights reserved.
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
- * 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
@@ -330,15 +330,6 @@ void ShenandoahHeap::increase_object_age(oop obj, uint additional_age) {
uint ShenandoahHeap::get_object_age(oop obj) {
markWord w = obj->mark();
assert(!w.is_marked(), "must not be forwarded");
- if (UseObjectMonitorTable) {
- assert(w.age() <= markWord::max_age, "Impossible!");
- return w.age();
- }
- if (w.has_monitor()) {
- w = w.monitor()->header();
- } else {
- assert(!w.has_displaced_mark_helper(), "Mark word should not be displaced");
- }
assert(w.age() <= markWord::max_age, "Impossible!");
return w.age();
}
diff --git a/src/hotspot/share/oops/markWord.cpp b/src/hotspot/share/oops/markWord.cpp
index 7d403660f8b8..2201633b160a 100644
--- a/src/hotspot/share/oops/markWord.cpp
+++ b/src/hotspot/share/oops/markWord.cpp
@@ -57,14 +57,6 @@ void markWord::print_on(outputStream* st, bool print_monitor_info) const {
// have to check has_monitor() before is_locked()
// Valhalla: inline types/arrays can't be monitored
st->print(" monitor(" INTPTR_FORMAT ")=", value());
- if (print_monitor_info && !UseObjectMonitorTable) {
- ObjectMonitor* mon = monitor();
- if (mon == nullptr) {
- st->print("null (this should never be seen!)");
- } else {
- mon->print_on(st);
- }
- }
} else if (is_locked()) { // last bits != 01 => 00
// thin locked
// Valhalla: inline types can not possess an object monitor
diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp
index 789d206f57c4..42ced7df0b2d 100644
--- a/src/hotspot/share/oops/markWord.hpp
+++ b/src/hotspot/share/oops/markWord.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
@@ -30,6 +30,7 @@
#include "oops/compressedKlass.hpp"
#include "oops/oopsHierarchy.hpp"
#include "runtime/globals.hpp"
+#include "utilities/debug.hpp"
#include "utilities/powerOfTwo.hpp"
// The markWord describes the header of an object.
@@ -53,8 +54,7 @@
//
// [header | 00] locked locked regular object header (fast-locking in use)
// [header | 01] unlocked regular object header
-// [header | 10] monitor inflated lock (UseObjectMonitorTable == true)
-// [ptr | 10] monitor inflated lock (UseObjectMonitorTable == false, header is swapped out)
+// [header | 10] monitor inflated lock
// [ptr | 11] marked used to mark an object (header is swapped out)
//
// - self-fwd - used by some GCs to indicate in-place forwarding.
@@ -254,21 +254,19 @@ class markWord {
return markWord((value() & ~lock_mask_in_place) | monitor_value);
}
ObjectMonitor* monitor() const {
- assert(has_monitor(), "check");
- assert(!UseObjectMonitorTable, "Locking with OM table does not use markWord for monitors");
- // Use xor instead of &~ to provide one extra tag-bit check.
- return (ObjectMonitor*) (value() ^ monitor_value);
+ // Locking with OM table does not use markWord for monitors.
+ ShouldNotCallThis();
+ return (ObjectMonitor*) nullptr;
}
static markWord encode(ObjectMonitor* monitor) {
- assert(!UseObjectMonitorTable, "Locking with OM table does not use markWord for monitors");
- uintptr_t tmp = (uintptr_t) monitor;
- return markWord(tmp | monitor_value);
+ // Locking with OM table does not use markWord for monitors.
+ ShouldNotCallThis();
+ return markWord(0);
}
bool has_monitor_pointer() const {
- intptr_t lockbits = value() & lock_mask_in_place;
- return !UseObjectMonitorTable && lockbits == monitor_value;
+ return false; // Locking with OM table does not use markWord for monitors.
}
bool has_displaced_mark_helper() const {
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index babd84a0097c..ab1ba7cce1c0 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -5560,19 +5560,6 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
Node* no_ctrl = nullptr;
Node* header = make_load(no_ctrl, header_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
- if (!UseObjectMonitorTable) {
- // Test the header to see if it is safe to read w.r.t. locking.
- // We cannot use the inline type mask as this may check bits that are overridden
- // by an object monitor's pointer when inflating locking.
- Node *lock_mask = _gvn.MakeConX(markWord::lock_mask_in_place);
- Node *lmasked_header = _gvn.transform(new AndXNode(header, lock_mask));
- Node *monitor_val = _gvn.MakeConX(markWord::monitor_value);
- Node *chk_monitor = _gvn.transform(new CmpXNode(lmasked_header, monitor_val));
- Node *test_monitor = _gvn.transform(new BoolNode(chk_monitor, BoolTest::eq));
-
- generate_slow_guard(test_monitor, slow_region);
- }
-
// Get the hash value and check to see that it has been properly assigned.
// We depend on hash_mask being at most 32 bits and avoid the use of
// hash_mask_in_place because it could be larger than 32 bits in a 64-bit
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index af8d0928b208..0f931099dd4c 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -3442,17 +3442,6 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
return JNI_OK;
}
-void Arguments::set_compact_headers_flags() {
-#ifdef _LP64
- if (UseCompactObjectHeaders && !UseObjectMonitorTable) {
- if (FLAG_IS_CMDLINE(UseObjectMonitorTable)) {
- warning("-UseObjectMonitorTable is incompatible with +UseCompactObjectHeaders; ignoring -UseObjectMonitorTable");
- }
- FLAG_SET_DEFAULT(UseObjectMonitorTable, true);
- }
-#endif
-}
-
jint Arguments::apply_ergo() {
// Set flags based on ergonomics.
jint result = set_ergonomics_flags();
@@ -3463,8 +3452,6 @@ jint Arguments::apply_ergo() {
GCConfig::arguments()->initialize();
- set_compact_headers_flags();
-
CompressedKlassPointers::pre_initialize();
CDSConfig::ergo_initialize();
diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
index 243349a4f0c0..bda1de4edea2 100644
--- a/src/hotspot/share/runtime/arguments.hpp
+++ b/src/hotspot/share/runtime/arguments.hpp
@@ -262,7 +262,6 @@ class Arguments : AllStatic {
static void set_conservative_max_heap_alignment();
static void set_use_compressed_oops();
static jint set_ergonomics_flags();
- static void set_compact_headers_flags();
// Bytecode rewriting
static void set_bytecode_flags();
diff --git a/src/hotspot/share/runtime/basicLock.cpp b/src/hotspot/share/runtime/basicLock.cpp
index 4a6e7402dfa5..73f9cc94b0aa 100644
--- a/src/hotspot/share/runtime/basicLock.cpp
+++ b/src/hotspot/share/runtime/basicLock.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
@@ -29,11 +29,9 @@
void BasicLock::print_on(outputStream* st, oop owner) const {
st->print("monitor");
- if (UseObjectMonitorTable) {
- ObjectMonitor* mon = object_monitor_cache();
- if (mon != nullptr) {
- mon->print_on(st);
- }
+ ObjectMonitor* mon = object_monitor_cache();
+ if (mon != nullptr) {
+ mon->print_on(st);
}
}
@@ -66,15 +64,8 @@ void BasicLock::move_to(oop obj, BasicLock* dest) {
// small (given the support for inflated fast-path locking in the fast_lock, etc)
// we'll leave that optimization for another time.
- if (UseObjectMonitorTable) {
- // Preserve the ObjectMonitor*, the cache is cleared when a box is reused
- // and only read while the lock is held, so no stale ObjectMonitor* is
- // encountered.
- dest->set_object_monitor_cache(object_monitor_cache());
- }
-#ifdef ASSERT
- else {
- dest->set_bad_monitor_deopt();
- }
-#endif
+ // Preserve the ObjectMonitor*, the cache is cleared when a box is reused
+ // and only read while the lock is held, so no stale ObjectMonitor* is
+ // encountered.
+ dest->set_object_monitor_cache(object_monitor_cache());
}
diff --git a/src/hotspot/share/runtime/basicLock.inline.hpp b/src/hotspot/share/runtime/basicLock.inline.hpp
index 9f0f26ee9570..29c7b65d1905 100644
--- a/src/hotspot/share/runtime/basicLock.inline.hpp
+++ b/src/hotspot/share/runtime/basicLock.inline.hpp
@@ -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
@@ -30,7 +30,6 @@
#include "runtime/objectMonitor.inline.hpp"
inline ObjectMonitor* BasicLock::object_monitor_cache() const {
- assert(UseObjectMonitorTable, "must be");
#if !defined(ZERO) && (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64) || defined(S390))
return reinterpret_cast(get_monitor());
#else
@@ -42,12 +41,10 @@ inline ObjectMonitor* BasicLock::object_monitor_cache() const {
}
inline void BasicLock::clear_object_monitor_cache() {
- assert(UseObjectMonitorTable, "must be");
set_monitor(nullptr);
}
inline void BasicLock::set_object_monitor_cache(ObjectMonitor* mon) {
- assert(UseObjectMonitorTable, "must be");
set_monitor(mon);
}
diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp
index 1364133d8ecf..bcf86d0f6a37 100644
--- a/src/hotspot/share/runtime/deoptimization.cpp
+++ b/src/hotspot/share/runtime/deoptimization.cpp
@@ -1481,15 +1481,7 @@ bool Deoptimization::relock_objects(JavaThread* thread, GrowableArraycurrent_waiting_monitor();
if (waiting_monitor != nullptr && waiting_monitor->object() == obj()) {
assert(fr.is_deoptimized_frame(), "frame must be scheduled for deoptimization");
- if (UseObjectMonitorTable) {
- mon_info->lock()->clear_object_monitor_cache();
- }
-#ifdef ASSERT
- else {
- assert(!UseObjectMonitorTable, "must be");
- mon_info->lock()->set_bad_monitor_deopt();
- }
-#endif
+ mon_info->lock()->clear_object_monitor_cache();
JvmtiDeferredUpdates::inc_relock_count_after_wait(deoptee_thread);
continue;
}
@@ -1499,12 +1491,9 @@ bool Deoptimization::relock_objects(JavaThread* thread, GrowableArrayclear_object_monitor_cache();
- }
+ // The BasicLock cache is expected to be either a valid ObjectMonitor*
+ // or nullptr. Right now it is garbage, hence we set it to nullptr.
+ lock->clear_object_monitor_cache();
ObjectSynchronizer::enter_for(obj, lock, deoptee_thread);
if (deoptee_thread->lock_stack().contains(obj())) {
ObjectSynchronizer::inflate_fast_locked_object(obj(), ObjectSynchronizer::InflateCause::inflate_cause_vm_internal,
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 26756ea00226..89b8fd11c94c 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -1985,10 +1985,6 @@ const int ObjectAlignmentInBytes = 8;
"Mark all threads after a safepoint, and clear on a modify " \
"fence. Add cleanliness checks.") \
\
- product(bool, UseObjectMonitorTable, true, DIAGNOSTIC, \
- "Use a table to record inflated monitors rather than the first " \
- "word of the object.") \
- \
product(int, FastLockingSpins, 8, DIAGNOSTIC, \
"Specifies the number of times fast locking will attempt to " \
"CAS the markWord before inflating. Between each CAS it will " \
diff --git a/src/hotspot/share/runtime/javaThread.inline.hpp b/src/hotspot/share/runtime/javaThread.inline.hpp
index 5b4556b56668..a5a3f9990cfb 100644
--- a/src/hotspot/share/runtime/javaThread.inline.hpp
+++ b/src/hotspot/share/runtime/javaThread.inline.hpp
@@ -258,7 +258,6 @@ inline InstanceKlass* JavaThread::class_being_initialized() const {
}
inline void JavaThread::om_set_monitor_cache(ObjectMonitor* monitor) {
- assert(UseObjectMonitorTable, "must be");
assert(monitor != nullptr, "use om_clear_monitor_cache to clear");
assert(this == current() || monitor->has_owner(this), "only add owned monitors for other threads");
assert(this == current() || is_obj_deopt_suspend(), "thread must not run concurrently");
@@ -267,9 +266,7 @@ inline void JavaThread::om_set_monitor_cache(ObjectMonitor* monitor) {
}
inline void JavaThread::om_clear_monitor_cache() {
- if (UseObjectMonitorTable) {
- _om_cache.clear();
- }
+ _om_cache.clear();
}
inline ObjectMonitor* JavaThread::om_get_from_monitor_cache(oop obj) {
diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp
index 22cc0c848910..ee74afcaf1ff 100644
--- a/src/hotspot/share/runtime/objectMonitor.cpp
+++ b/src/hotspot/share/runtime/objectMonitor.cpp
@@ -344,25 +344,11 @@ void ObjectMonitor::ExitOnSuspend::operator()(JavaThread* current) {
}
}
-#define assert_mark_word_consistency() \
- assert(UseObjectMonitorTable || object()->mark() == markWord::encode(this), \
- "object mark must match encoded this: mark=" INTPTR_FORMAT \
- ", encoded this=" INTPTR_FORMAT, object()->mark().value(), \
- markWord::encode(this).value());
-
// -----------------------------------------------------------------------------
// Enter support
bool ObjectMonitor::enter_is_async_deflating() {
if (is_being_async_deflated()) {
- if (!UseObjectMonitorTable) {
- const oop l_object = object();
- if (l_object != nullptr) {
- // Attempt to restore the header/dmw to the object's header so that
- // we only retry once if the deflater thread happens to be slow.
- install_displaced_markword_in_object(l_object);
- }
- }
return true;
}
@@ -489,7 +475,6 @@ bool ObjectMonitor::spin_enter(JavaThread* current) {
if (try_spin(current)) {
assert(has_owner(current), "must be current: owner=" INT64_FORMAT, owner_raw());
assert(_recursions == 0, "must be 0: recursions=%zd", _recursions);
- assert_mark_word_consistency();
return true;
}
@@ -629,7 +614,6 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread* current, ObjectMonito
assert(_recursions == 0, "invariant");
assert(has_owner(current), "invariant");
assert(!has_successor(current), "invariant");
- assert_mark_word_consistency();
// The thread -- now the owner -- is back in vm mode.
// Report the glorious news via TI,DTrace and jvmstat.
@@ -856,12 +840,7 @@ bool ObjectMonitor::deflate_monitor(Thread* current) {
}
}
- if (UseObjectMonitorTable) {
- ObjectSynchronizer::deflate_monitor(obj, this);
- } else if (obj != nullptr) {
- // Install the old mark word if nobody else has already done it.
- install_displaced_markword_in_object(obj);
- }
+ ObjectSynchronizer::deflate_monitor(obj, this);
if (event.should_commit()) {
post_monitor_deflate_event(&event, obj);
@@ -872,60 +851,6 @@ bool ObjectMonitor::deflate_monitor(Thread* current) {
return true; // Success, ObjectMonitor has been deflated.
}
-// Install the displaced mark word (dmw) of a deflating ObjectMonitor
-// into the header of the object associated with the monitor. This
-// idempotent method is called by a thread that is deflating a
-// monitor and by other threads that have detected a race with the
-// deflation process.
-void ObjectMonitor::install_displaced_markword_in_object(const oop obj) {
- assert(!UseObjectMonitorTable, "ObjectMonitorTable has no dmw");
- // This function must only be called when (owner == DEFLATER_MARKER
- // && contentions <= 0), but we can't guarantee that here because
- // those values could change when the ObjectMonitor gets moved from
- // the global free list to a per-thread free list.
-
- guarantee(obj != nullptr, "must be non-null");
-
- // Separate loads in is_being_async_deflated(), which is almost always
- // called before this function, from the load of dmw/header below.
-
- // _contentions and dmw/header may get written by different threads.
- // Make sure to observe them in the same order when having several observers.
- OrderAccess::loadload_for_IRIW();
-
- const oop l_object = object_peek();
- if (l_object == nullptr) {
- // ObjectMonitor's object ref has already been cleared by async
- // deflation or GC so we're done here.
- return;
- }
- assert(l_object == obj, "object=" INTPTR_FORMAT " must equal obj="
- INTPTR_FORMAT, p2i(l_object), p2i(obj));
-
- markWord dmw = header();
- // The dmw has to be neutral (not null, not locked and not marked).
- assert(dmw.is_neutral(), "must be neutral: dmw=" INTPTR_FORMAT, dmw.value());
-
- // Install displaced mark word if the object's header still points
- // to this ObjectMonitor. More than one racing caller to this function
- // can rarely reach this point, but only one can win.
- markWord res = obj->cas_set_mark(dmw, markWord::encode(this));
- if (res != markWord::encode(this)) {
- // This should be rare so log at the Info level when it happens.
- log_info(monitorinflation)("install_displaced_markword_in_object: "
- "failed cas_set_mark: new_mark=" INTPTR_FORMAT
- ", old_mark=" INTPTR_FORMAT ", res=" INTPTR_FORMAT,
- dmw.value(), markWord::encode(this).value(),
- res.value());
- }
-
- // Note: It does not matter which thread restored the header/dmw
- // into the object's header. The thread deflating the monitor just
- // wanted the object's header restored and it is. The threads that
- // detected a race with the deflation process also wanted the
- // object's header restored before they retry their operation and
- // because it is restored they will only retry once.
-}
// Convert the fields used by is_busy() to a string that can be
// used for diagnostic output.
@@ -1977,7 +1902,6 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
// Verify a few postconditions
assert(has_owner(current), "invariant");
assert(!has_successor(current), "invariant");
- assert_mark_word_consistency();
if (ce != nullptr && ce->is_virtual_thread()) {
current->post_vthread_pinned_event(&vthread_pinned_event, "Object.wait", result);
diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp
index 848eae6df2ac..3c126a034484 100644
--- a/src/hotspot/share/runtime/objectMonitor.hpp
+++ b/src/hotspot/share/runtime/objectMonitor.hpp
@@ -154,13 +154,9 @@ class ObjectMonitor : public CHeapObj {
// ParkEvent of unblocker thread.
static ParkEvent* _vthread_unparker_ParkEvent;
- // Because of frequent access, the metadata field is at offset zero (0).
- // Enforced by the assert() in metadata_addr().
- // * Locking with UseObjectMonitorTable:
- // Contains the _object's hashCode.
- // * Locking without UseObjectMonitorTable:
- // Contains the displaced object header word - mark
- volatile uintptr_t _metadata; // metadata
+ // Because of frequent access, the _metadata field is at offset zero (0),
+ // which is enforced by a STATIC_ASSERT() in metadata_addr().
+ volatile uintptr_t _metadata; // contains the _object's hashCode
WeakHandle _object; // backward object pointer
// Separate _metadata and _owner on different cache lines since both can
// have busy multi-threaded access. _metadata and _object are set at initial
@@ -415,7 +411,6 @@ class ObjectMonitor : public CHeapObj {
public:
// Deflation support
bool deflate_monitor(Thread* current);
- void install_displaced_markword_in_object(const oop obj);
// JFR support
static bool is_jfr_excluded(const Klass* monitor_klass);
diff --git a/src/hotspot/share/runtime/objectMonitor.inline.hpp b/src/hotspot/share/runtime/objectMonitor.inline.hpp
index efdc33cd4412..5e9d3dee5624 100644
--- a/src/hotspot/share/runtime/objectMonitor.inline.hpp
+++ b/src/hotspot/share/runtime/objectMonitor.inline.hpp
@@ -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
@@ -38,6 +38,7 @@
#include "runtime/synchronizer.hpp"
#include "runtime/threadIdentifier.hpp"
#include "utilities/checkedCast.hpp"
+#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
inline int64_t ObjectMonitor::owner_id_from(JavaThread* thread) {
@@ -74,22 +75,22 @@ inline volatile uintptr_t* ObjectMonitor::metadata_addr() {
}
inline markWord ObjectMonitor::header() const {
- assert(!UseObjectMonitorTable, "Locking with OM table does not use header");
+ // Locking with OM table does not use header.
+ ShouldNotCallThis();
return markWord(metadata());
}
inline void ObjectMonitor::set_header(markWord hdr) {
- assert(!UseObjectMonitorTable, "Locking with OM table does not use header");
+ // Locking with OM table does not use header.
+ ShouldNotCallThis();
set_metadata(hdr.value());
}
inline intptr_t ObjectMonitor::hash() const {
- assert(UseObjectMonitorTable, "Only used when locking with OM table");
return metadata();
}
inline void ObjectMonitor::set_hash(intptr_t hash) {
- assert(UseObjectMonitorTable, "Only used when locking with OM table");
set_metadata(hash);
}
diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
index ac487b9f1d1c..bb773dc523bc 100644
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -3865,15 +3865,7 @@ JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *current) )
kptr2 = fr.next_monitor_in_interpreter_frame(kptr2) ) {
if (kptr2->obj() != nullptr) { // Avoid 'holes' in the monitor array
BasicLock *lock = kptr2->lock();
- if (UseObjectMonitorTable) {
- buf[i] = (intptr_t)lock->object_monitor_cache();
- }
-#ifdef ASSERT
- else {
- buf[i] = badDispHeaderOSR;
- }
-#endif
- i++;
+ buf[i++] = (intptr_t)lock->object_monitor_cache();
buf[i++] = cast_from_oop(kptr2->obj());
}
}
diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp
index e5b70ebe426d..fb2f8bd43033 100644
--- a/src/hotspot/share/runtime/synchronizer.cpp
+++ b/src/hotspot/share/runtime/synchronizer.cpp
@@ -680,98 +680,22 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
assert(!obj->klass()->is_inline_klass(), "FastHashCode should not be called for inline classes");
while (true) {
- ObjectMonitor* monitor = nullptr;
markWord temp, test;
intptr_t hash;
markWord mark = obj->mark_acquire();
- // If UseObjectMonitorTable is set the hash can simply be installed in the
- // object header, since the monitor isn't in the object header.
- if (UseObjectMonitorTable || !mark.has_monitor()) {
- hash = mark.hash();
- if (hash != 0) { // if it has a hash, just return it
- return hash;
- }
- hash = get_next_hash(current, obj); // get a new hash
- temp = mark.copy_set_hash(hash); // merge the hash into header
- // try to install the hash
- test = obj->cas_set_mark(temp, mark);
- if (test == mark) { // if the hash was installed, return it
- return hash;
- }
- // CAS failed, retry
- continue;
-
- // Failed to install the hash. It could be that another thread
- // installed the hash just before our attempt or inflation has
- // occurred or... so we fall thru to inflate the monitor for
- // stability and then install the hash.
- } else {
- assert(!mark.is_unlocked() && !mark.is_fast_locked(), "invariant");
- monitor = mark.monitor();
- temp = monitor->header();
- assert(temp.is_neutral(), "invariant: header=" INTPTR_FORMAT, temp.value());
- hash = temp.hash();
- if (hash != 0) {
- // It has a hash.
-
- // Separate load of dmw/header above from the loads in
- // is_being_async_deflated().
-
- // dmw/header and _contentions may get written by different threads.
- // Make sure to observe them in the same order when having several observers.
- OrderAccess::loadload_for_IRIW();
-
- if (monitor->is_being_async_deflated()) {
- // But we can't safely use the hash if we detect that async
- // deflation has occurred. So we attempt to restore the
- // header/dmw to the object's header so that we only retry
- // once if the deflater thread happens to be slow.
- monitor->install_displaced_markword_in_object(obj);
- continue;
- }
- return hash;
- }
- // Fall thru so we only have one place that installs the hash in
- // the ObjectMonitor.
- }
-
- // NOTE: an async deflation can race after we get the monitor and
- // before we can update the ObjectMonitor's header with the hash
- // value below.
- assert(mark.has_monitor(), "must be");
- monitor = mark.monitor();
-
- // Load ObjectMonitor's header/dmw field and see if it has a hash.
- mark = monitor->header();
- assert(mark.is_neutral(), "invariant: header=" INTPTR_FORMAT, mark.value());
+ // The hash is located in the object header.
hash = mark.hash();
- if (hash == 0) { // if it does not have a hash
- hash = get_next_hash(current, obj); // get a new hash
- temp = mark.copy_set_hash(hash) ; // merge the hash into header
- assert(temp.is_neutral(), "invariant: header=" INTPTR_FORMAT, temp.value());
- uintptr_t v = AtomicAccess::cmpxchg(monitor->metadata_addr(), mark.value(), temp.value());
- test = markWord(v);
- if (test != mark) {
- // The attempt to update the ObjectMonitor's header/dmw field
- // did not work. This can happen if another thread managed to
- // merge in the hash just before our cmpxchg().
- // If we add any new usages of the header/dmw field, this code
- // will need to be updated.
- hash = test.hash();
- assert(test.is_neutral(), "invariant: header=" INTPTR_FORMAT, test.value());
- assert(hash != 0, "should only have lost the race to a thread that set a non-zero hash");
- }
- if (monitor->is_being_async_deflated() && !UseObjectMonitorTable) {
- // If we detect that async deflation has occurred, then we
- // attempt to restore the header/dmw to the object's header
- // so that we only retry once if the deflater thread happens
- // to be slow.
- monitor->install_displaced_markword_in_object(obj);
- continue;
- }
+ if (hash != 0) { // if it has a hash, just return it
+ return hash;
}
- // We finally get the hash.
- return hash;
+ hash = get_next_hash(current, obj); // get a new hash
+ temp = mark.copy_set_hash(hash); // merge the hash into header
+ // try to install the hash
+ test = obj->cas_set_mark(temp, mark);
+ if (test == mark) { // if the hash was installed, return it
+ return hash;
+ }
+ // CAS failed, retry
}
}
@@ -1237,9 +1161,7 @@ size_t ObjectSynchronizer::deflate_idle_monitors() {
unlinked_count = _in_use_list.unlink_deflated(deflated_count, &delete_list, &safepointer);
GrowableArray table_delete_list;
- if (UseObjectMonitorTable) {
- ObjectMonitorTable::rebuild(&table_delete_list);
- }
+ ObjectMonitorTable::rebuild(&table_delete_list);
log.before_handshake(unlinked_count);
@@ -1260,9 +1182,7 @@ size_t ObjectSynchronizer::deflate_idle_monitors() {
// Delete the unlinked ObjectMonitors.
deleted_count = delete_monitors(&delete_list, &safepointer);
- if (UseObjectMonitorTable) {
- ObjectMonitorTable::destroy(&table_delete_list);
- }
+ ObjectMonitorTable::destroy(&table_delete_list);
assert(unlinked_count == deleted_count, "must be");
}
@@ -1457,19 +1377,6 @@ void ObjectSynchronizer::chk_in_use_entry(ObjectMonitor* n, outputStream* out,
}
const markWord mark = obj->mark();
- // Note: When using ObjectMonitorTable we may observe an intermediate state,
- // where the monitor is globally visible, but no thread has yet transitioned
- // the markWord. To avoid reporting a false positive during this transition, we
- // skip the `!mark.has_monitor()` test if we are using the ObjectMonitorTable.
- if (!UseObjectMonitorTable && !mark.has_monitor()) {
- out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
- "object does not think it has a monitor: obj="
- INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n),
- p2i(obj), mark.value());
- *error_cnt_p = *error_cnt_p + 1;
- return;
- }
-
ObjectMonitor* const obj_mon = read_monitor(obj, mark);
if (n != obj_mon) {
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
@@ -1499,7 +1406,7 @@ void ObjectSynchronizer::log_in_use_monitor_details(outputStream* out, bool log_
monitors_iterate([&](ObjectMonitor* monitor) {
if (is_interesting(monitor)) {
const oop obj = monitor->object_peek();
- const intptr_t hash = UseObjectMonitorTable ? monitor->hash() : monitor->header().hash();
+ const intptr_t hash = monitor->hash();
ResourceMark rm;
out->print(INTPTR_FORMAT " %d%d%d " INTPTR_FORMAT " %s", p2i(monitor),
monitor->is_busy(), hash != 0, monitor->has_owner(),
@@ -1562,8 +1469,6 @@ static void post_monitor_inflate_event(EventJavaMonitorInflate* event,
}
ObjectMonitor* ObjectSynchronizer::get_or_insert_monitor(oop object, JavaThread* current, ObjectSynchronizer::InflateCause cause) {
- assert(UseObjectMonitorTable, "must be");
-
EventJavaMonitorInflate event;
bool inserted;
@@ -1584,7 +1489,6 @@ ObjectMonitor* ObjectSynchronizer::get_or_insert_monitor(oop object, JavaThread*
// Add the hashcode to the monitor to match the object and put it in the hashtable.
ObjectMonitor* ObjectSynchronizer::add_monitor(ObjectMonitor* monitor, oop obj) {
- assert(UseObjectMonitorTable, "must be");
assert(obj == monitor->object(), "must be");
intptr_t hash = obj->mark().hash();
@@ -1595,15 +1499,12 @@ ObjectMonitor* ObjectSynchronizer::add_monitor(ObjectMonitor* monitor, oop obj)
}
void ObjectSynchronizer::remove_monitor(ObjectMonitor* monitor, oop obj) {
- assert(UseObjectMonitorTable, "must be");
assert(monitor->object_peek() == obj, "must be, cleared objects are removed by is_dead");
ObjectMonitorTable::remove_monitor_entry(monitor);
}
void ObjectSynchronizer::deflate_mark_word(oop obj) {
- assert(UseObjectMonitorTable, "must be");
-
markWord mark = obj->mark_acquire();
assert(!mark.has_no_hash(), "obj with inflated monitor must have had a hash");
@@ -1614,9 +1515,6 @@ void ObjectSynchronizer::deflate_mark_word(oop obj) {
}
void ObjectSynchronizer::create_om_table() {
- if (!UseObjectMonitorTable) {
- return;
- }
ObjectMonitorTable::create();
}
@@ -1684,18 +1582,15 @@ class ObjectSynchronizer::CacheSetter : StackObj {
_monitor(nullptr) {}
~CacheSetter() {
- // Only use the cache if using the table.
- if (UseObjectMonitorTable) {
- if (_monitor != nullptr) {
- // If the monitor is already in the BasicLock cache then it is most
- // likely in the thread cache, do not set it again to avoid reordering.
- if (_monitor != _lock->object_monitor_cache()) {
- _thread->om_set_monitor_cache(_monitor);
- _lock->set_object_monitor_cache(_monitor);
- }
- } else {
- _lock->clear_object_monitor_cache();
+ if (_monitor != nullptr) {
+ // If the monitor is already in the BasicLock cache then it is most
+ // likely in the thread cache, do not set it again to avoid reordering.
+ if (_monitor != _lock->object_monitor_cache()) {
+ _thread->om_set_monitor_cache(_monitor);
+ _lock->set_object_monitor_cache(_monitor);
}
+ } else {
+ _lock->clear_object_monitor_cache();
}
}
@@ -1754,7 +1649,6 @@ inline bool ObjectSynchronizer::fast_lock_try_enter(oop obj, LockStack& lock_sta
}
bool ObjectSynchronizer::fast_lock_spin_enter(oop obj, LockStack& lock_stack, JavaThread* current, bool observed_deflation) {
- assert(UseObjectMonitorTable, "must be");
// Will spin with exponential backoff with an accumulative O(2^spin_limit) spins.
const int log_spin_limit = os::is_MP() ? FastLockingSpins : 1;
const int log_min_safepoint_check_interval = 10;
@@ -1801,7 +1695,7 @@ void ObjectSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* lock
// deoptimizing and re-locking locks. See Deoptimization::relock_objects
assert(locking_thread == Thread::current() || locking_thread->is_obj_deopt_suspend(), "must be");
- assert(!UseObjectMonitorTable || lock->object_monitor_cache() == nullptr, "must be cleared");
+ assert(lock->object_monitor_cache() == nullptr, "must be cleared");
JavaThread* current = JavaThread::current();
VerifyThreadState vts(locking_thread, current);
@@ -1825,7 +1719,7 @@ void ObjectSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* lock
}
assert(monitor != nullptr, "ObjectSynchronizer::enter_for must succeed");
- assert(!UseObjectMonitorTable || lock->object_monitor_cache() == nullptr, "unused. already cleared");
+ assert(lock->object_monitor_cache() == nullptr, "unused. already cleared");
}
void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current) {
@@ -1866,7 +1760,7 @@ void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current)
// If deflation has been observed we also spin while deflation is ongoing.
if (fast_lock_try_enter(obj(), lock_stack, current)) {
return;
- } else if (UseObjectMonitorTable && fast_lock_spin_enter(obj(), lock_stack, current, observed_deflation)) {
+ } else if (fast_lock_spin_enter(obj(), lock_stack, current, observed_deflation)) {
return;
}
@@ -1923,13 +1817,9 @@ void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current)
assert(mark.has_monitor(), "must be");
// The monitor exists
ObjectMonitor* monitor;
- if (UseObjectMonitorTable) {
- monitor = read_caches(current, lock, object);
- if (monitor == nullptr) {
- monitor = get_monitor_from_table(object);
- }
- } else {
- monitor = ObjectSynchronizer::read_monitor(mark);
+ monitor = read_caches(current, lock, object);
+ if (monitor == nullptr) {
+ monitor = get_monitor_from_table(object);
}
if (monitor->has_anonymous_owner()) {
assert(current->lock_stack().contains(object), "current must have object on its lock stack");
@@ -1990,131 +1880,12 @@ ObjectMonitor* ObjectSynchronizer::inflate_locked_or_imse(oop obj, ObjectSynchro
}
}
-ObjectMonitor* ObjectSynchronizer::inflate_into_object_header(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, Thread* current) {
-
- // The JavaThread* locking parameter requires that the locking_thread == JavaThread::current,
- // or is suspended throughout the call by some other mechanism.
- // Even with fast locking the thread might be nullptr when called from a non
- // JavaThread. (As may still be the case from FastHashCode). However it is only
- // important for the correctness of the fast locking algorithm that the thread
- // is set when called from ObjectSynchronizer::enter from the owning thread,
- // ObjectSynchronizer::enter_for from any thread, or ObjectSynchronizer::exit.
- EventJavaMonitorInflate event;
-
- for (;;) {
- const markWord mark = object->mark_acquire();
-
- // The mark can be in one of the following states:
- // * inflated - If the ObjectMonitor owner is anonymous and the
- // locking_thread owns the object lock, then we make the
- // locking_thread the ObjectMonitor owner and remove the
- // lock from the locking_thread's lock stack.
- // * fast-locked - Coerce it to inflated from fast-locked.
- // * unlocked - Aggressively inflate the object.
-
- // CASE: inflated
- if (mark.has_monitor()) {
- ObjectMonitor* inf = mark.monitor();
- markWord dmw = inf->header();
- assert(dmw.is_neutral(), "invariant: header=" INTPTR_FORMAT, dmw.value());
- if (inf->has_anonymous_owner() &&
- locking_thread != nullptr && locking_thread->lock_stack().contains(object)) {
- inf->set_owner_from_anonymous(locking_thread);
- size_t removed = locking_thread->lock_stack().remove(object);
- inf->set_recursions(removed - 1);
- }
- return inf;
- }
-
- // CASE: fast-locked
- // Could be fast-locked either by the locking_thread or by some other thread.
- //
- // Note that we allocate the ObjectMonitor speculatively, _before_
- // attempting to set the object's mark to the new ObjectMonitor. If
- // the locking_thread owns the monitor, then we set the ObjectMonitor's
- // owner to the locking_thread. Otherwise, we set the ObjectMonitor's owner
- // to anonymous. If we lose the race to set the object's mark to the
- // new ObjectMonitor, then we just delete it and loop around again.
- //
- if (mark.is_fast_locked()) {
- ObjectMonitor* monitor = new ObjectMonitor(object);
- monitor->set_header(mark.set_unlocked());
- bool own = locking_thread != nullptr && locking_thread->lock_stack().contains(object);
- if (own) {
- // Owned by locking_thread.
- monitor->set_owner(locking_thread);
- } else {
- // Owned by somebody else.
- monitor->set_anonymous_owner();
- }
- markWord monitor_mark = markWord::encode(monitor);
- markWord old_mark = object->cas_set_mark(monitor_mark, mark);
- if (old_mark == mark) {
- // Success! Return inflated monitor.
- if (own) {
- size_t removed = locking_thread->lock_stack().remove(object);
- monitor->set_recursions(removed - 1);
- }
- // Once the ObjectMonitor is configured and object is associated
- // with the ObjectMonitor, it is safe to allow async deflation:
- ObjectSynchronizer::_in_use_list.add(monitor);
-
- log_inflate(current, object, cause);
- if (event.should_commit()) {
- post_monitor_inflate_event(&event, object, cause);
- }
- return monitor;
- } else {
- delete monitor;
- continue; // Interference -- just retry
- }
- }
-
- // CASE: unlocked
- // TODO-FIXME: for entry we currently inflate and then try to CAS _owner.
- // If we know we're inflating for entry it's better to inflate by swinging a
- // pre-locked ObjectMonitor pointer into the object header. A successful
- // CAS inflates the object *and* confers ownership to the inflating thread.
- // In the current implementation we use a 2-step mechanism where we CAS()
- // to inflate and then CAS() again to try to swing _owner from null to current.
- // An inflateTry() method that we could call from enter() would be useful.
-
- assert(mark.is_unlocked(), "invariant: header=" INTPTR_FORMAT, mark.value());
- ObjectMonitor* m = new ObjectMonitor(object);
- // prepare m for installation - set monitor to initial state
- m->set_header(mark);
-
- if (object->cas_set_mark(markWord::encode(m), mark) != mark) {
- delete m;
- m = nullptr;
- continue;
- // interference - the markword changed - just retry.
- // The state-transitions are one-way, so there's no chance of
- // live-lock -- "Inflated" is an absorbing state.
- }
-
- // Once the ObjectMonitor is configured and object is associated
- // with the ObjectMonitor, it is safe to allow async deflation:
- ObjectSynchronizer::_in_use_list.add(m);
-
- log_inflate(current, object, cause);
- if (event.should_commit()) {
- post_monitor_inflate_event(&event, object, cause);
- }
- return m;
- }
-}
-
ObjectMonitor* ObjectSynchronizer::inflate_fast_locked_object(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current) {
VerifyThreadState vts(locking_thread, current);
assert(locking_thread->lock_stack().contains(object), "locking_thread must have object on its lock stack");
ObjectMonitor* monitor;
- if (!UseObjectMonitorTable) {
- return inflate_into_object_header(object, cause, locking_thread, current);
- }
-
// Inflating requires a hash code
ObjectSynchronizer::FastHashCode(current, object);
@@ -2169,21 +1940,6 @@ ObjectMonitor* ObjectSynchronizer::inflate_and_enter(oop object, BasicLock* lock
ObjectMonitor* monitor = nullptr;
- if (!UseObjectMonitorTable) {
- // Do the old inflate and enter.
- monitor = inflate_into_object_header(object, cause, locking_thread, current);
-
- bool entered;
- if (locking_thread == current) {
- entered = monitor->enter(locking_thread);
- } else {
- entered = monitor->enter_for(locking_thread);
- }
-
- // enter returns false for deflation found.
- return entered ? monitor : nullptr;
- }
-
NoSafepointVerifier nsv;
// Try to get the monitor from the thread-local cache.
@@ -2330,24 +2086,15 @@ void ObjectSynchronizer::deflate_monitor(oop obj, ObjectMonitor* monitor) {
}
ObjectMonitor* ObjectSynchronizer::get_monitor_from_table(oop obj) {
- assert(UseObjectMonitorTable, "must be");
return ObjectMonitorTable::monitor_get(obj);
}
-ObjectMonitor* ObjectSynchronizer::read_monitor(markWord mark) {
- return mark.monitor();
-}
-
ObjectMonitor* ObjectSynchronizer::read_monitor(oop obj) {
return ObjectSynchronizer::read_monitor(obj, obj->mark());
}
ObjectMonitor* ObjectSynchronizer::read_monitor(oop obj, markWord mark) {
- if (!UseObjectMonitorTable) {
- return read_monitor(mark);
- } else {
- return ObjectSynchronizer::get_monitor_from_table(obj);
- }
+ return ObjectSynchronizer::get_monitor_from_table(obj);
}
bool ObjectSynchronizer::quick_enter_internal(oop obj, BasicLock* lock, JavaThread* current) {
@@ -2381,26 +2128,19 @@ bool ObjectSynchronizer::quick_enter_internal(oop obj, BasicLock* lock, JavaThre
#endif
if (mark.has_monitor()) {
- ObjectMonitor* monitor;
- if (UseObjectMonitorTable) {
- monitor = read_caches(current, lock, obj);
- } else {
- monitor = ObjectSynchronizer::read_monitor(mark);
- }
+ ObjectMonitor* monitor = read_caches(current, lock, obj);
if (monitor == nullptr) {
// Take the slow-path on a cache miss.
return false;
}
- if (UseObjectMonitorTable) {
- // Set the monitor regardless of success.
- // Either we successfully lock on the monitor, or we retry with the
- // monitor in the slow path. If the monitor gets deflated, it will be
- // cleared, either by the CacheSetter if we fast lock in enter or in
- // inflate_and_enter when we see that the monitor is deflated.
- lock->set_object_monitor_cache(monitor);
- }
+ // Set the monitor regardless of success.
+ // Either we successfully lock on the monitor, or we retry with the
+ // monitor in the slow path. If the monitor gets deflated, it will be
+ // cleared, either by the CacheSetter if we fast lock in enter or in
+ // inflate_and_enter when we see that the monitor is deflated.
+ lock->set_object_monitor_cache(monitor);
if (monitor->spin_enter(current)) {
return true;
diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp
index 58d0e88a026e..922d988e290a 100644
--- a/src/hotspot/share/runtime/synchronizer.hpp
+++ b/src/hotspot/share/runtime/synchronizer.hpp
@@ -125,7 +125,6 @@ class ObjectSynchronizer : AllStatic {
public:
static const char* inflate_cause_name(const InflateCause cause);
- static ObjectMonitor* read_monitor(markWord mark);
static ObjectMonitor* read_monitor(oop obj);
static ObjectMonitor* read_monitor(oop obj, markWord mark);
@@ -234,7 +233,6 @@ class ObjectSynchronizer : AllStatic {
static bool fast_lock_spin_enter(oop obj, LockStack& lock_stack, JavaThread* current, bool observed_deflation);
public:
- static ObjectMonitor* inflate_into_object_header(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, Thread* current);
static ObjectMonitor* inflate_locked_or_imse(oop object, ObjectSynchronizer::InflateCause cause, TRAPS);
static ObjectMonitor* inflate_fast_locked_object(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current);
static ObjectMonitor* inflate_and_enter(oop object, BasicLock* lock, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current);
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Mark.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Mark.java
index c41372810a35..851ec52abf04 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Mark.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Mark.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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
@@ -158,19 +158,14 @@ public ObjectMonitor monitor() {
if (Assert.ASSERTS_ENABLED) {
Assert.that(hasMonitor(), "check");
}
- if (VM.getVM().getCommandLineFlag("UseObjectMonitorTable").getBool()) {
- Iterator it = ObjectSynchronizer.objectMonitorIterator();
- while (it != null && it.hasNext()) {
- ObjectMonitor mon = (ObjectMonitor)it.next();
- if (getAddress().equals(mon.object())) {
- return mon;
- }
+ Iterator it = ObjectSynchronizer.objectMonitorIterator();
+ while (it != null && it.hasNext()) {
+ ObjectMonitor mon = (ObjectMonitor)it.next();
+ if (getAddress().equals(mon.object())) {
+ return mon;
}
- return null;
}
- // Use xor instead of &~ to provide one extra tag-bit check.
- Address monAddr = valueAsAddress().xorWithMask(monitorValue);
- return new ObjectMonitor(monAddr);
+ return null;
}
public boolean hasDisplacedMarkHelper() {
return ((value() & unlockedValue) == 0);
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java
index cb82471cf588..9c2cef7d73b5 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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
@@ -55,12 +55,7 @@ public long identityHashValueFor(Oop obj) {
// FIXME: can not generate marks in debugging system
return mark.hash();
} else if (mark.hasMonitor()) {
- if (VM.getVM().getCommandLineFlag("UseObjectMonitorTable").getBool()) {
- return mark.hash();
- }
- ObjectMonitor monitor = mark.monitor();
- Mark temp = monitor.header();
- return temp.hash();
+ return mark.hash();
} else {
if (Assert.ASSERTS_ENABLED) {
Assert.that(VM.getVM().isDebugging(), "Can not access displaced header otherwise");
diff --git a/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java b/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java
index 58a90f8ba544..cc55e697a6f6 100644
--- a/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java
+++ b/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java
@@ -66,26 +66,6 @@
* @run driver VMOptionWarning Develop
*/
-/* @test VMOptionWarningCompactObjectHeaders
- * @bug 8360700
- * @summary Warn if -XX:+UseCompactObjectHeaders is used with -XX:-UseObjectMonitorTable
- * @requires vm.flagless
- * @library /test/lib
- * @modules java.base/jdk.internal.misc
- * java.management
- * @run driver VMOptionWarning CompactObjectHeaders
- */
-
-/* @test VMOptionWarningUseObjectMonitorTable
- * @bug 8360700
- * @summary Warn if -XX:-UseObjectMonitorTable is used without -XX:-UseCompactObjectHeaders
- * @requires vm.flagless
- * @library /test/lib
- * @modules java.base/jdk.internal.misc
- * java.management
- * @run driver VMOptionWarning UseObjectMonitorTable
- */
-
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.Platform;
@@ -127,20 +107,6 @@ public static void main(String[] args) throws Exception {
output.shouldContain("Error: VM option 'VerifyStack' is develop and is available only in debug version of VM.");
break;
}
- case "CompactObjectHeaders": {
- pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+UseCompactObjectHeaders", "-XX:+UnlockDiagnosticVMOptions", "-XX:-UseObjectMonitorTable", "-version");
- output = new OutputAnalyzer(pb.start());
- output.shouldHaveExitValue(0);
- output.shouldContain("warning: -UseObjectMonitorTable is incompatible with +UseCompactObjectHeaders; ignoring -UseObjectMonitorTable");
- break;
- }
- case "UseObjectMonitorTable": {
- pb = ProcessTools.createLimitedTestJavaProcessBuilder("-XX:+UnlockDiagnosticVMOptions", "-XX:-UseObjectMonitorTable", "-version");
- output = new OutputAnalyzer(pb.start());
- output.shouldHaveExitValue(0);
- output.shouldContain("warning: -UseObjectMonitorTable is incompatible with +UseCompactObjectHeaders; ignoring -UseObjectMonitorTable");
- break;
- }
default: {
throw new RuntimeException("Invalid argument: " + args[0]);
}
diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java
index a8c03e259fc8..176d757dff25 100644
--- a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java
+++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointersEncodingScheme.java
@@ -50,7 +50,6 @@ private static void test(long forceAddress, boolean COH, long classSpaceSize, lo
"-XX:-UseCompressedOops", // keep VM from optimizing heap location
"-XX:+UnlockExperimentalVMOptions",
"-XX:" + (COH ? "+" : "-") + "UseCompactObjectHeaders",
- "-XX:" + (COH ? "+" : "-") + "UseObjectMonitorTable",
"-XX:CompressedClassSpaceBaseAddress=" + forceAddress,
"-XX:CompressedClassSpaceSize=" + classSpaceSize,
"-Xmx64m",
diff --git a/test/hotspot/jtreg/runtime/Monitor/UseObjectMonitorTableTest.java b/test/hotspot/jtreg/runtime/Monitor/UseObjectMonitorTableTest.java
index 6af1602e3380..fd6ece349a24 100644
--- a/test/hotspot/jtreg/runtime/Monitor/UseObjectMonitorTableTest.java
+++ b/test/hotspot/jtreg/runtime/Monitor/UseObjectMonitorTableTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 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,9 +26,7 @@
* @summary A collection of small tests using synchronized, wait, notify to try
* and achieve good cheap coverage of UseObjectMonitorTable.
* @library /test/lib
- * @run main/othervm -XX:+UnlockDiagnosticVMOptions
- * -XX:+UseObjectMonitorTable
- * UseObjectMonitorTableTest
+ * @run main/othervm UseObjectMonitorTableTest
*/
/**
@@ -37,7 +35,6 @@
* @library /test/lib
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
* -XX:GuaranteedAsyncDeflationInterval=1
- * -XX:+UseObjectMonitorTable
* UseObjectMonitorTableTest
*/
From 0ffccaa0830faabf09a9c2637ab2b5f5e886518a Mon Sep 17 00:00:00 2001
From: Aleksey Shipilev
Date: Thu, 13 Aug 2026 12:17:08 +0000
Subject: [PATCH 05/88] 8390122: Shenandoah: SBS::oop_load drops memory
ordering decorators
Reviewed-by: wkemper, xpeng
---
.../gc/shenandoah/shenandoahBarrierSet.hpp | 6 ++--
.../shenandoahBarrierSet.inline.hpp | 28 +++++++++----------
2 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp
index e3619c3f45f3..4ae1f03a08da 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp
@@ -110,9 +110,6 @@ class ShenandoahBarrierSet: public BarrierSet {
template
inline oop load_reference_barrier(DecoratorSet decorators, oop obj, T* load_addr);
- template
- inline oop oop_load(DecoratorSet decorators, T* addr);
-
template
inline oop oop_cmpxchg(DecoratorSet decorators, T* addr, oop compare_value, oop new_value);
@@ -147,6 +144,9 @@ class ShenandoahBarrierSet: public BarrierSet {
typedef BarrierSet::AccessBarrier Raw;
private:
+ template
+ static oop oop_load_common(DecoratorSet resolved_decorators, T* addr);
+
template
static void oop_store_common(T* addr, oop value);
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
index 304d1430c4a2..92f360216f40 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
@@ -233,14 +233,6 @@ inline void ShenandoahBarrierSet::write_ref_field_post(T* field, oop new_value)
*byte = CardTable::dirty_card_val();
}
-template
-inline oop ShenandoahBarrierSet::oop_load(DecoratorSet decorators, T* addr) {
- oop value = RawAccess<>::oop_load(addr);
- value = load_reference_barrier(decorators, value, addr);
- keep_alive_if_weak(decorators, value);
- return value;
-}
-
template
inline oop ShenandoahBarrierSet::oop_cmpxchg(DecoratorSet decorators, T* addr, oop compare_value, oop new_value) {
shenandoah_assert_not_in_cset_except(nullptr, compare_value, (compare_value == nullptr || ShenandoahHeap::heap()->cancelled_gc()));
@@ -274,27 +266,35 @@ inline oop ShenandoahBarrierSet::oop_xchg(DecoratorSet decorators, T* addr, oop
return RawAccess<>::oop_atomic_xchg(addr, new_value);
}
+template
+template
+inline oop ShenandoahBarrierSet::AccessBarrier::oop_load_common(DecoratorSet resolved_decorators, T* addr) {
+ // This raw access inherits decorators that are needed for proper memory ordering.
+ oop value = Raw::template oop_load(addr);
+ ShenandoahBarrierSet* bs = barrier_set();
+ value = bs->load_reference_barrier(resolved_decorators, value, addr);
+ bs->keep_alive_if_weak(resolved_decorators, value);
+ return value;
+}
+
template
template
inline oop ShenandoahBarrierSet::AccessBarrier::oop_load_not_in_heap(T* addr) {
assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "must be absent");
- ShenandoahBarrierSet* const bs = ShenandoahBarrierSet::barrier_set();
- return bs->oop_load(decorators, addr);
+ return oop_load_common(decorators, addr);
}
template
template
inline oop ShenandoahBarrierSet::AccessBarrier::oop_load_in_heap(T* addr) {
assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "must be absent");
- ShenandoahBarrierSet* const bs = ShenandoahBarrierSet::barrier_set();
- return bs->oop_load(decorators, addr);
+ return oop_load_common(decorators, addr);
}
template
inline oop ShenandoahBarrierSet::AccessBarrier::oop_load_in_heap_at(oop base, ptrdiff_t offset) {
- ShenandoahBarrierSet* const bs = ShenandoahBarrierSet::barrier_set();
DecoratorSet resolved_decorators = AccessBarrierSupport::resolve_possibly_unknown_oop_ref_strength(base, offset);
- return bs->oop_load(resolved_decorators, AccessInternal::oop_field_addr(base, offset));
+ return oop_load_common(resolved_decorators, AccessInternal::oop_field_addr(base, offset));
}
template
From 740621f198c5f55322348da3b74ba43464f51abc Mon Sep 17 00:00:00 2001
From: Suchismith Roy
Date: Thu, 13 Aug 2026 12:38:27 +0000
Subject: [PATCH 06/88] 8382604: Remove unused kscratch temp register from
mask_opers_evex
Reviewed-by: galder, amitkumar
---
src/hotspot/cpu/x86/x86.ad | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad
index 393f37ef8e36..217edb03f122 100644
--- a/src/hotspot/cpu/x86/x86.ad
+++ b/src/hotspot/cpu/x86/x86.ad
@@ -24809,12 +24809,11 @@ instruct long_to_mask_evex(kReg dst, rRegL src) %{
ins_pipe( pipe_slow );
%}
-instruct mask_opers_evex(kReg dst, kReg src1, kReg src2, kReg kscratch) %{
+instruct mask_opers_evex(kReg dst, kReg src1, kReg src2) %{
match(Set dst (AndVMask src1 src2));
match(Set dst (OrVMask src1 src2));
match(Set dst (XorVMask src1 src2));
- effect(TEMP kscratch);
- format %{ "mask_opers_evex $dst, $src1, $src2\t! using $kscratch as TEMP" %}
+ format %{ "mask_opers_evex $dst, $src1, $src2" %}
ins_encode %{
const MachNode* mask1 = static_cast(this->in(this->operand_index($src1)));
const MachNode* mask2 = static_cast(this->in(this->operand_index($src2)));
From 5a912628cd1e446251cada96fe67d92c9bfaa7ff Mon Sep 17 00:00:00 2001
From: Ashay Rane
Date: Thu, 13 Aug 2026 12:51:38 +0000
Subject: [PATCH 07/88] 8389209: Add sccache support to the build
Reviewed-by: erikj
---
doc/building.html | 9 +++
doc/building.md | 9 +++
make/autoconf/build-performance.m4 | 111 ++++++++++++++++++++++++++++-
make/autoconf/configure.ac | 3 +
make/autoconf/help.m4 | 3 +
make/autoconf/spec.gmk.template | 5 +-
6 files changed, 137 insertions(+), 3 deletions(-)
diff --git a/doc/building.html b/doc/building.html
index be3c8c364d7b..ed77db508346 100644
--- a/doc/building.html
+++ b/doc/building.html
@@ -146,6 +146,7 @@ Building the JDK
Virus
Checking
Ccache
+Sccache
Icecc /
@@ -1814,6 +1815,14 @@ Ccache
often rebuild the same sources. Your mileage may vary however, so we
recommend evaluating it for yourself. To enable it, make sure it's on
the path and configure with --enable-ccache.
+The JDK build supports building with sccache when using gcc, clang, or
+Microsoft toolchains. To enable it, make sure the sccache binary is on the path
+(or specify the path to the binary using the SCCACHE argument to
+the configure script) and configure with --enable-sccache. To
+optionally specify where sccache stores its cache files, use
+--with-sccache-dir. Precompiled headers are disabled when sccache
+is enabled.
By default, the Hotspot build uses pre-compiled headers (PCH) on the
toolchains were it is properly supported (clang, gcc, and Visual
diff --git a/doc/building.md b/doc/building.md
index adf116764fa0..7e9211d8f66d 100644
--- a/doc/building.md
+++ b/doc/building.md
@@ -1555,6 +1555,15 @@ the same sources. Your mileage may vary however, so we recommend evaluating it
for yourself. To enable it, make sure it's on the path and configure with
`--enable-ccache`.
+### Sccache
+
+The JDK build supports building with sccache when using gcc, clang, or Microsoft
+toolchains. To enable it, make sure the sccache binary is on the path (or
+specify the path to the binary using the `SCCACHE` argument to the configure
+script) and configure with `--enable-sccache`. To optionally specify where
+sccache stores its cache files, use `--with-sccache-dir`. Precompiled headers
+are disabled when sccache is enabled.
+
### Precompiled Headers
By default, the Hotspot build uses pre-compiled headers (PCH) on the toolchains
diff --git a/make/autoconf/build-performance.m4 b/make/autoconf/build-performance.m4
index dfc9e979d2fd..88817c359695 100644
--- a/make/autoconf/build-performance.m4
+++ b/make/autoconf/build-performance.m4
@@ -252,6 +252,109 @@ AC_DEFUN([BPERF_SETUP_CCACHE_USAGE],
fi
])
+AC_DEFUN([BPERF_SETUP_SCCACHE],
+[
+ # Check if sccache is available
+ SCCACHE_AVAILABLE=true
+
+ UTIL_LOOKUP_TOOLCHAIN_PROGS(SCCACHE, sccache)
+
+ AC_MSG_CHECKING([if sccache is available])
+ if test "x$TOOLCHAIN_TYPE" != "xgcc" && test "x$TOOLCHAIN_TYPE" != "xclang" && \
+ test "x$TOOLCHAIN_TYPE" != "xmicrosoft"; then
+ AC_MSG_RESULT([no, not supported for toolchain type $TOOLCHAIN_TYPE])
+ SCCACHE_AVAILABLE=false
+ elif test "x$SCCACHE" = "x"; then
+ AC_MSG_RESULT([no, sccache binary missing or not executable])
+ SCCACHE_AVAILABLE=false
+ else
+ AC_MSG_RESULT([yes])
+ fi
+
+ SCCACHE_STATUS=""
+ UTIL_ARG_ENABLE(NAME: sccache, DEFAULT: false, AVAILABLE: $SCCACHE_AVAILABLE,
+ DESC: [enable using sccache to speed up recompilations],
+ CHECKING_MSG: [if sccache is enabled],
+ IF_ENABLED: [
+ if test "x$CCACHE" != x; then
+ AC_MSG_ERROR([Cannot enable both ccache and sccache])
+ fi
+ # Versions of sccache before 0.10.0 can restore stale or incorrect
+ # dependency files for cached C/C++ compilations, breaking our build.
+ SCCACHE_VERSION=[`$SCCACHE --version | head -n1 | $CUT -d " " -f 2 | $TR -d '\r'`]
+ if test "x$SCCACHE_VERSION" = x; then
+ AC_MSG_ERROR([Could not determine sccache version])
+ fi
+ HAS_BAD_SCCACHE=[`$ECHO $SCCACHE_VERSION | \
+ $GREP -e '^0\.[0-9]\.' -e '^0\.[0-9]$'`]
+ if test "x$HAS_BAD_SCCACHE" != "x"; then
+ AC_MSG_ERROR([[sccache 0.10.0 or later is required, found $SCCACHE_VERSION]])
+ fi
+ SCCACHE_STATUS="Active ($SCCACHE_VERSION)"
+ ],
+ IF_DISABLED: [
+ SCCACHE=""
+ ])
+ AC_SUBST(SCCACHE)
+
+ AC_ARG_WITH([sccache-dir],
+ [AS_HELP_STRING([--with-sccache-dir],
+ [where to store sccache files @<:@~/.cache/sccache@:>@])])
+
+ if test "x$with_sccache_dir" != x; then
+ SCCACHE_DIR="$with_sccache_dir"
+ SCCACHE_DIR_FOR_SCCACHE="$SCCACHE_DIR"
+
+ # Ideally, we'd use `UTIL_FIXUP_PATH()`, but it expects the supplied path to
+ # already exist, which might not be true for the sccache directory during
+ # the configure step. As a workaround, we manually invoke fixpath.sh.
+ if test "x$OPENJDK_BUILD_OS" = "xwindows"; then
+ SCCACHE_DIR_FOR_SCCACHE=`$FIXPATH_BASE -m print "$SCCACHE_DIR_FOR_SCCACHE"`
+ fi
+
+ SET_SCCACHE_DIR="SCCACHE_DIR=$SCCACHE_DIR_FOR_SCCACHE"
+ if test "x$SCCACHE" = x; then
+ AC_MSG_WARN([--with-sccache-dir has no meaning when sccache is not enabled])
+ fi
+ fi
+
+ if test "x$SCCACHE" != x; then
+ BPERF_SETUP_SCCACHE_USAGE
+ fi
+])
+
+AC_DEFUN([BPERF_SETUP_SCCACHE_USAGE],
+[
+ if test "x$SCCACHE" != x; then
+ if test "x$USE_PRECOMPILED_HEADER" = "xtrue"; then
+ if test "x$PRECOMPILED_HEADERS_EXPLICITLY_SET" = "xtrue"; then
+ AC_MSG_ERROR([Cannot use sccache with precompiled headers. Use --disable-precompiled-headers.])
+ else
+ AC_MSG_NOTICE([Disabling precompiled headers because sccache is enabled])
+ USE_PRECOMPILED_HEADER=false
+ fi
+ fi
+
+ # On Windows, the sccache binary must be launched through fixpath and the
+ # compiler argument passed to sccache must be the actual compiler
+ # (gcc/clang/cl) and not another fixpath invocation, otherwise sccache will
+ # try to execute fixpath as the compiler.
+ [ if [[ "$OPENJDK_BUILD_OS" = "windows" && "$SCCACHE" =~ ^"$FIXPATH " ]]; then ]
+ [ if [[ "$CC" =~ ^"$FIXPATH " ]]; then ]
+ CC="${CC#"$FIXPATH "}"
+ [ fi ]
+ [ if [[ "$CXX" =~ ^"$FIXPATH " ]]; then ]
+ CXX="${CXX#"$FIXPATH "}"
+ [ fi ]
+ [ fi ]
+
+ if test "x$SET_SCCACHE_DIR" != x; then
+ SCCACHE="$SET_SCCACHE_DIR $SCCACHE"
+ mkdir -p "$SCCACHE_DIR" > /dev/null 2>&1
+ fi
+ fi
+])
+
################################################################################
#
# Runs icecc-create-env once and prints the error if it fails
@@ -372,7 +475,13 @@ AC_DEFUN_ONCE([BPERF_SETUP_PRECOMPILED_HEADERS],
UTIL_ARG_ENABLE(NAME: precompiled-headers, DEFAULT: auto,
RESULT: USE_PRECOMPILED_HEADER, AVAILABLE: $PRECOMPILED_HEADERS_AVAILABLE,
- DESC: [enable using precompiled headers when compiling C++])
+ DESC: [enable using precompiled headers when compiling C++],
+ IF_GIVEN: [
+ PRECOMPILED_HEADERS_EXPLICITLY_SET=true
+ ],
+ IF_NOT_GIVEN: [
+ PRECOMPILED_HEADERS_EXPLICITLY_SET=false
+ ])
AC_SUBST(USE_PRECOMPILED_HEADER)
])
diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac
index 59380cdfe444..87c5f53a1e96 100644
--- a/make/autoconf/configure.ac
+++ b/make/autoconf/configure.ac
@@ -293,6 +293,9 @@ BPERF_SETUP_PRECOMPILED_HEADERS
# Setup use of ccache, if available
BPERF_SETUP_CCACHE
+# Setup use of sccache, if available
+BPERF_SETUP_SCCACHE
+
################################################################################
#
# And now the finish...
diff --git a/make/autoconf/help.m4 b/make/autoconf/help.m4
index d8c0b2ffaeff..23541cea3efa 100644
--- a/make/autoconf/help.m4
+++ b/make/autoconf/help.m4
@@ -335,6 +335,9 @@ AC_DEFUN_ONCE([HELP_PRINT_SUMMARY_AND_WARNINGS],
if test "x$CCACHE_STATUS" != "x"; then
$ECHO "* ccache status: $CCACHE_STATUS"
fi
+ if test "x$SCCACHE_STATUS" != "x"; then
+ $ECHO "* sccache status: $SCCACHE_STATUS"
+ fi
$ECHO ""
if test "x$BUILDING_MULTIPLE_JVM_VARIANTS" = "xtrue"; then
diff --git a/make/autoconf/spec.gmk.template b/make/autoconf/spec.gmk.template
index 3779ec32e45f..1cfaf1186d50 100644
--- a/make/autoconf/spec.gmk.template
+++ b/make/autoconf/spec.gmk.template
@@ -545,7 +545,7 @@ ADLC_LANGSTD_CXXFLAGS := @ADLC_LANGSTD_CXXFLAGS@
ADLC_LDFLAGS := @ADLC_LDFLAGS@
# Tools that potentially need to be cross compilation aware.
-CC := @CCACHE@ @ICECC@ @CC@
+CC := @SCCACHE@ @CCACHE@ @ICECC@ @CC@
# CFLAGS used to compile the jdk native libraries (C-code)
CFLAGS_JDKLIB := @CFLAGS_JDKLIB@
@@ -571,7 +571,7 @@ EXTRA_CXXFLAGS := @EXTRA_CXXFLAGS@
EXTRA_LDFLAGS := @EXTRA_LDFLAGS@
EXTRA_ASFLAGS := @EXTRA_ASFLAGS@
-CXX := @CCACHE@ @ICECC@ @CXX@
+CXX := @SCCACHE@ @CCACHE@ @ICECC@ @CXX@
CPP := @CPP@
@@ -740,6 +740,7 @@ RCFLAGS := @RCFLAGS@
AWK := @AWK@
BASENAME := @BASENAME@
CAT := @CAT@
+SCCACHE := @SCCACHE@
CCACHE := @CCACHE@
# CD is going away, but remains to cater for legacy makefiles.
CD := cd
From 66a0483a94105f36a7d32bfe4b72abdaaa32fe74 Mon Sep 17 00:00:00 2001
From: Gui Cao
Date: Thu, 13 Aug 2026 12:52:12 +0000
Subject: [PATCH 08/88] 8388458: RISC-V: Use LSB for conditional card mark in
G1 post-write barrier
Co-authored-by: Dingli Zhang
Reviewed-by: ayang, fyang, tschatzl
---
.../gc/g1/g1BarrierSetAssembler_riscv.cpp | 34 ++++++++++++++-----
1 file changed, 25 insertions(+), 9 deletions(-)
diff --git a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp
index bae1349ba14d..fa236bf8eadf 100644
--- a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp
@@ -118,13 +118,20 @@ void G1BarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* mas
// Iterate from start card to end card (inclusive).
__ bind(loop);
if (UseCondCardMark) {
+ // All non-clean cards (dirty, to-cset, from-remset) have bit0 == 0.
+ static_assert((G1CardTable::g1_dirty_card & 1U) == 0
+ && (G1CardTable::g1_to_cset_card & 1U) == 0
+ && (G1CardTable::g1_from_remset_card & 1U) == 0,
+ "cards needing scan must have bit0 == 0");
+ // Clean card has bit0 == 1.
+ static_assert(((uint)G1CardTable::clean_card_val() & 1U) == 1,
+ "clean card must have bit0 == 1");
__ lbu(tmp, Address(start, 0));
- static_assert((uint)G1CardTable::clean_card_val() == 0xff, "must be");
- __ subi(tmp, tmp, G1CardTable::clean_card_val()); // Convert to clean_card_value() to a comparison
- // against zero to avoid use of an extra temp.
- __ bnez(tmp, next);
+ __ test_bit(tmp, tmp, 0); // test bit0: clean has bit0 == 1, non-clean has bit0 == 0
+ __ beqz(tmp, next); // skip store if already non-clean
}
+ // `sb zr` writes 0, which must be the dirty value.
static_assert(G1CardTable::dirty_card_val() == 0, "must be to use zr");
__ sb(zr, Address(start, 0));
@@ -264,14 +271,23 @@ static void generate_post_barrier(MacroAssembler* masm,
Address card_table_address(xthread, G1ThreadLocalData::card_table_base_offset());
__ ld(tmp2, card_table_address); // tmp2 := card table base address
__ add(tmp1, tmp1, tmp2); // tmp1 := card address
+
if (UseCondCardMark) {
- static_assert((uint)G1CardTable::clean_card_val() == 0xff, "must be");
+ // All non-clean cards (dirty, to-cset, from-remset) have bit0 == 0.
+ static_assert((G1CardTable::g1_dirty_card & 1U) == 0
+ && (G1CardTable::g1_to_cset_card & 1U) == 0
+ && (G1CardTable::g1_from_remset_card & 1U) == 0,
+ "cards needing scan must have bit0 == 0");
+ // Clean card has bit0 == 1.
+ static_assert(((uint)G1CardTable::clean_card_val() & 1U) == 1,
+ "clean card must have bit0 == 1");
__ lbu(tmp2, Address(tmp1, 0)); // tmp2 := card
- __ subi(tmp2, tmp2, G1CardTable::clean_card_val()); // Convert to clean_card_value() to a comparison
- // against zero to avoid use of an extra temp.
- __ bnez(tmp2, done);
+ __ test_bit(tmp2, tmp2, 0); // test bit0: clean has bit0 == 1, non-clean has bit0 == 0
+ __ beqz(tmp2, done); // skip store if already non-clean
}
- static_assert((uint)G1CardTable::dirty_card_val() == 0, "must be to use zr");
+
+ // `sb zr` writes 0, which must be the dirty value.
+ static_assert(G1CardTable::dirty_card_val() == 0, "must be to use zr");
__ sb(zr, Address(tmp1, 0));
}
From b8207347b8f9fec8dccddbad58cc48db89972d5f Mon Sep 17 00:00:00 2001
From: Suchismith Roy
Date: Thu, 13 Aug 2026 13:08:24 +0000
Subject: [PATCH 09/88] 8386510: AArch64: MacroAssembler constructor undefined
behaviour
Reviewed-by: adinn, aph
---
src/hotspot/cpu/aarch64/assembler_aarch64.hpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
index 89f3dd63f426..0c7d4c272cde 100644
--- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
@@ -457,17 +457,18 @@ class Address {
Address(address target, relocInfo::relocType rtype = relocInfo::external_word_type);
- Address(Register base, RegisterOrConstant index, extend ext = lsl()) {
+ Address(Register base, RegisterOrConstant index, extend ext = lsl(0)) {
if (index.is_register()) {
_mode = base_plus_offset_reg;
new (&_nonliteral) Nonliteral(base, index.as_register(), 0, ext);
} else {
guarantee(ext.option() == ext::uxtx, "should be");
assert(index.is_constant(), "should be");
+ assert(ext.shift() == 0, "must be");
_mode = base_plus_offset;
new (&_nonliteral) Nonliteral(base,
noreg,
- index.as_constant() << ext.shift());
+ index.as_constant());
}
}
From 1f74b50bfaebb8eb1d5d8d14acce389c24c95c54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Du=C5=A1an=20B=C3=A1lek?=
Date: Thu, 13 Aug 2026 15:03:23 +0000
Subject: [PATCH 10/88] 8389868: Empty LocalVariableTable attribute is
generated for an unused variable in clinit
Reviewed-by: liach, jlahoda
---
.../com/sun/tools/javac/jvm/ClassWriter.java | 5 +-
.../EmptyLocalVariableTableTest.java | 93 +++++++++++++++++++
2 files changed, 96 insertions(+), 2 deletions(-)
create mode 100644 test/langtools/tools/javac/classfiles/attributes/LocalVariableTable/EmptyLocalVariableTableTest.java
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
index 1fa6e7736f8d..afd3fed8757e 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
@@ -1144,10 +1144,11 @@ void writeCode(Code code) {
}
// counter for number of generic local variables
- if (code.varDebugInfo && code.varBufferSize > 0) {
+ int lvtSize = code.getLVTSize();
+ if (code.varDebugInfo && lvtSize > 0) {
int nGenericVars = 0;
int alenIdx = writeAttr(names.LocalVariableTable);
- databuf.appendChar(code.getLVTSize());
+ databuf.appendChar(lvtSize);
for (int i=0; i unused = null;
+ }
+ }
+ static {
+ Class> unused = null;
+ }
+ }
+ """)
+ .run()
+ .writeAll();
+ ClassFile.of().parse(classes.resolve("UnusedVariable.class")).methods().stream()
+ .forEach(mm -> {
+ CodeAttribute codeAttribute = mm.findAttribute(Attributes.code()).orElse(null);
+ Assertions.assertNotNull(codeAttribute);
+ codeAttribute.findAttributes(Attributes.localVariableTable()).stream()
+ .forEach(attr -> {
+ Assertions.assertFalse(attr.localVariables().isEmpty(), "Empty LocalVariableTableAttribute found");
+ });
+ });
+ }
+
+ @BeforeEach
+ public void setUp(TestInfo info) {
+ base = Paths.get(".")
+ .resolve(info.getTestMethod()
+ .orElseThrow()
+ .getName());
+ }
+}
From bc6d4fd10d30f4202d9cc12b48e845722be4d651 Mon Sep 17 00:00:00 2001
From: Aleksey Shipilev
Date: Thu, 13 Aug 2026 15:50:35 +0000
Subject: [PATCH 11/88] 8390215: Shenandoah: Simplify GC logging by dropping
unnecessary suffixes
Reviewed-by: wkemper, xpeng
---
.../gc/shenandoah/shenandoahConcurrentGC.cpp | 159 ++++--------------
.../gc/shenandoah/shenandoahConcurrentGC.hpp | 14 --
.../gc/shenandoah/shenandoahDegeneratedGC.cpp | 42 +++--
.../share/gc/shenandoah/shenandoahUtils.hpp | 17 +-
4 files changed, 74 insertions(+), 158 deletions(-)
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
index 18f8f5a4142d..fe7e5211e0a6 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp
@@ -105,7 +105,7 @@ ShenandoahGC::ShenandoahDegenPoint ShenandoahConcurrentGC::degen_point() const {
void ShenandoahConcurrentGC::entry_concurrent_update_refs_prepare(ShenandoahHeap* const heap) {
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- const char* msg = conc_init_update_refs_event_message();
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent init update refs", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs_prepare);
EventMark em("%s", msg);
@@ -117,8 +117,7 @@ void ShenandoahConcurrentGC::entry_concurrent_update_refs_prepare(ShenandoahHeap
void ShenandoahConcurrentGC::entry_update_card_table() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
-
- static const char* msg = "Concurrent update cards";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent update cards", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_card_table);
EventMark em("%s", msg);
@@ -295,7 +294,7 @@ void ShenandoahConcurrentGC::entry_complete_abbreviated_cycle() {
ShenandoahGenerationalHeap* const heap = ShenandoahGenerationalHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- static const char* msg = "Concurrent complete abbreviated cycle";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent complete abbreviated cycle", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::complete_abbreviated);
EventMark em("%s", msg);
@@ -376,7 +375,10 @@ void ShenandoahConcurrentGC::vmop_entry_final_verify() {
}
void ShenandoahConcurrentGC::entry_init_mark() {
- const char* msg = init_mark_event_message();
+ ShenandoahHeap* const heap = ShenandoahHeap::heap();
+ assert(!heap->has_forwarded_objects(), "Should not have forwarded objects here");
+
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Init Mark", "");
ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::init_mark);
EventMark em("%s", msg);
@@ -388,7 +390,11 @@ void ShenandoahConcurrentGC::entry_init_mark() {
}
void ShenandoahConcurrentGC::entry_final_mark() {
- const char* msg = final_mark_event_message();
+ ShenandoahHeap* const heap = ShenandoahHeap::heap();
+ assert(!heap->has_forwarded_objects() || heap->is_concurrent_old_mark_in_progress(),
+ "Should not have forwarded objects during final mark, unless old gen concurrent mark is running");
+
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Final Mark", "");
ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_mark);
EventMark em("%s", msg);
@@ -400,7 +406,7 @@ void ShenandoahConcurrentGC::entry_final_mark() {
}
void ShenandoahConcurrentGC::entry_init_update_refs() {
- static const char* msg = "Pause Init Update Refs";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Init Update Refs", "");
ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::init_update_refs);
EventMark em("%s", msg);
@@ -409,7 +415,7 @@ void ShenandoahConcurrentGC::entry_init_update_refs() {
}
void ShenandoahConcurrentGC::entry_final_update_refs() {
- static const char* msg = "Pause Final Update Refs";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Final Update Refs", "");
ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_update_refs);
EventMark em("%s", msg);
@@ -421,7 +427,7 @@ void ShenandoahConcurrentGC::entry_final_update_refs() {
}
void ShenandoahConcurrentGC::entry_final_verify() {
- const char* msg = verify_final_event_message();
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Verify Final", "");
ShenandoahPausePhase gc_phase(msg, ShenandoahPhaseTimings::final_verify);
EventMark em("%s", msg);
@@ -435,7 +441,7 @@ void ShenandoahConcurrentGC::entry_reset() {
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
{
- const char* msg = conc_reset_event_message();
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent reset", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_reset);
EventMark em("%s", msg);
@@ -450,7 +456,8 @@ void ShenandoahConcurrentGC::entry_scan_remembered_set() {
if (_generation->is_young()) {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- const char* msg = "Concurrent remembered set scanning";
+
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent remembered set scanning", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::init_scan_rset);
EventMark em("%s", msg);
@@ -466,7 +473,7 @@ void ShenandoahConcurrentGC::entry_scan_remembered_set() {
void ShenandoahConcurrentGC::entry_mark_roots() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- const char* msg = "Concurrent marking roots";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent marking roots", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_mark_roots);
EventMark em("%s", msg);
@@ -480,8 +487,11 @@ void ShenandoahConcurrentGC::entry_mark_roots() {
void ShenandoahConcurrentGC::entry_mark() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
+ assert(!heap->has_forwarded_objects() || heap->is_concurrent_old_mark_in_progress(),
+ "Should not have forwarded objects concurrent mark, unless old gen concurrent mark is running");
+
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- const char* msg = conc_mark_event_message();
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent marking", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_mark);
EventMark em("%s", msg);
@@ -496,7 +506,7 @@ void ShenandoahConcurrentGC::entry_mark() {
void ShenandoahConcurrentGC::entry_thread_roots() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
- static const char* msg = "Concurrent thread roots";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent thread roots", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_thread_roots);
EventMark em("%s", msg);
@@ -511,7 +521,7 @@ void ShenandoahConcurrentGC::entry_thread_roots() {
void ShenandoahConcurrentGC::entry_weak_refs() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
- const char* msg = conc_weak_refs_event_message();
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent weak references", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_refs);
EventMark em("%s", msg);
@@ -527,7 +537,7 @@ void ShenandoahConcurrentGC::entry_weak_refs() {
void ShenandoahConcurrentGC::entry_weak_roots() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- const char* msg = conc_weak_roots_event_message();
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent weak roots", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_weak_roots);
EventMark em("%s", msg);
@@ -543,7 +553,7 @@ void ShenandoahConcurrentGC::entry_weak_roots() {
void ShenandoahConcurrentGC::entry_class_unloading() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- static const char* msg = "Concurrent class unloading";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent class unloading", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_class_unload);
EventMark em("%s", msg);
@@ -559,7 +569,7 @@ void ShenandoahConcurrentGC::entry_class_unloading() {
void ShenandoahConcurrentGC::entry_strong_roots() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- static const char* msg = "Concurrent strong roots";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent strong roots", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_strong_roots);
EventMark em("%s", msg);
@@ -577,7 +587,7 @@ void ShenandoahConcurrentGC::entry_strong_roots() {
void ShenandoahConcurrentGC::entry_cleanup_early() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- const char* msg = conc_cleanup_event_message();
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent cleanup", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_cleanup_early, true /* log_heap_usage */);
EventMark em("%s", msg);
@@ -596,8 +606,7 @@ void ShenandoahConcurrentGC::entry_cleanup_early() {
void ShenandoahConcurrentGC::entry_evacuate() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
-
- static const char* msg = "Concurrent evacuation";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent evacuation", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_evac);
EventMark em("%s", msg);
@@ -613,8 +622,7 @@ void ShenandoahConcurrentGC::entry_evacuate() {
void ShenandoahConcurrentGC::entry_update_thread_roots() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
-
- static const char* msg = "Concurrent update thread roots";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent update thread roots", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_thread_roots);
EventMark em("%s", msg);
@@ -627,7 +635,7 @@ void ShenandoahConcurrentGC::entry_update_thread_roots() {
void ShenandoahConcurrentGC::entry_update_refs() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- static const char* msg = "Concurrent update references";
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent update references", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_update_refs);
EventMark em("%s", msg);
@@ -643,7 +651,7 @@ void ShenandoahConcurrentGC::entry_update_refs() {
void ShenandoahConcurrentGC::entry_cleanup_complete() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- const char* msg = conc_cleanup_event_message();
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent cleanup", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_cleanup_complete, true /* log_heap_usage */);
EventMark em("%s", msg);
@@ -655,7 +663,7 @@ void ShenandoahConcurrentGC::entry_cleanup_complete() {
void ShenandoahConcurrentGC::entry_reset_after_collect() {
ShenandoahHeap* const heap = ShenandoahHeap::heap();
TraceCollectorStats tcs(heap->monitoring_support()->concurrent_collection_counters());
- const char* msg = conc_reset_after_collect_event_message();
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent reset after collect", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_reset_after_collect);
EventMark em("%s", msg);
@@ -1250,7 +1258,7 @@ void ShenandoahConcurrentGC::op_final_update_refs() {
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();
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Concurrent final roots", "");
ShenandoahConcurrentPhase gc_phase(msg, ShenandoahPhaseTimings::conc_final_roots);
EventMark em("%s", msg);
@@ -1295,100 +1303,3 @@ bool ShenandoahConcurrentGC::check_cancellation_and_abort(ShenandoahDegenPoint p
}
return false;
}
-
-const char* ShenandoahConcurrentGC::init_mark_event_message() const {
- ShenandoahHeap* const heap = ShenandoahHeap::heap();
- assert(!heap->has_forwarded_objects(), "Should not have forwarded objects here");
- if (heap->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Init Mark", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Init Mark", "");
- }
-}
-
-const char* ShenandoahConcurrentGC::final_mark_event_message() const {
- ShenandoahHeap* const heap = ShenandoahHeap::heap();
- assert(!heap->has_forwarded_objects() || heap->is_concurrent_old_mark_in_progress(),
- "Should not have forwarded objects during final mark, unless old gen concurrent mark is running");
-
- if (heap->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Final Mark", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Final Mark", "");
- }
-}
-
-const char* ShenandoahConcurrentGC::conc_mark_event_message() const {
- ShenandoahHeap* const heap = ShenandoahHeap::heap();
- assert(!heap->has_forwarded_objects() || heap->is_concurrent_old_mark_in_progress(),
- "Should not have forwarded objects concurrent mark, unless old gen concurrent mark is running");
- if (heap->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent marking", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent marking", "");
- }
-}
-
-const char* ShenandoahConcurrentGC::conc_reset_event_message() const {
- if (ShenandoahHeap::heap()->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent reset", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent reset", "");
- }
-}
-
-const char* ShenandoahConcurrentGC::conc_reset_after_collect_event_message() const {
- if (ShenandoahHeap::heap()->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent reset after collect", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent reset after collect", "");
- }
-}
-
-const char* ShenandoahConcurrentGC::verify_final_event_message() const {
- if (ShenandoahHeap::heap()->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Verify Final", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Verify Final", "");
- }
-}
-
-const char* ShenandoahConcurrentGC::conc_final_roots_event_message() const {
- if (ShenandoahHeap::heap()->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent Final Roots", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent Final Roots", "");
- }
-}
-
-const char* ShenandoahConcurrentGC::conc_weak_refs_event_message() const {
- if (ShenandoahHeap::heap()->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak references", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak references", "");
- }
-}
-
-const char* ShenandoahConcurrentGC::conc_weak_roots_event_message() const {
- if (ShenandoahHeap::heap()->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak roots", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent weak roots", "");
- }
-}
-
-const char* ShenandoahConcurrentGC::conc_cleanup_event_message() const {
- if (ShenandoahHeap::heap()->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent cleanup", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent cleanup", "");
- }
-}
-
-const char* ShenandoahConcurrentGC::conc_init_update_refs_event_message() const {
- if (ShenandoahHeap::heap()->unload_classes()) {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent Init Update Refs", " (unload classes)");
- } else {
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Concurrent Init Update Refs", "");
- }
-}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp
index e763d1853e3f..cf1398e3de69 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.hpp
@@ -132,20 +132,6 @@ class ShenandoahConcurrentGC : public ShenandoahGC {
void start_mark();
static bool has_in_place_promotions(ShenandoahHeap* heap);
-
- // Messages for GC trace events, they have to be immortal for
- // passing around the logging/tracing systems
- const char* init_mark_event_message() const;
- const char* final_mark_event_message() const;
- const char* verify_final_event_message() const;
- const char* conc_final_roots_event_message() const;
- const char* conc_mark_event_message() const;
- const char* conc_reset_event_message() const;
- const char* conc_reset_after_collect_event_message() const;
- const char* conc_weak_refs_event_message() const;
- const char* conc_weak_roots_event_message() const;
- const char* conc_cleanup_event_message() const;
- const char* conc_init_update_refs_event_message() const;
};
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCONCURRENTGC_HPP
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
index 3c3cdc4a90a8..439e23af2812 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahDegeneratedGC.cpp
@@ -465,21 +465,35 @@ void ShenandoahDegenGC::op_degenerated_futile() {
const char* ShenandoahDegenGC::degen_event_message(ShenandoahDegenPoint point) const {
switch (point) {
- case _degenerated_unset:
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " ()");
- case _degenerated_outside_cycle:
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Outside of Cycle)");
- case _degenerated_roots:
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Roots)");
- case _degenerated_mark:
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Mark)");
- case _degenerated_evac:
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Evacuation)");
- case _degenerated_update_refs:
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (Update Refs)");
- default:
+ case _degenerated_unset: {
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " ()");
+ return msg;
+ }
+ case _degenerated_outside_cycle: {
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (Outside of Cycle)");
+ return msg;
+ }
+ case _degenerated_roots: {
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (Roots)");
+ return msg;
+ }
+ case _degenerated_mark: {
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (Mark)");
+ return msg;
+ }
+ case _degenerated_evac: {
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (Evacuation)");
+ return msg;
+ }
+ case _degenerated_update_refs: {
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (Update Refs)");
+ return msg;
+ }
+ default: {
ShouldNotReachHere();
- SHENANDOAH_RETURN_EVENT_MESSAGE(_generation->type(), "Pause Degenerated GC", " (?)");
+ SHENANDOAH_EVENT_MESSAGE(msg, _generation->type(), "Pause Degenerated GC", " (?)");
+ return msg;
+ }
}
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp
index 4750a0cb2db6..e9761ebeb863 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahUtils.hpp
@@ -47,19 +47,24 @@
class GCTimer;
class ShenandoahGeneration;
-#define SHENANDOAH_RETURN_EVENT_MESSAGE(generation_type, prefix, postfix) \
+#define SHENANDOAH_EVENT_MESSAGE(loc, generation_type, prefix, postfix) \
+ const char* loc; \
switch (generation_type) { \
case NON_GEN: \
- return prefix postfix; \
+ loc = prefix postfix; \
+ break; \
case GLOBAL: \
- return prefix " (Global)" postfix; \
+ loc = prefix " (Global)" postfix; \
+ break; \
case YOUNG: \
- return prefix " (Young)" postfix; \
+ loc = prefix " (Young)" postfix; \
+ break; \
case OLD: \
- return prefix " (Old)" postfix; \
+ loc = prefix " (Old)" postfix; \
+ break; \
default: \
ShouldNotReachHere(); \
- return prefix " (Unknown)" postfix; \
+ loc = prefix " (Unknown)" postfix; \
} \
class ShenandoahGCSession : public StackObj {
From f9ee545b2e843069f9326b7e4cc0f4ff43685b9d Mon Sep 17 00:00:00 2001
From: Ioi Lam
Date: Thu, 13 Aug 2026 16:02:41 +0000
Subject: [PATCH 12/88] 8389293: Remove developer flag -XX:ArchiveHeapTestClass
Reviewed-by: kvn, dholmes
---
src/hotspot/share/cds/aotClassInitializer.cpp | 14 +-
src/hotspot/share/cds/cds_globals.hpp | 7 +-
src/hotspot/share/cds/heapShared.cpp | 176 +----------
src/hotspot/share/cds/heapShared.hpp | 6 -
src/hotspot/share/classfile/moduleEntry.cpp | 13 +-
.../share/classfile/systemDictionary.cpp | 2 +-
src/hotspot/share/runtime/threads.cpp | 3 -
test/hotspot/jtreg/TEST.groups | 2 -
.../cds/appcds/aotCache/AOTMapTest.java | 35 ++-
.../cds/appcds/aotCache/FlatArrayTest.java | 223 ++++++++++++++
...App.java => AOTMapTestValhallaHelper.java} | 52 ++--
.../cacheObject/ArchiveHeapTestClass.java | 279 ------------------
.../cacheObject/ArchivedFlatArrayApp.java | 157 ----------
.../cacheObject/ArchivedFlatArrayTest.java | 76 -----
.../cacheObject/ArchivedIntegerCacheTest.java | 14 +-
.../cacheObject/ArchivedIntegerHolder.java | 38 ---
.../cacheObject/CheckIntegerCacheApp.java | 8 -
17 files changed, 294 insertions(+), 811 deletions(-)
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.java
rename test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/{AOTMapTestApp.java => AOTMapTestValhallaHelper.java} (71%)
delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java
delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayApp.java
delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java
delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerHolder.java
diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp
index 9ef96282aeb7..7f39d35bd8d6 100644
--- a/src/hotspot/share/cds/aotClassInitializer.cpp
+++ b/src/hotspot/share/cds/aotClassInitializer.cpp
@@ -36,7 +36,7 @@
#include "runtime/java.hpp"
#include "runtime/javaCalls.hpp"
-DEBUG_ONLY(InstanceKlass* _aot_init_class = nullptr;)
+DEBUG_ONLY(InstanceKlass* _aot_init_test_class = nullptr;)
bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
assert(!ArchiveBuilder::is_active() || !ArchiveBuilder::current()->is_in_buffer_space(ik), "must be source klass");
@@ -65,10 +65,10 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
//
// Check that no user code is executed during the assembly phase. Otherwise the user
// code may introduce undesirable environment dependencies into the heap image.
- // If any of these two flags are set, we allow user code to be executed
+ // If AOTInitTestClass is set, we allow user code to be executed
// in the assembly phase. Note that these flags are strictly for the purpose
// of testing HotSpot and are not available in product builds.
- if (AOTInitTestClass == nullptr && ArchiveHeapTestClass == nullptr) {
+ if (AOTInitTestClass == nullptr) {
if (ik->defined_by_boot_loader()) {
// We allow boot classes to be AOT-initialized, except for classes from
// -Xbootclasspath (cp index >= 1) be AOT-initialized, as such classes may be
@@ -258,7 +258,7 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) {
}
#ifdef ASSERT
- if (ik == _aot_init_class) {
+ if (ik == _aot_init_test_class) {
return true;
}
#endif
@@ -356,12 +356,12 @@ void AOTClassInitializer::init_test_class(TRAPS) {
vm_exit_during_initialization("Invalid name for AOTInitTestClass", AOTInitTestClass);
}
- _aot_init_class = InstanceKlass::cast(k);
- _aot_init_class->initialize(CHECK);
+ _aot_init_test_class = InstanceKlass::cast(k);
+ _aot_init_test_class->initialize(CHECK);
}
}
bool AOTClassInitializer::has_test_class() {
- return _aot_init_class != nullptr;
+ return _aot_init_test_class != nullptr;
}
#endif
diff --git a/src/hotspot/share/cds/cds_globals.hpp b/src/hotspot/share/cds/cds_globals.hpp
index 640cde848b8d..e6320ad0f858 100644
--- a/src/hotspot/share/cds/cds_globals.hpp
+++ b/src/hotspot/share/cds/cds_globals.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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
@@ -65,11 +65,6 @@
"Average number of symbols per bucket in shared table") \
range(2, 246) \
\
- develop(ccstr, ArchiveHeapTestClass, nullptr, \
- "For JVM internal testing only. The static field named " \
- "\"archivedObjects\" of the specified class is stored in the " \
- "CDS archive heap") \
- \
develop(ccstr, AOTInitTestClass, nullptr, \
"For JVM internal testing only. The specified class is stored " \
"in the initialized state in the AOT cache ") \
diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp
index 9f7f5a05ace2..4f92eb486ee0 100644
--- a/src/hotspot/share/cds/heapShared.cpp
+++ b/src/hotspot/share/cds/heapShared.cpp
@@ -105,14 +105,6 @@ size_t HeapShared::_alloc_size[HeapShared::ALLOC_STAT_SLOTS];
size_t HeapShared::_total_obj_count;
size_t HeapShared::_total_obj_size;
-#ifndef PRODUCT
-#define ARCHIVE_TEST_FIELD_NAME "archivedObjects"
-static Array* _archived_ArchiveHeapTestClass = nullptr;
-static const char* _test_class_name = nullptr;
-static Klass* _test_class = nullptr;
-static const ArchivedKlassSubGraphInfoRecord* _test_class_record = nullptr;
-#endif
-
#ifdef ASSERT
// All classes that have at least one instance in the cached heap.
static ArchivableKlassTable* _dumptime_classes_with_cached_oops = nullptr;
@@ -142,9 +134,6 @@ static ArchivableStaticFieldInfo archive_subgraph_entry_fields[] = {
{ARCHIVED_BOOT_LAYER_CLASS, ARCHIVED_BOOT_LAYER_FIELD},
{"java/lang/Module$ArchivedData", "archivedData"},
-#ifndef PRODUCT
- {nullptr, nullptr}, // Extra slot for -XX:ArchiveHeapTestClass
-#endif
{nullptr, nullptr},
};
@@ -1204,19 +1193,9 @@ void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) {
}
}
-#ifndef PRODUCT
- if (!ik->module()->is_named() && ik->package() == nullptr && ArchiveHeapTestClass != nullptr) {
- // This class is loaded by ArchiveHeapTestClass
- return;
- }
- const char* testcls_msg = ", or a test class in an unnamed package of an unnamed module";
-#else
- const char* testcls_msg = "";
-#endif
-
ResourceMark rm;
- log_error(aot, heap)("Class %s not allowed in archive heap. Must be in java.base%s%s",
- ik->external_name(), lambda_msg, testcls_msg);
+ log_error(aot, heap)("Class %s not allowed in archive heap. Must be in java.base%s",
+ ik->external_name(), lambda_msg);
AOTMetaspace::unrecoverable_writing_error();
}
@@ -1326,29 +1305,12 @@ void HeapShared::write_subgraph_info_table() {
d_table->iterate(©);
writer.dump(&_run_time_subgraph_info_table, "subgraphs");
-#ifndef PRODUCT
- if (ArchiveHeapTestClass != nullptr) {
- size_t len = strlen(ArchiveHeapTestClass) + 1;
- Array* array = ArchiveBuilder::new_ro_array((int)len);
- strncpy(array->adr_at(0), ArchiveHeapTestClass, len);
- _archived_ArchiveHeapTestClass = array;
- }
-#endif
if (log_is_enabled(Info, aot, heap)) {
print_stats();
}
}
void HeapShared::serialize_tables(SerializeClosure* soc) {
-
-#ifndef PRODUCT
- soc->do_ptr(&_archived_ArchiveHeapTestClass);
- if (soc->reading() && _archived_ArchiveHeapTestClass != nullptr) {
- _test_class_name = _archived_ArchiveHeapTestClass->adr_at(0);
- setup_test_class(_test_class_name);
- }
-#endif
-
_run_time_subgraph_info_table.serialize_header(soc);
soc->do_ptr(&_run_time_special_subgraph);
DEBUG_ONLY(soc->do_ptr(&_runtime_classes_with_cached_oops));
@@ -1517,13 +1479,6 @@ HeapShared::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAP
unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(k);
const ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0);
-#ifndef PRODUCT
- if (_test_class_name != nullptr && k->name()->equals(_test_class_name) && record != nullptr) {
- _test_class = k;
- _test_class_record = record;
- }
-#endif
-
// Initialize from archived data. Currently this is done only
// during VM initialization time. No lock is needed.
if (record == nullptr) {
@@ -2148,18 +2103,6 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[],
TempNewSymbol field_name = SymbolTable::new_symbol(info->field_name);
ResourceMark rm; // for stringStream::as_string() etc.
-#ifndef PRODUCT
- bool is_test_class = (ArchiveHeapTestClass != nullptr) && (strcmp(info->klass_name, ArchiveHeapTestClass) == 0);
- const char* test_class_name = ArchiveHeapTestClass;
-#else
- bool is_test_class = false;
- const char* test_class_name = ""; // avoid C++ printf checks warnings.
-#endif
-
- if (is_test_class) {
- log_warning(aot)("Loading ArchiveHeapTestClass %s ...", test_class_name);
- }
-
Klass* k = SystemDictionary::resolve_or_fail(klass_name, true, THREAD);
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
@@ -2178,35 +2121,15 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[],
assert(InstanceKlass::cast(ik)->defined_by_boot_loader(),
"Only support boot classes");
- if (is_test_class) {
- if (ik->module()->is_named()) {
- // We don't want ArchiveHeapTestClass to be abused to easily load/initialize arbitrary
- // core-lib classes. You need to at least append to the bootclasspath.
- stringStream st;
- st.print("ArchiveHeapTestClass %s is not in unnamed module", test_class_name);
- THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), st.as_string());
- }
-
- if (ik->package() != nullptr) {
- // This restriction makes HeapShared::is_a_test_class_in_unnamed_module() easy.
- stringStream st;
- st.print("ArchiveHeapTestClass %s is not in unnamed package", test_class_name);
- THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), st.as_string());
- }
- } else {
- if (ik->module()->name() != vmSymbols::java_base()) {
- // We don't want to deal with cases when a module is unavailable at runtime.
- // FUTURE -- load from archived heap only when module graph has not changed
- // between dump and runtime.
- stringStream st;
- st.print("%s is not in java.base module", info->klass_name);
- THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), st.as_string());
- }
+ if (ik->module()->name() != vmSymbols::java_base()) {
+ // We don't want to deal with cases when a module is unavailable at runtime.
+ // FUTURE -- load from archived heap only when module graph has not changed
+ // between dump and runtime.
+ stringStream st;
+ st.print("%s is not in java.base module", info->klass_name);
+ THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), st.as_string());
}
- if (is_test_class) {
- log_warning(aot)("Initializing ArchiveHeapTestClass %s ...", test_class_name);
- }
ik->initialize(CHECK);
ArchivableStaticFieldFinder finder(ik, field_name);
@@ -2230,89 +2153,8 @@ void HeapShared::init_subgraph_entry_fields(TRAPS) {
}
}
-#ifndef PRODUCT
-void HeapShared::setup_test_class(const char* test_class_name) {
- ArchivableStaticFieldInfo* p = archive_subgraph_entry_fields;
- int num_slots = sizeof(archive_subgraph_entry_fields) / sizeof(ArchivableStaticFieldInfo);
- assert(p[num_slots - 2].klass_name == nullptr, "must have empty slot that's patched below");
- assert(p[num_slots - 1].klass_name == nullptr, "must have empty slot that marks the end of the list");
-
- if (test_class_name != nullptr) {
- p[num_slots - 2].klass_name = test_class_name;
- p[num_slots - 2].field_name = ARCHIVE_TEST_FIELD_NAME;
- }
-}
-
-// See if ik is one of the test classes that are pulled in by -XX:ArchiveHeapTestClass
-// during runtime. This may be called before the module system is initialized so
-// we cannot rely on InstanceKlass::module(), etc.
-bool HeapShared::is_a_test_class_in_unnamed_module(Klass* ik) {
- if (_test_class != nullptr) {
- if (ik == _test_class) {
- return true;
- }
- Array* klasses = _test_class_record->subgraph_object_klasses();
- if (klasses == nullptr) {
- return false;
- }
-
- for (int i = 0; i < klasses->length(); i++) {
- Klass* k = klasses->at(i);
- if (k == ik) {
- Symbol* name;
- if (k->is_instance_klass()) {
- name = InstanceKlass::cast(k)->name();
- } else if (k->is_objArray_klass()) {
- Klass* bk = ObjArrayKlass::cast(k)->bottom_klass();
- if (!bk->is_instance_klass()) {
- return false;
- }
- name = bk->name();
- } else {
- return false;
- }
-
- // See KlassSubGraphInfo::check_allowed_klass() - we only allow test classes
- // to be:
- // (A) java.base classes (which must not be in the unnamed module)
- // (B) test classes which must be in the unnamed package of the unnamed module.
- // So if we see a '/' character in the class name, it must be in (A);
- // otherwise it must be in (B).
- if (name->index_of_at(0, "/", 1) >= 0) {
- return false; // (A)
- }
-
- return true; // (B)
- }
- }
- }
-
- return false;
-}
-
-void HeapShared::initialize_test_class_from_archive(JavaThread* current) {
- Klass* k = _test_class;
- if (k != nullptr && is_archived_heap_in_use()) {
- JavaThread* THREAD = current;
- ExceptionMark em(THREAD);
- const ArchivedKlassSubGraphInfoRecord* record =
- resolve_or_init_classes_for_subgraph_of(k, /*do_init=*/false, THREAD);
-
- // The _test_class is in the unnamed module, so it can't call CDS.initializeFromArchive()
- // from its method. So we set up its "archivedObjects" field first, before
- // calling its . This is not strictly clean, but it's a convenient way to write unit
- // test cases (see test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java).
- if (record != nullptr) {
- init_archived_fields_for(k, record);
- }
- resolve_or_init_classes_for_subgraph_of(k, /*do_init=*/true, THREAD);
- }
-}
-#endif
-
void HeapShared::init_for_dumping(TRAPS) {
if (CDSConfig::is_dumping_heap()) {
- setup_test_class(ArchiveHeapTestClass);
init_subgraph_entry_fields(CHECK);
}
}
diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp
index 8f7378a4a5ac..48ce3bc40dcd 100644
--- a/src/hotspot/share/cds/heapShared.hpp
+++ b/src/hotspot/share/cds/heapShared.hpp
@@ -442,7 +442,6 @@ class HeapShared: AllStatic {
// Run-time only
static void clear_root(int index);
static void get_segment_indexes(int index, int& segment_index, int& internal_index);
- static void setup_test_class(const char* test_class_name) PRODUCT_RETURN;
#endif // INCLUDE_CDS_JAVA_HEAP
public:
@@ -469,11 +468,6 @@ class HeapShared: AllStatic {
static void write_subgraph_info_table() NOT_CDS_JAVA_HEAP_RETURN;
static void serialize_tables(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
-#ifndef PRODUCT
- static bool is_a_test_class_in_unnamed_module(Klass* ik) NOT_CDS_JAVA_HEAP_RETURN_(false);
- static void initialize_test_class_from_archive(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
-#endif
-
static void initialize_java_lang_invoke(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
static void init_classes_for_special_subgraph(Handle loader, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp
index c7fadeaea9ba..c73d15c44d31 100644
--- a/src/hotspot/share/classfile/moduleEntry.cpp
+++ b/src/hotspot/share/classfile/moduleEntry.cpp
@@ -617,18 +617,7 @@ void ModuleEntryTable::patch_javabase_entries(JavaThread* current, Handle module
for (int i = 0; i < list_length; i++) {
Klass* k = list->at(i);
assert(k->is_klass(), "List should only hold classes");
-#ifndef PRODUCT
- if (HeapShared::is_a_test_class_in_unnamed_module(k)) {
- // We allow -XX:ArchiveHeapTestClass to archive additional classes
- // into the CDS heap, but these must be in the unnamed module.
- ModuleEntry* unnamed_module = ClassLoaderData::the_null_class_loader_data()->unnamed_module();
- Handle unnamed_module_handle(current, unnamed_module->module_oop());
- java_lang_Class::fixup_module_field(k, unnamed_module_handle);
- } else
-#endif
- {
- java_lang_Class::fixup_module_field(k, module_handle);
- }
+ java_lang_Class::fixup_module_field(k, module_handle);
k->class_loader_data()->dec_keep_alive_ref_count();
}
diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp
index 87af1458c182..6141eeb7bd04 100644
--- a/src/hotspot/share/classfile/systemDictionary.cpp
+++ b/src/hotspot/share/classfile/systemDictionary.cpp
@@ -1003,7 +1003,7 @@ bool SystemDictionary::is_shared_class_visible_impl(Symbol* class_name,
// has restricted the classes can be loaded at this step to be only:
// [1] cs->is_modules_image(): classes in java.base, or,
// [2] HeapShared::is_a_test_class_in_unnamed_module(ik): classes in bootstrap/unnamed module
- assert(cl->is_modules_image() || HeapShared::is_a_test_class_in_unnamed_module(ik),
+ assert(cl->is_modules_image(),
"only these classes can be loaded before the module system is initialized");
assert(class_loader.is_null(), "sanity");
return true;
diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
index 8c414e10ba9e..6663966f36e0 100644
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -784,9 +784,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
if (CDSConfig::is_using_aot_linked_classes()) {
AOTLinkedClassBulkLoader::init_non_javabase_classes(THREAD);
}
-#ifndef PRODUCT
- HeapShared::initialize_test_class_from_archive(THREAD);
-#endif
JFR_ONLY(Jfr::on_create_vm_2();)
diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups
index b7dcba10a353..78eee6addea3 100644
--- a/test/hotspot/jtreg/TEST.groups
+++ b/test/hotspot/jtreg/TEST.groups
@@ -553,8 +553,6 @@ hotspot_aot_classlinking = \
-runtime/cds/appcds/aotProfile \
-runtime/cds/appcds/ArchivedFieldMetadataMismatchTest.java \
-runtime/cds/appcds/BadBSM.java \
- -runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java \
- -runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java \
-runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java \
-runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java \
-runtime/cds/appcds/CDSandJFR.java \
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java
index 832c9c336783..5f9652dc7bec 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java
@@ -27,6 +27,7 @@
* @summary Test the contents of -Xlog:aot+map with AOT workflow
* @requires vm.cds.supports.aot.class.linking
* @library /test/lib /test/hotspot/jtreg/runtime/cds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @modules java.base/jdk.internal.misc
* @build AOTMapTest Hello
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTMapTestApp
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar Hello
@@ -39,6 +40,7 @@
* @summary Test the contents of -Xlog:aot+map with dynamic CDS archive
* @requires vm.cds.supports.aot.class.linking
* @library /test/lib /test/hotspot/jtreg/runtime/cds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @modules java.base/jdk.internal.misc
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @build AOTMapTest Hello
@@ -55,16 +57,22 @@
* @requires vm.cds.supports.aot.class.linking & vm.debug & vm.cds.write.archived.java.heap
* @library /test/lib /test/hotspot/jtreg/runtime/cds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* @modules java.base/jdk.internal.value java.base/jdk.internal.misc java.base/jdk.internal.vm.annotation
- * @build Hello
- * @compile test-classes/AOTMapTestApp.java
- * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTMapTestApp AOTMapTestApp$Wrapper AOTMapTestApp$WrapperWrapper
- * AOTMapTestApp$ArchivedData Hello
- * @run main/othervm/timeout=240 AOTMapTest STATIC
+ * @build Hello AOTMapTest
+ * @compile test-classes/AOTMapTestValhallaHelper.java
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
+ * AOTMapTestApp
+ * Hello
+ * AOTMapTestValhallaHelper
+ * AOTMapTestValhallaHelper$Wrapper
+ * AOTMapTestValhallaHelper$WrapperWrapper
+ * AOTMapTestValhallaHelper$ArchivedData
+ * @run main/othervm/timeout=240 AOTMapTest AOT --two-step-training
*/
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
+import jdk.internal.misc.PreviewFeatures;
import java.util.ArrayList;
import jdk.test.lib.cds.CDSAppTester;
import jdk.test.lib.helpers.ClassFileInstaller;
@@ -126,15 +134,17 @@ public String[] vmArgs(RunMode runMode) {
vmArgs.add("-Xmx128M");
vmArgs.add("-Xlog:aot=debug");
+ vmArgs.add("--add-exports");
+ vmArgs.add("java.base/jdk.internal.misc=ALL-UNNAMED");
- if (isStaticWorkflow()) {
+ if (PreviewFeatures.isEnabled()) {
vmArgs.add("--enable-preview");
vmArgs.add("--add-exports");
vmArgs.add("java.base/jdk.internal.value=ALL-UNNAMED");
- vmArgs.add("--add-exports");
- vmArgs.add("java.base/jdk.internal.misc=ALL-UNNAMED");
- vmArgs.add("-Xbootclasspath/a:" + appJar);
- vmArgs.add("-XX:ArchiveHeapTestClass=AOTMapTestApp");
+
+ if (runMode == RunMode.ASSEMBLY) {
+ vmArgs.add("-XX:AOTInitTestClass=AOTMapTestValhallaHelper");
+ }
}
// filesize=0 ensures that a large map file not broken up in multiple files.
@@ -165,6 +175,11 @@ class AOTMapTestApp {
public static void main(String[] args) throws Exception {
System.out.println("Hello AOTMapTestApp");
testCustomLoader();
+
+ if (PreviewFeatures.isEnabled()) {
+ Class> c = Class.forName("AOTMapTestValhallaHelper");
+ c.newInstance();
+ }
}
static void testCustomLoader() throws Exception {
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.java
new file mode 100644
index 000000000000..94f2234f2a40
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/FlatArrayTest.java
@@ -0,0 +1,223 @@
+/*
+ * 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 Test AOT-cached flat arrays
+ * @requires vm.cds.supports.aot.class.linking
+ * @requires vm.debug
+ * @enablePreview
+ * @library /test/jdk/lib/testlibrary /test/lib
+ * @modules java.base/jdk.internal.value
+ * @build FlatArrayTest
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
+ * FlatArrayTestApp MyAOTInitedClass CharPair Wrapper
+ * @run driver FlatArrayTest AOT --two-step-training
+ */
+
+import java.util.Arrays;
+import jdk.internal.value.ValueClass;
+
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.StringArrayUtils;
+
+public class FlatArrayTest {
+ static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+ static final String mainClass = FlatArrayTestApp.class.getName();
+
+ public static void main(String[] args) throws Exception {
+ new Tester().run(args);
+ }
+
+ static class Tester extends CDSAppTester {
+ public Tester() {
+ super(mainClass);
+ }
+
+ @Override
+ public String classpath(RunMode runMode) {
+ return appJar;
+ }
+
+ @Override
+ public String[] vmArgs(RunMode runMode) {
+ String args[] = StringArrayUtils.concat("--enable-preview",
+ "--add-exports",
+ "java.base/jdk.internal.value=ALL-UNNAMED");
+ if (runMode == RunMode.ASSEMBLY) {
+ args = StringArrayUtils.concat(args,
+ "-Xlog:aot+class=debug",
+ "-XX:AOTInitTestClass=MyAOTInitedClass");
+ }
+
+ return args;
+ }
+
+ @Override
+ public String[] appCommandLine(RunMode runMode) {
+ return new String[] {
+ mainClass,
+ runMode.toString(),
+ };
+ }
+
+ @Override
+ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
+ if (runMode == RunMode.TRAINING) {
+ out.shouldContain("Y = 123");
+ } else if (runMode == RunMode.ASSEMBLY) {
+ out.shouldMatch("klasses.* app .*MyAOTInitedClass .* inited");
+ out.shouldMatch("klasses.* app .*CharPair .* inited");
+ out.shouldMatch("klasses.* app .*Wrapper .* inited");
+ } else if (runMode == RunMode.PRODUCTION) {
+ out.shouldContain("Y = 45");
+ }
+ }
+ }
+}
+
+class FlatArrayTestApp {
+ static int X = 45;
+ public static void main(String[] args) {
+ X = 123;
+ MyAOTInitedClass.test(args[0]);
+ }
+}
+
+value class CharPair implements Comparable {
+ char c0, c1;
+
+ public String toString() {
+ return "(" + c0 + ", " + c1 + ")";
+ }
+
+ public int compareTo(CharPair o) {
+ return (c0 - o.c0) - (c1 - o.c1);
+ }
+
+ public CharPair(char c0, char c1) {
+ this.c0 = c0;
+ this.c1 = c1;
+ }
+}
+
+value class Wrapper implements Comparable {
+ Integer i;
+
+ public String toString() {
+ return i.toString();
+ }
+
+ public int compareTo(Wrapper o) {
+ return i - o.i;
+ }
+
+ Wrapper(int i) {
+ this.i = new Integer(i);
+ }
+}
+
+// This class is stored in the AOT cache in the initialized state.
+class MyAOTInitedClass {
+ // Note that when MyAOTInitedClass is initialized in the assembly run, FlatArrayTestApp.main()
+ // is not executed, so the cached value of MyAOTInitedClass.Y will be 45;
+ static int Y = FlatArrayTestApp.X;
+
+ static Integer[] intArray;
+ static CharPair[] charPairArray;
+ static Wrapper[] wrapperArray;
+
+ static CharPair charPair;
+ static Wrapper wrapper;
+
+ static {
+ intArray = new Integer[3];
+ intArray[0] = null;
+ System.out.println("TEST: " + (intArray[0] == null));
+ intArray[0] = new Integer(0);
+ intArray[1] = new Integer(1);
+ intArray[2] = new Integer(2);
+
+ charPairArray = new CharPair[3];
+ charPairArray[0] = new CharPair('a', 'b');
+ charPairArray[1] = new CharPair('c', 'd');
+ charPairArray[2] = new CharPair('e', 'f');
+
+ wrapperArray = new Wrapper[3];
+ wrapperArray[0] = new Wrapper(0);
+ wrapperArray[1] = new Wrapper(1);
+ wrapperArray[2] = new Wrapper(2);
+
+ charPair = new CharPair('x', 'y');
+ wrapper = new Wrapper(5);
+ }
+
+ static void test(String runMode) {
+ System.out.println("Y = " + Y);
+ if (runMode.equals("PRODUCTION") && Y != 45) {
+ throw new RuntimeException("MyAOTInitedClass must be AOT-inited");
+ }
+
+ if (!ValueClass.isFlatArray(intArray)) {
+ throw new RuntimeException("Integer array should be flat");
+ }
+
+ if (!ValueClass.isFlatArray(charPairArray)) {
+ throw new RuntimeException("CharPair array should be flat");
+ }
+
+ if (!ValueClass.isFlatArray(wrapperArray)) {
+ throw new RuntimeException("Wrapper array should be flat");
+ }
+
+ // Ensure archived arrays are restored properly
+ Integer[] runtimeIntArray = new Integer[3];
+ runtimeIntArray[0] = new Integer(0);
+ runtimeIntArray[1] = new Integer(1);
+ runtimeIntArray[2] = new Integer(2);
+
+ CharPair[] runtimeCharPairArray = new CharPair[3];
+ runtimeCharPairArray[0] = new CharPair('a', 'b');
+ runtimeCharPairArray[1] = new CharPair('c', 'd');
+ runtimeCharPairArray[2] = new CharPair('e', 'f');
+
+ Wrapper[] runtimeWrapperArray = new Wrapper[3];
+ runtimeWrapperArray[0] = new Wrapper(0);
+ runtimeWrapperArray[1] = new Wrapper(1);
+ runtimeWrapperArray[2] = new Wrapper(2);
+
+ if (Arrays.compare(intArray, runtimeIntArray) != 0) {
+ throw new RuntimeException("Integer array not restored correctly");
+ }
+
+ if (Arrays.compare(charPairArray, runtimeCharPairArray) != 0) {
+ throw new RuntimeException("CharPair array not restored correctly");
+ }
+
+ if (Arrays.compare(wrapperArray, runtimeWrapperArray) != 0) {
+ throw new RuntimeException("Wrapper array not restored correctly");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestApp.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestValhallaHelper.java
similarity index 71%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestApp.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestValhallaHelper.java
index c8b338cbc42d..e237e5de7be7 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestApp.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/test-classes/AOTMapTestValhallaHelper.java
@@ -21,13 +21,13 @@
* questions.
*/
-import jdk.internal.misc.PreviewFeatures;
import jdk.internal.value.ValueClass;
import jdk.internal.vm.annotation.NullRestricted;
// Test app for flat arrays in the AOT map
-public class AOTMapTestApp {
-
+// This class is AOT-initialized during the AOT assembly phase (see
+// the use of -XX:AOTInitTestClass in ../AOTMapTest.java)
+public class AOTMapTestValhallaHelper {
public static value class Wrapper implements Comparable {
Integer i;
@@ -96,48 +96,42 @@ public static class ArchivedData {
wrapperWrapper = new WrapperWrapper(0xbbbb6666);
a = 0x7788;
b = new Integer(0x8899);
+ super();
}
}
- static ArchivedData archivedObjects;
- static {
- if (archivedObjects == null) {
- archivedObjects = new ArchivedData();
- } else {
- System.out.println("Initialized from CDS");
- System.out.println("boxArray " + archivedObjects.boxArray);
- System.out.println("wrapperArray " + archivedObjects.wrapperArray);
- System.out.println("wrapperWrapperArray " + archivedObjects.wrapperWrapperArray);
- System.out.println("objArray " + archivedObjects.objArray);
- System.out.println("wrapper " + archivedObjects.wrapper);
- System.out.println("wrapperWrapper " + archivedObjects.wrapperWrapper);
- System.out.println("a " + archivedObjects.a);
- System.out.println("b " + archivedObjects.b);
- }
- }
-
- public static void main(String[] args) throws Exception {
- System.out.println("Hello FlatAOTMapTestApp");
- Class.forName("Hello");
-
- if (PreviewFeatures.isEnabled() && !ValueClass.isFlatArray(archivedObjects.boxArray)) {
+ // This object will be stored in the AOT cache.
+ static ArchivedData archivedObjects = new ArchivedData();
+
+ // This is called by reflection code in AOTMapTestApp.main() in ../AOTMapTest.java
+ public AOTMapTestValhallaHelper() {
+ System.out.println("boxArray " + archivedObjects.boxArray);
+ System.out.println("wrapperArray " + archivedObjects.wrapperArray);
+ System.out.println("wrapperWrapperArray " + archivedObjects.wrapperWrapperArray);
+ System.out.println("objArray " + archivedObjects.objArray);
+ System.out.println("wrapper " + archivedObjects.wrapper);
+ System.out.println("wrapperWrapper " + archivedObjects.wrapperWrapper);
+ System.out.println("a " + archivedObjects.a);
+ System.out.println("b " + archivedObjects.b);
+
+ if (!ValueClass.isFlatArray(archivedObjects.boxArray)) {
throw new RuntimeException("Boxing class array should be flat");
}
- if (PreviewFeatures.isEnabled() && (!ValueClass.isNullRestrictedArray(archivedObjects.nullFreeBoxArray) ||
+ if ((!ValueClass.isNullRestrictedArray(archivedObjects.nullFreeBoxArray) ||
!ValueClass.isFlatArray(archivedObjects.nullFreeBoxArray))) {
throw new RuntimeException("Boxing class array should be null-free and flat");
}
- if (PreviewFeatures.isEnabled() && !ValueClass.isFlatArray(archivedObjects.wrapperArray)) {
+ if (!ValueClass.isFlatArray(archivedObjects.wrapperArray)) {
throw new RuntimeException("Wrapper array should be flat");
}
- if (PreviewFeatures.isEnabled() && !ValueClass.isFlatArray(archivedObjects.wrapperWrapperArray)) {
+ if (!ValueClass.isFlatArray(archivedObjects.wrapperWrapperArray)) {
throw new RuntimeException("WrapperWrapper array should be flat");
}
- if (PreviewFeatures.isEnabled() && ValueClass.isFlatArray(archivedObjects.objArray)) {
+ if (ValueClass.isFlatArray(archivedObjects.objArray)) {
throw new RuntimeException("Object array should not be flat");
}
}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java
deleted file mode 100644
index 3b1ccff1bfa7..000000000000
--- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
- * 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 8214781 8293187
- * @summary Test for the -XX:ArchiveHeapTestClass flag
- * @requires vm.debug == true & vm.cds.write.archived.java.heap
- * @requires vm.cds.supports.aot.class.linking
- * @modules java.logging
- * @library /test/jdk/lib/testlibrary /test/lib
- * /test/hotspot/jtreg/runtime/cds/appcds
- * /test/hotspot/jtreg/runtime/cds/appcds/test-classes
- * @build ArchiveHeapTestClass Hello pkg.ClassInPackage
- * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar
- * CDSTestClassA CDSTestClassA$XX CDSTestClassA$YY
- * CDSTestClassB CDSTestClassC CDSTestClassD
- * CDSTestClassE CDSTestClassF
- * pkg.ClassInPackage
- * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar Hello
- * @run driver ArchiveHeapTestClass
- */
-
-import jdk.test.lib.cds.CDSTestUtils;
-import jdk.test.lib.Platform;
-import jdk.test.lib.helpers.ClassFileInstaller;
-import jdk.test.lib.process.OutputAnalyzer;
-
-public class ArchiveHeapTestClass {
- static final String bootJar = ClassFileInstaller.getJarPath("boot.jar");
- static final String appJar = ClassFileInstaller.getJarPath("app.jar");
- static final String[] appClassList = {"Hello"};
-
- static final String CDSTestClassA_name = CDSTestClassA.class.getName();
- static final String CDSTestClassB_name = CDSTestClassB.class.getName();
- static final String CDSTestClassC_name = CDSTestClassC.class.getName();
- static final String CDSTestClassD_name = CDSTestClassD.class.getName();
- static final String CDSTestClassE_name = CDSTestClassE.class.getName();
- static final String CDSTestClassF_name = CDSTestClassF.class.getName();
- static final String ClassInPackage_name = pkg.ClassInPackage.class.getName().replace('.', '/');
- static final String ARCHIVE_TEST_FIELD_NAME = "archivedObjects";
-
- public static void main(String[] args) throws Exception {
- testDebugBuild();
- }
-
- static OutputAnalyzer dumpHelloOnly(String... extraOpts) throws Exception {
- return TestCommon.dump(appJar, appClassList, extraOpts);
- }
-
- static OutputAnalyzer dumpBootAndHello(String bootClass, String... extraOpts) throws Exception {
- String classlist[] = TestCommon.concat(appClassList, bootClass);
- extraOpts = TestCommon.concat(extraOpts,
- "-Xbootclasspath/a:" + bootJar,
- "-XX:ArchiveHeapTestClass=" + bootClass,
- "-Xlog:aot+heap");
- return TestCommon.dump(appJar, classlist, extraOpts);
- }
-
- static int caseNum = 0;
- static void testCase(String s) {
- System.out.println("==================================================");
- System.out.println(" Test " + (++caseNum) + ": " + s);
- }
-
- static void mustContain(OutputAnalyzer output, String... expectStrs) throws Exception {
- for (String s : expectStrs) {
- output.shouldContain(s);
- }
- }
-
- static void mustFail(OutputAnalyzer output, String... expectStrs) throws Exception {
- mustContain(output, expectStrs);
- output.shouldNotHaveExitValue(0);
- }
-
- static void mustSucceed(OutputAnalyzer output, String... expectStrs) throws Exception {
- mustContain(output, expectStrs);
- output.shouldHaveExitValue(0);
- }
-
- static void testDebugBuild() throws Exception {
- OutputAnalyzer output;
-
- testCase("Simple positive case");
- output = dumpBootAndHello(CDSTestClassA_name);
- mustSucceed(output, CDSTestClassA.getOutput()); // make sure is executed
- output.shouldMatch("warning.*aot.*Loading ArchiveHeapTestClass " + CDSTestClassA_name);
- output.shouldMatch("warning.*aot.*Initializing ArchiveHeapTestClass " + CDSTestClassA_name);
- output.shouldContain("Archived field " + CDSTestClassA_name + "::" + ARCHIVE_TEST_FIELD_NAME);
- output.shouldMatch("Archived object klass CDSTestClassA .*\\[LCDSTestClassA;");
- output.shouldMatch("Archived object klass CDSTestClassA .*CDSTestClassA\\$YY");
-
- TestCommon.run("-Xbootclasspath/a:" + bootJar, "-cp", appJar, "-Xlog:aot+heap", CDSTestClassA_name)
- .assertNormalExit(CDSTestClassA.getOutput(),
- "resolve subgraph " + CDSTestClassA_name);
-
- testCase("Class doesn't exist");
- output = dumpHelloOnly("-XX:ArchiveHeapTestClass=NoSuchClass");
- mustFail(output, "Fail to initialize archive heap: NoSuchClass cannot be loaded");
-
- testCase("Class doesn't exist (objarray)");
- output = dumpHelloOnly("-XX:ArchiveHeapTestClass=[LNoSuchClass;");
- mustFail(output, "Fail to initialize archive heap: [LNoSuchClass; cannot be loaded");
-
- testCase("Not an instance klass");
- output = dumpHelloOnly("-XX:ArchiveHeapTestClass=[Ljava/lang/Object;");
- mustFail(output, "Fail to initialize archive heap: [Ljava/lang/Object; is not an instance class");
-
- testCase("Not in boot loader");
- output = dumpHelloOnly("-XX:ArchiveHeapTestClass=Hello");
- mustFail(output, "Fail to initialize archive heap: Hello cannot be loaded by the boot loader");
-
- testCase("Not from unnamed module");
- output = dumpHelloOnly("-XX:ArchiveHeapTestClass=java/lang/Object");
- mustFail(output, "ArchiveHeapTestClass java/lang/Object is not in unnamed module");
-
- testCase("Not from unnamed package");
- output = dumpBootAndHello(ClassInPackage_name);
- mustFail(output, "ArchiveHeapTestClass pkg/ClassInPackage is not in unnamed package");
-
- testCase("Field not found");
- output = dumpBootAndHello(CDSTestClassB_name);
- mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassB::archivedObjects");
-
- testCase("Not a static field");
- output = dumpBootAndHello(CDSTestClassC_name);
- mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassC::archivedObjects");
-
- testCase("Not a T_OBJECT field");
- output = dumpBootAndHello(CDSTestClassD_name);
- mustFail(output, "Unable to find the static T_OBJECT field CDSTestClassD::archivedObjects");
-
- if (!CDSTestUtils.isAOTClassLinkingEnabled()) {
- testCase("Use a disallowed class: in unnamed module but not in unname package");
- output = dumpBootAndHello(CDSTestClassE_name);
- mustFail(output, "Class pkg.ClassInPackage not allowed in archive heap");
-
- testCase("Use a disallowed class: not in java.base module");
- output = dumpBootAndHello(CDSTestClassF_name);
- mustFail(output, "Class java.util.logging.Level not allowed in archive heap");
- }
- }
-}
-
-class CDSTestClassA {
- static final String output = "CDSTestClassA. was executed";
- static Object[] archivedObjects;
- static {
- // The usual convention would be to call this here:
- // CDS.initializeFromArchive(CDSTestClassA.class);
- // However, the CDS class is not exported to the unnamed module by default,
- // and we don't want to use "--add-exports java.base/jdk.internal.misc=ALL-UNNAMED", as
- // that would disable the archived full module graph, which will disable
- // CDSConfig::is_using_aot_linked_classes().
- //
- // Instead, HeapShared::initialize_test_class_from_archive() will set up the
- // "archivedObjects" field first, before calling CDSTestClassA.. So
- // if we see that archivedObjects is magically non-null here, that means
- // it has been restored from the CDS archive.
- if (archivedObjects == null) {
- archivedObjects = new Object[5];
- archivedObjects[0] = output;
- archivedObjects[1] = new CDSTestClassA[0];
- archivedObjects[2] = new YY();
- archivedObjects[3] = new int[0];
- archivedObjects[4] = new int[2][2];
- } else {
- System.out.println("Initialized from CDS");
- }
- System.out.println(output);
- System.out.println("CDSTestClassA module = " + CDSTestClassA.class.getModule());
- System.out.println("CDSTestClassA package = " + CDSTestClassA.class.getPackage());
- System.out.println("CDSTestClassA[] module = " + archivedObjects[1].getClass().getModule());
- System.out.println("CDSTestClassA[] package = " + archivedObjects[1].getClass().getPackage());
- }
-
- static String getOutput() {
- return output;
- }
-
- public static void main(String args[]) {
- if (CDSTestClassA.class.getModule().isNamed()) {
- throw new RuntimeException("CDSTestClassA must be in unnamed module");
- }
- if (CDSTestClassA.class.getPackage() != null) {
- throw new RuntimeException("CDSTestClassA must be in null package");
- }
- if (archivedObjects[1].getClass().getModule().isNamed()) {
- throw new RuntimeException("CDSTestClassA[] must be in unnamed module");
- }
- if (archivedObjects[1].getClass().getPackage() != null) {
- throw new RuntimeException("CDSTestClassA[] must be in null package");
- }
- XX.doit();
- YY.doit();
- }
-
- // This is an inner class that has NOT been archived.
- static class XX {
- static void doit() {
- System.out.println("XX module = " + XX.class.getModule());
- System.out.println("XX package = " + XX.class.getPackage());
-
- if (XX.class.getModule().isNamed()) {
- throw new RuntimeException("XX must be in unnamed module");
- }
- if (XX.class.getPackage() != null) {
- throw new RuntimeException("XX must be in null package");
- }
- }
- }
-
- // This is an inner class that HAS been archived.
- static class YY {
- static void doit() {
- System.out.println("YY module = " + YY.class.getModule());
- System.out.println("YY package = " + YY.class.getPackage());
-
- if (YY.class.getModule().isNamed()) {
- throw new RuntimeException("YY must be in unnamed module");
- }
- if (YY.class.getPackage() != null) {
- throw new RuntimeException("YY must be in null package");
- }
- }
- }
-}
-
-class CDSTestClassB {
- // No field named "archivedObjects"
-}
-
-class CDSTestClassC {
- Object[] archivedObjects; // Not a static field
-}
-
-class CDSTestClassD {
- static int archivedObjects; // Not an int field
-}
-
-class CDSTestClassE {
- static Object[] archivedObjects;
- static {
- // Not in unnamed package of unnamed module
- archivedObjects = new Object[1];
- archivedObjects[0] = new pkg.ClassInPackage();
- }
-}
-
-class CDSTestClassF {
- static Object[] archivedObjects;
- static {
- // Not in java.base
- archivedObjects = new Object[1];
- archivedObjects[0] = java.util.logging.Level.OFF;
- }
-}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayApp.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayApp.java
deleted file mode 100644
index ec6944cda459..000000000000
--- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayApp.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * 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.util.Arrays;
-import jdk.internal.value.ValueClass;
-
-public class ArchivedFlatArrayApp {
-
- // Check that arrays of both migrated value
- // classes and custom value classes are archived
- public static class ArchivedData {
- Integer[] intArray;
- CharPair[] charPairArray;
- Wrapper[] wrapperArray;
- }
-
- public static value class CharPair implements Comparable {
- char c0, c1;
-
- public String toString() {
- return "(" + c0 + ", " + c1 + ")";
- }
-
- public int compareTo(CharPair o) {
- return (c0 - o.c0) - (c1 - o.c1);
- }
-
- public CharPair(char c0, char c1) {
- this.c0 = c0;
- this.c1 = c1;
- }
- }
-
- public static value class Wrapper implements Comparable {
- Integer i;
-
- public String toString() {
- return i.toString();
- }
-
- public int compareTo(Wrapper o) {
- return i - o.i;
- }
-
- Wrapper(int i) {
- this.i = new Integer(i);
- }
- }
-
- static ArchivedData archivedObjects;
- static boolean restored;
- static {
- if (archivedObjects == null) {
- restored = false;
- System.out.println("Not archived");
- archivedObjects = new ArchivedData();
-
- archivedObjects.intArray = new Integer[3];
- archivedObjects.intArray[0] = new Integer(0);
- archivedObjects.intArray[1] = new Integer(1);
- archivedObjects.intArray[2] = new Integer(2);
-
- archivedObjects.charPairArray = new CharPair[3];
- archivedObjects.charPairArray[0] = new CharPair('a', 'b');
- archivedObjects.charPairArray[1] = new CharPair('c', 'd');
- archivedObjects.charPairArray[2] = new CharPair('e', 'f');
-
- archivedObjects.wrapperArray = new Wrapper[3];
- archivedObjects.wrapperArray[0] = new Wrapper(0);
- archivedObjects.wrapperArray[1] = new Wrapper(1);
- archivedObjects.wrapperArray[2] = new Wrapper(2);
- } else {
- restored = true;
- System.out.println("Initialized from CDS");
- System.out.println("intArray " + archivedObjects.intArray);
- System.out.println("charPairArray " + archivedObjects.charPairArray);
- System.out.println("wrapperArray " + archivedObjects.wrapperArray);
- }
-
- for (Integer i : archivedObjects.intArray) {
- System.out.println(i);
- }
-
- for (CharPair c : archivedObjects.charPairArray) {
- System.out.println(c);
- }
-
- for (Wrapper w : archivedObjects.wrapperArray) {
- System.out.println(w);
- }
- }
-
- public static void main(String[] args) {
- if (!ValueClass.isFlatArray(archivedObjects.intArray)) {
- throw new RuntimeException("Integer array should be flat");
- }
-
- if (!ValueClass.isFlatArray(archivedObjects.charPairArray)) {
- throw new RuntimeException("CharPair array should be flat");
- }
-
- if (!ValueClass.isFlatArray(archivedObjects.wrapperArray)) {
- throw new RuntimeException("Wrapper array should be flat");
- }
-
- if (restored) {
- // Ensure archived arrays are restored properly
- Integer[] runtimeIntArray = new Integer[3];
- runtimeIntArray[0] = new Integer(0);
- runtimeIntArray[1] = new Integer(1);
- runtimeIntArray[2] = new Integer(2);
-
- CharPair[] runtimeCharPairArray = new CharPair[3];
- runtimeCharPairArray[0] = new CharPair('a', 'b');
- runtimeCharPairArray[1] = new CharPair('c', 'd');
- runtimeCharPairArray[2] = new CharPair('e', 'f');
-
- Wrapper[] runtimeWrapperArray = new Wrapper[3];
- runtimeWrapperArray[0] = new Wrapper(0);
- runtimeWrapperArray[1] = new Wrapper(1);
- runtimeWrapperArray[2] = new Wrapper(2);
-
- if (Arrays.compare(archivedObjects.intArray, runtimeIntArray) != 0) {
- throw new RuntimeException("Integer array not restored correctly");
- }
-
- if (Arrays.compare(archivedObjects.charPairArray, runtimeCharPairArray) != 0) {
- throw new RuntimeException("CharPair array not restored correctly");
- }
-
- if (Arrays.compare(archivedObjects.wrapperArray, runtimeWrapperArray) != 0) {
- throw new RuntimeException("Wrapper array not restored correctly");
- }
- }
- }
-}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java
deleted file mode 100644
index 90838fd92be3..000000000000
--- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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 archived flat arrays
- * @requires vm.cds.write.archived.java.heap
- * @requires vm.debug
- * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds
- * @enablePreview
- * @modules java.base/jdk.internal.value
- * @compile ArchivedFlatArrayApp.java ArchivedArrayLayoutsApp.java
- * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar archived_flat_array.jar ArchivedFlatArrayApp
- * ArchivedFlatArrayApp$ArchivedData
- * ArchivedFlatArrayApp$CharPair
- * ArchivedFlatArrayApp$Wrapper
- * ArchivedArrayLayoutsApp
- * ArchivedArrayLayoutsApp$Point
- * ArchivedArrayLayoutsApp$ArchivedData
- * @run main/othervm ArchivedFlatArrayTest
- */
-
-import jdk.test.lib.cds.CDSTestUtils;
-import jdk.test.lib.process.OutputAnalyzer;
-import jdk.test.lib.helpers.ClassFileInstaller;
-
-public class ArchivedFlatArrayTest {
-
- static String appJar = ClassFileInstaller.getJarPath("archived_flat_array.jar");
- static String mainClass = "ArchivedFlatArrayApp";
- static String mainClass2 = "ArchivedArrayLayoutsApp";
-
- public static void test(String className, String[] classlist) throws Exception {
- String[] suffix = TestCommon.list("--enable-preview",
- "-Xbootclasspath/a:" + appJar,
- "-XX:ArchiveHeapTestClass=" + className,
- "--add-exports",
- "java.base/jdk.internal.value=ALL-UNNAMED",
- "-Xlog:aot+heap");
-
- OutputAnalyzer output = TestCommon.dump(appJar, classlist, suffix);
- output.shouldHaveExitValue(0);
- output.shouldContain("Archived field " + className + "::archivedObjects");
-
- output = TestCommon.exec(appJar, TestCommon.concat(suffix, className));
- output.shouldHaveExitValue(0);
- output.shouldContain("init subgraph " + className);
- output.shouldContain("Initialized from CDS");
- }
-
- public static void main(String[] args) throws Exception {
- test(mainClass, TestCommon.list(mainClass, "ArchivedFlatArrayApp$ArchivedData", "ArchivedFlatArrayApp$CharPair"));
- test(mainClass2, TestCommon.list(mainClass2, "ArchivedArrayLayoutsApp$ArchivedData", "ArchivedArrayLayoutsApp$Point"));
- }
-}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java
index f69f2be48641..4bd852750908 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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,11 +27,10 @@
* @summary Test primitive box caches integrity in various scenarios (IntegerCache etc)
* @requires vm.cds.write.archived.java.heap
* @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds
- * @compile --add-exports java.base/jdk.internal.misc=ALL-UNNAMED CheckIntegerCacheApp.java ArchivedIntegerHolder.java
+ * @compile --add-exports java.base/jdk.internal.misc=ALL-UNNAMED CheckIntegerCacheApp.java
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boxCache.jar CheckIntegerCacheApp
- * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boxCache-boot.jar ArchivedIntegerHolder
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:./WhiteBox.jar ArchivedIntegerCacheTest
*/
@@ -47,15 +46,10 @@ public class ArchivedIntegerCacheTest {
private static WhiteBox WB = WhiteBox.getWhiteBox();
public static String[] mixArgs(String... args) {
- String bootJar = ClassFileInstaller.getJarPath("boxCache-boot.jar");
-
- String[] newArgs = new String[args.length + 5];
+ String[] newArgs = new String[args.length + 2];
newArgs[0] = "--add-exports";
newArgs[1] = "java.base/jdk.internal.misc=ALL-UNNAMED";
- newArgs[2] = "-Xbootclasspath/a:" + bootJar;
- newArgs[3] = "-XX:+IgnoreUnrecognizedVMOptions";
- newArgs[4] = "-XX:ArchiveHeapTestClass=ArchivedIntegerHolder";
- System.arraycopy(args, 0, newArgs, 5, args.length);
+ System.arraycopy(args, 0, newArgs, 2, args.length);
return newArgs;
}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerHolder.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerHolder.java
deleted file mode 100644
index c8ab0db7d34d..000000000000
--- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedIntegerHolder.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- *
- */
-
-import jdk.internal.misc.CDS;
-
-public class ArchivedIntegerHolder {
- public static Object[] archivedObjects;
- static {
- CDS.initializeFromArchive(ArchivedIntegerHolder.class);
- if (archivedObjects == null) {
- archivedObjects = new Object[256];
- for (int i = -128; i <= 127; i++) {
- archivedObjects[i + 128] = Integer.valueOf(i);
- }
- }
- }
-}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/CheckIntegerCacheApp.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/CheckIntegerCacheApp.java
index 5068d203a916..32f305c0d96f 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/CheckIntegerCacheApp.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/CheckIntegerCacheApp.java
@@ -65,14 +65,6 @@ public static void main(String[] args) throws Exception {
}
}
- // Check that archived integer cache agrees with runtime integer cache.
- for (int i = -128; i <= 127; i++) {
- if (ArchivedIntegerHolder.archivedObjects[i + 128] != Integer.valueOf(i)) {
- throw new RuntimeException(
- "FAILED. Archived and runtime caches disagree for " + i);
- }
- }
-
int high = Integer.parseInt(args[0]);
if (Integer.valueOf(high) != Integer.valueOf(high)) {
throw new RuntimeException(
From 4771360c9b59ed67ed94d30447e78dab48383f4e Mon Sep 17 00:00:00 2001
From: Fairoz Matte
Date: Thu, 13 Aug 2026 17:12:13 +0000
Subject: [PATCH 13/88] 8389866: ZGC: Accepts an extremely negative
ZAllocationSpikeTolerance value
Fix ZGC's handling of ZAllocationSpikeTolerance by rejecting invalid values.
Reviewed-by: stefank, tschatzl
---
src/hotspot/share/gc/z/z_globals.hpp | 1 +
.../gc/z/TestZAllocationSpikeTolerance.java | 78 +++++++++++++++++++
2 files changed, 79 insertions(+)
create mode 100644 test/hotspot/jtreg/gc/z/TestZAllocationSpikeTolerance.java
diff --git a/src/hotspot/share/gc/z/z_globals.hpp b/src/hotspot/share/gc/z/z_globals.hpp
index ceffd490ba00..06adba80be03 100644
--- a/src/hotspot/share/gc/z/z_globals.hpp
+++ b/src/hotspot/share/gc/z/z_globals.hpp
@@ -36,6 +36,7 @@
\
product(double, ZAllocationSpikeTolerance, 2.0, \
"Allocation spike tolerance factor") \
+ range(0, INT_MAX) \
\
product(double, ZFragmentationLimit, 5.0, \
"Maximum allowed heap fragmentation") \
diff --git a/test/hotspot/jtreg/gc/z/TestZAllocationSpikeTolerance.java b/test/hotspot/jtreg/gc/z/TestZAllocationSpikeTolerance.java
new file mode 100644
index 000000000000..678829c33e71
--- /dev/null
+++ b/test/hotspot/jtreg/gc/z/TestZAllocationSpikeTolerance.java
@@ -0,0 +1,78 @@
+/*
+ * 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 TestZAllocationSpikeTolerance
+ * @bug 8389866
+ * @summary Verifies TestZAllocationSpikeTolerance range
+ * - fails gracefully for invalid value
+ * - succeeds for valid value
+ * @library /test/lib
+ * @requires vm.gc.Z
+ * @run driver gc.z.TestZAllocationSpikeTolerance
+ */
+package gc.z;
+
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class TestZAllocationSpikeTolerance {
+ public static void main(String[] args) throws Exception {
+ testInvalidValue();
+ testValidValue();
+ }
+
+ private static void testInvalidValue() throws Exception {
+ ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-XX:+UseZGC",
+ "-XX:ZAllocationSpikeTolerance=-1", // invalid range
+ "-version"
+ );
+
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ // Ensure no crash (no assert failure)
+ output.shouldNotContain("assert");
+
+ // Expected graceful error output
+ output.shouldContain("ZAllocationSpikeTolerance");
+ output.shouldContain("outside the allowed range");
+ output.shouldContain("Error: A fatal exception has occurred. Program will exit.");
+
+ // Graceful exit with error code 1
+ output.shouldHaveExitValue(1);
+ }
+
+ private static void testValidValue() throws Exception {
+ ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-XX:+UseZGC",
+ "-XX:ZAllocationSpikeTolerance=1", // valid range
+ "-version"
+ );
+
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+ output.shouldHaveExitValue(0);
+ }
+}
From 3efdba901cb6273408990d5dfb412008166205b6 Mon Sep 17 00:00:00 2001
From: Patricio Chilano Mateo
Date: Thu, 13 Aug 2026 20:17:27 +0000
Subject: [PATCH 14/88] 8388596: Virtual-thread freeze asserts on MethodHandle
frame with scalarized value argument
Co-authored-by: Tobias Hartmann
Reviewed-by: fbredberg, fparain, coleenp
---
.../share/runtime/continuationFreezeThaw.cpp | 12 +---
.../TestVirtualThreadMethodHandle.java | 61 +++++++++++++++++++
2 files changed, 62 insertions(+), 11 deletions(-)
create mode 100644 test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadMethodHandle.java
diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
index 7916d4423d2f..bfc5fa2b71e5 100644
--- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp
+++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
@@ -1280,23 +1280,13 @@ freeze_result FreezeBase::recurse_freeze_compiled_frame(frame& f, frame& caller,
int real_frame_size = 0;
bool augmented = f.was_augmented_on_entry(real_frame_size);
if (augmented) {
+ assert(f.cb()->as_nmethod()->is_compiled_by_c2(), "should be c2 compiled");
// The args reside inside the frame so clear argsize. If the caller is compiled,
// this will cause the stack arguments passed by the caller to be freezed when
// freezing the caller frame itself. If the caller is interpreted this will have
// the effect of discarding the arg area created in the i2c stub.
argsize = 0;
fsize = real_frame_size - (callee_interpreted ? 0 : callee_argsize);
-#ifdef ASSERT
- nmethod* nm = f.cb()->as_nmethod();
- Method* method = nm->method();
- address return_pc = ContinuationHelper::CompiledFrame::return_pc(f);
- CodeBlob* caller_cb = CodeCache::find_blob_fast(return_pc);
- assert(nm->is_compiled_by_c2(), "caller should be c2 compiled");
- assert((!caller_cb->is_nmethod() && nm->is_compiled_by_c2()) ||
- (nm->compiler_type() != caller_cb->as_nmethod()->compiler_type()) ||
- (nm->is_compiled_by_c2() && !method->is_static() && method->method_holder()->is_inline_klass()),
- "frame should not be extended");
-#endif
}
log_develop_trace(continuations)("recurse_freeze_compiled_frame %s _size: %d fsize: %d argsize: %d augmented: %d",
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadMethodHandle.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadMethodHandle.java
new file mode 100644
index 000000000000..5a2bc9985493
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadMethodHandle.java
@@ -0,0 +1,61 @@
+/*
+ * 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 8388596
+ * @summary Test freezing a c2 compiled MethodHandle target frame with a value class argument
+ * @enablePreview
+ * @requires vm.continuations
+ * @library /test/lib
+ * @run main/othervm -XX:-Inline -Xbatch -XX:-TieredCompilation TestVirtualThreadMethodHandle
+ */
+
+import java.lang.invoke.*;
+
+import jdk.test.lib.Asserts;
+
+public class TestVirtualThreadMethodHandle {
+ static value class V { int a=0, b=0, c=0, d=0, e=0, f=0, g=0; }
+ static boolean failed;
+
+ static MethodHandle MH;
+
+ static void target(V v) { Thread.yield(); }
+
+ static void run() {
+ try {
+ for (int n = 0; n < 20_000; n++) MH.invokeExact(new V());
+ } catch (Throwable t) {
+ failed = true;
+ throw new RuntimeException("MethodHandle invocation failed", t);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ MH = MethodHandles.lookup().findStatic(TestVirtualThreadMethodHandle.class, "target", MethodType.methodType(void.class, V.class));
+ run();
+ Thread.startVirtualThread(TestVirtualThreadMethodHandle::run).join();
+ Asserts.assertFalse(failed, "MethodHandle invocation failed");
+ }
+}
From 844f1fea58acf40f103cf3fa244533e4ded8ef44 Mon Sep 17 00:00:00 2001
From: Vicente Romero
Date: Thu, 13 Aug 2026 21:32:25 +0000
Subject: [PATCH 15/88] 8389071: Javac crashes with OutOfMemoryError during
Least Upper Bound (LUB) inference of highly entangled F-bounded generic
classes
Reviewed-by: mcimadamore
---
.../com/sun/tools/javac/code/Types.java | 89 ++++++++++++-------
.../javac/generics/inference/T8389071.java | 61 +++++++++++++
2 files changed, 116 insertions(+), 34 deletions(-)
create mode 100644 test/langtools/tools/javac/generics/inference/T8389071.java
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
index 5b83447ce486..5173f37bcf9c 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
@@ -3864,28 +3864,31 @@ public List union(List cl1, List cl2) {
* Intersect two closures
*/
public List intersect(List cl1, List cl2) {
- if (cl1 == cl2)
- return cl1;
- if (cl1.isEmpty() || cl2.isEmpty())
- return List.nil();
- if (cl1.head.tsym.precedes(cl2.head.tsym, this))
- return intersect(cl1.tail, cl2);
- if (cl2.head.tsym.precedes(cl1.head.tsym, this))
- return intersect(cl1, cl2.tail);
- if (isSameType(cl1.head, cl2.head))
- return intersect(cl1.tail, cl2.tail).prepend(cl1.head);
- if (cl1.head.tsym == cl2.head.tsym &&
- cl1.head.hasTag(CLASS) && cl2.head.hasTag(CLASS)) {
- if (cl1.head.isParameterized() && cl2.head.isParameterized()) {
- Type merge = merge(cl1.head,cl2.head);
- return intersect(cl1.tail, cl2.tail).prepend(merge);
- }
- if (cl1.head.isRaw() || cl2.head.isRaw())
- return intersect(cl1.tail, cl2.tail).prepend(erasure(cl1.head));
- }
- return intersect(cl1.tail, cl2.tail);
+ return intersectHelper(cl1, cl2, new HashMap<>());
}
// where
+ private List intersectHelper(List cl1, List cl2, Map mergeCache) {
+ if (cl1 == cl2)
+ return cl1;
+ if (cl1.isEmpty() || cl2.isEmpty())
+ return List.nil();
+ if (cl1.head.tsym.precedes(cl2.head.tsym, this))
+ return intersectHelper(cl1.tail, cl2, mergeCache);
+ if (cl2.head.tsym.precedes(cl1.head.tsym, this))
+ return intersectHelper(cl1, cl2.tail, mergeCache);
+ if (isSameType(cl1.head, cl2.head))
+ return intersectHelper(cl1.tail, cl2.tail, mergeCache).prepend(cl1.head);
+ if (cl1.head.tsym == cl2.head.tsym &&
+ cl1.head.hasTag(CLASS) && cl2.head.hasTag(CLASS)) {
+ if (cl1.head.isParameterized() && cl2.head.isParameterized()) {
+ Type merge = merge(cl1.head,cl2.head, mergeCache);
+ return intersectHelper(cl1.tail, cl2.tail, mergeCache).prepend(merge);
+ }
+ if (cl1.head.isRaw() || cl2.head.isRaw())
+ return intersectHelper(cl1.tail, cl2.tail, mergeCache).prepend(erasure(cl1.head));
+ }
+ return intersectHelper(cl1.tail, cl2.tail, mergeCache);
+ }
class TypePair {
final Type t1;
final Type t2;
@@ -3923,8 +3926,11 @@ boolean sameTypeComparator(Type t, Type s) {
}
};
- Set mergeCache = new HashSet<>();
- private Type merge(Type c1, Type c2) {
+ private Type merge(Type c1, Type c2, Map mergeCache) {
+ TypePair pair = new TypePair(c1, c2);
+ Type cached = mergeCache.get(pair);
+ if (cached != null && cached != noType) return cached;
+
ClassType class1 = (ClassType) c1;
List act1 = class1.getTypeArguments();
ClassType class2 = (ClassType) c2;
@@ -3938,14 +3944,15 @@ private Type merge(Type c1, Type c2) {
} else if (containsType(act2.head, act1.head)) {
merged.append(act2.head);
} else {
- TypePair pair = new TypePair(c1, c2);
Type m;
- if (mergeCache.add(pair)) {
- m = new WildcardType(lub(wildUpperBound(act1.head),
- wildUpperBound(act2.head)),
+ if (mergeCache.get(pair) == null) {
+ mergeCache.put(pair, noType);
+ m = new WildcardType(lubHelper(mergeCache,
+ wildUpperBound(act1.head),
+ wildUpperBound(act2.head)),
BoundKind.EXTENDS,
syms.boundClass);
- mergeCache.remove(pair);
+ mergeCache.remove(pair, noType);
} else {
m = new WildcardType(syms.objectType,
BoundKind.UNBOUND,
@@ -3960,8 +3967,14 @@ private Type merge(Type c1, Type c2) {
Assert.check(act1.isEmpty() && act2.isEmpty() && typarams.isEmpty());
// There is no spec detailing how type annotations are to
// be inherited. So set it to noAnnotations for now
- return new ClassType(class1.getEnclosingType(), merged.toList(),
- class1.tsym);
+ Type result = new ClassType(class1.getEnclosingType(), merged.toList(),
+ class1.tsym);
+ /* We need to store this result, to potentially avoid OOM errors that can be produced when many types
+ * that are equal but with different identity are created while determining the lub, in particular
+ * when F-bounded generic classes are present
+ */
+ mergeCache.put(pair, result);
+ return result;
}
/**
@@ -4020,7 +4033,7 @@ public List closureMin(List cl) {
* not exist return null.
*/
public Type lub(List ts) {
- return lub(ts.toArray(new Type[ts.length()]));
+ return lubHelper(new HashMap<>(), ts.toArray(new Type[ts.length()]));
}
/**
@@ -4028,6 +4041,14 @@ public Type lub(List ts) {
* does not exist return the type of null (bottom).
*/
public Type lub(Type... ts) {
+ return lubHelper(new HashMap<>(), ts);
+ }
+
+ private Type lubHelper(Map mergeCache, List ts) {
+ return lubHelper(mergeCache, ts.toArray(new Type[ts.length()]));
+ }
+
+ private Type lubHelper(Map mergeCache, Type... ts) {
final int UNKNOWN_BOUND = 0;
final int ARRAY_BOUND = 1;
final int CLASS_BOUND = 2;
@@ -4086,7 +4107,7 @@ public Type lub(Type... ts) {
}
}
// lub(A[], B[]) is lub(A, B)[]
- return new ArrayType(lub(elements), syms.arrayClass);
+ return new ArrayType(lubHelper(mergeCache, elements), syms.arrayClass);
case CLASS_BOUND:
// calculate lub(A, B)
@@ -4105,7 +4126,7 @@ public Type lub(Type... ts) {
for (int i = startIdx + 1 ; i < ts.length ; i++) {
Type t = ts[i];
if (t.hasTag(CLASS) || t.hasTag(TYPEVAR))
- cl = intersect(cl, erasedSupertypes(t));
+ cl = intersectHelper(cl, erasedSupertypes(t), mergeCache);
}
//step 2 - compute minimal erased candidate set (MEC)
List mec = closureMin(cl);
@@ -4116,7 +4137,7 @@ public Type lub(Type... ts) {
List lci = firstSuperType != null ? List.of(firstSuperType) : List.nil();
for (int i = startIdx + 1 ; i < ts.length ; i++) {
Type superType = asSuper(ts[i], erasedSupertype.tsym);
- lci = intersect(lci, superType != null ? List.of(superType) : List.nil());
+ lci = intersectHelper(lci, superType != null ? List.of(superType) : List.nil(), mergeCache);
}
candidates = candidates.appendList(lci);
}
@@ -4132,7 +4153,7 @@ public Type lub(Type... ts) {
classes = classes.prepend(ts[i]);
}
// lub(A, B[]) is lub(A, arraySuperType)
- return lub(classes);
+ return lubHelper(mergeCache, classes);
}
}
diff --git a/test/langtools/tools/javac/generics/inference/T8389071.java b/test/langtools/tools/javac/generics/inference/T8389071.java
new file mode 100644
index 000000000000..a0c2fcd1b71c
--- /dev/null
+++ b/test/langtools/tools/javac/generics/inference/T8389071.java
@@ -0,0 +1,61 @@
+/*
+ * 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 8389071
+ * @summary javac OutOfMemoryError when computing lub for types with self-referential
+ * witness-typed interfaces
+ * @compile T8389071.java
+ */
+
+import java.util.Arrays;
+
+class T8389071 {
+ private interface I> {}
+ private interface J> {}
+ private interface K> {}
+ private interface L> {}
+ private interface M> {}
+ private interface N> {}
+
+ private static class ConsStruct {
+ private static class Empty extends ConsStruct {}
+ private static class Cons extends ConsStruct {}
+ }
+
+ private static class A6 extends ConsStruct.Cons implements
+ I>, J>, K>, L>, M>, N> {}
+ private static class B6 extends ConsStruct.Cons> implements
+ I>, J>, K>, L>, M>, N> {}
+ private static class C6 extends ConsStruct.Cons> implements
+ I>, J>, K>, L>, M>, N> {}
+
+ void foo() {
+ A6 a = new A6<>();
+ B6 b = new B6<>();
+ C6 c = new C6<>();
+ java.util.List> list =
+ Arrays.asList(a, b, c);
+ }
+}
From 993f7b35cfcbaf97f60b3957564752f9d049f7ec Mon Sep 17 00:00:00 2001
From: Patricio Chilano Mateo
Date: Thu, 13 Aug 2026 21:49:06 +0000
Subject: [PATCH 16/88] 8389187: -XX:+VerifyContinuations triggers
assert(max_thawing_size() == calculated_max_size)
Co-authored-by: Tobias Hartmann
Reviewed-by: fbredberg, fparain
---
src/hotspot/cpu/aarch64/frame_aarch64.cpp | 4 +-
src/hotspot/cpu/ppc/frame_ppc.cpp | 4 +-
src/hotspot/cpu/riscv/frame_riscv.cpp | 4 +-
src/hotspot/cpu/s390/frame_s390.cpp | 4 +-
src/hotspot/cpu/x86/frame_x86.cpp | 4 +-
src/hotspot/share/oops/stackChunkOop.cpp | 11 +++-
.../runtime/stackChunkFrameStream.inline.hpp | 14 ++++-
.../TestVirtualThreadExtendedFrame.java | 57 +++++++++++++++++++
.../inlinetypes/TestVirtualThreads.java | 14 +++++
.../jdk/internal/vm/Continuation/Fuzz.java | 21 ++++++-
10 files changed, 122 insertions(+), 15 deletions(-)
create mode 100644 test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadExtendedFrame.java
diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.cpp b/src/hotspot/cpu/aarch64/frame_aarch64.cpp
index bbfd85282993..cc8d8b82e7b0 100644
--- a/src/hotspot/cpu/aarch64/frame_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/frame_aarch64.cpp
@@ -805,8 +805,8 @@ intptr_t* frame::repair_sender_sp(nmethod* nm, intptr_t* sp, intptr_t** saved_fp
}
bool frame::was_augmented_on_entry(int& real_size) const {
- assert(is_compiled_frame(), "");
- if (_cb->as_nmethod_or_null()->needs_stack_repair()) {
+ assert(_cb != nullptr && _cb->is_nmethod(), "");
+ if (_cb->as_nmethod()->needs_stack_repair()) {
// The stack increment resides just below the saved FP on the stack and
// records the total frame size excluding the two words for saving FP and LR
// (see MacroAssembler::remove_frame).
diff --git a/src/hotspot/cpu/ppc/frame_ppc.cpp b/src/hotspot/cpu/ppc/frame_ppc.cpp
index 3803be5fddb1..69e164cfa87b 100644
--- a/src/hotspot/cpu/ppc/frame_ppc.cpp
+++ b/src/hotspot/cpu/ppc/frame_ppc.cpp
@@ -505,8 +505,8 @@ intptr_t* frame::repair_sender_sp(nmethod* nm, intptr_t* sp, intptr_t** saved_fp
}
bool frame::was_augmented_on_entry(int& real_size) const {
- assert(is_compiled_frame(), "");
- if (_cb->as_nmethod_or_null()->needs_stack_repair()) {
+ assert(_cb != nullptr && _cb->is_nmethod(), "");
+ if (_cb->as_nmethod()->needs_stack_repair()) {
Unimplemented();
}
real_size = _cb->frame_size();
diff --git a/src/hotspot/cpu/riscv/frame_riscv.cpp b/src/hotspot/cpu/riscv/frame_riscv.cpp
index bf659c6053a0..20efa12d832f 100644
--- a/src/hotspot/cpu/riscv/frame_riscv.cpp
+++ b/src/hotspot/cpu/riscv/frame_riscv.cpp
@@ -629,8 +629,8 @@ intptr_t* frame::repair_sender_sp(nmethod* nm, intptr_t* sp, intptr_t** saved_fp
}
bool frame::was_augmented_on_entry(int& real_size) const {
- assert(is_compiled_frame(), "");
- assert(!_cb->as_nmethod_or_null()->needs_stack_repair(), "unimplemented");
+ assert(_cb != nullptr && _cb->is_nmethod(), "");
+ assert(!_cb->as_nmethod()->needs_stack_repair(), "unimplemented");
real_size = _cb->frame_size();
return false;
}
diff --git a/src/hotspot/cpu/s390/frame_s390.cpp b/src/hotspot/cpu/s390/frame_s390.cpp
index 58a3c8b231c5..bcbe64788213 100644
--- a/src/hotspot/cpu/s390/frame_s390.cpp
+++ b/src/hotspot/cpu/s390/frame_s390.cpp
@@ -760,8 +760,8 @@ intptr_t* frame::repair_sender_sp(nmethod* nm, intptr_t* sp, intptr_t** saved_fp
}
bool frame::was_augmented_on_entry(int& real_size) const {
- assert(is_compiled_frame(), "");
- if (_cb->as_nmethod_or_null()->needs_stack_repair()) {
+ assert(_cb != nullptr && _cb->is_nmethod(), "");
+ if (_cb->as_nmethod()->needs_stack_repair()) {
Unimplemented();
}
real_size = _cb->frame_size();
diff --git a/src/hotspot/cpu/x86/frame_x86.cpp b/src/hotspot/cpu/x86/frame_x86.cpp
index 746fe62b4f05..d7ebd9c1a91a 100644
--- a/src/hotspot/cpu/x86/frame_x86.cpp
+++ b/src/hotspot/cpu/x86/frame_x86.cpp
@@ -660,8 +660,8 @@ intptr_t* frame::repair_sender_sp(nmethod* nm, intptr_t* sp, intptr_t** saved_fp
}
bool frame::was_augmented_on_entry(int& real_size) const {
- assert(is_compiled_frame(), "");
- if (_cb->as_nmethod_or_null()->needs_stack_repair()) {
+ assert(_cb != nullptr && _cb->is_nmethod(), "");
+ if (_cb->as_nmethod()->needs_stack_repair()) {
// The stack increment resides just below the saved rbp on the stack
// and does not account for the return address and rbp (see MacroAssembler::remove_frame).
intptr_t* real_frame_size_addr = unextended_sp() + _cb->frame_size() - sender_sp_offset - 1;
diff --git a/src/hotspot/share/oops/stackChunkOop.cpp b/src/hotspot/share/oops/stackChunkOop.cpp
index 67e5e474ccec..6033b4e44490 100644
--- a/src/hotspot/share/oops/stackChunkOop.cpp
+++ b/src/hotspot/share/oops/stackChunkOop.cpp
@@ -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
@@ -534,6 +534,15 @@ class VerifyStackChunkFrameClosure {
assert(num_oops >= 0, "");
_argsize = f.stack_argsize() + frame::metadata_words_at_top;
+ if (f.is_compiled()) {
+ int real_frame_size = 0;
+ frame fr = f.to_frame();
+ if (fr.was_augmented_on_entry(real_frame_size)) {
+ // Extended frames exclude stack arguments passed by caller as they are
+ // never accessed. For interpreted callers they are discarded when freezing.
+ _argsize = 0;
+ }
+ }
_size += fsize;
_num_oops += num_oops;
if (f.is_interpreted()) {
diff --git a/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp b/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp
index b799e424178b..bfdf39b1c3b5 100644
--- a/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp
+++ b/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, 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
@@ -178,8 +178,16 @@ inline bool StackChunkFrameStream::is_interpreted() c
//
template
inline int StackChunkFrameStream::frame_size() const {
- return is_interpreted() ? interpreter_frame_size()
- : cb()->frame_size() + stack_argsize() + frame::metadata_words_at_top;
+ if (is_interpreted()) {
+ return interpreter_frame_size();
+ } else if (is_compiled() && cb()->as_nmethod()->needs_stack_repair()) {
+ int real_frame_size = 0;
+ frame f = to_frame();
+ if (f.was_augmented_on_entry(real_frame_size)) {
+ return real_frame_size;
+ }
+ }
+ return cb()->frame_size() + stack_argsize() + frame::metadata_words_at_top;
}
template
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadExtendedFrame.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadExtendedFrame.java
new file mode 100644
index 000000000000..41c240d159ae
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreadExtendedFrame.java
@@ -0,0 +1,57 @@
+/*
+ * 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 8389187
+ * @summary Verify stack chunks with extended compiled frames
+ * @enablePreview
+ * @requires vm.debug == true & vm.continuations
+ * @library /test/lib
+ * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+VerifyContinuations TestVirtualThreadExtendedFrame
+ */
+
+public class TestVirtualThreadExtendedFrame {
+ static value class V {
+ int a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0;
+ }
+
+ static volatile int sink;
+
+ static void recurse(V value, int depth, boolean park) {
+ if (depth > 0) {
+ recurse(value, depth - 1, park);
+ } else if (park) {
+ sink = value.a0;
+ Thread.yield();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ for (int i = 0; i < 10_000; i++) {
+ recurse(new V(), 2, false);
+ }
+ Thread thread = Thread.startVirtualThread(() -> recurse(new V(), 2, true));
+ thread.join();
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreads.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreads.java
index 3607459a5baf..f66ab190a27a 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreads.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestVirtualThreads.java
@@ -204,6 +204,20 @@
* compiler.valhalla.inlinetypes.TestVirtualThreads 50000
*/
+/*
+ * @test id=verify-cont
+ * @key randomness
+ * @summary Test that Virtual Threads work well with Value Objects.
+ * @library /test/lib /compiler/whitebox /
+ * @enablePreview
+ * @requires vm.debug == true & vm.continuations
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+VerifyContinuations
+ * compiler.valhalla.inlinetypes.TestVirtualThreads
+ */
+
package compiler.valhalla.inlinetypes;
import java.lang.reflect.Method;
diff --git a/test/jdk/jdk/internal/vm/Continuation/Fuzz.java b/test/jdk/jdk/internal/vm/Continuation/Fuzz.java
index 71038b003e31..25b5601188f0 100644
--- a/test/jdk/jdk/internal/vm/Continuation/Fuzz.java
+++ b/test/jdk/jdk/internal/vm/Continuation/Fuzz.java
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+* Copyright (c) 2018, 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
@@ -58,6 +58,25 @@
* Fuzz
*/
+/*
+ * @test id=verify-cont
+ * @key randomness
+ * @summary Fuzz tests for jdk.internal.vm.Continuation
+ * @requires vm.debug == true & vm.continuations
+ * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4)
+ * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true
+ * @modules java.base java.base/jdk.internal.vm.annotation java.base/jdk.internal.vm
+ * @library /test/lib
+ * @enablePreview
+ * @build java.base/java.lang.StackWalkerHelper
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
+ *
+ * @run main/othervm/timeout=1200 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
+ * -XX:+VerifyContinuations
+ * Fuzz
+ */
+
import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;
From aa0fbef91aa8d4f69353c40cd01a6d3b502fc477 Mon Sep 17 00:00:00 2001
From: Ashay Rane
Date: Thu, 13 Aug 2026 22:21:44 +0000
Subject: [PATCH 17/88] 8387792: Enable PAC-RET for VM code on Windows/ARM64
Reviewed-by: haosun, erikj
---
doc/building.html | 12 +++--
doc/building.md | 8 ++--
make/autoconf/flags-cflags.m4 | 16 +++++--
make/autoconf/flags-other.m4 | 2 +-
src/hotspot/os/windows/os_windows.cpp | 16 +++++++
.../windows_aarch64/pauth_windows_aarch64.S | 47 +++++++++++++++++++
.../pauth_windows_aarch64.inline.hpp | 5 +-
7 files changed, 92 insertions(+), 14 deletions(-)
create mode 100644 src/hotspot/os_cpu/windows_aarch64/pauth_windows_aarch64.S
diff --git a/doc/building.html b/doc/building.html
index ed77db508346..53defa0c61c8 100644
--- a/doc/building.html
+++ b/doc/building.html
@@ -366,11 +366,13 @@ Building on aarch64
also possible to use cross-compiling .
Date: Fri, 14 Aug 2026 10:16:19 +0000
Subject: [PATCH 18/88] 8387332: Template Framework: Add utility methods to
build scopes
Reviewed-by: thartmann, mhaessig
---
.../lib/template_framework/Template.java | 229 ++++++++++++++++++
.../inlinetypes/TestArraysCopyOf.java | 45 +---
...TestScalarizedCallingConventionLimits.java | 21 +-
.../examples/TestTutorial.java | 135 ++++++++++-
.../tests/TestTemplate.java | 147 +++++++++--
5 files changed, 501 insertions(+), 76 deletions(-)
diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java
index 81a4bdd935b0..6dda7658523d 100644
--- a/test/hotspot/jtreg/compiler/lib/template_framework/Template.java
+++ b/test/hotspot/jtreg/compiler/lib/template_framework/Template.java
@@ -23,11 +23,15 @@
package compiler.lib.template_framework;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.function.BiFunction;
import java.util.function.Function;
+import java.util.function.IntFunction;
import java.util.function.Supplier;
import java.util.List;
+import java.util.stream.IntStream;
import compiler.lib.compile_framework.CompileFramework;
import compiler.lib.ir_framework.TestFramework;
@@ -1063,4 +1067,229 @@ static Token addStructuralName(String name, StructuralName.Type type) {
static StructuralName.FilteredSet structuralNames() {
return new StructuralName.FilteredSet();
}
+
+
+ /**
+ * Create {@code count} times a {@link ScopeToken}. This can be used inside another {@link #scope}.
+ *
+ *
+ * {@snippet lang=java:
+ * // Output:
+ * // scope
+ * // scope
+ * // scope
+ * Template.make(() -> scope(
+ * repeat(3, scope("scope\n"))
+ * ));
+ * }
+ *
+ * @param count How many times do we repeat the provided scope?
+ * @param scope The scope() method.
+ * @return A list of ScopeTokens.
+ */
+ static List repeat(int count, ScopeToken scope) {
+ return repeat(count, _ -> scope);
+ }
+
+ /**
+ * Same as {@link #repeat(int, ScopeToken)} but instead of a {@link ScopeToken}, this method takes a
+ * {@code scopeFactory} {@link IntFunction} that takes an integer index, denoting the current iteration index
+ * ranging from iteration 0 to count - 1, and passes it into a {@link #scope}. This can be used inside another
+ * {@link #scope}:
+ *
+ *
+ * {@snippet lang=java:
+ * // Output:
+ * // 1: scope for index 0
+ * // 1: scope for index 1
+ * // 2: scope for index 0
+ * // 2: scope for index 1
+ * // 2: scope for index 2
+ * Template.make(() -> scope(
+ * repeat(2, index -> scope("1: scope for index " + index +"\n")),
+ * repeat(3, index -> scope("2: scope for index " + index +"\n"))
+ * ));
+ * }
+ *
+ * @param count How many times do we repeat the provided scope?
+ * @param scope The scope() function taking an iteration index.
+ * @return A list of ScopeTokens.
+ */
+ static List repeat(int count, IntFunction scope) {
+ if (count < 0) {
+ throw new IllegalArgumentException("count must not be negative: " + count);
+ }
+ return IntStream.range(0, count)
+ .mapToObj(scope)
+ .toList();
+ }
+
+ /**
+ * Same as {@link #repeat(int, ScopeToken)} but join all resulting {@link ScopeToken}s together by using an
+ * additional {@code delimiter} which is put into a separate {@link ScopeToken}s in between.
+ *
+ *
+ * {@snippet lang=java:
+ * // Output:
+ * // Element 1, Element 2, Element 3
+ * Template.make(() -> scope(
+ * repeatAndJoin(3, ", ", (index) -> scope("Element " + index))
+ * ));
+ * }
+ *
+ * @param count How many times do we repeat the provided scope?
+ * @param delimiter The delimiter to join the individual repeated scopes together.
+ * @param scope The scope() function.
+ * @return A list of ScopeTokens.
+ */
+ static List repeatAndJoin(int count, String delimiter, ScopeToken scope) {
+ return repeatAndJoin(count, delimiter, (_) -> scope);
+ }
+
+ /**
+ * Same as the indexed {@link #repeat(int, IntFunction)} but join all resulting {@link ScopeToken}s together by using
+ * an additional {@code delimiter} which is put into a separate {@link ScopeToken}s in between. This is the indexed
+ * version of {@link #repeatAndJoin(int, String, ScopeToken)}.
+ *
+ *
+ * {@snippet lang=java:
+ * // Output:
+ * // Element 0, Element 1, Element 2
+ * Template.make(() -> scope(
+ * repeatAndJoin(3, ", ", (index) -> scope("Element " + index))
+ * ));
+ * }
+ *
+ * @param count How many times do we repeat the provided scope?
+ * @param delimiter The delimiter to join the individual repeated scopes together.
+ * @param scope The scope() function taking an iteration index.
+ * @return A list of ScopeTokens.
+ */
+ static List repeatAndJoin(int count, String delimiter, IntFunction scope) {
+ if (count < 0) {
+ throw new IllegalArgumentException("count must not be negative: " + count);
+ }
+
+ List scopeTokens = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ if (i > 0) {
+ // Add delimiter before the actual scope, skipping the very first element.
+ scopeTokens.add(scope(delimiter));
+ }
+ // Create the actual scope.
+ scopeTokens.add(scope.apply(i));
+ }
+ // Return an unmodifiable list.
+ return List.copyOf(scopeTokens);
+ }
+
+ /**
+ * Map each element in {@code list} to the {@code scopeFactory} which is a {@link Function} taking an individual
+ * element of the {@code list} which is passed into a {@link #scope}.
+ *
+ *
+ * {@snippet lang=java:
+ * // Output:
+ * // Element 1
+ * // Element 2
+ * // Element 3
+ * Template.make(() -> scope(
+ * map(List.of(1, 2, 3), element -> scope("Element " + element +"\n"))
+ * ));
+ * }
+ *
+ * @param list List of elements to be mapped to the provided scope() function.
+ * @param scope The scope() function taking a list element.
+ * @return A list of ScopeTokens.
+ */
+ static List map(List list, Function scope) {
+ return map(list, (element, _) -> scope.apply(element));
+ }
+
+ /**
+ * Same as {@link #map(List, Function)} but the {@code scopeFactory} is a {@link BiFunction} that takes an additional
+ * integer index, denoting the current list index ranging from iteration 0 to count - 1. Each element and its list
+ * index element are passed into a {@link #scope}.
+ *
+ *
+ * {@snippet lang=java :
+ * // Output:
+ * // Element 1 at index 0
+ * // Element 2 at index 1
+ * // Element 3 at index 2
+ * Template.make(() -> scope(
+ * map(List.of(1, 2, 3), (element, index) -> scope("Element " + element + " at index " + index + "\n"))
+ * ));
+ * }
+ *
+ * @param list List of elements to be mapped to the provided scope() function.
+ * @param scope The scope() function taking a list element and list index.
+ * @return A list of ScopeTokens.
+ */
+ static List map(List list, BiFunction scope) {
+ List scopeTokens = new ArrayList<>(list.size());
+ int i = 0;
+ for (T element : list) {
+ scopeTokens.add(scope.apply(element, i));
+ i++;
+ }
+
+ // Return an unmodifiable list.
+ return List.copyOf(scopeTokens);
+ }
+
+ /**
+ * Same as {@link #map(List, Function)} but join all resulting {@link ScopeToken}s together by using an additional
+ * {@code delimiter} which is put into a separate {@link ScopeToken}s in between.
+ *
+ *
+ * {@snippet lang=java:
+ * // Output:
+ * // Element 1, Element 2, Element 3
+ * Template.make(() -> scope(
+ * mapAndJoin(List.of(1, 2, 3), ", ", (element) -> scope("Element " + element))
+ * ));
+ *}
+ * @param list List of elements to be mapped to the provided scope() function and then be joined with delimiter.
+ * @param delimiter The delimiter to join the individual scopes for all list elements together.
+ * @param scope The scope() function taking a list element.
+ * @return A list of ScopeTokens.
+ */
+ static List mapAndJoin(List list, String delimiter, Function scope) {
+ return mapAndJoin(list, delimiter, (element, _) -> scope.apply(element));
+ }
+
+ /**
+ * Same as the indexed {@link #map(List, BiFunction)} but join all resulting {@link ScopeToken}s together by using
+ * an additional {@code delimiter} which is put into a separate {@link ScopeToken}s in between. This is the indexed
+ * version of {@link #mapAndJoin(List, String, Function)}.
+ *
+ *
+ * {@snippet lang=java:
+ * // Output:
+ * // Element 1 at index 0, Element 2 at index 1, Element 3 at index 2
+ * Template.make(() -> scope(
+ * mapAndJoin(List.of(1, 2, 3), ", ", (element, index) -> scope("Element " + element + " at index " + index))
+ * ));
+ *}
+ * @param list List of elements to be mapped to the provided scope() function and then be joined with delimiter.
+ * @param delimiter The delimiter to join the individual scopes for all list elements together.
+ * @param scope The scope() function taking a list element and list index.
+ * @return A list of ScopeTokens.
+ */
+ static List mapAndJoin(List list, String delimiter, BiFunction scope) {
+ List scopeTokens = new ArrayList<>();
+ int i = 0;
+ for (T item : list) {
+ if (i > 0) {
+ // Add delimiter before the actual scope, skipping the very first element.
+ scopeTokens.add(scope(delimiter));
+ }
+ // Create the actual scope.
+ scopeTokens.add(scope.apply(item, i));
+ i++;
+ }
+ // Return an unmodifiable list.
+ return List.copyOf(scopeTokens);
+ }
}
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java
index a37249a0e36e..7e0c3ad14937 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestArraysCopyOf.java
@@ -112,13 +112,8 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.BiFunction;
-import java.util.function.IntFunction;
-import java.util.stream.IntStream;
-import java.util.stream.Stream;
import static compiler.lib.template_framework.Template.*;
-import static compiler.lib.template_framework.Template.scope;
public class TestArraysCopyOf {
private static final RestrictableGenerator RANDOM_LENGTH = Generators.G.ints().restricted(0, 32);
@@ -226,9 +221,9 @@ static interface I {}
public #klass(\
""",
- concat(primitiveTypes, (type, _) -> scope(type.name() + " _" + type.name())),
+ mapAndJoin(primitiveTypes, ", ", type -> scope(type.name() + " _" + type.name())),
") {\n",
- loop(primitiveTypes, (type, _) -> scope(
+ map(primitiveTypes, type -> scope(
let("type", type.name()),
" this._#type = _#type;\n")),
// Initializer
@@ -238,7 +233,7 @@ static interface I {}
static #klass init() {
return new #klass(\
""",
- concat(primitiveTypes, (type, _) -> scope(type.con())),
+ mapAndJoin(primitiveTypes, ", ", type -> scope(type.con())),
");\n",
"""
}
@@ -249,7 +244,7 @@ static interface I {}
static Object[] oArr = new Object[1];
""",
// Passing A.class, int.class etc. Should always throw.
- loop(instanceAndPrimitiveClasses.size(), i -> scope(
+ repeat(instanceAndPrimitiveClasses.size(), i -> scope(
let("i", uniqueIndex.getAndIncrement()),
let("klass", instanceAndPrimitiveClasses.get(i)),
let("test", "testNonArrayClass_" + instanceAndPrimitiveClasses.get(i)),
@@ -267,7 +262,7 @@ static interface I {}
}
""")),
// Passing in primitive type arrays which like int[].class which should throw.
- loop(primitiveTypeClasses.size(), i -> scope(
+ repeat(primitiveTypeClasses.size(), i -> scope(
let("i", uniqueIndex.getAndIncrement()),
let("klass", primitiveTypeClasses.get(i) + "[]"),
let("test", "testPrimitiveArrayClass_" + primitiveTypeClasses.get(i)),
@@ -285,7 +280,7 @@ static interface I {}
}
""")),
// Normal tests with non-primitive type arrays.
- loop(concreteInstanceClasses.size(), i -> scope(
+ repeat(concreteInstanceClasses.size(), i -> scope(
let("i", uniqueIndex.getAndIncrement()),
let("klass_name", concreteInstanceClasses.get(i).name()),
let("klass", concreteInstanceClasses.get(i).name() + "[]"),
@@ -312,7 +307,7 @@ static interface I {}
}
""")),
// Normal tests with value class arrays.
- loop(newValueClassArrayScopes, (init, i) -> scope(
+ map(newValueClassArrayScopes, (init, i) -> scope(
let("i", uniqueIndex.getAndIncrement()),
let("klass_name", valueClasses.get(i).name()),
let("klass", valueClasses.get(i).name() + "[]"),
@@ -360,28 +355,4 @@ static String generateTestMethodString() {
}
""";
}
-
- static List loop(int limit, IntFunction function) {
- return IntStream.range(0, limit)
- .mapToObj(function)
- .toList();
- }
-
- static List loop(List items, BiFunction function) {
- return IntStream.range(0, items.size())
- .mapToObj(i -> function.apply(items.get(i), i))
- .toList();
- }
-
- static List concat(List items, BiFunction function) {
- return IntStream.range(0, items.size())
- .boxed()
- .flatMap(i -> {
- ScopeToken token = function.apply(items.get(i), i);
- return i == items.size() - 1 ?
- Stream.of(token) :
- Stream.of(token, scope(", "));
- })
- .toList();
- }
-}
\ No newline at end of file
+}
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestScalarizedCallingConventionLimits.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestScalarizedCallingConventionLimits.java
index 054d8b63e699..0287e6b4b333 100644
--- a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestScalarizedCallingConventionLimits.java
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestScalarizedCallingConventionLimits.java
@@ -51,8 +51,7 @@
import compiler.lib.template_framework.ScopeToken;
import compiler.lib.template_framework.Template;
-import static compiler.lib.template_framework.Template.let;
-import static compiler.lib.template_framework.Template.scope;
+import static compiler.lib.template_framework.Template.*;
public class TestScalarizedCallingConventionLimits {
private static final String GENERATED_CLASS_NAME = "GeneratedScalarizedCallingConventionLimits";
@@ -89,7 +88,7 @@ static int hash(Object... values) {
""",
// Generate interfaces with value and identity-class implementations and a method
// with a varying number of arguments to stress test the calling convention.
- loop(MAX_ARGUMENT_COUNT, argumentCount -> scope(
+ repeat(MAX_ARGUMENT_COUNT, argumentCount -> scope(
let("argumentCount", argumentCount),
let("classname", "ValueImpl" + argumentCount),
let("parameters", commaSeparated(argumentCount, i -> "Integer a" + i)),
@@ -138,19 +137,19 @@ public int m(#parameters) {
""")),
// Generate methods with a value class receiver with a varying number of oop fields to stress
// test code buffers during nmethod entry point generation (oops need GC barriers etc.)
- loop(MAX_OOP_RECEIVER_FIELD_COUNT, fieldCount -> scope(
+ repeat(MAX_OOP_RECEIVER_FIELD_COUNT, fieldCount -> scope(
let("fieldCount", fieldCount),
let("arguments", commaSeparated(fieldCount, i -> "f" + i)),
"""
static value class OopReceiver#fieldCount {
""",
- loop(fieldCount, i -> scope(
+ repeat(fieldCount, i -> scope(
" Object f" + i + ";\n")),
"""
OopReceiver#fieldCount(Object[] values) {
""",
- loop(fieldCount, i -> scope(
+ repeat(fieldCount, i -> scope(
" this.f" + i + " = values[" + i + "];\n")),
"""
}
@@ -176,9 +175,9 @@ int m() {
"""
public static void run() {
""",
- loop(MAX_ARGUMENT_COUNT, i -> scope(
+ repeat(MAX_ARGUMENT_COUNT, i -> scope(
" test" + i + "();\n")),
- loop(MAX_OOP_RECEIVER_FIELD_COUNT, i -> scope(
+ repeat(MAX_OOP_RECEIVER_FIELD_COUNT, i -> scope(
" testOopReceiver" + i + "();\n")),
"""
}
@@ -190,11 +189,5 @@ public static void run() {
private static String commaSeparated(int count, IntFunction element) {
return IntStream.range(0, count).mapToObj(element).collect(Collectors.joining(", "));
}
-
- private static List loop(int limit, IntFunction function) {
- return IntStream.range(0, limit)
- .mapToObj(function)
- .toList();
- }
}
diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java
index 7de32d1bc10b..ccea11078c13 100644
--- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java
+++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestTutorial.java
@@ -43,19 +43,11 @@
import compiler.lib.template_framework.TemplateBinding;
import compiler.lib.template_framework.DataName;
import compiler.lib.template_framework.StructuralName;
-import static compiler.lib.template_framework.Template.scope;
-import static compiler.lib.template_framework.Template.transparentScope;
-import static compiler.lib.template_framework.Template.hashtagScope;
-import static compiler.lib.template_framework.Template.let;
-import static compiler.lib.template_framework.Template.$;
-import static compiler.lib.template_framework.Template.fuel;
-import static compiler.lib.template_framework.Template.addDataName;
-import static compiler.lib.template_framework.Template.dataNames;
-import static compiler.lib.template_framework.Template.addStructuralName;
-import static compiler.lib.template_framework.Template.structuralNames;
+
import static compiler.lib.template_framework.DataName.Mutability.MUTABLE;
import static compiler.lib.template_framework.DataName.Mutability.IMMUTABLE;
import static compiler.lib.template_framework.DataName.Mutability.MUTABLE_OR_IMMUTABLE;
+import static compiler.lib.template_framework.Template.*;
import compiler.lib.template_framework.library.Hooks;
@@ -71,6 +63,7 @@ public static void main(String[] args) {
comp.addJavaSourceCode("p.xyz.InnerTest3", generateWithHashtagAndDollarReplacements());
comp.addJavaSourceCode("p.xyz.InnerTest3b", generateWithHashtagAndDollarReplacements2());
comp.addJavaSourceCode("p.xyz.InnerTest3c", generateWithHashtagAndDollarReplacements3());
+ comp.addJavaSourceCode("p.xyz.InnerTest12", generateScopeBuildingUtilityMethods());
comp.addJavaSourceCode("p.xyz.InnerTest4", generateWithCustomHooks());
comp.addJavaSourceCode("p.xyz.InnerTest5", generateWithLibraryHooks());
comp.addJavaSourceCode("p.xyz.InnerTest6", generateWithRecursionAndBindingsAndFuel());
@@ -95,6 +88,7 @@ public static void main(String[] args) {
comp.invoke("p.xyz.InnerTest3", "main", new Object[] {});
comp.invoke("p.xyz.InnerTest3b", "main", new Object[] {});
comp.invoke("p.xyz.InnerTest3c", "main", new Object[] {});
+ comp.invoke("p.xyz.InnerTest12", "main", new Object[] {});
comp.invoke("p.xyz.InnerTest4", "main", new Object[] {});
comp.invoke("p.xyz.InnerTest5", "main", new Object[] {});
comp.invoke("p.xyz.InnerTest6", "main", new Object[] {});
@@ -482,6 +476,127 @@ public static void main() {
return templateClass.render();
}
+
+ // The following examples show how to use the additional utility methods to build scopes in a top down readable way.
+ // Note that the code was specially indented with spaces such that the generated code is nicely formatted while the
+ // generating source code remains easy to read.
+ static String generateScopeBuildingUtilityMethods() {
+ return Template.make(() -> scope(
+ """
+ package p.xyz;
+
+ public class InnerTest12 {
+ int iFld;
+ static int iFld0;
+ static int iFld1;
+ static int iFld2;
+ boolean flagA;
+ boolean flagB;
+ boolean flagC;
+
+ public static void main() {
+ var t = new InnerTest12();
+ t.testRepeat1();
+ t.testRepeat2();
+ t.testRepeatAndJoin();
+ t.testJoin(1, 2, 3);
+ }
+
+ void testRepeat1() {
+ """,
+
+ // Assume you want to repeat incrementing iFld. You could either duplicate the lines:
+ " iFld += 3;\n",
+ " iFld += 3;\n",
+ " iFld += 3;\n",
+ // Or you can use the repeat() utility method to achieve the same task:
+ repeat(3, scope(
+ " iFld += 5;\n")),
+ """
+ }
+
+ """,
+
+ // Sometimes repeats should be slightly different, for example, to generate code. Let's assume you want
+ // to create a couple of equally looking classes. You can use the indexed repeat() utility method.
+ // Let's create vie classes:
+ repeat(5, i -> scope(
+ " class MyClass", i, "{}\n")),
+ """
+
+ void testRepeat2() {
+ """,
+ // And then create an instance for each of them:
+ repeat(5, i -> scope(
+ " new MyClass", i, "();\n")),
+ // Or you directly define 'i' with let to use #-replacements (you can, of course, also define more
+ // variables with let()):
+ repeat(5, i -> scope(
+ let("i", i),
+ " new MyClass#i();\n")),
+ """
+ }
+
+ """,
+
+ // Let's assume that you want to put some varying strings into the same scope multiple times. We can use
+ // the map() utility method to map a string to a scope. For exapmle, you could define your class names
+ // upfront and use them directly:
+ map(List.of("One", "Two", "Three"), klass -> scope(
+ let("klass", klass),
+ " class #klass{}\n")),
+ "\n",
+ // If you still need an incrementing index, for example, to also generate different field accesses,
+ // you can use the indexed map() version:
+ map(List.of("Four", "Five", "Six"), (klass, fieldIndex) -> scope(
+ let("klass", klass),
+ let("fieldIndex", fieldIndex),
+ """
+ class #klass{
+ void test() {
+ InnerTest12.iFld#fieldIndex = 34; // Assign to a specific static field
+ }
+ }
+
+ """)),
+
+ // Suppose you have a method with 5 int args:
+ " void fiveInts(int a, int b, int c, int d, int e) {}\n",
+ // and want to call it with all zeros. You could be tempted to use repeat() with "0 ,", but what about
+ // the last comma? You need to omit it, otherwise it will fail to compile. What we need here is a join
+ // and not a bare concat of repeated scopes. We can use the repeatAndJoin() utility method for that:
+ """
+
+ void testRepeatAndJoin() {
+ """,
+ " fiveInts(", repeatAndJoin(5, ", ", scope("0")), ");\n",
+
+ // If we want to have some ascending numbers, we can use the indexed repeatAndJoin() version:
+ " fiveInts(", repeatAndJoin(5, ", ", index -> scope("" + index)), ");\n",
+
+ // Let's assume you want to AND several conditions together. You can use the utility method mapAndJoin()
+ // that maps and then joins the resulting scopes together:
+ " if (", mapAndJoin(List.of("flagA", "flagB", "flagC"), " && ", flag -> scope(flag)), ") {\n",
+ """
+ }
+ }
+
+ """,
+
+ // You can also use the indexed version of mapAndJoin() which is useful, for example, to define a method
+ // with different parameter names:
+ " void testJoin(",
+ mapAndJoin(List.of("int", "float", "long"), ", ", (type, index) -> scope(
+ let("type", type),
+ let("index", index),
+ "#type x#index")), ") {}\n",
+ """
+ }
+ """
+ )).render();
+ }
+
+
// In this example, we look at the use of Hooks. They allow us to reach back, to outer
// scopes. For example, we can reach out from inside a method body to a hook anchored at
// the top of the class, and insert a field.
diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java
index f56a9d5b2311..a05b2f6d8f03 100644
--- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java
+++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestTemplate.java
@@ -34,9 +34,7 @@
package template_framework.tests;
-import java.util.Arrays;
import java.util.List;
-import java.util.HashSet;
import compiler.lib.template_framework.Template;
import compiler.lib.template_framework.DataName;
@@ -44,22 +42,11 @@
import compiler.lib.template_framework.Hook;
import compiler.lib.template_framework.TemplateBinding;
import compiler.lib.template_framework.RendererException;
-import static compiler.lib.template_framework.Template.scope;
-import static compiler.lib.template_framework.Template.transparentScope;
-import static compiler.lib.template_framework.Template.nameScope;
-import static compiler.lib.template_framework.Template.hashtagScope;
-import static compiler.lib.template_framework.Template.setFuelCostScope;
-import static compiler.lib.template_framework.Template.$;
-import static compiler.lib.template_framework.Template.let;
-import static compiler.lib.template_framework.Template.fuel;
-import static compiler.lib.template_framework.Template.setFuelCost;
-import static compiler.lib.template_framework.Template.addDataName;
-import static compiler.lib.template_framework.Template.dataNames;
-import static compiler.lib.template_framework.Template.addStructuralName;
-import static compiler.lib.template_framework.Template.structuralNames;
+
import static compiler.lib.template_framework.DataName.Mutability.MUTABLE;
import static compiler.lib.template_framework.DataName.Mutability.IMMUTABLE;
import static compiler.lib.template_framework.DataName.Mutability.MUTABLE_OR_IMMUTABLE;
+import static compiler.lib.template_framework.Template.*;
/**
* The tests in this file are mostly there to ensure that the Template Rendering
@@ -171,6 +158,14 @@ public static void main(String[] args) {
testHookAndScopes1();
testHookAndScopes2();
testHookAndScopes3();
+ testRepeat();
+ testRepeatIndexed();
+ testRepeatAndJoin();
+ testRepeatAndJoinIndexed();
+ testMap();
+ testMapIndexed();
+ testMapAndJoin();
+ testMapAndJoinIndexed();
// The following tests should all fail, with an expected exception and message.
expectRendererException(() -> testFailingNestedRendering(), "Nested render not allowed.");
@@ -3373,6 +3368,128 @@ public static void testHookAndScopes3() {
checkEQ(code, expected);
}
+ static void testRepeat() {
+ var templateString = Template.make(() -> scope(
+ repeat(3, scope("repeat\n"))
+ )).render();
+
+ String expected =
+ """
+ repeat
+ repeat
+ repeat
+ """;
+ checkEQ(templateString, expected);
+ }
+
+ static void testRepeatIndexed() {
+ var templateString = Template.make(() -> scope(
+ repeat(2, index -> scope("1: scope for index " + index +"\n")),
+ repeat(3, index -> scope("2: scope for index " + index +"\n"))
+ )).render();
+
+ String expected =
+ """
+ 1: scope for index 0
+ 1: scope for index 1
+ 2: scope for index 0
+ 2: scope for index 1
+ 2: scope for index 2
+ """;
+ checkEQ(templateString, expected);
+ }
+
+ static void testRepeatAndJoin() {
+ var templateString = Template.make(() -> scope(
+ repeatAndJoin(2, ",\n", scope("1: scope")),
+ "\n\n",
+ repeatAndJoin(3, ",\n", scope("2: scope")),
+ "\n"
+ )).render();
+
+ String expected =
+ """
+ 1: scope,
+ 1: scope
+
+ 2: scope,
+ 2: scope,
+ 2: scope
+ """;
+ checkEQ(templateString, expected);
+ }
+
+ static void testRepeatAndJoinIndexed() {
+ var templateString = Template.make(() -> scope(
+ repeatAndJoin(2, ",\n", index -> scope("1: scope for index " + index)),
+ "\n\n",
+ repeatAndJoin(3, ",\n", index -> scope("2: scope for index " + index)),
+ "\n"
+ )).render();
+
+ String expected =
+ """
+ 1: scope for index 0,
+ 1: scope for index 1
+
+ 2: scope for index 0,
+ 2: scope for index 1,
+ 2: scope for index 2
+ """;
+ checkEQ(templateString, expected);
+ }
+
+ static void testMap() {
+ var templateString = Template.make(() -> scope(
+ map(List.of(1, 2, 3), element -> scope("Element " + element +"\n"))
+ )).render();
+
+ String expected =
+ """
+ Element 1
+ Element 2
+ Element 3
+ """;
+ checkEQ(templateString, expected);
+ }
+
+ static void testMapIndexed() {
+ var templateString = Template.make(() -> scope(
+ map(List.of(1, 2, 3), (element, index) -> scope("Element " + element + " at index " + index + "\n"))
+ )).render();
+
+ String expected =
+ """
+ Element 1 at index 0
+ Element 2 at index 1
+ Element 3 at index 2
+ """;
+ checkEQ(templateString, expected);
+ }
+
+ static void testMapAndJoin() {
+ var templateString = Template.make(() -> scope(
+ mapAndJoin(List.of(1, 2, 3), ", ", (element) -> scope("Element " + element))
+ )).render();
+
+ String expected = "Element 1, Element 2, Element 3";
+ checkEQ(templateString, expected);
+ }
+
+ static void testMapAndJoinIndexed() {
+ var templateString = Template.make(() -> scope(
+ mapAndJoin(List.of(1, 2, 3), ", ",
+ (element, index) -> scope("Element " + element + " at index " + index))
+ )).render();
+
+ String expected = "Element 1 at index 0, Element 2 at index 1, Element 3 at index 2";
+ checkEQ(templateString, expected);
+ }
+
+ /*
+ * Failing tests start here
+ */
+
public static void testFailingNestedRendering() {
var template1 = Template.make(() -> scope(
"alpha\n"
From a0d9b0affd12e3b02664dab1847a77dabbbc1169 Mon Sep 17 00:00:00 2001
From: Jan Lahoda
Date: Fri, 14 Aug 2026 10:26:55 +0000
Subject: [PATCH 19/88] 8389987: null is accepted as synchronized lock
Reviewed-by: mcimadamore
---
.../com/sun/tools/javac/comp/Attr.java | 2 +-
.../com/sun/tools/javac/comp/Check.java | 14 +-
.../tools/javac/resources/compiler.properties | 2 +-
.../tools/javac/attr/Synchronized.java | 148 ++++++++++++++++++
4 files changed, 159 insertions(+), 7 deletions(-)
create mode 100644 test/langtools/tools/javac/attr/Synchronized.java
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
index f8b200ab3b98..edbe49164230 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -1965,7 +1965,7 @@ private Symbol enumConstant(JCTree tree, Type enumType) {
}
public void visitSynchronized(JCSynchronized tree) {
- boolean identityType = chk.checkIdentityType(tree.pos(), attribExpr(tree.lock, env));
+ boolean identityType = chk.checkIdentityRefType(tree.pos(), attribExpr(tree.lock, env));
if (identityType && tree.lock.type != null && tree.lock.type.isValueBased()) {
log.warning(tree.pos(), LintWarnings.AttemptToSynchronizeOnInstanceOfValueBasedClass);
}
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
index 9cc7e187d146..f42d7d5d5166 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
@@ -733,14 +733,15 @@ Type checkRefType(DiagnosticPosition pos, Type t) {
t);
}
- /** Check that type is an identity type, i.e. not a value type.
+ /** Check that type is an identity reference type, i.e. a reference type and
+ * not a value type.
* When not discernible statically, give it the benefit of doubt
* and defer to runtime.
*
* @param pos Position to be used for error reporting.
* @param t The type to be checked.
*/
- boolean checkIdentityType(DiagnosticPosition pos, Type t) {
+ boolean checkIdentityRefType(DiagnosticPosition pos, Type t) {
if (t.hasTag(TYPEVAR)) {
t = types.skipTypeVars(t, false);
}
@@ -748,12 +749,15 @@ boolean checkIdentityType(DiagnosticPosition pos, Type t) {
IntersectionClassType ict = (IntersectionClassType)t;
boolean result = true;
for (Type component : ict.getExplicitComponents()) {
- result &= checkIdentityType(pos, component);
+ result &= checkIdentityRefType(pos, component);
}
return result;
}
- if (t.isPrimitive() || (t.isValueClass() && !t.tsym.isAbstract())) {
- typeTagError(pos, diags.fragment(Fragments.TypeReqIdentity), t);
+ if (!t.isReference() || (t.isValueClass() && !t.tsym.isAbstract())) {
+ Fragment required =
+ allowValueClasses ? Fragments.TypeReqIdentity
+ : Fragments.TypeReqRef;
+ typeTagError(pos, diags.fragment(required), t);
return false;
}
return true;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
index 172b2bb735ec..c3f186bc22f6 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
@@ -2938,7 +2938,7 @@ compiler.misc.type.req.exact=\
class or interface without bounds
compiler.misc.type.req.identity=\
- a type with identity
+ identity class
# 0: type
compiler.misc.type.parameter=\
diff --git a/test/langtools/tools/javac/attr/Synchronized.java b/test/langtools/tools/javac/attr/Synchronized.java
new file mode 100644
index 000000000000..783a709dbd2f
--- /dev/null
+++ b/test/langtools/tools/javac/attr/Synchronized.java
@@ -0,0 +1,148 @@
+/*
+ * 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 8389987
+ * @summary Verify correct handling of synchronized
+ * @library /tools/lib
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.compiler/com.sun.tools.javac.util
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @run junit Synchronized
+*/
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import toolbox.JavacTask;
+import toolbox.Task;
+import toolbox.ToolBox;
+
+public class Synchronized {
+
+ private Path base;
+ private ToolBox tb = new ToolBox();
+
+ @Test
+ public void testSynchronizedNull() throws Exception {
+ Path src = base.resolve("src");
+ Path classes = base.resolve("classes");
+ tb.writeJavaFiles(src,
+ """
+ public class Test {
+ void t() {
+ synchronized (null) {}
+ }
+ }
+ """);
+
+ Files.createDirectories(classes);
+
+ List log;
+ List expected;
+
+ log = new JavacTask(tb)
+ .options("-XDrawDiagnostics")
+ .outdir(classes)
+ .files(tb.findJavaFiles(src))
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutputLines(Task.OutputKind.DIRECT);
+
+ expected = List.of(
+ "Test.java:3:9: compiler.err.type.found.req: compiler.misc.type.null, (compiler.misc.type.req.ref)",
+ "1 error"
+ );
+
+ assertEquals(expected, log);
+
+ log = new JavacTask(tb)
+ .options("-XDrawDiagnostics",
+ "--enable-preview", "--release", System.getProperty("java.specification.version"))
+ .outdir(classes)
+ .files(tb.findJavaFiles(src))
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutputLines(Task.OutputKind.DIRECT);
+
+ expected = List.of(
+ "Test.java:3:9: compiler.err.type.found.req: compiler.misc.type.null, (compiler.misc.type.req.identity)",
+ "1 error"
+ );
+
+ assertEquals(expected, log);
+ }
+
+ @Test
+ public void testSynchronizedValueClassValueClassesDisabled() throws Exception {
+ Path src = base.resolve("src");
+ Path classes = base.resolve("classes");
+ tb.writeJavaFiles(src,
+ """
+ public value class Test {
+ void t() {
+ synchronized (new Test()) {}
+ }
+ }
+ """);
+
+ Files.createDirectories(classes);
+
+ List log;
+ List expected;
+
+ log = new JavacTask(tb)
+ .options("-XDrawDiagnostics",
+ "-XDshould-stop.at=WARN")
+ .outdir(classes)
+ .files(tb.findJavaFiles(src))
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutputLines(Task.OutputKind.DIRECT);
+
+ expected = List.of(
+ "Test.java:1:8: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.value.classes)",
+ "Test.java:3:9: compiler.err.type.found.req: Test, (compiler.misc.type.req.ref)",
+ "2 errors"
+ );
+
+ assertEquals(expected, log);
+ }
+
+ @BeforeEach
+ public void setUp(TestInfo info) {
+ base = Paths.get(".")
+ .resolve(info.getTestMethod()
+ .orElseThrow()
+ .getName());
+ }
+}
From 88dfb74bbeefcf2b0aa11835183bcd949998fc8f Mon Sep 17 00:00:00 2001
From: Jan Lahoda
Date: Fri, 14 Aug 2026 10:27:16 +0000
Subject: [PATCH 20/88] 8389659: Cannot invoke
"com.sun.tools.javac.code.Type.getTag()" because "type" is null on invalid
LHS
Reviewed-by: mcimadamore
---
.../com/sun/tools/javac/code/Kinds.java | 9 +-
.../com/sun/tools/javac/comp/Attr.java | 2 +-
.../tools/javac/SuperInit/InitAssignOp.java | 292 ++++++++++++++++++
3 files changed, 298 insertions(+), 5 deletions(-)
create mode 100644 test/langtools/tools/javac/SuperInit/InitAssignOp.java
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Kinds.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Kinds.java
index 0650884f3e4c..a9b06237f23d 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Kinds.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Kinds.java
@@ -174,6 +174,7 @@ public static class KindSelector {
public static final KindSelector MDL = new KindSelector(0x40);
public static final KindSelector ERR = new KindSelector(0x7f);
public static final KindSelector ASG = new KindSelector(0x84);
+ public static final KindSelector ASG_OP = new KindSelector(0x184);
//common derived selectors
public static final KindSelector TYP_PCK = of(TYP, PCK);
@@ -182,14 +183,14 @@ public static class KindSelector {
public static final KindSelector VAL_TYP = of(VAL, TYP);
public static final KindSelector VAL_TYP_PCK = of(VAL, TYP, PCK);
- private final byte data;
+ private final int data;
private KindSelector(int data) {
- this.data = (byte) data;
+ this.data = data;
}
public static KindSelector of(KindSelector... kindSelectors) {
- byte newData = 0;
+ int newData = 0;
for (KindSelector kindSel : kindSelectors) {
newData |= kindSel.data;
}
@@ -205,7 +206,7 @@ public boolean contains(KindSelector other) {
}
public boolean isAssignment() {
- return ASG.subset(this) && !VAL.subset(this);
+ return ASG.subset(this) && !ASG_OP.subset(this);
}
/** A set of KindName(s) representing a set of symbol's kinds. */
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
index edbe49164230..5a32378a9311 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -180,7 +180,7 @@ protected Attr(Context context) {
statInfo = new ResultInfo(KindSelector.NIL, Type.noType);
varAssignmentInfo = new ResultInfo(KindSelector.ASG, Type.noType);
- varAssignmentOpInfo = new ResultInfo(KindSelector.of(KindSelector.VAL, KindSelector.ASG), Type.noType);
+ varAssignmentOpInfo = new ResultInfo(KindSelector.ASG_OP, Type.noType);
unknownExprInfo = new ResultInfo(KindSelector.VAL, Type.noType);
methodAttrInfo = new MethodAttrInfo();
unknownTypeInfo = new ResultInfo(KindSelector.TYP, Type.noType);
diff --git a/test/langtools/tools/javac/SuperInit/InitAssignOp.java b/test/langtools/tools/javac/SuperInit/InitAssignOp.java
new file mode 100644
index 000000000000..499a2ba32503
--- /dev/null
+++ b/test/langtools/tools/javac/SuperInit/InitAssignOp.java
@@ -0,0 +1,292 @@
+/*
+ * 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 8389659
+ * @summary Verify assign op is handled correctly in the constructor prologue
+ * @library /tools/lib
+ * @modules
+ * jdk.compiler/com.sun.tools.javac.api
+ * jdk.compiler/com.sun.tools.javac.file
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.compiler/com.sun.tools.javac.util
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @run junit ${test.main.class}
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+
+import toolbox.JavaTask;
+import toolbox.JavacTask;
+import toolbox.Task;
+import toolbox.ToolBox;
+
+public class InitAssignOp {
+
+ private final ToolBox tb = new ToolBox();
+ private Path base;
+
+ @Test
+ void testInitAssignOp() throws Exception {
+ record TestCase(String source, List expectedCompilationOutput) {}
+ TestCase[] tests = new TestCase[] {
+ new TestCase("""
+ public class Test {
+ private int i;
+
+ public Test() {
+ ++this.i;
+ super();
+ }
+ public static void main(String... args) {
+ System.out.println(new Test().i);
+ }
+ }
+ """,
+ List.of(
+ "Test.java:5:15: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.value.classes)",
+ "1 error"
+ )),
+ new TestCase("""
+ public class Test {
+ private int i;
+
+ public Test() {
+ this.i++;
+ super();
+ }
+ public static void main(String... args) {
+ System.out.println(new Test().i);
+ }
+ }
+ """,
+ List.of(
+ "Test.java:5:13: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.value.classes)",
+ "1 error"
+ )),
+ new TestCase("""
+ public class Test {
+ private int i;
+
+ public Test() {
+ this.i += 1;
+ super();
+ }
+ public static void main(String... args) {
+ System.out.println(new Test().i);
+ }
+ }
+ """,
+ List.of(
+ "Test.java:5:13: compiler.err.preview.feature.disabled.plural: (compiler.misc.feature.value.classes)",
+ "1 error"
+ )),
+ };
+ for (TestCase test : tests) {
+ Path classes = base.resolve("classes");
+ Files.createDirectories(classes);
+ List out;
+
+ out =
+ new JavacTask(tb)
+ .options("-XDrawDiagnostics")
+ .outdir(classes)
+ .sources(test.source())
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutputLines(Task.OutputKind.DIRECT);
+ Assertions.assertEquals(test.expectedCompilationOutput(), out);
+ new JavacTask(tb)
+ .options("--enable-preview", "--release", System.getProperty("java.specification.version"))
+ .outdir(classes)
+ .sources(test.source())
+ .run()
+ .writeAll();
+
+ out =
+ new JavaTask(tb)
+ .vmOptions("--enable-preview")
+ .classpath(classes.toString())
+ .className("Test")
+ .run()
+ .getOutputLines(Task.OutputKind.STDOUT);
+ List expectedRunOutput = List.of(
+ "1"
+ );
+ Assertions.assertEquals(expectedRunOutput, out);
+ }
+ }
+
+ @Test
+ void testInitAssignOpErrors() throws Exception {
+ record TestCase(String source, List expectedCompilationOutput) {}
+ TestCase[] tests = new TestCase[] {
+ new TestCase("""
+ public class Test {
+ public Test() {
+ ++0;
+ super();
+ }
+ }
+ """,
+ List.of(
+ "Test.java:3:11: compiler.err.unexpected.type: kindname.variable, kindname.value",
+ "1 error"
+ )),
+ new TestCase("""
+ public class Test {
+ public Test() {
+ ++test();
+ super();
+ }
+ private static int test() { return 1; }
+ }
+ """,
+ List.of(
+ "Test.java:3:15: compiler.err.unexpected.type: kindname.variable, kindname.value",
+ "1 error"
+ )),
+ new TestCase("""
+ public class Test {
+ public Test(int i) {
+ ++(i + 1);
+ super();
+ }
+ }
+ """,
+ List.of(
+ "Test.java:3:14: compiler.err.unexpected.type: kindname.variable, kindname.value",
+ "1 error"
+ )),
+
+ new TestCase("""
+ public class Test {
+ public Test() {
+ 0++;
+ super();
+ }
+ }
+ """,
+ List.of(
+ "Test.java:3:9: compiler.err.unexpected.type: kindname.variable, kindname.value",
+ "1 error"
+ )),
+ new TestCase("""
+ public class Test {
+ public Test() {
+ test()++;
+ super();
+ }
+ private static int test() { return 1; }
+ }
+ """,
+ List.of(
+ "Test.java:3:13: compiler.err.unexpected.type: kindname.variable, kindname.value",
+ "1 error"
+ )),
+ new TestCase("""
+ public class Test {
+ public Test(int i) {
+ (i + 1)++;
+ super();
+ }
+ }
+ """,
+ List.of(
+ "Test.java:3:12: compiler.err.unexpected.type: kindname.variable, kindname.value",
+ "1 error"
+ )),
+
+ new TestCase("""
+ public class Test {
+ public Test() {
+ 0 += 1;
+ super();
+ }
+ }
+ """,
+ List.of(
+ "Test.java:3:9: compiler.err.unexpected.type: kindname.variable, kindname.value",
+ "1 error"
+ )),
+ new TestCase("""
+ public class Test {
+ public Test() {
+ test() += 1;
+ super();
+ }
+ private static int test() { return 1; }
+ }
+ """,
+ List.of(
+ "Test.java:3:13: compiler.err.unexpected.type: kindname.variable, kindname.value",
+ "1 error"
+ )),
+ new TestCase("""
+ public class Test {
+ public Test(int i) {
+ (i + 1) += 1;
+ super();
+ }
+ }
+ """,
+ List.of(
+ "Test.java:3:12: compiler.err.unexpected.type: kindname.variable, kindname.value",
+ "1 error"
+ )),
+ };
+ for (TestCase test : tests) {
+ Path classes = base.resolve("classes");
+ Files.createDirectories(classes);
+ List out;
+
+ out =
+ new JavacTask(tb)
+ .options("-XDrawDiagnostics",
+ "--enable-preview", "--release", System.getProperty("java.specification.version"))
+ .outdir(classes)
+ .sources(test.source())
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutputLines(Task.OutputKind.DIRECT);
+ Assertions.assertEquals(test.expectedCompilationOutput(), out);
+ }
+ }
+
+ @BeforeEach
+ public void setUp(TestInfo info) {
+ base = Paths.get(".")
+ .resolve(info.getTestMethod()
+ .orElseThrow()
+ .getName());
+ }
+
+}
From 782b49e89e1b6ce2020ee5b753e246cd5db60094 Mon Sep 17 00:00:00 2001
From: Stefan Karlsson
Date: Fri, 14 Aug 2026 12:33:34 +0000
Subject: [PATCH 21/88] 8378525: ZGC: Refactor ZBarrierSet to better fit with
ZGC coding style
Reviewed-by: aboldtch, eosterlund
---
src/hotspot/share/gc/z/zBarrierSet.inline.hpp | 190 +++++++++---------
.../share/oops/flatArrayKlass.inline.hpp | 3 +-
src/hotspot/share/oops/inlineKlass.hpp | 11 +-
src/hotspot/share/oops/inlineKlass.inline.hpp | 29 ++-
4 files changed, 122 insertions(+), 111 deletions(-)
diff --git a/src/hotspot/share/gc/z/zBarrierSet.inline.hpp b/src/hotspot/share/gc/z/zBarrierSet.inline.hpp
index 926079dab0ff..b68bf8ce35e7 100644
--- a/src/hotspot/share/gc/z/zBarrierSet.inline.hpp
+++ b/src/hotspot/share/gc/z/zBarrierSet.inline.hpp
@@ -32,6 +32,7 @@
#include "gc/z/zHeap.hpp"
#include "gc/z/zNMethod.hpp"
#include "gc/z/zUtils.inline.hpp"
+#include "oops/accessBackend.hpp"
#include "oops/inlineKlass.inline.hpp"
#include "oops/objArrayOop.hpp"
#include "utilities/copy.hpp"
@@ -448,125 +449,114 @@ inline void ZBarrierSet::AccessBarrier::clone_in_heap(o
clone_obj(to_zaddress(src), to_zaddress(dst), ZUtils::words_to_bytes(size));
}
-static inline void copy_primitive_payload(const void* src, const void* dst, const size_t payload_size_bytes, size_t& copied_bytes) {
- if (payload_size_bytes == 0) {
+// Iterate over a value payload and visit all blocks of primitive fields and all oops.
+template
+void value_primitive_and_oop_iterate(InlineKlass* klass,
+ address payload,
+ size_t payload_size,
+ PrimitiveFunction primitive_function,
+ OopFunction oop_function) {
+ if (!klass->contains_oops()) {
+ // Only primitive fields
+ primitive_function(0, payload_size);
return;
}
- void* src_payload = (void*)(address(src) + copied_bytes);
- void* dst_payload = (void*)(address(dst) + copied_bytes);
- Copy::copy_value_content(src_payload, dst_payload, payload_size_bytes);
- copied_bytes += payload_size_bytes;
-}
+ size_t visited = 0;
-static inline void clear_primitive_payload(const void* dst, const size_t payload_size_bytes, size_t& copied_bytes) {
- if (payload_size_bytes == 0) {
- return;
- }
+ // Visit primitive and oop fields up until and including the last oop field
+ klass->oop_iterate_value_payload_f(payload,
+ [&](oop* p) {
+ const size_t oop_offset = (address)p - payload;
+
+ assert(visited <= oop_offset, "Negative sized leading payload segment");
+
+ // Visit any previous unvisited primitive fields
+ if (oop_offset > visited) {
+ const size_t size = oop_offset - visited;
+ primitive_function(visited, size);
+ visited += size;
+ }
+
+ // Visit the oop field
+ oop_function(oop_offset);
+ visited += sizeof(zpointer);
+ });
- void* dst_payload = (void*)(address(dst) + copied_bytes);
- Copy::fill_to_memory_atomic(dst_payload, payload_size_bytes);
- copied_bytes += payload_size_bytes;
+ // Visit any trailing primitive payload after the last oop
+ assert(visited <= payload_size, "Negative sized trailing payload segment");
+ if (payload_size > visited) {
+ const size_t size = payload_size - visited;
+ primitive_function(visited, size);
+ }
}
template
inline void ZBarrierSet::AccessBarrier::value_copy_in_heap(const ValuePayload& src, const ValuePayload& dst) {
precond(src.klass() == dst.klass());
- const LayoutKind lk = LayoutKindHelper::get_copy_layout(src.layout_kind(), dst.layout_kind());
- const InlineKlass* md = src.klass();
- if (md->contains_oops()) {
- assert(!LayoutKindHelper::is_atomic_flat(lk) ||
- (md->nonstatic_oop_map_count() == 1 &&
- md->layout_size_in_bytes(lk) == sizeof(zpointer)),
- "ZGC can only handle atomic flat values with a single oop");
-
- // Iterate over each oop map, performing:
- // 1) possibly raw copy for any primitive payload before each map
- // 2) load and store barrier for each oop
- // 3) possibly raw copy for any primitive payload trailer
-
- // addr() points at the payload start, the oop map offset are relative to
- // the object header, adjust address to account for this discrepancy.
- const address src_addr = src.addr();
- const address dst_addr = dst.addr();
- const address oop_map_adjusted_src_addr = src_addr - md->payload_offset();
- OopMapBlock* map = md->start_of_nonstatic_oop_maps();
- const OopMapBlock* const end = map + md->nonstatic_oop_map_count();
- size_t size_in_bytes = md->layout_size_in_bytes(lk);
- size_t copied_bytes = 0;
- while (map != end) {
- zpointer* src_p = (zpointer*)(oop_map_adjusted_src_addr + map->offset());
- const uintptr_t oop_offset = uintptr_t(src_p) - uintptr_t(src_addr);
- zpointer* dst_p = (zpointer*)(uintptr_t(dst_addr) + oop_offset);
-
- // Copy any leading primitive payload before every cluster of oops
- assert(copied_bytes < oop_offset || copied_bytes == oop_offset, "Negative sized leading payload segment");
- copy_primitive_payload(src_addr, dst_addr, oop_offset - copied_bytes, copied_bytes);
-
- // Copy a cluster of oops
- for (const zpointer* const src_end = src_p + map->count(); src_p < src_end; src_p++, dst_p++) {
- oop_copy_one(dst_p, src_p);
- copied_bytes += sizeof(zpointer);
- }
- map++;
- }
+ InlineKlass* const klass = src.klass();
- // Copy trailing primitive payload after potential oops
- assert(copied_bytes < size_in_bytes || copied_bytes == size_in_bytes, "Negative sized trailing payload segment");
- copy_primitive_payload(src_addr, dst_addr, size_in_bytes - copied_bytes, copied_bytes);
- } else {
- Raw::value_copy_in_heap(src, dst);
- }
+ const LayoutKind layout_kind = LayoutKindHelper::get_copy_layout(src.layout_kind(), dst.layout_kind());
+ const size_t payload_size = klass->layout_size_in_bytes(layout_kind);
+
+ // The addr() points at the payload start, not the object start.
+ const address src_payload = src.addr();
+ const address dst_payload = dst.addr();
+
+ auto primitive_function = [&](size_t offset, size_t size) {
+ Copy::copy_value_content(src_payload + offset, dst_payload + offset, size);
+ };
+
+ auto oop_function = [&](size_t offset) {
+ zpointer* const src_p = (zpointer*)(src_payload + offset);
+ zpointer* const dst_p = (zpointer*)(dst_payload + offset);
+ const OopCopyResult result = oop_copy_one(dst_p, src_p);
+ assert(result == OopCopyResult::ok, "Unexpected copy checks");
+ };
+
+ value_primitive_and_oop_iterate(
+ klass,
+ dst_payload,
+ payload_size,
+ primitive_function,
+ oop_function);
}
template
inline void ZBarrierSet::AccessBarrier::value_store_null_in_heap(const ValuePayload& dst) {
- const LayoutKind lk = dst.layout_kind();
- assert(!LayoutKindHelper::is_null_free_flat(lk), "Cannot store null in null free layout");
- const InlineKlass* md = dst.klass();
-
- if (md->contains_oops()) {
- assert(!LayoutKindHelper::is_atomic_flat(lk) ||
- (md->nonstatic_oop_map_count() == 1 &&
- md->layout_size_in_bytes(lk) == sizeof(zpointer)),
- "ZGC can only handle atomic flat values with a single oop");
-
- // Iterate over each oop map, performing:
- // 1) possibly raw clear for any primitive payload before each map
- // 2) store barrier and clear for each oop
- // 3) possibly raw clear for any primitive payload trailer
-
- // addr() points at the payload start, the oop map offset are relative to
- // the object header, adjust address to account for this discrepancy.
- const address dst_addr = dst.addr();
- const address oop_map_adjusted_dst_addr = dst_addr - md->payload_offset();
- OopMapBlock* map = md->start_of_nonstatic_oop_maps();
- const OopMapBlock* const end = map + md->nonstatic_oop_map_count();
- size_t size_in_bytes = md->layout_size_in_bytes(lk);
- size_t copied_bytes = 0;
- while (map != end) {
- zpointer* dst_p = (zpointer*)(oop_map_adjusted_dst_addr + map->offset());
- const uintptr_t oop_offset = uintptr_t(dst_p) - uintptr_t(dst_addr);
-
- // Clear any leading primitive payload before every cluster of oops
- assert(copied_bytes < oop_offset || copied_bytes == oop_offset, "Negative sized leading payload segment");
- clear_primitive_payload(dst_addr, oop_offset - copied_bytes, copied_bytes);
-
- // Clear a cluster of oops
- for (const zpointer* const dst_end = dst_p + map->count(); dst_p < dst_end; dst_p++) {
- oop_clear_one(dst_p);
- copied_bytes += sizeof(zpointer);
- }
- map++;
- }
+ InlineKlass* const klass = dst.klass();
+ const LayoutKind layout_kind = dst.layout_kind();
- // Clear trailing primitive payload after potential oops
- assert(copied_bytes < size_in_bytes || copied_bytes == size_in_bytes, "Negative sized trailing payload segment");
- clear_primitive_payload(dst_addr, size_in_bytes - copied_bytes, copied_bytes);
- } else {
+ assert(!LayoutKindHelper::is_null_free_flat(layout_kind),
+ "Cannot store null in null free layout");
+
+ if (!klass->contains_oops()) {
+ // All fields are primitives
Raw::value_store_null(dst);
+ return;
}
+
+ const size_t payload_size = klass->layout_size_in_bytes(layout_kind);
+
+ // The addr() points at the payload start, not the object start.
+ const address dst_payload = dst.addr();
+
+ auto primitive_function = [&](size_t offset, size_t size) {
+ Copy::clear_value_content(dst_payload + offset, size);
+ };
+
+ auto oop_function = [&](size_t offset) {
+ zpointer* const p = (zpointer*)(dst_payload + offset);
+ const OopCopyResult result = oop_clear_one(p);
+ assert(result == OopCopyResult::ok, "Unexpected copy checks");
+ };
+
+ value_primitive_and_oop_iterate(klass,
+ dst_payload,
+ payload_size,
+ primitive_function,
+ oop_function);
}
//
diff --git a/src/hotspot/share/oops/flatArrayKlass.inline.hpp b/src/hotspot/share/oops/flatArrayKlass.inline.hpp
index eb990ddcf40c..11eaa6c78809 100644
--- a/src/hotspot/share/oops/flatArrayKlass.inline.hpp
+++ b/src/hotspot/share/oops/flatArrayKlass.inline.hpp
@@ -65,7 +65,6 @@ void FlatArrayKlass::oop_oop_iterate_elements_specialized_bounded(flatArrayOop a
const int addr_incr = 1 << shift;
uintptr_t elem_addr = (uintptr_t)a->base();
uintptr_t stop_addr = elem_addr + ((uintptr_t)a->length() << shift);
- const int oop_offset = element_klass()->payload_offset();
if (elem_addr < lo) {
uintptr_t diff = lo - elem_addr;
@@ -78,7 +77,7 @@ void FlatArrayKlass::oop_oop_iterate_elements_specialized_bounded(flatArrayOop a
const uintptr_t end = stop_addr;
while (elem_addr < end) {
- element_klass()->oop_iterate_specialized_bounded((address)(elem_addr - oop_offset), closure, lo, hi);
+ element_klass()->oop_iterate_value_payload_bounded((address)elem_addr, closure, lo, hi);
elem_addr += addr_incr;
}
}
diff --git a/src/hotspot/share/oops/inlineKlass.hpp b/src/hotspot/share/oops/inlineKlass.hpp
index 8be85a989e82..63922f20b6f7 100644
--- a/src/hotspot/share/oops/inlineKlass.hpp
+++ b/src/hotspot/share/oops/inlineKlass.hpp
@@ -279,12 +279,17 @@ class InlineKlass: public InstanceKlass {
bool contains_oops() const { return nonstatic_oop_map_count() > 0; }
int nonstatic_oop_count();
- // oop iterate raw inline type data pointer (where oop_addr may not be an oop, but backing/array-element)
+ // oop iterate the payload of a value object.
+ //
+ // * Function: void function(T* p)
+ template
+ inline void oop_iterate_value_payload_f(address payload, Function function);
+
template
- inline void oop_iterate_specialized(const address oop_addr, OopClosureType* closure);
+ inline void oop_iterate_value_payload(address payload, OopClosureType* closure);
template
- inline void oop_iterate_specialized_bounded(const address oop_addr, OopClosureType* closure, uintptr_t lo, uintptr_t hi);
+ inline void oop_iterate_value_payload_bounded(address payload, OopClosureType* closure, uintptr_t lo, uintptr_t hi);
// Support for the scalarized calling convention
void initialize_calling_convention(TRAPS);
diff --git a/src/hotspot/share/oops/inlineKlass.inline.hpp b/src/hotspot/share/oops/inlineKlass.inline.hpp
index 393c07fe9faa..96882dd2f5cd 100644
--- a/src/hotspot/share/oops/inlineKlass.inline.hpp
+++ b/src/hotspot/share/oops/inlineKlass.inline.hpp
@@ -115,30 +115,47 @@ inline address InlineKlass::payload_addr(oop o) const {
return cast_from_oop(o) + payload_offset();
}
-template
-void InlineKlass::oop_iterate_specialized(const address oop_addr, OopClosureType* closure) {
+template
+void InlineKlass::oop_iterate_value_payload_f(address payload, Function function) {
OopMapBlock* map = start_of_nonstatic_oop_maps();
OopMapBlock* const end_map = map + nonstatic_oop_map_count();
+ // OopMap offsets are relative to an object header, but we are iterating over
+ // inlined value payloads, which often don't have an object header. Set up a
+ // synthetic object base that can be used by the oop map offset calculations.
+ const address synthetic_object_base = payload - payload_offset();
+
for (; map < end_map; map++) {
- T* p = (T*) (oop_addr + map->offset());
+ T* p = (T*) (synthetic_object_base + map->offset());
T* const end = p + map->count();
for (; p < end; ++p) {
- Devirtualizer::do_oop(closure, p);
+ function(p);
}
}
}
template
-inline void InlineKlass::oop_iterate_specialized_bounded(const address oop_addr, OopClosureType* closure, uintptr_t lo, uintptr_t hi) {
+void InlineKlass::oop_iterate_value_payload(address payload, OopClosureType* closure) {
+ oop_iterate_value_payload_f(payload, [&](T* p) {
+ Devirtualizer::do_oop(closure, p);
+ });
+}
+
+template
+inline void InlineKlass::oop_iterate_value_payload_bounded(address payload, OopClosureType* closure, uintptr_t lo, uintptr_t hi) {
OopMapBlock* map = start_of_nonstatic_oop_maps();
OopMapBlock* const end_map = map + nonstatic_oop_map_count();
T* const l = (T*) lo;
T* const h = (T*) hi;
+ // OopMap offsets are relative to an object header, but we are iterating over
+ // inlined value payloads, which often don't have an object header. Set up a
+ // synthetic object base that can be used by the oop map offset calculations.
+ const address synthetic_object_base = payload - payload_offset();
+
for (; map < end_map; map++) {
- T* p = (T*) (oop_addr + map->offset());
+ T* p = (T*) (synthetic_object_base + map->offset());
T* end = p + map->count();
if (p < l) {
p = l;
From 16bf2730aa19fafb65d5bf8aff1f499579975c9a Mon Sep 17 00:00:00 2001
From: Vladimir Kozlov
Date: Fri, 14 Aug 2026 16:23:09 +0000
Subject: [PATCH 22/88] 8390329: Obsolete AlwaysCompileLoopMethods VM flag
Reviewed-by: dholmes, mchevalier
---
src/hotspot/share/compiler/compilationPolicy.cpp | 3 +--
src/hotspot/share/compiler/compileBroker.cpp | 4 ++--
src/hotspot/share/compiler/compileBroker.hpp | 1 -
src/hotspot/share/compiler/compileTask.hpp | 2 +-
src/hotspot/share/runtime/arguments.cpp | 6 +-----
src/hotspot/share/runtime/arguments.hpp | 1 -
src/hotspot/share/runtime/globals.hpp | 4 ----
7 files changed, 5 insertions(+), 16 deletions(-)
diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp
index 81c03587416a..2b451ebb5b3a 100644
--- a/src/hotspot/share/compiler/compilationPolicy.cpp
+++ b/src/hotspot/share/compiler/compilationPolicy.cpp
@@ -79,8 +79,7 @@ bool CompilationPolicy::must_be_compiled(const methodHandle& m, int comp_level)
if (m->has_compiled_code()) return false; // already compiled
if (!can_be_compiled(m, comp_level)) return false;
- return !UseInterpreter || // must compile all methods
- (AlwaysCompileLoopMethods && m->has_loops() && CompileBroker::should_compile_new_jobs()); // eagerly compile loop methods
+ return !UseInterpreter; // must compile all methods
}
void CompilationPolicy::maybe_compile_early(const methodHandle& m, TRAPS) {
diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp
index 099f29f96bf0..7ca7b57b43b7 100644
--- a/src/hotspot/share/compiler/compileBroker.cpp
+++ b/src/hotspot/share/compiler/compileBroker.cpp
@@ -1744,7 +1744,7 @@ void CompileBroker::compiler_thread_loop() {
// Never compile a method if breakpoints are present in it
if (method()->number_of_breakpoints() == 0) {
// Compile the method.
- if ((UseCompiler || AlwaysCompileLoopMethods) && CompileBroker::should_compile_new_jobs()) {
+ if (UseCompiler && CompileBroker::should_compile_new_jobs()) {
invoke_compiler_on_method(task);
thread->start_idle_timer();
} else {
@@ -2152,7 +2152,7 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
*/
void CompileBroker::handle_full_code_cache(CodeBlobType code_blob_type) {
UseInterpreter = true;
- if (UseCompiler || AlwaysCompileLoopMethods ) {
+ if (UseCompiler) {
if (xtty != nullptr) {
stringStream s;
// Dump code cache state into a buffer before locking the tty,
diff --git a/src/hotspot/share/compiler/compileBroker.hpp b/src/hotspot/share/compiler/compileBroker.hpp
index f1d69c8c0bf4..db2e90699136 100644
--- a/src/hotspot/share/compiler/compileBroker.hpp
+++ b/src/hotspot/share/compiler/compileBroker.hpp
@@ -369,7 +369,6 @@ class CompileBroker: AllStatic {
static void disable_compilation_forever() {
UseCompiler = false;
- AlwaysCompileLoopMethods = false;
AtomicAccess::xchg(&_should_compile_new_jobs, jint(shutdown_compilation));
}
diff --git a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp
index 1bed0ce06c1c..4e1cdedf3d82 100644
--- a/src/hotspot/share/compiler/compileTask.hpp
+++ b/src/hotspot/share/compiler/compileTask.hpp
@@ -59,7 +59,7 @@ class CompileTask : public CHeapObj {
Reason_Tiered, // Tiered-policy
Reason_Replay, // ciReplay
Reason_Whitebox, // Whitebox API
- Reason_MustBeCompiled, // Used for -Xcomp or AlwaysCompileLoopMethods (see CompilationPolicy::must_be_compiled())
+ Reason_MustBeCompiled, // Used for -Xcomp (see CompilationPolicy::must_be_compiled())
Reason_Count
};
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index 0f931099dd4c..2cd0c6bbdd27 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -96,7 +96,6 @@ const char* Arguments::_sun_java_launcher = DEFAULT_JAVA_LAUNCHER;
bool Arguments::_executing_unit_tests = false;
// These parameters are reset in method parse_vm_init_args()
-bool Arguments::_AlwaysCompileLoopMethods = AlwaysCompileLoopMethods;
bool Arguments::_UseOnStackReplacement = UseOnStackReplacement;
bool Arguments::_BackgroundCompilation = BackgroundCompilation;
bool Arguments::_ClipInlining = ClipInlining;
@@ -536,7 +535,6 @@ static SpecialFlag const special_jvm_flags[] = {
// --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in:
{ "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() },
{ "InitiatingHeapOccupancyPercent", JDK_Version::jdk(27), JDK_Version::jdk(28), JDK_Version::jdk(29) },
- { "AlwaysCompileLoopMethods", JDK_Version::jdk(27), JDK_Version::jdk(28), JDK_Version::jdk(29) },
// -------------- Obsolete Flags - sorted by expired_in --------------
@@ -547,6 +545,7 @@ static SpecialFlag const special_jvm_flags[] = {
#ifdef _LP64
{ "UseCompressedClassPointers", JDK_Version::jdk(25), JDK_Version::jdk(27), JDK_Version::undefined() },
#endif
+ { "AlwaysCompileLoopMethods", JDK_Version::jdk(27), JDK_Version::jdk(28), JDK_Version::jdk(29) },
#ifdef ASSERT
{ "DummyObsoleteTestFlag", JDK_Version::undefined(), JDK_Version::jdk(18), JDK_Version::undefined() },
@@ -1358,7 +1357,6 @@ void Arguments::set_mode_flags(Mode mode) {
// Default values may be platform/compiler dependent -
// use the saved values
ClipInlining = Arguments::_ClipInlining;
- AlwaysCompileLoopMethods = Arguments::_AlwaysCompileLoopMethods;
UseOnStackReplacement = Arguments::_UseOnStackReplacement;
BackgroundCompilation = Arguments::_BackgroundCompilation;
@@ -1370,7 +1368,6 @@ void Arguments::set_mode_flags(Mode mode) {
case _int:
UseCompiler = false;
UseLoopCounter = false;
- AlwaysCompileLoopMethods = false;
UseOnStackReplacement = false;
break;
case _mixed:
@@ -1676,7 +1673,6 @@ Arguments::ArgsRange Arguments::parse_memory_size(const char* s,
jint Arguments::parse_vm_init_args(GrowableArrayCHeap* all_args) {
// Save default settings for some mode flags
- Arguments::_AlwaysCompileLoopMethods = AlwaysCompileLoopMethods;
Arguments::_UseOnStackReplacement = UseOnStackReplacement;
Arguments::_ClipInlining = ClipInlining;
Arguments::_BackgroundCompilation = BackgroundCompilation;
diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
index bda1de4edea2..80f993eba70b 100644
--- a/src/hotspot/share/runtime/arguments.hpp
+++ b/src/hotspot/share/runtime/arguments.hpp
@@ -253,7 +253,6 @@ class Arguments : AllStatic {
static bool _has_jdwp_agent;
// Used to save default settings
- static bool _AlwaysCompileLoopMethods;
static bool _UseOnStackReplacement;
static bool _BackgroundCompilation;
static bool _ClipInlining;
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 89b8fd11c94c..295e92018ee3 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -1224,10 +1224,6 @@ const int ObjectAlignmentInBytes = 8;
product(bool, UseCompiler, true, \
"Use Just-In-Time compilation") \
\
- product(bool, AlwaysCompileLoopMethods, false, \
- "(Deprecated) When using recompilation, never interpret methods " \
- "containing loops") \
- \
product(int, AllocatePrefetchStyle, 1, \
"0 = no prefetch, " \
"1 = generate prefetch instructions for each allocation, " \
From 982afa5d48c0d65f7038489b6d7ae555e7e14cfe Mon Sep 17 00:00:00 2001
From: Ioi Lam
Date: Sat, 15 Aug 2026 01:26:39 +0000
Subject: [PATCH 23/88] 8388525: Disallow different module options between AOT
training and assembly
Reviewed-by: asmehra, matsaave
---
src/hotspot/share/cds/aotClassLocation.cpp | 117 +++++++-----
src/hotspot/share/cds/aotClassLocation.hpp | 9 +-
src/hotspot/share/cds/aotMetaspace.cpp | 6 +-
src/hotspot/share/cds/aotMetaspace.hpp | 4 +-
src/hotspot/share/cds/filemap.cpp | 23 +--
src/hotspot/share/classfile/modules.cpp | 58 ++++--
.../appcds/aotCache/InvalidModuleOptions.java | 176 ++++++++++++++++++
.../cds/appcds/aotCache/OldClassSupport2.java | 5 -
.../com.moretest/com/moretest/Bar.java | 27 +++
.../modules2/com.moretest/module-info.java | 27 +++
.../com/evenmoretest/Baz.java | 27 +++
.../com.evenmoretest/module-info.java | 27 +++
.../cds/appcds/aotClassLinking/AddReads.java | 6 +-
.../jigsaw/modulepath/ModulePathAndFMG.java | 16 +-
14 files changed, 435 insertions(+), 93 deletions(-)
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/InvalidModuleOptions.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/com/moretest/Bar.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/module-info.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/com/evenmoretest/Baz.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/module-info.java
diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp
index aaff14d2b3a1..464bacd1ca07 100644
--- a/src/hotspot/share/cds/aotClassLocation.cpp
+++ b/src/hotspot/share/cds/aotClassLocation.cpp
@@ -98,6 +98,7 @@ class ClassLocationStream {
int current() const { return _current; }
bool is_empty() const { return _array.length() == 0; }
+ int size() const { return _array.length(); }
};
class BootCpClassLocationStream : public ClassLocationStream {
@@ -435,7 +436,7 @@ bool AOTClassLocation::check(const char* runtime_path, bool has_aot_linked_class
}
}
- log_info(class, path)("ok");
+ log_info(class, path)("ok (file size and timestamp have not changed)");
return true;
}
@@ -760,7 +761,7 @@ bool AOTClassLocationConfig::check_classpaths(bool is_boot_classpath, bool has_a
LogTarget(Info, class, path) lt;
if (lt.is_enabled()) {
LogStream ls(lt);
- ls.print("Checking %s classpath", which);
+ ls.print("Checking %s classpath from index [%d]", which, index_start);
ls.print_cr("%s", use_lcp_match ? " (with longest common prefix substitution)" : "");
ls.print("- expected : '");
print_dumptime_classpath(ls, index_start, index_end, use_lcp_match, _dumptime_lcp_len, runtime_lcp, runtime_lcp_len);
@@ -832,68 +833,94 @@ bool AOTClassLocationConfig::check_paths_existence(ClassLocationStream& runtime_
return exist;
}
-bool AOTClassLocationConfig::check_module_paths(bool has_aot_linked_classes, int index_start, int index_end,
- ClassLocationStream& runtime_css,
- bool* has_extra_module_paths) const {
- if (index_start >= index_end && runtime_css.is_empty()) { // nothing to check
- return true;
- }
-
+bool AOTClassLocationConfig::check_module_paths(bool has_aot_linked_classes, bool has_full_module_graph,
+ ModulePathClassLocationStream& runtime_module_css) const {
+ const int index_start = module_path_start_index();
+ const int index_end = module_path_end_index();
ResourceMark rm;
LogTarget(Info, class, path) lt;
if (lt.is_enabled()) {
LogStream ls(lt);
- ls.print_cr("Checking module paths");
+ ls.print_cr("Checking module paths from index [%d]", index_start);
ls.print("- expected : '");
print_dumptime_classpath(ls, index_start, index_end, false, 0, nullptr, 0);
ls.print_cr("'");
ls.print("- actual : '");
- runtime_css.print(&ls);
+ runtime_module_css.print(&ls);
ls.print_cr("'");
}
- // Make sure all the dumptime module paths exist and are unchanged
+ // All JAR files in the dumptime module paths must exist and must be unchanged,
+ // or else we have a "hard" failure, as we can no longer guarantee that archived
+ // classes from the module paths remain unchanged at runtime.
for (int i = index_start; i < index_end; i++) {
const AOTClassLocation* cs = class_location_at(i);
const char* dumptime_path = cs->path();
assert(!cs->from_cpattr(), "not applicable for module path");
- log_info(class, path)("Checking '%s' %s", dumptime_path, cs->file_type_string());
+ log_info(class, path)("Checking [%d] '%s' %s", i, dumptime_path, cs->file_type_string());
if (!cs->check(dumptime_path, has_aot_linked_classes)) {
return false;
}
}
- // We allow runtime_css to be a superset of the module paths specified in dumptime. E.g.,
- // Dumptime: A:C
- // Runtime: A:B:C
- runtime_css.start();
+ if (!check_module_paths_exact_match(runtime_module_css)) {
+ if (CDSConfig::new_aot_flags_used()) {
+ // New AOT workflow requires an exact match for --module-paths.
+ return false;
+ } else {
+ // For classical CDS, we have a "soft" failure if the runtime module paths are
+ // not an exact match with the dumptime module paths:
+ // Classes from the module path will not be loaded if they are
+ // rejected by SystemDictionary::is_shared_class_visible().
+ if (has_full_module_graph) {
+ CDSConfig::disable_full_module_graph();
+ AOTMetaspace::report_loading_error("full module graph: disabled because extra module path(s) are specified");
+ }
+
+ if (CDSConfig::is_dumping_dynamic_archive() && num_module_paths() > 0) {
+ CDSConfig::disable_dumping_dynamic_archive();
+ aot_log_warning(aot)("Dynamic archiving is disabled because base layer archive has a different module path");
+ }
+ }
+ }
+
+ return true;
+}
+
+bool AOTClassLocationConfig::check_module_paths_exact_match(ModulePathClassLocationStream& runtime_module_css) const {
+ if (runtime_module_css.has_non_jar_modules()) {
+ AOTMetaspace::report_loading_error("module path contains sub-directories or non-JAR files (incompatible with full module graph)");
+ return false;
+ }
+
+ const int index_start = module_path_start_index();
+ const int index_end = module_path_end_index();
+
+ runtime_module_css.start();
for (int i = index_start; i < index_end; i++) {
const AOTClassLocation* cs = class_location_at(i);
const char* dumptime_path = cs->path();
- while (true) {
- if (!runtime_css.has_next()) {
- aot_log_warning(aot)("module path has fewer elements than expected");
- *has_extra_module_paths = true;
- return true;
- }
- // Both this->class_locations() and runtime_css are alphabetically sorted. Skip
- // items in runtime_css until we see dumptime_path.
- const char* runtime_path = runtime_css.get_next();
- if (!os::same_files(dumptime_path, runtime_path)) {
- *has_extra_module_paths = true;
- return true;
- } else {
- break;
- }
+ if (!runtime_module_css.has_next()) {
+ AOTMetaspace::report_loading_error("module path has fewer elements (%d) than expected (%d)", runtime_module_css.size(), num_module_paths());
+ return false;
+ }
+ const char* runtime_path = runtime_module_css.get_next();
+ // Both dumptime and runtime module paths are alphabetically sorted, so we just need to
+ // compare each element at the same position.
+ if (!os::same_files(dumptime_path, runtime_path)) {
+ AOTMetaspace::report_loading_error("module path at [%d] is different: expected %s actual %s",
+ i, dumptime_path, runtime_path);
+ return false;
}
}
- if (runtime_css.has_next()) {
- *has_extra_module_paths = true;
+ if (runtime_module_css.has_next()) {
+ AOTMetaspace::report_loading_error("module path has more elements (%d) than expected (%d)", runtime_module_css.size(), num_module_paths());
+ return false;
}
return true;
@@ -967,12 +994,13 @@ bool AOTClassLocationConfig::need_lcp_match_helper(int start, int end, ClassLoca
return true;
}
-bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_linked_classes, bool* has_extra_module_paths) const {
+bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_linked_classes, bool has_full_module_graph) const {
ResourceMark rm;
AllClassLocationStreams all_css;
log_locations(cache_filename, /*is_write=*/false);
+ // (1) Check JRT modules image
const char* jrt = ClassLoader::get_jrt_entry()->name();
log_info(class, path)("Checking [0] (modules image)");
bool success = class_location_at(0)->check(jrt, has_aot_linked_classes);
@@ -980,13 +1008,9 @@ bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_l
if (!success) {
return false;
}
- if (class_locations()->length() == 1) {
- if ((module_path_start_index() >= module_path_end_index()) && Arguments::get_property("jdk.module.path") != nullptr) {
- *has_extra_module_paths = true;
- } else {
- *has_extra_module_paths = false;
- }
- } else {
+
+ {
+ // (2) Check boot/app classpath
bool use_lcp_match = need_lcp_match(all_css);
const char* runtime_lcp;
size_t runtime_lcp_len;
@@ -1011,11 +1035,10 @@ bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_l
log_info(class, path)("Archived app classpath validation: %s", success ? "passed" : "failed");
}
+ // (3) Check module paths
if (success) {
- success = check_module_paths(has_aot_linked_classes, module_path_start_index(), module_path_end_index(),
- all_css.module_path(), has_extra_module_paths);
- log_info(class, path)("Archived module path validation: %s%s", success ? "passed" : "failed",
- (*has_extra_module_paths) ? " (extra module paths found)" : "");
+ success = check_module_paths(has_aot_linked_classes, has_full_module_graph, all_css.module_path());
+ log_info(class, path)("Archived module path validation: %s", success ? "passed" : "failed");
}
if (runtime_lcp_len > 0) {
@@ -1033,7 +1056,7 @@ bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_l
if (CDSConfig::is_dumping_final_static_archive()) {
aot_log_error(aot)("class path and/or module path are not compatible with the "
"ones specified when the AOTConfiguration file was recorded%s", hint_msg);
- vm_exit_during_initialization("Unable to use create AOT cache.", nullptr);
+ AOTMetaspace::unrecoverable_writing_error("Unable to use create AOT cache.");
} else {
aot_log_error(aot)("%s%s", mismatch_msg, hint_msg);
AOTMetaspace::unrecoverable_loading_error();
diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp
index 89a5e6bc9394..bdf50535c9ea 100644
--- a/src/hotspot/share/cds/aotClassLocation.hpp
+++ b/src/hotspot/share/cds/aotClassLocation.hpp
@@ -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
@@ -36,6 +36,7 @@
class AllClassLocationStreams;
class ClassLocationStream;
class ClassPathZipEntry;
+class ModulePathClassLocationStream;
class LogStream;
// An AOTClassLocation is a location where the application is configured to load Java classes
@@ -164,8 +165,8 @@ class AOTClassLocationConfig : public CHeapObj {
bool check_classpaths(bool is_boot_classpath, bool has_aot_linked_classes,
int index_start, int index_end, ClassLocationStream& runtime_css,
bool use_lcp_match, const char* runtime_lcp, size_t runtime_lcp_len) const;
- bool check_module_paths(bool has_aot_linked_classes, int index_start, int index_end, ClassLocationStream& runtime_css,
- bool* has_extra_module_paths) const;
+ bool check_module_paths(bool has_aot_linked_classes, bool has_full_module_graph, ModulePathClassLocationStream& runtime_module_css) const;
+ bool check_module_paths_exact_match(ModulePathClassLocationStream& runtime_module_css) const;
bool file_exists(const char* filename) const;
bool check_paths_existence(ClassLocationStream& runtime_css) const;
@@ -270,7 +271,7 @@ class AOTClassLocationConfig : public CHeapObj {
AOTClassLocationConfig* write_to_archive() const;
// Functions used only during runtime
- bool validate(const char* cache_filename, bool has_aot_linked_classes, bool* has_extra_module_paths) const;
+ bool validate(const char* cache_filename, bool has_aot_linked_classes, bool has_full_module_graph) const;
bool is_valid_classpath_index(int classpath_index, InstanceKlass* ik);
diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp
index 31840f04c38a..ca0fd2f4e9e9 100644
--- a/src/hotspot/share/cds/aotMetaspace.cpp
+++ b/src/hotspot/share/cds/aotMetaspace.cpp
@@ -1466,6 +1466,7 @@ bool AOTMetaspace::in_aot_cache_static_region(void* p) {
// - There's an error that indicates that the archive(s) files were corrupt or otherwise damaged.
// - When -XX:+RequireSharedSpaces is specified, AND the JVM cannot load the archive(s) due
// to version or classpath mismatch.
+[[noreturn]]
void AOTMetaspace::unrecoverable_loading_error(const char* message) {
report_loading_error("%s", message);
@@ -1476,6 +1477,7 @@ void AOTMetaspace::unrecoverable_loading_error(const char* message) {
} else {
vm_exit_during_initialization("Unable to use shared archive. Unrecoverable archive loading error (run with -Xlog:aot,cds for details)", message);
}
+ ShouldNotReachHere();
}
void AOTMetaspace::report_loading_error(const char* format, ...) {
@@ -1511,15 +1513,17 @@ void AOTMetaspace::report_loading_error(const char* format, ...) {
// This function is called when the JVM is unable to write the specified CDS archive due to an
// unrecoverable error.
+[[noreturn]]
void AOTMetaspace::unrecoverable_writing_error(const char* message) {
writing_error(message);
vm_direct_exit(1);
+ ShouldNotReachHere();
}
// This function is called when the JVM is unable to write the specified CDS archive due to a
// an error. The error will be propagated
void AOTMetaspace::writing_error(const char* message) {
- aot_log_error(aot)("An error has occurred while writing the shared archive file.");
+ aot_log_error(aot)("An error has occurred while writing the %s.", CDSConfig::type_of_archive_being_written());
if (message != nullptr) {
aot_log_error(aot)("%s", message);
}
diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp
index 163fff73f8f7..9a2151f73083 100644
--- a/src/hotspot/share/cds/aotMetaspace.hpp
+++ b/src/hotspot/share/cds/aotMetaspace.hpp
@@ -119,9 +119,9 @@ class AOTMetaspace : AllStatic {
static bool preimage_static_archive_dumped() NOT_CDS_RETURN_(false);
- static void unrecoverable_loading_error(const char* message = "unrecoverable error");
+ [[noreturn]] static void unrecoverable_loading_error(const char* message = "unrecoverable error");
static void report_loading_error(const char* format, ...) ATTRIBUTE_PRINTF(1, 0);
- static void unrecoverable_writing_error(const char* message = nullptr);
+ [[noreturn]] static void unrecoverable_writing_error(const char* message = nullptr);
static void writing_error(const char* message = nullptr);
static void make_method_handle_intrinsics_shareable() NOT_CDS_RETURN;
diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp
index 81c49d6fc8a8..a7320dc23cfe 100644
--- a/src/hotspot/share/cds/filemap.cpp
+++ b/src/hotspot/share/cds/filemap.cpp
@@ -405,8 +405,8 @@ bool FileMapInfo::validate_class_location() {
assert(CDSConfig::is_using_archive(), "runtime only");
AOTClassLocationConfig* config = header()->class_location_config();
- bool has_extra_module_paths = false;
- if (!config->validate(full_path(), header()->has_aot_linked_classes(), &has_extra_module_paths)) {
+
+ if (!config->validate(full_path(), header()->has_aot_linked_classes(), header()->has_full_module_graph())) {
if (PrintSharedArchiveAndExit) {
AOTMetaspace::set_archive_loading_failed();
return true;
@@ -415,11 +415,6 @@ bool FileMapInfo::validate_class_location() {
}
}
- if (header()->has_full_module_graph() && has_extra_module_paths) {
- CDSConfig::disable_full_module_graph();
- AOTMetaspace::report_loading_error("full module graph: disabled because extra module path(s) are specified");
- }
-
if (CDSConfig::is_dumping_dynamic_archive()) {
// Only support dynamic dumping with the usage of the default CDS archive
// or a simple base archive.
@@ -430,13 +425,6 @@ bool FileMapInfo::validate_class_location() {
aot_log_warning(aot)(
"Dynamic archiving is disabled because base layer archive has appended boot classpath");
}
- if (config->num_module_paths() > 0) {
- if (has_extra_module_paths) {
- CDSConfig::disable_dumping_dynamic_archive();
- aot_log_warning(aot)(
- "Dynamic archiving is disabled because base layer archive has a different module path");
- }
- }
}
#if INCLUDE_JVMTI
@@ -1870,6 +1858,13 @@ bool FileMapInfo::validate_aot_class_linking() {
#endif
}
+ if (CDSConfig::is_dumping_final_static_archive() && header()->aot_class_linking_value() && !CDSConfig::is_dumping_aot_linked_classes()) {
+ ResourceMark rm;
+ const char* msg = err_msg("AOT class linking was enabled in training run but has been disabled%s",
+ (CDSConfig::is_dumping_full_module_graph() ? "" : " due to incompatible module options"));
+ AOTMetaspace::unrecoverable_writing_error(msg);
+ }
+
return true;
}
diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp
index 6892d694d63a..1e5f127815b6 100644
--- a/src/hotspot/share/classfile/modules.cpp
+++ b/src/hotspot/share/classfile/modules.cpp
@@ -587,29 +587,59 @@ Modules::ArchivedProperty& Modules::archived_prop(size_t i) {
void Modules::ArchivedProperty::runtime_check() const {
ResourceMark rm;
- const char* runtime_value = get_flattened_value();
+ const char* old_value = _archived_value;
+ const char* new_value = get_flattened_value();
aot_log_info(aot)("archived module property %s: %s", _prop,
- _archived_value != nullptr ? _archived_value : "(null)");
+ old_value != nullptr ? old_value : "(null)");
+
+ bool mismatch = false;
+ const char* old_label1;
+ const char* old_label2;
+ const char* new_label1;
+ const char* new_label2;
+
+ if (CDSConfig::is_dumping_final_static_archive()) {
+ old_label1 = "in AOTConfiguration";
+ old_label2 = ", AOTConfiguration =";
+ new_label1 = "for current JVM";
+ new_label2 = "current =";
+ } else if (CDSConfig::new_aot_flags_used()) {
+ old_label1 = "in AOTCache";
+ old_label2 = ", AOTCache =";
+ new_label1 = "for current JVM";
+ new_label2 = "current =";
+ } else {
+ old_label1 = "during dump time";
+ old_label2 = " dump time";
+ new_label1 = "during runtime";
+ new_label2 = "runtime";
+ }
- bool disable = false;
- if (runtime_value == nullptr) {
- if (_archived_value != nullptr) {
- AOTMetaspace::report_loading_error("Mismatched values for property %s: %s specified during dump time but not during runtime", _prop, _archived_value);
- disable = true;
+ if (new_value == nullptr) {
+ if (old_value != nullptr) {
+ AOTMetaspace::report_loading_error("Mismatched values for property %s: %s specified %s but not %s",
+ _prop, old_value, old_label1, new_label1);
+ mismatch = true;
}
} else {
- if (_archived_value == nullptr) {
- AOTMetaspace::report_loading_error("Mismatched values for property %s: %s specified during runtime but not during dump time", _prop, runtime_value);
- disable = true;
- } else if (strcmp(runtime_value, _archived_value) != 0) {
- AOTMetaspace::report_loading_error("Mismatched values for property %s: runtime %s dump time %s", _prop, runtime_value, _archived_value);
- disable = true;
+ if (old_value == nullptr) {
+ AOTMetaspace::report_loading_error("Mismatched values for property %s: %s specified %s but not %s",
+ _prop, new_value, new_label1, old_label1);
+ mismatch = true;
+ } else if (strcmp(new_value, old_value) != 0) {
+ AOTMetaspace::report_loading_error("Mismatched values for property %s: %s %s%s %s",
+ _prop, new_label2, new_value, old_label2, old_value);
+ mismatch = true;
}
}
- if (disable) {
+ if (mismatch) {
AOTMetaspace::report_loading_error("Disabling full module graph");
CDSConfig::disable_full_module_graph();
+
+ if (CDSConfig::is_dumping_final_static_archive()) {
+ AOTMetaspace::unrecoverable_writing_error("mismatched module options");
+ }
}
}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/InvalidModuleOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/InvalidModuleOptions.java
new file mode 100644
index 000000000000..9e7e77a40c65
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/InvalidModuleOptions.java
@@ -0,0 +1,176 @@
+/*
+ * 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 Disallow different module options between AOT training and assembly
+ * @bug 8388525
+ * @requires vm.cds
+ * @requires vm.flagless
+ * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
+ * @build Hello
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello
+ * @run driver InvalidModuleOptions
+ */
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import jdk.test.lib.cds.CDSModulePackager;
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.helpers.ClassFileInstaller;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.StringArrayUtils;
+
+public class InvalidModuleOptions {
+ static final Path modulesSrc = Paths.get(System.getProperty("test.src")).resolve("modules");
+ static final Path modulesSrc2 = Paths.get(System.getProperty("test.src")).resolve("modules2");
+ static final Path modulesSrc3 = Paths.get(System.getProperty("test.src")).resolve("modules3");
+ static String appJar = ClassFileInstaller.getJarPath("hello.jar");
+ static String aotConfigFile = "hello.aotconfig";
+ static String aotCacheFile = "hello.aot";
+ static String helloClass = "Hello";
+ static String modulePath1;
+ static String modulePath2;
+ static String modulePath3;
+
+ static String[][] testCases = {
+ // --module-path
+ {
+ "", null,
+ "module path has fewer elements (0) than expected (1)",
+ },
+ {
+ "", "",
+ "module path at [2] is different",
+ },
+ {
+ "", "",
+ "module path at [3] is different",
+ },
+ {
+ null, "",
+ "module path has more elements (1) than expected (0)",
+ },
+
+ // --add-modules
+ {
+ null, "--add-modules=java.instrument",
+ "Mismatched values for property jdk.module.addmods: java.instrument specified for current JVM but not in AOTConfiguration"
+ },
+ {
+ "--add-modules=java.instrument", null,
+ "Mismatched values for property jdk.module.addmods: java.instrument specified in AOTConfiguration but not for current JVM"},
+ {
+ "--add-modules=java.instrument", "--add-modules=java.base",
+ "Mismatched values for property jdk.module.addmods: current = java.base, AOTConfiguration = java.instrument"
+ },
+
+ // --enable-native-access
+ // Just test one variation, as HotSpot handles this the same way as --add-modules.
+ {
+ null, "--enable-native-access=ALL_UNNAMED",
+ "Mismatched values for property jdk.module.enable.native.access: ALL_UNNAMED specified for current JVM but not in AOTConfiguration"
+ },
+
+ // -Djdk.module.showModuleResolution=true also disabled full module graph:
+ {
+ null, "-Djdk.module.showModuleResolution=true",
+ "full module graph: disabled due to incompatible property: jdk.module.showModuleResolution=true",
+ "AOT class linking was enabled in training run but has been disabled due to incompatible module options",
+ },
+ };
+
+ public static void main(String[] args) throws Exception {
+ CDSModulePackager modulePackager1 = new CDSModulePackager(modulesSrc, Paths.get("test-modules1"));
+ modulePackager1.createModularJar("com.test");
+ modulePath1 = modulePackager1.getOutputDir().toString();
+
+ CDSModulePackager modulePackager2 = new CDSModulePackager(modulesSrc2, Paths.get("test-modules2"));
+ modulePackager2.createModularJar("com.moretest");
+ modulePath2 = modulePackager2.getOutputDir().toString();
+
+ CDSModulePackager modulePackager3 = new CDSModulePackager(modulesSrc3, Paths.get("test-modules3"));
+ modulePackager3.createModularJar("com.evenmoretest");
+ modulePath3 = modulePackager3.getOutputDir().toString();
+
+ for (int i = 0; i < testCases.length; i++) {
+ String[] testSpec = testCases[i];
+ String trainOpt = testSpec[0];
+ String assemblyOpt = testSpec[1];
+
+ String[] trainCmds = new String[] {
+ "-XX:AOTMode=record",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-Xlog:aot=debug",
+ "-cp", appJar
+ };
+ if (trainOpt != null) {
+ trainCmds = concat(trainCmds, trainOpt);
+ }
+ trainCmds = StringArrayUtils.concat(trainCmds, helloClass);
+
+ String[] assemblyCmds = new String[] {
+ "-XX:AOTMode=create",
+ "-XX:AOTConfiguration=" + aotConfigFile,
+ "-XX:AOTCache=" + aotCacheFile,
+ "-Xlog:cds,aot,class+path",
+ "-cp", appJar
+ };
+ if (assemblyOpt != null) {
+ assemblyCmds = concat(assemblyCmds, assemblyOpt);
+ }
+
+ ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(trainCmds);
+ OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "train" + i);
+ out.shouldContain("Hello World");
+ out.shouldContain("AOTConfiguration recorded: " + aotConfigFile);
+ out.shouldHaveExitValue(0);
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(assemblyCmds);
+ out = CDSTestUtils.executeAndLog(pb, "asm" + i);
+ out.shouldContain("An error has occurred while writing the AOT cache");
+ for (int j = 2; j < testSpec.length; j++) {
+ out.shouldContain(testSpec[j]);
+ }
+ out.shouldNotContain("AOTCache creation is complete");
+ out.shouldNotHaveExitValue(0);
+ }
+ }
+
+ static String[] concat(String[] opts, String extra) {
+ if (extra.equals("")) {
+ return StringArrayUtils.concat(opts, "--module-path", modulePath1);
+ } else if (extra.equals("")) {
+ return StringArrayUtils.concat(opts, "--module-path", modulePath2);
+ } else if (extra.equals("")) {
+ return StringArrayUtils.concat(opts, "--module-path", modulePath1 + File.pathSeparator + modulePath2);
+ } else if (extra.equals("")) {
+ return StringArrayUtils.concat(opts, "--module-path", modulePath1 + File.pathSeparator + modulePath3);
+ } else {
+ return StringArrayUtils.concat(opts, extra);
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java
index b0db450e3525..55eab06c33f1 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldClassSupport2.java
@@ -48,11 +48,6 @@ public static void main(String[] args) throws Exception {
// Explicitly disable
Tester tester1 = new Tester("-XX:-AOTClassLinking");
tester1.run(new String[] {"AOT", "--two-step-training"} );
-
- // Full module graph caching is disabled with -Djdk.module.showModuleResolution=true.
- // This will disable AOT class linking.
- Tester tester2 = new Tester("-Djdk.module.showModuleResolution=true");
- tester2.run(new String[] {"AOT", "--two-step-training"} );
}
static class Tester extends CDSAppTester {
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/com/moretest/Bar.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/com/moretest/Bar.java
new file mode 100644
index 000000000000..6d95229f96f8
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/com/moretest/Bar.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.moretest;
+
+public class Bar {}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/module-info.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/module-info.java
new file mode 100644
index 000000000000..7fd2c23a27ac
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules2/com.moretest/module-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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
+ * 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.
+ *
+ */
+
+module com.moretest {
+ exports com.moretest;
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/com/evenmoretest/Baz.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/com/evenmoretest/Baz.java
new file mode 100644
index 000000000000..9f077be2ce4a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/com/evenmoretest/Baz.java
@@ -0,0 +1,27 @@
+/*
+ * 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 com.evenmoretest;
+
+public class Baz {}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/module-info.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/module-info.java
new file mode 100644
index 000000000000..4aaf8811fe4e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/modules3/com.evenmoretest/module-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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
+ * 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.
+ *
+ */
+
+module com.evenmoretest {
+ exports com.evenmoretest;
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java
index 95fc9f0f7000..347e61fed4ca 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java
@@ -162,7 +162,11 @@ public String modulepath(RunMode runMode) {
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
if (runMode == RunMode.PRODUCTION) {
out.shouldContain("full module graph: disabled");
- out.shouldContain("Mismatched values for property jdk.module.addreads: runtime com.norequires=ALL-UNNAMED dump time com.norequires=org.astro");
+ if (isStaticWorkflow()) {
+ out.shouldContain("Mismatched values for property jdk.module.addreads: runtime com.norequires=ALL-UNNAMED dump time com.norequires=org.astro");
+ } else {
+ out.shouldContain("Mismatched values for property jdk.module.addreads: current = com.norequires=ALL-UNNAMED, AOTCache = com.norequires=org.astro");
+ }
} else if (runMode == RunMode.ASSEMBLY) {
out.shouldMatch("(full module graph: enabled)|(Full module graph = enabled)");
} else {
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java
index 41fa1a6493c8..120a7315c7a2 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java
@@ -84,6 +84,7 @@ public class ModulePathAndFMG {
private static String TEST_FROM_JAR = "class,load.*com.foos.Test.*[.]jar";
private static String TEST_FROM_CDS = "class,load.*com.foos.Test.*shared objects file";
private static String MAP_FAILED = "Unable to use shared archive";
+ private static String NON_JAR_FILES = "module path contains sub-directories or non-JAR files";
private static String PATH_SEPARATOR = File.pathSeparator;
private static String appClasses[] = {MAIN_CLASS, TEST_CLASS};
private static String prefix[] = {"-Djava.class.path=", "-Xlog:cds,class+load,class+path=info"};
@@ -116,9 +117,6 @@ public static void buildTestModule() throws Exception {
dupDir = Files.createTempDirectory(USER_DIR, DUP_LIBS);
dupJar = dupDir.resolve(DUP_MODULE + ".jar");
Files.copy(testJar, dupJar, StandardCopyOption.REPLACE_EXISTING);
-
- badJar = libsDir.resolve(MAIN_MODULE + ".JAR");
- Files.copy(mainJar, badJar, StandardCopyOption.REPLACE_EXISTING);
}
public static void buildJmod() throws Exception {
@@ -136,6 +134,9 @@ public static void buildJmod() throws Exception {
public static void main(String... args) throws Exception {
runWithModulePath();
runWithExplodedModule();
+
+ badJar = libsDir.resolve(MAIN_MODULE + ".JAR");
+ Files.copy(mainJar, badJar, StandardCopyOption.REPLACE_EXISTING);
runWithJmodAndBadJar();
}
@@ -329,18 +330,22 @@ public static void runWithJmodAndBadJar() throws Exception {
.assertAbnormalExit(out -> {
out.shouldContain(FMG_DISABLED)
.shouldNotContain(FMG_ENABLED)
+ .shouldContain(NON_JAR_FILES)
.shouldContain(FIND_EXCEPTION_MESSAGE);
});
runModulePath += PATH_SEPARATOR + testJar.toString();
+
+ // non-jar files in runtime --module is incompatible with FMG
tty("12. run with CDS on, with module path com.bars.jar:com.foos.jmod:com.foos.jar");
TestCommon.runWithModules(prefix,
null, // --upgrade-module-path
runModulePath, // --module-path
MAIN_MODULE) // -m
.assertNormalExit(out -> {
- out.shouldNotContain(FMG_DISABLED)
- .shouldContain(FMG_ENABLED)
+ out.shouldContain(FMG_DISABLED)
+ .shouldNotContain(FMG_ENABLED)
+ .shouldContain(NON_JAR_FILES)
.shouldMatch(TEST_FROM_CDS)
.shouldMatch(MAIN_FROM_CDS)
.shouldContain(CLASS_FOUND_MESSAGE);
@@ -355,6 +360,7 @@ public static void runWithJmodAndBadJar() throws Exception {
.assertAbnormalExit(out -> {
out.shouldContain(FMG_DISABLED)
.shouldNotContain(FMG_ENABLED)
+ .shouldContain(NON_JAR_FILES)
.shouldMatch(MODULE_NOT_RECOGNIZED);
});
}
From 2749a4c0b430a83ff23ede63167c7a7e39a51c6f Mon Sep 17 00:00:00 2001
From: Chuanqi Zang
Date: Sat, 15 Aug 2026 07:50:53 +0000
Subject: [PATCH 24/88] 8389677: RISC-V: Prefer vmv.v.i to zero vector
registers
Co-authored-by: Pengcheng Wang
Reviewed-by: dzhang, fyang
---
.../cpu/riscv/c2_MacroAssembler_riscv.cpp | 10 +++----
.../cpu/riscv/macroAssembler_riscv.cpp | 6 ++---
src/hotspot/cpu/riscv/riscv_v.ad | 27 ++++++++-----------
3 files changed, 19 insertions(+), 24 deletions(-)
diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
index e70840e9e52b..b4277f729aee 100644
--- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp
@@ -1837,7 +1837,7 @@ void C2_MacroAssembler::arrays_hashcode_v(Register ary, Register cnt, Register r
vsetvli(consumed, cnt, Assembler::e32, Assembler::m2);
vle32_v(v_coeffs, t1); // 31^^(stride - 1) ... 31^^0
- vmv_v_x(v_sum, x0);
+ vmv_v_i(v_sum, 0);
bind(VEC_LOOP);
arrays_hashcode_elload_v(v_src, v_tmp, ary, eltype);
@@ -2602,7 +2602,7 @@ void C2_MacroAssembler::java_round_float_v(VectorRegister dst, VectorRegister sr
// replacing vfclass with feq as performance optimization
vmfeq_vv(v0, src, src);
// set dst = 0 in cases of NaN
- vmv_v_x(dst, zr);
+ vmv_v_i(dst, 0);
// dst = (src + 0.5) rounded down towards negative infinity
vfadd_vf(dst, src, ftmp, Assembler::v0_t);
@@ -2626,7 +2626,7 @@ void C2_MacroAssembler::java_round_double_v(VectorRegister dst, VectorRegister s
// replacing vfclass with feq as performance optimization
vmfeq_vv(v0, src, src);
// set dst = 0 in cases of NaN
- vmv_v_x(dst, zr);
+ vmv_v_i(dst, 0);
// dst = (src + 0.5) rounded down towards negative infinity
vfadd_vf(dst, src, ftmp, Assembler::v0_t);
@@ -2684,7 +2684,7 @@ void C2_MacroAssembler::clear_array_v(Register base, Register cnt) {
// making zero words
vsetvli(t0, cnt, Assembler::e64, Assembler::m4);
- vxor_vv(v4, v4, v4);
+ vmv_v_i(v4, 0);
bind(loop);
vsetvli(t0, cnt, Assembler::e64, Assembler::m4);
@@ -3281,7 +3281,7 @@ void C2_MacroAssembler::integer_narrow_v(VectorRegister dst, BasicType dst_bt, u
#define VFCVT_SAFE(VFLOATCVT) \
void C2_MacroAssembler::VFLOATCVT##_safe(VectorRegister dst, VectorRegister src) { \
assert_different_registers(dst, src); \
- vxor_vv(dst, dst, dst); \
+ vmv_v_i(dst, 0); \
vmfeq_vv(v0, src, src); \
VFLOATCVT(dst, src, Assembler::v0_t); \
}
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
index b6ac430a6033..166915ba018e 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
@@ -2182,7 +2182,7 @@ void MacroAssembler::vector_update_crc32(Register crc, Register buf, Register le
vsetivli(zr, N, Assembler::e32, Assembler::m1, Assembler::mu, Assembler::tu);
}
- vmv_v_x(vcrc, zr);
+ vmv_v_i(vcrc, 0);
vmv_s_x(vcrc, crc);
// multiple of 64
@@ -2327,7 +2327,7 @@ void MacroAssembler::kernel_crc32_vclmul_fold_vectorsize_16(Register crc, Regist
vle64_v(v6, buf); addi(buf, buf, STEP);
vle64_v(v7, buf); addi(buf, buf, STEP);
- vmv_v_x(v31, zr);
+ vmv_v_i(v31, 0);
vsetivli(zr, 1, Assembler::e32, Assembler::m1, Assembler::mu, Assembler::tu);
vmv_s_x(v31, crc);
vsetivli(zr, N, Assembler::e64, Assembler::m1, Assembler::mu, Assembler::tu);
@@ -2450,7 +2450,7 @@ void MacroAssembler::kernel_crc32_vclmul_fold_vectorsize_32(Register crc, Regist
// now, v1 should contains: 010101...
// initial crc
- vmv_v_x(v24, zr);
+ vmv_v_i(v24, 0);
vsetivli(zr, 1, Assembler::e32, Assembler::m4, Assembler::mu, Assembler::tu);
vmv_s_x(v24, crc);
vsetivli(zr, N, Assembler::e64, Assembler::m4, Assembler::mu, Assembler::tu);
diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad
index 07ce0f7885a3..bf291adce59c 100644
--- a/src/hotspot/cpu/riscv/riscv_v.ad
+++ b/src/hotspot/cpu/riscv/riscv_v.ad
@@ -41,7 +41,7 @@ source %{
__ vsex_v(reg, base, sew, vm);
} else {
if (vm == Assembler::v0_t) {
- __ vxor_vv(reg, reg, reg);
+ __ vmv_v_i(reg, 0);
}
__ vlex_v(reg, base, sew, vm);
}
@@ -241,7 +241,7 @@ instruct vstoremask(vReg dst, vRegMask_V0 v0, immI size) %{
format %{ "vstoremask $dst, V0 # elem size is $size byte[s]" %}
ins_encode %{
__ vsetvli_helper(T_BOOLEAN, Matcher::vector_length(this));
- __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg));
+ __ vmv_v_i(as_VectorRegister($dst$$reg), 0);
__ vmerge_vim(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), 1);
%}
ins_pipe(pipe_slow);
@@ -4809,7 +4809,7 @@ instruct vcvtFtoL(vReg dst, vReg src, vRegMask_V0 v0) %{
format %{ "vcvtFtoL $dst, $src" %}
ins_encode %{
__ vsetvli_helper(T_LONG, Matcher::vector_length(this));
- __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg));
+ __ vmv_v_i(as_VectorRegister($dst$$reg), 0);
__ vsetvli_helper(T_FLOAT, Matcher::vector_length(this), Assembler::mf2);
__ vmfeq_vv(as_VectorRegister($v0$$reg), as_VectorRegister($src$$reg), as_VectorRegister($src$$reg));
__ vfwcvt_rtz_x_f_v(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg), Assembler::v0_t);
@@ -4842,7 +4842,7 @@ instruct vcvtDtoX_narrow(vReg dst, vReg src, vRegMask_V0 v0) %{
__ vsetvli_helper(T_DOUBLE, Matcher::vector_length(this));
__ vmfeq_vv(as_VectorRegister($v0$$reg), as_VectorRegister($src$$reg), as_VectorRegister($src$$reg));
__ vsetvli_helper(T_INT, Matcher::vector_length(this), Assembler::mf2);
- __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg));
+ __ vmv_v_i(as_VectorRegister($dst$$reg), 0);
__ vfncvt_rtz_x_f_w(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg), Assembler::v0_t);
BasicType bt = Matcher::vector_element_basic_type(this);
if (bt == T_BYTE || bt == T_SHORT) {
@@ -4903,7 +4903,7 @@ instruct reinterpretResize(vReg dst, vReg src) %{
"invalid vector length");
BasicType bt = Matcher::vector_element_basic_type(this);
__ vsetvli_helper(bt, Matcher::vector_length(this));
- __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg));
+ __ vmv_v_i(as_VectorRegister($dst$$reg), 0);
__ vsetvli_helper(T_BYTE, length_in_bytes_resize);
__ vmv_v_v(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg));
%}
@@ -4931,7 +4931,7 @@ instruct vmask_reinterpret_diff_esize(vRegMask dst, vRegMask_V0 src, vReg tmp) %
ins_encode %{
BasicType from_bt = Matcher::vector_element_basic_type(this, $src);
__ vsetvli_helper(from_bt, Matcher::vector_length(this, $src));
- __ vxor_vv(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg));
+ __ vmv_v_i(as_VectorRegister($tmp$$reg), 0);
__ vmerge_vim(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), -1);
BasicType to_bt = Matcher::vector_element_basic_type(this);
__ vsetvli_helper(to_bt, Matcher::vector_length(this));
@@ -4990,8 +4990,7 @@ instruct rearrange_masked(vReg dst, vReg src, vReg shuffle, vRegMask_V0 v0) %{
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
__ vsetvli_helper(bt, Matcher::vector_length(this));
- __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg),
- as_VectorRegister($dst$$reg));
+ __ vmv_v_i(as_VectorRegister($dst$$reg), 0);
__ vrgather_vv(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg),
as_VectorRegister($shuffle$$reg), Assembler::v0_t);
%}
@@ -5105,8 +5104,7 @@ instruct vcompress(vReg dst, vReg src, vRegMask_V0 v0) %{
ins_encode %{
BasicType bt = Matcher::vector_element_basic_type(this);
__ vsetvli_helper(bt, Matcher::vector_length(this));
- __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg),
- as_VectorRegister($dst$$reg));
+ __ vmv_v_i(as_VectorRegister($dst$$reg), 0);
__ vcompress_vm(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg),
as_VectorRegister($v0$$reg));
%}
@@ -5121,8 +5119,7 @@ instruct vexpand(vReg dst, vReg src, vRegMask_V0 v0, vReg tmp) %{
BasicType bt = Matcher::vector_element_basic_type(this);
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ viota_m(as_VectorRegister($tmp$$reg), as_VectorRegister($v0$$reg));
- __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg),
- as_VectorRegister($dst$$reg));
+ __ vmv_v_i(as_VectorRegister($dst$$reg), 0);
__ vrgather_vv(as_VectorRegister($dst$$reg), as_VectorRegister($src$$reg),
as_VectorRegister($tmp$$reg), Assembler::v0_t);
%}
@@ -5383,8 +5380,7 @@ instruct gather_loadS_masked(vReg dst, indirect mem, vReg idx, vRegMask_V0 v0, v
Assembler::SEW sew = Assembler::elemtype_to_sew(bt);
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg), (int)sew);
- __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg),
- as_VectorRegister($dst$$reg));
+ __ vmv_v_i(as_VectorRegister($dst$$reg), 0);
__ vluxei32_v(as_VectorRegister($dst$$reg), as_Register($mem$$base),
as_VectorRegister($tmp$$reg), Assembler::v0_t);
%}
@@ -5402,8 +5398,7 @@ instruct gather_loadD_masked(vReg dst, indirect mem, vReg idx, vRegMask_V0 v0, v
__ vsetvli_helper(bt, Matcher::vector_length(this));
__ vzext_vf2(as_VectorRegister($tmp$$reg), as_VectorRegister($idx$$reg));
__ vsll_vi(as_VectorRegister($tmp$$reg), as_VectorRegister($tmp$$reg), (int)sew);
- __ vxor_vv(as_VectorRegister($dst$$reg), as_VectorRegister($dst$$reg),
- as_VectorRegister($dst$$reg));
+ __ vmv_v_i(as_VectorRegister($dst$$reg), 0);
__ vluxei64_v(as_VectorRegister($dst$$reg), as_Register($mem$$base),
as_VectorRegister($tmp$$reg), Assembler::v0_t);
%}
From 3d47518697e794bf4afb577f16afb278fbf3d30a Mon Sep 17 00:00:00 2001
From: Prasanta Sadhukhan
Date: Sat, 15 Aug 2026 08:16:46 +0000
Subject: [PATCH 25/88] 8387267: Editor for the last column in JTable is hard
to activate after AUTO_RESIZE_LAST_COLUMN was configured 8388467: Test
"api/javax_swing/interactive/JTableTests.html" JTable freezes during rapid
column resize
Reviewed-by: angorya, kizune, jdv
---
.../share/classes/javax/swing/JTable.java | 70 ++++++++++----
.../JTable/TestAutoResizeLastColumn.java | 92 +++++++++++++++++++
2 files changed, 142 insertions(+), 20 deletions(-)
create mode 100644 test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java
diff --git a/src/java.desktop/share/classes/javax/swing/JTable.java b/src/java.desktop/share/classes/javax/swing/JTable.java
index fa8110d1517e..a13059f93b42 100644
--- a/src/java.desktop/share/classes/javax/swing/JTable.java
+++ b/src/java.desktop/share/classes/javax/swing/JTable.java
@@ -453,6 +453,14 @@ public enum PrintMode {
* needed.
*/
private boolean columnSelectionAdjusting;
+
+ /*
+ * True after column widths have been initialized/synchronized by layout.
+ * Used to distinguish the first preferred-width layout from later normal
+ * AUTO_RESIZE_LAST_COLUMN layouts.
+ */
+ private boolean columnWidthsInitialized;
+
/**
* The last value of getValueIsAdjusting from the row selection models
* valueChanged notification. Used to test if a repaint is needed.
@@ -1264,12 +1272,6 @@ public void setAutoResizeMode(int mode) {
autoResizeMode = mode;
resizeAndRepaint();
if (tableHeader != null) {
- if (mode == JTable.AUTO_RESIZE_LAST_COLUMN) {
- int colCnt = columnModel.getColumnCount();
- if (colCnt > 0) {
- tableHeader.setResizingColumn(columnModel.getColumn(colCnt - 1));
- }
- }
tableHeader.resizeAndRepaint();
}
firePropertyChange("autoResizeMode", old, autoResizeMode);
@@ -3193,21 +3195,8 @@ private int viewIndexForColumn(TableColumn aColumn) {
*/
public void doLayout() {
- boolean prefWidthSet = false;
TableColumn resizingColumn = getResizingColumn();
- // doLayout is called for both pack and show
- // so if initial preferred width is set by user then
- // it needs to be honoured even if resizingColumn
- // is set to last column on account of
- // AUTO_RESIZE_LAST_COLUMN autoResizeMode
- for (int i = 0; i < columnModel.getColumnCount(); i++) {
- if (columnModel.getColumn(i).getPreferredWidth() != 75
- && columnModel.getColumn(i).getWidth() == 75) {
- prefWidthSet = true;
- break;
- }
- }
- if (resizingColumn == null || prefWidthSet) {
+ if (resizingColumn == null) {
setWidthsFromPreferredWidths(false);
}
else {
@@ -3294,7 +3283,42 @@ public void sizeColumnsToFit(int resizingColumn) {
}
}
+ private void accommodateLastColumnOnly() {
+ int columnCount = getColumnCount();
+ if (columnCount == 0) {
+ return;
+ }
+
+ int delta = getWidth() - getColumnModel().getTotalColumnWidth();
+ if (delta != 0) {
+ accommodateDelta(columnCount - 1, delta);
+ }
+ }
+
+ private void setWidthsFromPreferredWidthsLastColumnOnly() {
+ int columnCount = getColumnCount();
+ if (columnCount == 0) {
+ return;
+ }
+
+ for (int i = 0; i < columnCount - 1; i++) {
+ TableColumn column = columnModel.getColumn(i);
+ column.setWidth(column.getPreferredWidth());
+ }
+
+ accommodateLastColumnOnly();
+ }
+
private void setWidthsFromPreferredWidths(final boolean inverse) {
+ if (!inverse && autoResizeMode == AUTO_RESIZE_LAST_COLUMN) {
+ if (!columnWidthsInitialized) {
+ setWidthsFromPreferredWidthsLastColumnOnly();
+ } else {
+ accommodateLastColumnOnly();
+ }
+ columnWidthsInitialized = true;
+ return;
+ }
int totalWidth = getWidth();
int totalPreferred = getPreferredSize().width;
int target = !inverse ? totalWidth : totalPreferred;
@@ -3323,6 +3347,7 @@ public void setSizeAt(int s, int i) {
};
adjustSizes(target, r, inverse);
+ columnWidthsInitialized = true;
}
@@ -3821,6 +3846,7 @@ public void setColumnModel(final TableColumnModel columnModel) {
if (columnModel == null) {
throw new IllegalArgumentException("Cannot set a null ColumnModel");
}
+ columnWidthsInitialized = false;
TableColumnModel old = this.columnModel;
if (columnModel != old) {
if (old != null) {
@@ -4634,6 +4660,10 @@ private void tableRowsDeleted(TableModelEvent e) {
* @see TableColumnModelListener
*/
public void columnAdded(TableColumnModelEvent e) {
+ if (columnWidthsInitialized) {
+ TableColumn column = columnModel.getColumn(e.getToIndex());
+ column.setWidth(column.getPreferredWidth());
+ }
// If I'm currently editing, then I should stop editing
if (isEditing()) {
removeEditor();
diff --git a/test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java b/test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java
new file mode 100644
index 000000000000..2cb1b9ae32e8
--- /dev/null
+++ b/test/jdk/javax/swing/JTable/TestAutoResizeLastColumn.java
@@ -0,0 +1,92 @@
+/*
+ * 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 8234071 8387267
+ * @summary AUTO_RESIZE_LAST_COLUMN should resize only the last column during table layout
+ * @run main TestAutoResizeLastColumn
+ */
+
+import javax.swing.JTable;
+import javax.swing.SwingUtilities;
+import javax.swing.table.TableColumnModel;
+
+public class TestAutoResizeLastColumn {
+ public static void main(String[] args) throws Exception {
+ SwingUtilities.invokeAndWait(TestAutoResizeLastColumn::testLastColumnOnly);
+ }
+
+ private static void testLastColumnOnly() {
+ JTable table = new JTable(3, 3);
+ table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
+
+ if (table.getTableHeader().getResizingColumn() != null) {
+ throw new RuntimeException(
+ "AUTO_RESIZE_LAST_COLUMN must not set resizingColumn");
+ }
+
+ TableColumnModel cm = table.getColumnModel();
+ for (int i = 0; i < cm.getColumnCount(); i++) {
+ cm.getColumn(i).setMinWidth(10);
+ cm.getColumn(i).setPreferredWidth(100);
+ cm.getColumn(i).setWidth(100);
+ }
+
+ table.setSize(300, 100);
+ table.doLayout();
+
+ assertWidth(cm, 0, 100);
+ assertWidth(cm, 1, 100);
+ assertWidth(cm, 2, 100);
+
+ table.setSize(360, 100);
+ table.doLayout();
+
+ /*
+ * AUTO_RESIZE_LAST_COLUMN means the +60 delta is absorbed by
+ * the last column only.
+ * Without fix all columns width will change to 120.
+ */
+ assertWidth(cm, 0, 100);
+ assertWidth(cm, 1, 100);
+ assertWidth(cm, 2, 160);
+
+ table.setSize(330, 100);
+ table.doLayout();
+
+ assertWidth(cm, 0, 100);
+ assertWidth(cm, 1, 100);
+ assertWidth(cm, 2, 130);
+ }
+
+ private static void assertWidth(TableColumnModel cm, int column, int expected) {
+ int actual = cm.getColumn(column).getWidth();
+ if (actual != expected) {
+ throw new RuntimeException(
+ "Unexpected width for column " + column
+ + ": expected " + expected
+ + ", actual " + actual);
+ }
+ }
+}
From 88521b3587dab95b37578cd943dab2074a0f608e Mon Sep 17 00:00:00 2001
From: Sergey Bylokhov
Date: Mon, 17 Aug 2026 00:54:50 +0000
Subject: [PATCH 26/88] 8390249: Add missing @Override annotations in
"javax.imageio.stream" package
Reviewed-by: psadhukhan, azvegint
---
.../stream/FileCacheImageInputStream.java | 9 +++-
.../stream/FileCacheImageOutputStream.java | 13 ++++-
.../imageio/stream/FileImageInputStream.java | 7 ++-
.../imageio/stream/FileImageOutputStream.java | 9 +++-
.../imageio/stream/ImageInputStream.java | 18 ++++++-
.../imageio/stream/ImageInputStreamImpl.java | 47 ++++++++++++++++++-
.../imageio/stream/ImageOutputStream.java | 17 ++++++-
.../imageio/stream/ImageOutputStreamImpl.java | 24 +++++++++-
.../stream/MemoryCacheImageInputStream.java | 9 +++-
.../stream/MemoryCacheImageOutputStream.java | 12 ++++-
10 files changed, 155 insertions(+), 10 deletions(-)
diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java
index de5f705faccf..adbbb2c0a77b 100644
--- a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java
+++ b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageInputStream.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -150,6 +150,7 @@ private long readUntil(long pos) throws IOException {
return pos;
}
+ @Override
public int read() throws IOException {
checkClosed();
bitOffset = 0;
@@ -163,6 +164,7 @@ public int read() throws IOException {
}
}
+ @Override
public int read(byte[] b, int off, int len) throws IOException {
checkClosed();
@@ -205,6 +207,7 @@ public int read(byte[] b, int off, int len) throws IOException {
* @see #isCachedMemory
* @see #isCachedFile
*/
+ @Override
public boolean isCached() {
return true;
}
@@ -218,6 +221,7 @@ public boolean isCached() {
* @see #isCached
* @see #isCachedMemory
*/
+ @Override
public boolean isCachedFile() {
return true;
}
@@ -232,6 +236,7 @@ public boolean isCachedFile() {
* @see #isCached
* @see #isCachedFile
*/
+ @Override
public boolean isCachedMemory() {
return false;
}
@@ -243,6 +248,7 @@ public boolean isCachedMemory() {
*
* @throws IOException if an error occurs.
*/
+ @Override
public void close() throws IOException {
super.close();
disposerRecord.dispose(); // this will close/delete the cache file
@@ -261,6 +267,7 @@ public StreamDisposerRecord(File cacheFile, RandomAccessFile cache) {
this.cache = cache;
}
+ @Override
public synchronized void dispose() {
if (cache != null) {
try {
diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java
index 8c80d3488e1f..a0d330c85499 100644
--- a/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java
+++ b/src/java.desktop/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -108,6 +108,7 @@ public FileCacheImageOutputStream(OutputStream stream, File cacheDir)
StreamCloser.addToQueue(closeAction);
}
+ @Override
public int read() throws IOException {
checkClosed();
bitOffset = 0;
@@ -118,6 +119,7 @@ public int read() throws IOException {
return val;
}
+ @Override
public int read(byte[] b, int off, int len) throws IOException {
checkClosed();
@@ -142,6 +144,7 @@ public int read(byte[] b, int off, int len) throws IOException {
return nbytes;
}
+ @Override
public void write(int b) throws IOException {
flushBits(); // this will call checkClosed() for us
cache.write(b);
@@ -149,6 +152,7 @@ public void write(int b) throws IOException {
maxStreamPos = Math.max(maxStreamPos, streamPos);
}
+ @Override
public void write(byte[] b, int off, int len) throws IOException {
flushBits(); // this will call checkClosed() for us
cache.write(b, off, len);
@@ -156,6 +160,7 @@ public void write(byte[] b, int off, int len) throws IOException {
maxStreamPos = Math.max(maxStreamPos, streamPos);
}
+ @Override
public long length() {
try {
checkClosed();
@@ -176,6 +181,7 @@ public long length() {
* than the flushed position.
* @throws IOException if any other I/O error occurs.
*/
+ @Override
public void seek(long pos) throws IOException {
checkClosed();
@@ -199,6 +205,7 @@ public void seek(long pos) throws IOException {
* @see #isCachedMemory
* @see #isCachedFile
*/
+ @Override
public boolean isCached() {
return true;
}
@@ -212,6 +219,7 @@ public boolean isCached() {
* @see #isCached
* @see #isCachedMemory
*/
+ @Override
public boolean isCachedFile() {
return true;
}
@@ -226,6 +234,7 @@ public boolean isCachedFile() {
* @see #isCached
* @see #isCachedFile
*/
+ @Override
public boolean isCachedMemory() {
return false;
}
@@ -238,6 +247,7 @@ public boolean isCachedMemory() {
*
* @throws IOException if an error occurs.
*/
+ @Override
public void close() throws IOException {
maxStreamPos = cache.length();
@@ -257,6 +267,7 @@ public void close() throws IOException {
* @param pos {@inheritDoc ImageOutputStream}
* @throws IOException {@inheritDoc ImageOutputStream}
*/
+ @Override
public void flushBefore(long pos) throws IOException {
long oFlushedPos = flushedPos;
super.flushBefore(pos); // this will call checkClosed() for us
diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java
index e5baee6e7c09..346ad7d09b78 100644
--- a/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java
+++ b/src/java.desktop/share/classes/javax/imageio/stream/FileImageInputStream.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -97,6 +97,7 @@ public FileImageInputStream(RandomAccessFile raf) {
Disposer.addRecord(disposerReferent, disposerRecord);
}
+ @Override
public int read() throws IOException {
checkClosed();
bitOffset = 0;
@@ -107,6 +108,7 @@ public int read() throws IOException {
return val;
}
+ @Override
public int read(byte[] b, int off, int len) throws IOException {
checkClosed();
bitOffset = 0;
@@ -124,6 +126,7 @@ public int read(byte[] b, int off, int len) throws IOException {
* @return the file length as a {@code long}, or
* {@code -1}.
*/
+ @Override
public long length() {
try {
checkClosed();
@@ -133,6 +136,7 @@ public long length() {
}
}
+ @Override
public void seek(long pos) throws IOException {
checkClosed();
if (pos < flushedPos) {
@@ -143,6 +147,7 @@ public void seek(long pos) throws IOException {
streamPos = raf.getFilePointer();
}
+ @Override
public void close() throws IOException {
super.close();
disposerRecord.dispose(); // this closes the RandomAccessFile
diff --git a/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java
index 8a22afd5b122..990f3e681448 100644
--- a/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java
+++ b/src/java.desktop/share/classes/javax/imageio/stream/FileImageOutputStream.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -89,6 +89,7 @@ public FileImageOutputStream(RandomAccessFile raf) {
Disposer.addRecord(disposerReferent, disposerRecord);
}
+ @Override
public int read() throws IOException {
checkClosed();
bitOffset = 0;
@@ -99,6 +100,7 @@ public int read() throws IOException {
return val;
}
+ @Override
public int read(byte[] b, int off, int len) throws IOException {
checkClosed();
bitOffset = 0;
@@ -109,18 +111,21 @@ public int read(byte[] b, int off, int len) throws IOException {
return nbytes;
}
+ @Override
public void write(int b) throws IOException {
flushBits(); // this will call checkClosed() for us
raf.write(b);
++streamPos;
}
+ @Override
public void write(byte[] b, int off, int len) throws IOException {
flushBits(); // this will call checkClosed() for us
raf.write(b, off, len);
streamPos += len;
}
+ @Override
public long length() {
try {
checkClosed();
@@ -141,6 +146,7 @@ public long length() {
* than the flushed position.
* @throws IOException if any other I/O error occurs.
*/
+ @Override
public void seek(long pos) throws IOException {
checkClosed();
if (pos < flushedPos) {
@@ -151,6 +157,7 @@ public void seek(long pos) throws IOException {
streamPos = raf.getFilePointer();
}
+ @Override
public void close() throws IOException {
super.close();
disposerRecord.dispose(); // this closes the RandomAccessFile
diff --git a/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStream.java
index 11251ac5938a..e648c5c00b49 100644
--- a/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStream.java
+++ b/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -186,6 +186,7 @@ public interface ImageInputStream extends DataInput, Closeable {
* @throws java.io.EOFException if the end of the stream is reached.
* @throws IOException if an I/O error occurs.
*/
+ @Override
boolean readBoolean() throws IOException;
/**
@@ -204,6 +205,7 @@ public interface ImageInputStream extends DataInput, Closeable {
* @throws java.io.EOFException if the end of the stream is reached.
* @throws IOException if an I/O error occurs.
*/
+ @Override
byte readByte() throws IOException;
/**
@@ -228,6 +230,7 @@ public interface ImageInputStream extends DataInput, Closeable {
* @throws java.io.EOFException if the end of the stream is reached.
* @throws IOException if an I/O error occurs.
*/
+ @Override
int readUnsignedByte() throws IOException;
/**
@@ -246,6 +249,7 @@ public interface ImageInputStream extends DataInput, Closeable {
*
* @see #getByteOrder
*/
+ @Override
short readShort() throws IOException;
/**
@@ -267,6 +271,7 @@ public interface ImageInputStream extends DataInput, Closeable {
*
* @see #getByteOrder
*/
+ @Override
int readUnsignedShort() throws IOException;
/**
@@ -284,6 +289,7 @@ public interface ImageInputStream extends DataInput, Closeable {
*
* @see #readUnsignedShort
*/
+ @Override
char readChar() throws IOException;
/**
@@ -302,6 +308,7 @@ public interface ImageInputStream extends DataInput, Closeable {
*
* @see #getByteOrder
*/
+ @Override
int readInt() throws IOException;
/**
@@ -340,6 +347,7 @@ public interface ImageInputStream extends DataInput, Closeable {
*
* @see #getByteOrder
*/
+ @Override
long readLong() throws IOException;
/**
@@ -358,6 +366,7 @@ public interface ImageInputStream extends DataInput, Closeable {
*
* @see #getByteOrder
*/
+ @Override
float readFloat() throws IOException;
/**
@@ -376,6 +385,7 @@ public interface ImageInputStream extends DataInput, Closeable {
*
* @see #getByteOrder
*/
+ @Override
double readDouble() throws IOException;
/**
@@ -410,6 +420,7 @@ public interface ImageInputStream extends DataInput, Closeable {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
String readLine() throws IOException;
/**
@@ -494,6 +505,7 @@ public interface ImageInputStream extends DataInput, Closeable {
* a valid modified UTF-8 encoding of a string.
* @throws IOException if an I/O error occurs.
*/
+ @Override
String readUTF() throws IOException;
/**
@@ -518,6 +530,7 @@ public interface ImageInputStream extends DataInput, Closeable {
* reading all the bytes.
* @throws IOException if an I/O error occurs.
*/
+ @Override
void readFully(byte[] b, int off, int len) throws IOException;
/**
@@ -537,6 +550,7 @@ public interface ImageInputStream extends DataInput, Closeable {
* reading all the bytes.
* @throws IOException if an I/O error occurs.
*/
+ @Override
void readFully(byte[] b) throws IOException;
/**
@@ -822,6 +836,7 @@ public interface ImageInputStream extends DataInput, Closeable {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
int skipBytes(int n) throws IOException;
/**
@@ -995,5 +1010,6 @@ public interface ImageInputStream extends DataInput, Closeable {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
void close() throws IOException;
}
diff --git a/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java b/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java
index 103fe4019e40..c672bcac4a57 100644
--- a/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java
+++ b/src/java.desktop/share/classes/javax/imageio/stream/ImageInputStreamImpl.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -114,10 +114,12 @@ protected final void checkClosed() throws IOException {
}
}
+ @Override
public void setByteOrder(ByteOrder byteOrder) {
this.byteOrder = byteOrder;
}
+ @Override
public ByteOrder getByteOrder() {
return byteOrder;
}
@@ -139,6 +141,7 @@ public ByteOrder getByteOrder() {
*
* @throws IOException if the stream has been closed.
*/
+ @Override
public abstract int read() throws IOException;
/**
@@ -154,6 +157,7 @@ public ByteOrder getByteOrder() {
* {@code null}.
* @throws IOException if an I/O error occurs.
*/
+ @Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@@ -185,8 +189,10 @@ public int read(byte[] b) throws IOException {
* {@code null}.
* @throws IOException if an I/O error occurs.
*/
+ @Override
public abstract int read(byte[] b, int off, int len) throws IOException;
+ @Override
public void readBytes(IIOByteBuffer buf, int len) throws IOException {
if (len < 0) {
throw new IndexOutOfBoundsException("len < 0!");
@@ -206,6 +212,7 @@ public void readBytes(IIOByteBuffer buf, int len) throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public boolean readBoolean() throws IOException {
int ch = this.read();
if (ch < 0) {
@@ -217,6 +224,7 @@ public boolean readBoolean() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public byte readByte() throws IOException {
int ch = this.read();
if (ch < 0) {
@@ -228,6 +236,7 @@ public byte readByte() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public int readUnsignedByte() throws IOException {
int ch = this.read();
if (ch < 0) {
@@ -239,6 +248,7 @@ public int readUnsignedByte() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public short readShort() throws IOException {
if (read(byteBuf, 0, 2) != 2) {
throw new EOFException();
@@ -251,6 +261,7 @@ public short readShort() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public int readUnsignedShort() throws IOException {
return ((int)readShort()) & 0xffff;
}
@@ -258,6 +269,7 @@ public int readUnsignedShort() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public char readChar() throws IOException {
return (char)readShort();
}
@@ -265,6 +277,7 @@ public char readChar() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public int readInt() throws IOException {
if (read(byteBuf, 0, 4) != 4) {
throw new EOFException();
@@ -278,6 +291,7 @@ public int readInt() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public long readUnsignedInt() throws IOException {
return ((long)readInt()) & 0xffffffffL;
}
@@ -285,6 +299,7 @@ public long readUnsignedInt() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public long readLong() throws IOException {
// REMIND: Once 6277756 is fixed, we should do a bulk read of all 8
// bytes here as we do in readShort() and readInt() for even better
@@ -302,6 +317,7 @@ public long readLong() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
@@ -309,10 +325,12 @@ public float readFloat() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
+ @Override
public String readLine() throws IOException {
StringBuilder input = new StringBuilder();
int c = -1;
@@ -347,6 +365,7 @@ public String readLine() throws IOException {
* @throws EOFException {@inheritDoc}
* @throws java.io.UTFDataFormatException {@inheritDoc}
*/
+ @Override
public String readUTF() throws IOException {
this.bitOffset = 0;
@@ -372,6 +391,7 @@ public String readUTF() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public void readFully(byte[] b, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
@@ -392,6 +412,7 @@ public void readFully(byte[] b, int off, int len) throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public void readFully(byte[] b) throws IOException {
readFully(b, 0, b.length);
}
@@ -399,6 +420,7 @@ public void readFully(byte[] b) throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public void readFully(short[] s, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > s.length || off + len < 0) {
@@ -418,6 +440,7 @@ public void readFully(short[] s, int off, int len) throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public void readFully(char[] c, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > c.length || off + len < 0) {
@@ -437,6 +460,7 @@ public void readFully(char[] c, int off, int len) throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public void readFully(int[] i, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > i.length || off + len < 0) {
@@ -456,6 +480,7 @@ public void readFully(int[] i, int off, int len) throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public void readFully(long[] l, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > l.length || off + len < 0) {
@@ -475,6 +500,7 @@ public void readFully(long[] l, int off, int len) throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public void readFully(float[] f, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > f.length || off + len < 0) {
@@ -494,6 +520,7 @@ public void readFully(float[] f, int off, int len) throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public void readFully(double[] d, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > d.length || off + len < 0) {
@@ -600,16 +627,19 @@ private void toDoubles(byte[] b, double[] d, int off, int len) {
}
}
+ @Override
public long getStreamPosition() throws IOException {
checkClosed();
return streamPos;
}
+ @Override
public int getBitOffset() throws IOException {
checkClosed();
return bitOffset;
}
+ @Override
public void setBitOffset(int bitOffset) throws IOException {
checkClosed();
if (bitOffset < 0 || bitOffset > 7) {
@@ -621,6 +651,7 @@ public void setBitOffset(int bitOffset) throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public int readBit() throws IOException {
checkClosed();
@@ -646,6 +677,7 @@ public int readBit() throws IOException {
/**
* @throws EOFException {@inheritDoc}
*/
+ @Override
public long readBits(int numBits) throws IOException {
checkClosed();
@@ -697,6 +729,7 @@ public long readBits(int numBits) throws IOException {
*
* @return -1L to indicate unknown length.
*/
+ @Override
public long length() {
return -1L;
}
@@ -716,6 +749,7 @@ public long length() {
* throws an {@code IOException} when computing either
* the starting or ending position.
*/
+ @Override
public int skipBytes(int n) throws IOException {
long pos = getStreamPosition();
seek(pos + n);
@@ -737,12 +771,14 @@ public int skipBytes(int n) throws IOException {
* throws an {@code IOException} when computing either
* the starting or ending position.
*/
+ @Override
public long skipBytes(long n) throws IOException {
long pos = getStreamPosition();
seek(pos + n);
return getStreamPosition() - pos;
}
+ @Override
public void seek(long pos) throws IOException {
checkClosed();
@@ -759,6 +795,7 @@ public void seek(long pos) throws IOException {
* Pushes the current stream position onto a stack of marked
* positions.
*/
+ @Override
public void mark() {
try {
markByteStack.push(Long.valueOf(getStreamPosition()));
@@ -776,6 +813,7 @@ public void mark() {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
public void reset() throws IOException {
if (markByteStack.empty()) {
return;
@@ -792,6 +830,7 @@ public void reset() throws IOException {
setBitOffset(offset);
}
+ @Override
public void flushBefore(long pos) throws IOException {
checkClosed();
if (pos < flushedPos) {
@@ -804,10 +843,12 @@ public void flushBefore(long pos) throws IOException {
flushedPos = pos;
}
+ @Override
public void flush() throws IOException {
flushBefore(getStreamPosition());
}
+ @Override
public long getFlushedPosition() {
return flushedPos;
}
@@ -816,6 +857,7 @@ public long getFlushedPosition() {
* Default implementation returns false. Subclasses should
* override this if they cache data.
*/
+ @Override
public boolean isCached() {
return false;
}
@@ -824,6 +866,7 @@ public boolean isCached() {
* Default implementation returns false. Subclasses should
* override this if they cache data in main memory.
*/
+ @Override
public boolean isCachedMemory() {
return false;
}
@@ -832,10 +875,12 @@ public boolean isCachedMemory() {
* Default implementation returns false. Subclasses should
* override this if they cache data in a temporary file.
*/
+ @Override
public boolean isCachedFile() {
return false;
}
+ @Override
public void close() throws IOException {
checkClosed();
diff --git a/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStream.java
index d6ccf5ee524a..39a1c0ff15ec 100644
--- a/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStream.java
+++ b/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -68,6 +68,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
void write(int b) throws IOException;
/**
@@ -87,6 +88,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
* {@code null}.
* @throws IOException if an I/O error occurs.
*/
+ @Override
void write(byte[] b) throws IOException;
/**
@@ -114,6 +116,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
* {@code null}.
* @throws IOException if an I/O error occurs.
*/
+ @Override
void write(byte[] b, int off, int len) throws IOException;
/**
@@ -131,6 +134,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
void writeBoolean(boolean v) throws IOException;
/**
@@ -149,6 +153,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
void writeByte(int v) throws IOException;
/**
@@ -179,6 +184,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
void writeShort(int v) throws IOException;
/**
@@ -191,6 +197,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
*
* @see #writeShort(int)
*/
+ @Override
void writeChar(int v) throws IOException;
/**
@@ -224,6 +231,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
void writeInt(int v) throws IOException;
/**
@@ -265,6 +273,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
void writeLong(long v) throws IOException;
/**
@@ -285,6 +294,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
void writeFloat(float v) throws IOException;
/**
@@ -306,6 +316,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
*
* @throws IOException if an I/O error occurs.
*/
+ @Override
void writeDouble(double v) throws IOException;
/**
@@ -334,6 +345,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
* {@code null}.
* @throws IOException if an I/O error occurs.
*/
+ @Override
void writeBytes(String s) throws IOException;
/**
@@ -362,6 +374,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
* {@code null}.
* @throws IOException if an I/O error occurs.
*/
+ @Override
void writeChars(String s) throws IOException;
/**
@@ -433,6 +446,7 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
* representation of {@code s} requires more than 65536 bytes.
* @throws IOException if an I/O error occurs.
*/
+ @Override
void writeUTF(String s) throws IOException;
/**
@@ -658,5 +672,6 @@ public interface ImageOutputStream extends ImageInputStream, DataOutput {
* position.
* @throws IOException if an I/O error occurs.
*/
+ @Override
void flushBefore(long pos) throws IOException;
}
diff --git a/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStreamImpl.java b/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStreamImpl.java
index 13231470a7aa..0295d77b1867 100644
--- a/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStreamImpl.java
+++ b/src/java.desktop/share/classes/javax/imageio/stream/ImageOutputStreamImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -48,22 +48,28 @@ public abstract class ImageOutputStreamImpl
public ImageOutputStreamImpl() {
}
+ @Override
public abstract void write(int b) throws IOException;
+ @Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
+ @Override
public abstract void write(byte[] b, int off, int len) throws IOException;
+ @Override
public void writeBoolean(boolean v) throws IOException {
write(v ? 1 : 0);
}
+ @Override
public void writeByte(int v) throws IOException {
write(v);
}
+ @Override
public void writeShort(int v) throws IOException {
if (byteOrder == ByteOrder.BIG_ENDIAN) {
ByteArray.setUnsignedShort(byteBuf, 0, v);
@@ -73,10 +79,12 @@ public void writeShort(int v) throws IOException {
write(byteBuf, 0, 2);
}
+ @Override
public void writeChar(int v) throws IOException {
writeShort(v);
}
+ @Override
public void writeInt(int v) throws IOException {
if (byteOrder == ByteOrder.BIG_ENDIAN) {
ByteArray.setInt(byteBuf, 0, v);
@@ -86,6 +94,7 @@ public void writeInt(int v) throws IOException {
write(byteBuf, 0, 4);
}
+ @Override
public void writeLong(long v) throws IOException {
if (byteOrder == ByteOrder.BIG_ENDIAN) {
ByteArray.setLong(byteBuf, 0, v);
@@ -100,14 +109,17 @@ public void writeLong(long v) throws IOException {
write(byteBuf, 4, 4);
}
+ @Override
public void writeFloat(float v) throws IOException {
writeInt(Float.floatToIntBits(v));
}
+ @Override
public void writeDouble(double v) throws IOException {
writeLong(Double.doubleToLongBits(v));
}
+ @Override
public void writeBytes(String s) throws IOException {
int len = s.length();
for (int i = 0 ; i < len ; i++) {
@@ -115,6 +127,7 @@ public void writeBytes(String s) throws IOException {
}
}
+ @Override
public void writeChars(String s) throws IOException {
int len = s.length();
@@ -140,6 +153,7 @@ public void writeChars(String s) throws IOException {
/**
* @throws UTFDataFormatException {@inheritDoc}
*/
+ @Override
public void writeUTF(String s) throws IOException {
int strlen = s.length();
int utflen = 0;
@@ -182,6 +196,7 @@ public void writeUTF(String s) throws IOException {
write(b, 0, utflen + 2);
}
+ @Override
public void writeShorts(short[] s, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > s.length || off + len < 0) {
@@ -208,6 +223,7 @@ public void writeShorts(short[] s, int off, int len) throws IOException {
write(b, 0, len*2);
}
+ @Override
public void writeChars(char[] c, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > c.length || off + len < 0) {
@@ -234,6 +250,7 @@ public void writeChars(char[] c, int off, int len) throws IOException {
write(b, 0, len*2);
}
+ @Override
public void writeInts(int[] i, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > i.length || off + len < 0) {
@@ -260,6 +277,7 @@ public void writeInts(int[] i, int off, int len) throws IOException {
write(b, 0, len*4);
}
+ @Override
public void writeLongs(long[] l, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > l.length || off + len < 0) {
@@ -286,6 +304,7 @@ public void writeLongs(long[] l, int off, int len) throws IOException {
write(b, 0, len*8);
}
+ @Override
public void writeFloats(float[] f, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > f.length || off + len < 0) {
@@ -312,6 +331,7 @@ public void writeFloats(float[] f, int off, int len) throws IOException {
write(b, 0, len*4);
}
+ @Override
public void writeDoubles(double[] d, int off, int len) throws IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (off < 0 || len < 0 || off + len > d.length || off + len < 0) {
@@ -338,10 +358,12 @@ public void writeDoubles(double[] d, int off, int len) throws IOException {
write(b, 0, len*8);
}
+ @Override
public void writeBit(int bit) throws IOException {
writeBits((1L & bit), 1);
}
+ @Override
public void writeBits(long bits, int numBits) throws IOException {
checkClosed();
diff --git a/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java
index 0f259a9ce464..9e4343add7a4 100644
--- a/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java
+++ b/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageInputStream.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -62,6 +62,7 @@ public MemoryCacheImageInputStream(InputStream stream) {
this.stream = stream;
}
+ @Override
public int read() throws IOException {
checkClosed();
bitOffset = 0;
@@ -73,6 +74,7 @@ public int read() throws IOException {
}
}
+ @Override
public int read(byte[] b, int off, int len) throws IOException {
checkClosed();
@@ -103,6 +105,7 @@ public int read(byte[] b, int off, int len) throws IOException {
}
}
+ @Override
public void flushBefore(long pos) throws IOException {
super.flushBefore(pos); // this will call checkClosed() for us
cache.disposeBefore(pos);
@@ -118,6 +121,7 @@ public void flushBefore(long pos) throws IOException {
* @see #isCachedMemory
* @see #isCachedFile
*/
+ @Override
public boolean isCached() {
return true;
}
@@ -131,6 +135,7 @@ public boolean isCached() {
* @see #isCached
* @see #isCachedMemory
*/
+ @Override
public boolean isCachedFile() {
return false;
}
@@ -144,6 +149,7 @@ public boolean isCachedFile() {
* @see #isCached
* @see #isCachedFile
*/
+ @Override
public boolean isCachedMemory() {
return true;
}
@@ -152,6 +158,7 @@ public boolean isCachedMemory() {
* Closes this {@code MemoryCacheImageInputStream}, freeing
* the cache. The source {@code InputStream} is not closed.
*/
+ @Override
public void close() throws IOException {
super.close();
stream = null;
diff --git a/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageOutputStream.java b/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageOutputStream.java
index dd7049689b4e..c61cc3110a6f 100644
--- a/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageOutputStream.java
+++ b/src/java.desktop/share/classes/javax/imageio/stream/MemoryCacheImageOutputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -61,6 +61,7 @@ public MemoryCacheImageOutputStream(OutputStream stream) {
this.stream = stream;
}
+ @Override
public int read() throws IOException {
checkClosed();
@@ -73,6 +74,7 @@ public int read() throws IOException {
return val;
}
+ @Override
public int read(byte[] b, int off, int len) throws IOException {
checkClosed();
@@ -107,18 +109,21 @@ public int read(byte[] b, int off, int len) throws IOException {
return len;
}
+ @Override
public void write(int b) throws IOException {
flushBits(); // this will call checkClosed() for us
cache.write(b, streamPos);
++streamPos;
}
+ @Override
public void write(byte[] b, int off, int len) throws IOException {
flushBits(); // this will call checkClosed() for us
cache.write(b, off, len, streamPos);
streamPos += len;
}
+ @Override
public long length() {
try {
checkClosed();
@@ -138,6 +143,7 @@ public long length() {
* @see #isCachedMemory
* @see #isCachedFile
*/
+ @Override
public boolean isCached() {
return true;
}
@@ -151,6 +157,7 @@ public boolean isCached() {
* @see #isCached
* @see #isCachedMemory
*/
+ @Override
public boolean isCachedFile() {
return false;
}
@@ -164,6 +171,7 @@ public boolean isCachedFile() {
* @see #isCached
* @see #isCachedFile
*/
+ @Override
public boolean isCachedMemory() {
return true;
}
@@ -174,6 +182,7 @@ public boolean isCachedMemory() {
* is released. The destination {@code OutputStream}
* is not closed.
*/
+ @Override
public void close() throws IOException {
long length = cache.getLength();
seek(length);
@@ -189,6 +198,7 @@ public void close() throws IOException {
* @param pos {@inheritDoc ImageOutputStream}
* @throws IOException {@inheritDoc ImageOutputStream}
*/
+ @Override
public void flushBefore(long pos) throws IOException {
long oFlushedPos = flushedPos;
super.flushBefore(pos); // this will call checkClosed() for us
From 157c49276bed43664eeb9c142cf2bd83064d8187 Mon Sep 17 00:00:00 2001
From: Prasanta Sadhukhan
Date: Mon, 17 Aug 2026 05:29:58 +0000
Subject: [PATCH 27/88] 8390183: Some typos in JavaDocs for the java.desktop
module
Reviewed-by: azvegint, kizune
---
.../share/classes/java/awt/image/DirectColorModel.java | 4 ++--
.../share/classes/java/beans/EventHandler.java | 2 +-
.../beans/beancontext/BeanContextServicesSupport.java | 2 +-
.../java/beans/beancontext/BeanContextSupport.java | 2 +-
.../share/classes/javax/imageio/ImageWriter.java | 4 ++--
.../classes/javax/imageio/metadata/IIOMetadataFormat.java | 4 ++--
.../javax/imageio/plugins/tiff/BaselineTIFFTagSet.java | 8 ++++----
.../classes/javax/imageio/plugins/tiff/TIFFDirectory.java | 4 ++--
.../classes/javax/imageio/spi/ImageInputStreamSpi.java | 4 ++--
.../classes/javax/imageio/spi/ImageOutputStreamSpi.java | 4 ++--
src/java.desktop/share/classes/javax/sound/SoundClip.java | 4 ++--
.../share/classes/javax/swing/JComponent.java | 2 +-
12 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/src/java.desktop/share/classes/java/awt/image/DirectColorModel.java b/src/java.desktop/share/classes/java/awt/image/DirectColorModel.java
index d7dc4a4fba7c..92af1b327d87 100644
--- a/src/java.desktop/share/classes/java/awt/image/DirectColorModel.java
+++ b/src/java.desktop/share/classes/java/awt/image/DirectColorModel.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 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
@@ -648,7 +648,7 @@ public int getBlue(Object inData) {
* {@code inData} is not large enough to hold a pixel value
* for this {@code ColorModel}
* @throws UnsupportedOperationException if this
- * {@code tranferType} is not supported by this
+ * {@code transferType} is not supported by this
* {@code ColorModel}
*/
public int getAlpha(Object inData) {
diff --git a/src/java.desktop/share/classes/java/beans/EventHandler.java b/src/java.desktop/share/classes/java/beans/EventHandler.java
index 60f4e7bafdae..8581fd250a8a 100644
--- a/src/java.desktop/share/classes/java/beans/EventHandler.java
+++ b/src/java.desktop/share/classes/java/beans/EventHandler.java
@@ -229,7 +229,7 @@
*
*
* The target property may also be "qualified" with an arbitrary number
- * of property prefixs delimited with the "." character. For example, the
+ * of property prefixes delimited with the "." character. For example, the
* following action listener:
*
* EventHandler.create(ActionListener.class, target, "a.b", "c.d")
diff --git a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java
index 17897f6865e8..1b2ab2885e60 100644
--- a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java
+++ b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextServicesSupport.java
@@ -1285,7 +1285,7 @@ private synchronized void readObject(ObjectInputStream ois) throws IOException,
protected transient HashMap services;
/**
- * The number of instances of a serializable {@code BeanContextServceProvider}.
+ * The number of instances of a serializable {@code BeanContextServiceProvider}.
*/
protected transient int serializable = 0;
diff --git a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java
index 884b36d4b12b..d289bc8e116b 100644
--- a/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java
+++ b/src/java.desktop/share/classes/java/beans/beancontext/BeanContextSupport.java
@@ -374,7 +374,7 @@ protected class BCSChild implements Serializable {
* methods that add children to the set.
*
* @param targetChild the child to create the Child on behalf of
- * @param peer the peer if the tragetChild and the peer are related by an implementation of BeanContextProxy
+ * @param peer the peer if the targetChild and the peer are related by an implementation of BeanContextProxy
* @return Subtype-specific subclass of Child without overriding collection methods
*/
diff --git a/src/java.desktop/share/classes/javax/imageio/ImageWriter.java b/src/java.desktop/share/classes/javax/imageio/ImageWriter.java
index f378e969a5a0..84c66721efe1 100644
--- a/src/java.desktop/share/classes/javax/imageio/ImageWriter.java
+++ b/src/java.desktop/share/classes/javax/imageio/ImageWriter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -1195,7 +1195,7 @@ public void prepareWriteEmpty(IIOMetadata streamMetadata,
* {@code prepareInsertEmpty} without a corresponding call to
* {@code endInsertEmpty} has been made.
* @throws IllegalStateException if a call to
- * {@code prepareReiplacePixels} has been made without a
+ * {@code prepareReplacePixels} has been made without a
* matching call to {@code endReplacePixels}.
* @throws IOException if an I/O error occurs during writing.
*/
diff --git a/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormat.java b/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormat.java
index b0cd40b6f6eb..06010a5da67c 100644
--- a/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormat.java
+++ b/src/java.desktop/share/classes/javax/imageio/metadata/IIOMetadataFormat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -443,7 +443,7 @@ public interface IIOMetadataFormat {
* interpretation of the value of the given attribute within the
* named element. If {@code getAttributeValueType} returns
* {@code VALUE_LIST}, then the legal value is a
- * whitespace-spearated list of values of the returned datatype.
+ * whitespace-separated list of values of the returned datatype.
*
* @param elementName the name of the element being queried.
* @param attrName the name of the attribute being queried.
diff --git a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/BaselineTIFFTagSet.java b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/BaselineTIFFTagSet.java
index ebeb456b164b..854e1816b99e 100644
--- a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/BaselineTIFFTagSet.java
+++ b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/BaselineTIFFTagSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2024, 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
@@ -314,21 +314,21 @@ public final class BaselineTIFFTagSet extends TIFFTagSet {
public static final int TAG_THRESHHOLDING = 263;
/**
- * A value to be used with the "Thresholding" tag.
+ * A value to be used with the "Threshholding" tag.
*
* @see #TAG_THRESHHOLDING
*/
public static final int THRESHHOLDING_NONE = 1;
/**
- * A value to be used with the "Thresholding" tag.
+ * A value to be used with the "Threshholding" tag.
*
* @see #TAG_THRESHHOLDING
*/
public static final int THRESHHOLDING_ORDERED_DITHER = 2;
/**
- * A value to be used with the "Thresholding" tag.
+ * A value to be used with the "Threshholding" tag.
*
* @see #TAG_THRESHHOLDING
*/
diff --git a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFDirectory.java b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFDirectory.java
index 197b351feb72..4d4765b7ef90 100644
--- a/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFDirectory.java
+++ b/src/java.desktop/share/classes/javax/imageio/plugins/tiff/TIFFDirectory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2025, 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
@@ -275,7 +275,7 @@ public void removeTagSet(TIFFTagSet tagSet) {
* has been defined or {@code null} otherwise.
*
* @return The parent {@code TIFFTag} of this
- * {@code TIFFDiectory} or {@code null}.
+ * {@code TIFFDirectory} or {@code null}.
*/
public TIFFTag getParentTag() {
return parentTag;
diff --git a/src/java.desktop/share/classes/javax/imageio/spi/ImageInputStreamSpi.java b/src/java.desktop/share/classes/javax/imageio/spi/ImageInputStreamSpi.java
index fb3b8d58e311..de82ed71f2c1 100644
--- a/src/java.desktop/share/classes/javax/imageio/spi/ImageInputStreamSpi.java
+++ b/src/java.desktop/share/classes/javax/imageio/spi/ImageInputStreamSpi.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -113,7 +113,7 @@ public Class> getInputClass() {
* Returns {@code true} if the {@code ImageInputStream}
* implementation associated with this service provider can
* optionally make use of a cache file for improved performance
- * and/or memory footrprint. If {@code false}, the value of
+ * and/or memory footprint. If {@code false}, the value of
* the {@code useCache} argument to
* {@code createInputStreamInstance} will be ignored.
*
diff --git a/src/java.desktop/share/classes/javax/imageio/spi/ImageOutputStreamSpi.java b/src/java.desktop/share/classes/javax/imageio/spi/ImageOutputStreamSpi.java
index a8fcf7ef19f7..7b795d889943 100644
--- a/src/java.desktop/share/classes/javax/imageio/spi/ImageOutputStreamSpi.java
+++ b/src/java.desktop/share/classes/javax/imageio/spi/ImageOutputStreamSpi.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -115,7 +115,7 @@ public Class> getOutputClass() {
* Returns {@code true} if the {@code ImageOutputStream}
* implementation associated with this service provider can
* optionally make use of a cache {@code File} for improved
- * performance and/or memory footrprint. If {@code false},
+ * performance and/or memory footprint. If {@code false},
* the value of the {@code cacheFile} argument to
* {@code createOutputStreamInstance} will be ignored.
*
diff --git a/src/java.desktop/share/classes/javax/sound/SoundClip.java b/src/java.desktop/share/classes/javax/sound/SoundClip.java
index 41ad11e5cacf..597de69d244a 100644
--- a/src/java.desktop/share/classes/javax/sound/SoundClip.java
+++ b/src/java.desktop/share/classes/javax/sound/SoundClip.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
@@ -123,7 +123,7 @@ public void play() {
*
* Threading notes : Most applications will not need to do anything except call {@code loop()}.
* The following is therefore something most applications need not be concerned about.
- * Play back is managed in a background thread, which is ususally a daemon thread.
+ * Play back is managed in a background thread, which is usually a daemon thread.
* Running daemon threads do not prevent the VM from exiting.
* So at least one thread must be alive to prevent the VM from terminating.
* A UI application with any window displayed automatically satisfies this requirement.
diff --git a/src/java.desktop/share/classes/javax/swing/JComponent.java b/src/java.desktop/share/classes/javax/swing/JComponent.java
index 9a7e67913ffc..943bfc946d70 100644
--- a/src/java.desktop/share/classes/javax/swing/JComponent.java
+++ b/src/java.desktop/share/classes/javax/swing/JComponent.java
@@ -3254,7 +3254,7 @@ public boolean getAutoscrolls() {
* locations). If you do not wish for this component to respond in any way
* to drops, you can disable drop support entirely either by removing the
* drop target ({@code setDropTarget(null)}) or by de-activating it
- * ({@code getDropTaget().setActive(false)}).
+ * ({@code getDropTarget().setActive(false)}).
*
* If the new {@code TransferHandler} is {@code null}, this method removes
* the drop target.
From 3ef007ac237627d08631a582713751b1accd72e4 Mon Sep 17 00:00:00 2001
From: Prasanta Sadhukhan
Date: Mon, 17 Aug 2026 05:30:35 +0000
Subject: [PATCH 28/88] 8390296: Swing MultiUIDefaults#containsKey
inconsistancy
Reviewed-by: azvegint, kizune
---
.../classes/javax/swing/MultiUIDefaults.java | 16 +++-
.../MultiUIDefaultsContainsKeyTest.java | 91 +++++++++++++++++++
2 files changed, 106 insertions(+), 1 deletion(-)
create mode 100644 test/jdk/javax/swing/MultiUIDefaults/MultiUIDefaultsContainsKeyTest.java
diff --git a/src/java.desktop/share/classes/javax/swing/MultiUIDefaults.java b/src/java.desktop/share/classes/javax/swing/MultiUIDefaults.java
index 8b65bfc962b4..db324fb676bd 100644
--- a/src/java.desktop/share/classes/javax/swing/MultiUIDefaults.java
+++ b/src/java.desktop/share/classes/javax/swing/MultiUIDefaults.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, 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
@@ -119,6 +119,20 @@ public Set keySet()
return set;
}
+ @Override
+ public boolean containsKey(Object key) {
+ if (super.containsKey(key)) {
+ return true;
+ }
+
+ for (UIDefaults table : tables) {
+ if (table != null && table.containsKey(key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public Enumeration elements()
{
diff --git a/test/jdk/javax/swing/MultiUIDefaults/MultiUIDefaultsContainsKeyTest.java b/test/jdk/javax/swing/MultiUIDefaults/MultiUIDefaultsContainsKeyTest.java
new file mode 100644
index 000000000000..3e1669cb9270
--- /dev/null
+++ b/test/jdk/javax/swing/MultiUIDefaults/MultiUIDefaultsContainsKeyTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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 8390296
+ * @summary Verifies MultiUIDefaults#containsKey and
+ * MultiUIDefaults#keySet().contains(key) produce same result
+ * @run main MultiUIDefaultsContainsKeyTest
+ */
+
+import javax.swing.UIManager;
+import javax.swing.UIDefaults;
+import javax.swing.LookAndFeel;
+
+public class MultiUIDefaultsContainsKeyTest {
+ private static final String KEY = "TabbedPane.isTabRollover";
+
+ public static void main(String[] args) throws Exception {
+ UIManager.setLookAndFeel(new KeyProvidingLookAndFeel());
+
+ boolean containsKey = UIManager.getDefaults().containsKey(KEY);
+ boolean keySetContains = UIManager.getDefaults().keySet().contains(KEY);
+
+ System.out.println("key: " + KEY);
+ System.out.println("defaults class: "
+ + UIManager.getDefaults().getClass().getName());
+ System.out.println("containsKey: " + containsKey);
+ System.out.println("keySet().contains: " + keySetContains);
+
+ if (containsKey != keySetContains) {
+ throw new RuntimeException("containsKey and keySet().contains disagree");
+ }
+
+ System.out.println("No inconsistency observed.");
+ }
+
+ private static final class KeyProvidingLookAndFeel extends LookAndFeel {
+ @Override
+ public String getName() {
+ return "auxiliary Look and Feel";
+ }
+
+ @Override
+ public String getID() {
+ return "Auxiliary";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Adds a defaults key for the MultiUIDefaults reproducer";
+ }
+
+ @Override
+ public boolean isNativeLookAndFeel() {
+ return false;
+ }
+
+ @Override
+ public boolean isSupportedLookAndFeel() {
+ return true;
+ }
+
+ @Override
+ public UIDefaults getDefaults() {
+ UIDefaults defaults = new UIDefaults();
+ defaults.put(KEY, Boolean.TRUE);
+ return defaults;
+ }
+ }
+}
From 5eaf42f6dad4ff9d92dad2cfc12c773010a74961 Mon Sep 17 00:00:00 2001
From: Guanqiang Han
Date: Mon, 17 Aug 2026 06:25:31 +0000
Subject: [PATCH 29/88] 8389579: C2: Missed Ideal optimization opportunity in
PhaseIterGVN for CompressBits and ExpandBits
Reviewed-by: mchevalier, thartmann
---
src/hotspot/share/opto/phaseX.cpp | 23 ++
...MissingCompressBitsAndExpandBitsIdeal.java | 295 ++++++++++++++++++
2 files changed, 318 insertions(+)
create mode 100644 test/hotspot/jtreg/compiler/c2/igvn/TestMissingCompressBitsAndExpandBitsIdeal.java
diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp
index 4e140337d708..9ec0af0e1701 100644
--- a/src/hotspot/share/opto/phaseX.cpp
+++ b/src/hotspot/share/opto/phaseX.cpp
@@ -2708,6 +2708,29 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_
return u->Opcode() == Op_AndI || u->Opcode() == Op_AndL;
});
}
+ // If changed LShift inputs, check CompressBits and ExpandBits users for
+ // compress(x, 1 << n), compress(x, -1 << n),
+ // expand(x, 1 << n), expand(x, -1 << n) optimizations.
+ if (use_op == Op_LShiftI || use_op == Op_LShiftL) {
+ add_users_to_worklist_if(worklist, use, [&](Node* u) {
+ return (u->Opcode() == Op_CompressBits || u->Opcode() == Op_ExpandBits) &&
+ u->in(2) == use;
+ });
+ }
+ // If changed ExpandBits inputs, check CompressBits users for
+ // compress(expand(x, m), m) optimization.
+ if (use_op == Op_ExpandBits) {
+ add_users_to_worklist_if(worklist, use, [&](Node* u) {
+ return u->Opcode() == Op_CompressBits && u->in(1) == use;
+ });
+ }
+ // If changed CompressBits inputs, check ExpandBits users for
+ // expand(compress(x, m), m) optimization.
+ if (use_op == Op_CompressBits) {
+ add_users_to_worklist_if(worklist, use, [&](Node* u) {
+ return u->Opcode() == Op_ExpandBits && u->in(1) == use;
+ });
+ }
// If changed AddI/SubI inputs, check CmpU for range check optimization.
if (use_op == Op_AddI || use_op == Op_SubI) {
add_users_to_worklist_if(worklist, use, [](Node* u) {
diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestMissingCompressBitsAndExpandBitsIdeal.java b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingCompressBitsAndExpandBitsIdeal.java
new file mode 100644
index 000000000000..8bb76c6155f6
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingCompressBitsAndExpandBitsIdeal.java
@@ -0,0 +1,295 @@
+/*
+ * 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.c2.igvn;
+
+import compiler.lib.generators.*;
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+
+/*
+ * @test
+ * @bug 8389579
+ * @key randomness
+ * @summary Test missing Ideal optimization opportunity for CompressBits and ExpandBits.
+ * @library /test/lib /
+ * @run driver ${test.main.class}
+ */
+
+public class TestMissingCompressBitsAndExpandBitsIdeal {
+ private static final Generator INTS = Generators.G.ints();
+ private static final Generator LONGS = Generators.G.longs();
+
+ private static final RestrictableGenerator INT_SHIFTS = Generators.G.ints()
+ .restricted(1, Integer.SIZE - 1);
+ private static final RestrictableGenerator LONG_SHIFTS = Generators.G.ints()
+ .restricted(1, Long.SIZE - 1);
+
+ public static void main(String[] args) {
+ TestFramework.runWithFlags("-XX:+IgnoreUnrecognizedVMOptions",
+ "-XX:VerifyIterativeGVN=100");
+ }
+
+ // compress(x, 1 << n) == (x >> n) & 1
+ @Test
+ @IR(counts = {IRNode.COMPRESS_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.COMPRESS_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static int testIntCompressWithOneLeftShift(int value, int shift) {
+ int i;
+ for (i = -10; i < 1; i++) {
+ }
+ int mask = i << shift;
+ return Integer.compress(value, mask);
+ }
+
+ // compress(x, -1 << n) == x >>> n
+ @Test
+ @IR(counts = {IRNode.COMPRESS_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.COMPRESS_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static int testIntCompressWithMinusOneLeftShift(int value, int shift) {
+ int i;
+ for (i = -10; i < -1; i++) {
+ }
+ int mask = i << shift;
+ return Integer.compress(value, mask);
+ }
+
+ // compress(x, 1L << n) == (x >> n) & 1L
+ @Test
+ @IR(counts = {IRNode.COMPRESS_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.COMPRESS_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static long testLongCompressWithOneLeftShift(long value, int shift) {
+ long i;
+ for (i = -10; i < 1; i++) {
+ }
+ long mask = i << shift;
+ return Long.compress(value, mask);
+ }
+
+ // compress(x, -1L << n) == x >>> n
+ @Test
+ @IR(counts = {IRNode.COMPRESS_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.COMPRESS_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static long testLongCompressWithMinusOneLeftShift(long value, int shift) {
+ long i;
+ for (i = -10; i < -1; i++) {
+ }
+ long mask = i << shift;
+ return Long.compress(value, mask);
+ }
+
+ // compress(expand(x, m), m) == x & compress(m, m)
+ @Test
+ @IR(counts = {IRNode.EXPAND_BITS, "> 0",
+ IRNode.COMPRESS_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.EXPAND_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static int testIntCompressExpandWithSameMask(int value, int mask) {
+ int i;
+ for (i = -10; i < 1; i++) {
+ }
+ int expandMask = mask * i;
+ return Integer.compress(Integer.expand(value, expandMask), mask);
+ }
+
+ // compress(expand(x, m), m) == x & compress(m, m)
+ @Test
+ @IR(counts = {IRNode.EXPAND_BITS, "> 0",
+ IRNode.COMPRESS_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.EXPAND_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static long testLongCompressExpandWithSameMask(long value, long mask) {
+ long i;
+ for (i = -10; i < 1; i++) {
+ }
+ long expandMask = mask * i;
+ return Long.compress(Long.expand(value, expandMask), mask);
+ }
+
+ // expand(x, 1 << n) == (x & 1) << n
+ @Test
+ @IR(counts = {IRNode.EXPAND_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.EXPAND_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static int testIntExpandWithOneLeftShift(int value, int shift) {
+ int result = 0;
+ for (int i = 1; i >= 1; i--) {
+ int x = value;
+ result = Integer.expand(x, i << shift);
+ }
+ return result;
+ }
+
+ // expand(x, -1 << n) == x << n
+ @Test
+ @IR(counts = {IRNode.EXPAND_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.EXPAND_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static int testIntExpandWithMinusOneLeftShift(int value, int shift) {
+ int result = 0;
+ for (int i = -1; i >= -1; i--) {
+ int x = value;
+ result = Integer.expand(x, i << shift);
+ }
+ return result;
+ }
+
+ // expand(x, 1L << n) == (x & 1L) << n
+ @Test
+ @IR(counts = {IRNode.EXPAND_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.EXPAND_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static long testLongExpandWithOneLeftShift(long value, int shift) {
+ long result = 0;
+ for (long i = 1L; i >= 1L; i--) {
+ long x = value;
+ result = Long.expand(x, i << shift);
+ }
+ return result;
+ }
+
+ // expand(x, -1L << n) == x << n
+ @Test
+ @IR(counts = {IRNode.EXPAND_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.EXPAND_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static long testLongExpandWithMinusOneLeftShift(long value, int shift) {
+ long result = 0;
+ for (long i = -1L; i >= -1L; i--) {
+ long x = value;
+ result = Long.expand(x, i << shift);
+ }
+ return result;
+ }
+
+ // expand(compress(x, m), m) == x & m
+ @Test
+ @IR(counts = {IRNode.COMPRESS_BITS, "> 0",
+ IRNode.EXPAND_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.EXPAND_BITS, IRNode.COMPRESS_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static int testIntExpandCompressWithSameMask(int value, int mask) {
+ int result = 0;
+ for (int i = 1; i >= 1; i--) {
+ int x = value;
+ result = Integer.expand(Integer.compress(x, mask * i), mask);
+ }
+ return result;
+ }
+
+ // expand(compress(x, m), m) == x & m
+ @Test
+ @IR(counts = {IRNode.COMPRESS_BITS, "> 0",
+ IRNode.EXPAND_BITS, "> 0"},
+ phase = {CompilePhase.AFTER_PARSING},
+ applyIfCPUFeature = {"bmi2", "true"})
+ @IR(failOn = {IRNode.EXPAND_BITS, IRNode.COMPRESS_BITS},
+ applyIfCPUFeature = {"bmi2", "true"})
+ public static long testLongExpandCompressWithSameMask(long value, long mask) {
+ long result = 0;
+ for (long i = 1L; i >= 1L; i--) {
+ long x = value;
+ result = Long.expand(Long.compress(x, mask * i), mask);
+ }
+ return result;
+ }
+
+ @Run(test = {"testIntCompressWithOneLeftShift",
+ "testIntCompressWithMinusOneLeftShift",
+ "testLongCompressWithOneLeftShift",
+ "testLongCompressWithMinusOneLeftShift",
+ "testIntCompressExpandWithSameMask",
+ "testLongCompressExpandWithSameMask",
+ "testIntExpandWithOneLeftShift",
+ "testIntExpandWithMinusOneLeftShift",
+ "testLongExpandWithOneLeftShift",
+ "testLongExpandWithMinusOneLeftShift",
+ "testIntExpandCompressWithSameMask",
+ "testLongExpandCompressWithSameMask"})
+ public static void runTest() {
+ for (int i = 0; i < 100; i++) {
+ int intValue = INTS.next();
+ int intShift = INT_SHIFTS.next();
+ int intMask = INTS.next();
+ long longValue = LONGS.next();
+ int longShift = LONG_SHIFTS.next();
+ long longMask = LONGS.next();
+
+ Asserts.assertEQ(testIntCompressWithOneLeftShift(intValue, intShift),
+ (intValue >> intShift) & 1);
+ Asserts.assertEQ(testIntCompressWithMinusOneLeftShift(intValue, intShift),
+ intValue >>> intShift);
+
+ Asserts.assertEQ(testLongCompressWithOneLeftShift(longValue, longShift),
+ (longValue >> longShift) & 1L);
+ Asserts.assertEQ(testLongCompressWithMinusOneLeftShift(longValue, longShift),
+ longValue >>> longShift);
+
+ Asserts.assertEQ(testIntCompressExpandWithSameMask(intValue, intMask),
+ intValue & Integer.compress(intMask, intMask));
+ Asserts.assertEQ(testLongCompressExpandWithSameMask(longValue, longMask),
+ longValue & Long.compress(longMask, longMask));
+
+ Asserts.assertEQ(testIntExpandWithOneLeftShift(intValue, intShift),
+ (intValue & 1) << intShift);
+ Asserts.assertEQ(testIntExpandWithMinusOneLeftShift(intValue, intShift),
+ intValue << intShift);
+
+ Asserts.assertEQ(testLongExpandWithOneLeftShift(longValue, longShift),
+ (longValue & 1L) << longShift);
+ Asserts.assertEQ(testLongExpandWithMinusOneLeftShift(longValue, longShift),
+ longValue << longShift);
+
+ Asserts.assertEQ(testIntExpandCompressWithSameMask(intValue, intMask),
+ intValue & intMask);
+ Asserts.assertEQ(testLongExpandCompressWithSameMask(longValue, longMask),
+ longValue & longMask);
+ }
+ }
+}
From 5962e8294d63dc30a4b87ed617f0c27eb09f2793 Mon Sep 17 00:00:00 2001
From: Daniel Skantz
Date: Mon, 17 Aug 2026 06:33:22 +0000
Subject: [PATCH 30/88] 8386580: Typing corrections and style improvements in
x86 ML DSA/KEM/SHA3 stub code
Reviewed-by: adinn, semery, chagedorn
---
.../x86/stubGenerator_x86_64_dilithium.cpp | 22 +++++++--------
.../cpu/x86/stubGenerator_x86_64_kyber.cpp | 28 ++++++++++---------
.../cpu/x86/stubGenerator_x86_64_sha3.cpp | 4 +--
src/hotspot/share/opto/library_call.cpp | 2 +-
src/hotspot/share/opto/runtime.cpp | 2 --
5 files changed, 29 insertions(+), 29 deletions(-)
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp
index de8f52a3c056..74ccc892c8ee 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_dilithium.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.
* Copyright (c) 2025, Intel Corporation. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -254,7 +254,7 @@ static auto whole_shuffle(Register scratch, KRegister mergeMask1, KRegister merg
// swap the second operand (zetas) since the odd slots contain the same number
// as the corresponding even one. This is indicated by input2NeedsShuffle=false)
//
-// The registers to be multiplied are in input1[] and inputs2[]. The results go
+// The registers to be multiplied are in input1[] and input2[]. The results go
// into output[]. Two scratch[] register arrays are expected. input1[] can
// overlap with either output[] or scratch1[]
// - If AVX512, all register arrays are of length 4
@@ -279,7 +279,7 @@ static auto whole_montMul(XMMRegister montQInvModR, XMMRegister dilithium_q,
// If so, use output:
const XMMRegister* scratch = scratch1 == input1 ? output: scratch1;
- // scratch = input1_even * intput2_even
+ // scratch = input1_even * input2_even
for (int i = 0; i < regCnt; i++) {
__ vpmuldq(scratch[i], input1[i], input2[i], vector_len);
}
@@ -308,7 +308,7 @@ static auto whole_montMul(XMMRegister montQInvModR, XMMRegister dilithium_q,
}
}
- // scratch1 = input1_even*intput2_even
+ // scratch1 = input1_even*input2_even
for (int i = 0; i < regCnt; i++) {
__ vpmuldq(scratch1[i], input1[i], input2[i], vector_len);
}
@@ -423,7 +423,7 @@ static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen,
// products will be added to and subtracted from the other half of the
// coefficients. In each level we just shuffle the coefficients that need to
// be multiplied by the zetas in one set, the rest to another set of vector
- // registers, then redistribute the addition/substraction results.
+ // registers, then redistribute the addition/subtraction results.
// For levels 0 and 1 the zetas are not different within the 4 xmm registers
// that we would use for them, so we use only one register.
@@ -649,7 +649,7 @@ static address generate_dilithiumAlmostNtt_avx(StubGenerator *stubgen,
}
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -898,7 +898,7 @@ static address generate_dilithiumAlmostInverseNtt_avx(StubGenerator *stubgen,
}
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -993,7 +993,7 @@ static address generate_dilithiumNttMult_avx(StubGenerator *stubgen,
__ jcc(Assembler::notEqual, L_loop);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -1002,7 +1002,7 @@ static address generate_dilithiumNttMult_avx(StubGenerator *stubgen,
return start;
}
-// Dilithium Motgomery multiply an array by a constant.
+// Dilithium Montgomery multiply an array by a constant.
// Implements
// static int implDilithiumMontMulByConstant(int[] coeffs, int constant) {}
//
@@ -1089,7 +1089,7 @@ static address generate_dilithiumMontMulByConstant_avx(StubGenerator *stubgen,
__ jcc(Assembler::notEqual, L_loop);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -1357,7 +1357,7 @@ static address generate_dilithiumDecomposePoly_avx(StubGenerator *stubgen,
__ jcc(Assembler::notEqual, L_loop);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp
index 840f848d3baf..cf2d32b6a603 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_kyber.cpp
@@ -349,11 +349,13 @@ static void store4regs(Register address, int offset, int sourceRegs[],
}
}
-// In all 3 invocations of this function we use the same registers:
-// xmm0-xmm7 for the input and the result,
-// xmm8-xmm15 as scratch registers and
-// xmm16-xmm17 for the constants,
-// so we don't pass register arguments.
+// This stub helper vectorizes the reduction in implKyberBarrettReduceJava.
+// In all invocations of this function we use the same registers:
+// Input: xmm0-xmm7 (signed short coefficients)
+// xmm16: Barrett multiplier
+// xmm17: q
+// Scratch: xmm8-xmm15
+// Output: xmm0-xmm7 (reduced coefficients each in [0, q])
static void barrettReduce(MacroAssembler *_masm) {
for (int i = 0; i < 8; i++) {
__ evpmulhw(xmm(i + 8), k0, xmm(i), xmm16, false, Assembler::AVX_512bit);
@@ -490,7 +492,7 @@ address generate_kyberNtt_avx512(StubGenerator *stubgen,
store4regs(coeffs, 256, xmm4_7, _masm);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -621,7 +623,7 @@ address generate_kyberInverseNtt_avx512(StubGenerator *stubgen,
store4regs(coeffs, 256, xmm12_15, _masm);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -771,7 +773,7 @@ address generate_kyberNttMult_avx512(StubGenerator *stubgen,
__ pop_ppx(r12);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -824,7 +826,7 @@ address generate_kyberAddPoly_2_avx512(StubGenerator *stubgen,
store4regs(result, 256, xmm4_7, _masm);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -886,7 +888,7 @@ address generate_kyberAddPoly_3_avx512(StubGenerator *stubgen,
store4regs(result, 256, xmm4_7, _masm);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -975,7 +977,7 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen,
__ jcc(Assembler::greater, VBMILoop);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -1051,7 +1053,7 @@ address generate_kyber12To16_avx512(StubGenerator *stubgen,
__ jcc(Assembler::greater, Loop);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
@@ -1096,7 +1098,7 @@ address generate_kyberBarrettReduce_avx512(StubGenerator *stubgen,
store4regs(coeffs, 256, xmm4_7, _masm);
__ leave(); // required for proper stackwalking of RuntimeStub frame
- __ mov64(rax, 0); // return 0
+ __ mov64(rax, 0); // Intrinsic returns a value of 0, whereas Java callees return 1
__ ret(0);
// record the stub entry and end
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp
index edfe89f5af00..6c04ef933040 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64_sha3.cpp
@@ -628,7 +628,7 @@ static address generate_sha3_implCompress_avx512(StubId stub_id,
// (2) - a lot of shuffles are inevitable, since there are not enough registers.
// To save some shuffles, column1-column3 and column2-4 are placed into
// the same 128-bit register. Column 0 is also grouped (by rows).
-// This means the SHA3 state fits into 12.5 regisers, leaving 3 registers as
+// This means the SHA3 state fits into 12.5 registers, leaving 3 registers as
// temporaries. This is mostly sufficient, except for the Theta step, where we
// have to buy two slots on the stack
static address generate_sha3_implCompress_avx2(StubId stub_id,
@@ -693,7 +693,7 @@ static address generate_sha3_implCompress_avx2(StubId stub_id,
__ subptr(rsp, reg_size*2);
// Registers for memory load
- // Notice the careful 'missalignment' of pairs.
+ // Notice the careful 'misalignment' of pairs.
// This helps XOR for all blocksizes
XMMRegister a0a1, _a2, a3a4;
XMMRegister a5a6, a7a8, _a9;
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index ab1ba7cce1c0..0fa509d50529 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -8615,7 +8615,7 @@ bool LibraryCallKit::inline_kyberNtt() {
if (!stubAddr) return false;
Node* coeffs = argument(0);
- Node* ntt_zetas = argument(1);
+ Node* ntt_zetas = argument(1);
coeffs = must_be_not_null(coeffs, true);
ntt_zetas = must_be_not_null(ntt_zetas, true);
diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp
index b5905bdd99e1..62ea21e7fe29 100644
--- a/src/hotspot/share/opto/runtime.cpp
+++ b/src/hotspot/share/opto/runtime.cpp
@@ -1538,7 +1538,6 @@ static const TypeFunc* make_kyberAddPoly_2_Type() {
return TypeFunc::make(domain, range);
}
-
// Kyber add 3 polynomials function
static const TypeFunc* make_kyberAddPoly_3_Type() {
int argcnt = 4;
@@ -1560,7 +1559,6 @@ static const TypeFunc* make_kyberAddPoly_3_Type() {
return TypeFunc::make(domain, range);
}
-
// Kyber XOF output parsing into polynomial coefficients candidates
// or decompress(12,...) function
static const TypeFunc* make_kyber12To16_Type() {
From 1d3c7e247c27126b8538ea21bbf3f2e64deb5d66 Mon Sep 17 00:00:00 2001
From: Marc Chevalier
Date: Mon, 17 Aug 2026 08:03:05 +0000
Subject: [PATCH 31/88] 8252185: [Valhalla] Improve performance of
identityHashCode for value objects
Reviewed-by: thartmann, fparain
---
src/hotspot/share/ci/ciInstance.cpp | 7 +
src/hotspot/share/ci/ciInstance.hpp | 2 +
src/hotspot/share/ci/ciInstanceKlass.cpp | 22 +
src/hotspot/share/ci/ciInstanceKlass.hpp | 5 +
.../share/classfile/classFileParser.cpp | 32 +
.../share/classfile/classFileParser.hpp | 1 +
src/hotspot/share/oops/inlineKlass.cpp | 8 +-
src/hotspot/share/oops/inlineKlass.hpp | 70 +-
src/hotspot/share/opto/callnode.cpp | 89 ++-
src/hotspot/share/opto/callnode.hpp | 1 +
src/hotspot/share/opto/compile.cpp | 2 +-
src/hotspot/share/opto/graphKit.hpp | 8 +
src/hotspot/share/opto/inlinetypenode.cpp | 109 +++
src/hotspot/share/opto/inlinetypenode.hpp | 4 +
src/hotspot/share/opto/library_call.cpp | 254 ++++++-
src/hotspot/share/opto/library_call.hpp | 4 +
src/hotspot/share/opto/parse2.cpp | 11 +-
src/hotspot/share/runtime/arguments.cpp | 1 +
src/hotspot/share/runtime/globals.hpp | 3 +
.../java/lang/runtime/ValueObjectMethods.java | 2 +-
.../test/ApplicableIRRulesPrinter.java | 10 +-
.../inlinetypes/TestHashcodeFastPath.java | 719 ++++++++++++++++++
.../openjdk/bench/valhalla/hash/FastPath.java | 502 ++++++++++++
23 files changed, 1809 insertions(+), 57 deletions(-)
create mode 100644 test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestHashcodeFastPath.java
create mode 100644 test/micro/org/openjdk/bench/valhalla/hash/FastPath.java
diff --git a/src/hotspot/share/ci/ciInstance.cpp b/src/hotspot/share/ci/ciInstance.cpp
index dd6976caa25b..790d3f47f87e 100644
--- a/src/hotspot/share/ci/ciInstance.cpp
+++ b/src/hotspot/share/ci/ciInstance.cpp
@@ -235,6 +235,13 @@ ciConstant ciInstance::field_value_by_offset(int field_offset) {
return field_value(field);
}
+intptr_t ciInstance::hash() const {
+ VM_ENTRY_MARK;
+ oop obj = get_oop();
+ markWord mark = obj->mark();
+ if (mark.is_marked()) return markWord::no_hash;
+ return mark.hash();
+}
// ------------------------------------------------------------------
// ciInstance::print_impl
//
diff --git a/src/hotspot/share/ci/ciInstance.hpp b/src/hotspot/share/ci/ciInstance.hpp
index 706349707a2e..335a605afc34 100644
--- a/src/hotspot/share/ci/ciInstance.hpp
+++ b/src/hotspot/share/ci/ciInstance.hpp
@@ -67,6 +67,8 @@ class ciInstance : public ciObject {
// Constant value of a field at the specified offset.
ciConstant field_value_by_offset(int field_offset);
+ intptr_t hash() const;
+
ciKlass* java_lang_Class_klass();
char* java_lang_String_str(char* buf, size_t buflen);
};
diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp
index 596a0a8415af..d6791fab709c 100644
--- a/src/hotspot/share/ci/ciInstanceKlass.cpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.cpp
@@ -698,6 +698,28 @@ bool ciInstanceKlass::has_object_fields() const {
);
}
+int ciInstanceKlass::number_of_nonoop_entries_in_acmp_map() const {
+ VM_ENTRY_MARK;
+ return get_instanceKlass()->acmp_maps_array()->at(0);
+}
+int ciInstanceKlass::number_of_oop_entries_in_acmp_map() const {
+ VM_ENTRY_MARK;
+ const Array* acmp_maps = get_instanceKlass()->acmp_maps_array();
+ int number_of_nonoop_entries = acmp_maps->at(0);
+ return acmp_maps->length() - number_of_nonoop_entries * 2 - 1;
+}
+AcmpMapSegment ciInstanceKlass::get_nonoop_segment_of_acmp_map(int i) const {
+ VM_ENTRY_MARK;
+ const Array* acmp_maps = get_instanceKlass()->acmp_maps_array();
+#ifdef ASSERT
+ int number_of_nonoop_entries = acmp_maps->at(0);
+ assert(0 <= i && i < number_of_nonoop_entries, "illegal index, should be in range [0, %d)", number_of_nonoop_entries);
+#endif
+ int offset = acmp_maps->at(2 * i + 1);
+ int size = acmp_maps->at(2 * i + 2);
+ return AcmpMapSegment(offset, size);
+}
+
bool ciInstanceKlass::compute_has_trusted_loader() {
ASSERT_IN_VM;
oop loader_oop = loader();
diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp
index 0f49fd197819..160c050d4680 100644
--- a/src/hotspot/share/ci/ciInstanceKlass.hpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.hpp
@@ -29,6 +29,7 @@
#include "ci/ciFlags.hpp"
#include "ci/ciKlass.hpp"
#include "ci/ciSymbol.hpp"
+#include "classfile/classFileParser.hpp"
#include "oops/instanceKlass.hpp"
// ciInstanceKlass
@@ -262,6 +263,10 @@ class ciInstanceKlass : public ciKlass {
return _nonstatic_fields->at(i);
}
+ int number_of_oop_entries_in_acmp_map() const;
+ int number_of_nonoop_entries_in_acmp_map() const;
+ AcmpMapSegment get_nonoop_segment_of_acmp_map(int i) const;
+
ciInstanceKlass* unique_concrete_subklass();
bool has_finalizable_subclass();
diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index 71a09f8d0142..99aef4bb6c76 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -5483,6 +5483,34 @@ void ClassFileParser::set_fast_acmp_members(InlineKlass* vk) const {
#endif // VM_LITTLE_ENDIAN
}
+// See the declarations of _fast_hashcode_offset and _fast_hashcode_shift in InlineKlass::Members
+// for details about the fast path logic, and the meaning of these values.
+void ClassFileParser::set_fast_hashcode_members(InlineKlass* vk) const {
+ if (_layout_info->_oop_acmp_map->length() > 0) { // Oops are not allowed in the fast path
+ return;
+ }
+ if (_layout_info->_nonoop_acmp_map->length() >= 2) { // We handle at most one segment...
+ return;
+ }
+
+ if (_layout_info->_nonoop_acmp_map->length() == 0) {
+ vk->set_fast_hashcode_offset(0);
+ vk->set_fast_hashcode_shift(0);
+ return;
+ }
+
+ assert(_layout_info->_nonoop_acmp_map->length() == 1, "trivially");
+
+ int piece_size = _layout_info->_nonoop_acmp_map->at(0)._size;
+ if (piece_size != 1 && piece_size != 2 && piece_size != 4 && piece_size != 8) { // ...and it must have a convenient size
+ return;
+ }
+
+ int piece_start = _layout_info->_nonoop_acmp_map->at(0)._offset;
+ vk->set_fast_hashcode_offset(piece_start - (BytesPerLong - piece_size));
+ vk->set_fast_hashcode_shift(BitsPerByte * (BytesPerLong - piece_size));
+}
+
void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
bool changed_by_loadhook,
const ClassInstanceInfo& cl_inst_info,
@@ -5722,6 +5750,10 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
set_fast_acmp_members(vk);
}
+ if (UseHashcodeFastPath) {
+ set_fast_hashcode_members(vk);
+ }
+
vk->initialize_calling_convention(CHECK);
}
diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp
index 29550a25121a..fe5b58dc4be0 100644
--- a/src/hotspot/share/classfile/classFileParser.hpp
+++ b/src/hotspot/share/classfile/classFileParser.hpp
@@ -543,6 +543,7 @@ class ClassFileParser {
void create_acmp_maps(InstanceKlass* ik, TRAPS);
void set_fast_acmp_members(InlineKlass* vk) const;
+ void set_fast_hashcode_members(InlineKlass* vk) const;
public:
ClassFileParser(ClassFileStream* stream,
diff --git a/src/hotspot/share/oops/inlineKlass.cpp b/src/hotspot/share/oops/inlineKlass.cpp
index 1ea28e9c6880..b37e1ff80f66 100644
--- a/src/hotspot/share/oops/inlineKlass.cpp
+++ b/src/hotspot/share/oops/inlineKlass.cpp
@@ -74,8 +74,10 @@ InlineKlass::Members::Members()
_nullable_non_atomic_size_in_bytes(-1),
_null_marker_offset(-1),
_fast_acmp_offset(-1),
- _fast_acmp_mask(0) {
-}
+ _fast_acmp_mask(0),
+ _fast_hashcode_offset(-1),
+ _fast_hashcode_shift(0)
+{}
InlineKlass::InlineKlass() {
assert(CDSConfig::is_dumping_archive() || UseSharedSpaces, "only for CDS");
@@ -576,6 +578,8 @@ void InlineKlass::Members::print_on(outputStream* st) const {
st->print_cr(BULLET"null marker offset: %d", _null_marker_offset);
st->print_cr(BULLET"fast acmp offset: %d", _fast_acmp_offset);
st->print_cr(BULLET"fast acmp mask: " INT64_FORMAT_X_0, _fast_acmp_mask);
+ st->print_cr(BULLET"fast hashcode offset: %d", _fast_hashcode_offset);
+ st->print_cr(BULLET"fast hashcode shift: %d", _fast_hashcode_shift);
}
#undef BULLET
diff --git a/src/hotspot/share/oops/inlineKlass.hpp b/src/hotspot/share/oops/inlineKlass.hpp
index 63922f20b6f7..97ef09a9d540 100644
--- a/src/hotspot/share/oops/inlineKlass.hpp
+++ b/src/hotspot/share/oops/inlineKlass.hpp
@@ -105,7 +105,7 @@ class InlineKlass: public InstanceKlass {
//
// This doesn't always apply, for instance, if there are oops among the fields, we shouldn't carelessly load and compare:
// the GC might move the object in between.
- // To signal this fast path cannot be done on this current class, simply put 0 in _fast_acmp_mask.
+ // To signal this fast path cannot be done on this current class, simply put -1 in _fast_acmp_offset.
//
// We also should take care of not loading further than the object, even if it means reading part of the header.
// For this reason, we can't use _payload_offset, but we need our special offset.
@@ -114,6 +114,60 @@ class InlineKlass: public InstanceKlass {
int _fast_acmp_offset; // if < 0, fast acmp doesn't apply
int64_t _fast_acmp_mask; // can be 0 for empty value classes
+ // When we can't intrinsify the identityHashCode, we can still avoid the Java call at runtime if the value object is nice
+ // enough. This fast path basically implements the method ValueObjectMethods::valueObjectHashCode in a special case. This
+ // special case is when there is at most one no-oop segment in the acmp maps, that this segment (if it exists) is 1, 2, 4
+ // or 8 byte long, and there is no oop in the acmp maps. Basically, valueObjectHashCode makes 0 or 1 iteration of the big
+ // outer loop, and one iteration of one of the inner loops. The fast path loads a long at the given offset, isolates the
+ // numeric value we are interested in, and does the arithmetic.
+ //
+ // There are cases:
+ // 1. hashcode fast path doesn't apply: we set _fast_hashcode_offset < 0
+ // 2. the object has no segments (i.e. it is empty): we set _fast_hashcode_offset = 0
+ // 3. the object has one segment: we set _fast_hashcode_offset according to where we should load.
+ //
+ // Alike for the acmp fast path, we must not load further than the object, and we use the same trick as for acmp, and we
+ // load possibly some part of the header. The cases 2. and 3. cannot collide since loading at offset 0 would read only the
+ // header, and no payload.
+ //
+ // But unlike acmp, we need the actual arithmetic value, and resetting irrelevant bits is not correct. To do that, we need
+ // to have a different logic wrt. endianness. Moreover, the fast path needs to handle differently when the segment is 8-byte
+ // long, just as valueObjectHashCode does, while the arithmetic for segments of size 1, 2 or 4 is the same. This is also known
+ // by a endianness-dependent test.
+ int _fast_hashcode_offset; // if < 0, fast hashcode doesn't apply
+
+ // It turns out we need the same helping data for little and big endian at the moment. Yet, the logic is not quite the same.
+ //
+ // === LITTLE ENDIAN ===
+ // In little endian, the memory layout, with a 4-byte segment whose value (as returned by getInt) would be 0x01 02 03 04. The
+ // memory layout of the object would be something like:
+ // v- start of payload
+ // ....header.... | 04 03 02 01
+ // \___________|___________/
+ // Not to load too far, we load at offset "start of payload" - 4, so, we get some header bytes, and we get the long value
+ // 0x01 02 03 04 HH HH HH HH, where HH are header bytes. To get the integer value, we can simply do an arithmetic right shift,
+ // by 4 bytes (32 bits) in this case. By doing an arithmetic right shift, we conserve the mathematical value, even if we cut
+ // higher bits (as long as we leave at least as much as the block we load).
+ //
+ // === BIG ENDIAN ===
+ // In big endian, the memory layout, with a 4-byte segment whose value (as returned by getInt) would be 0x01 02 03 04. The
+ // memory layout of the object would be something like:
+ // v- start of payload
+ // ....header.... | 01 02 03 04
+ // \___________|___________/
+ // Not to load too far, we load at offset "start of payload" - 4, so, we get some header bytes, and we get the long value
+ // 0xHH HH HH HH 01 02 03 04, where HH are header bytes. To get the integer value, we can simply so a left shift, which
+ // fills the lower bits with 0, followed by a arithmetic right shift, to preserve the mathematical value. The shift magnitude
+ // is equal to the number of bits we need to discard. In this example, that is 32.
+ //
+ // === COMMON ===
+ // This field is saying by how much we need to shift. Since we keep 1, 2, 4 or 8 bytes, the legal values of _fast_hashcode_shift
+ // are 8 * (8 - (1, 2, 4, 8)) = 8 * (7, 6, 4, 0) = 56, 48, 32, 0.
+ //
+ // The fast path is aware we are loading a long if the shift is 0.
+ // Value is not specified (and does not matter) if _fast_hashcode_offset <= 0
+ int _fast_hashcode_shift;
+
Members();
void print_on(outputStream* st) const;
@@ -212,6 +266,12 @@ class InlineKlass: public InstanceKlass {
int64_t fast_acmp_mask() const { return members()._fast_acmp_mask; }
void set_fast_acmp_mask(int64_t mask) { members()._fast_acmp_mask = mask; }
+ int fast_hashcode_offset() const { return members()._fast_hashcode_offset; }
+ void set_fast_hashcode_offset(int offset) { members()._fast_hashcode_offset = offset; }
+
+ int fast_hashcode_shift() const { return members()._fast_hashcode_shift; }
+ void set_fast_hashcode_shift(int shift) { members()._fast_hashcode_shift = shift; }
+
bool supports_nullable_layouts() const {
return has_nullable_non_atomic_layout() || has_nullable_atomic_layout();
}
@@ -339,6 +399,14 @@ class InlineKlass: public InstanceKlass {
return byte_offset_of(Members, _fast_acmp_mask);
}
+ static ByteSize fast_hashcode_offset_offset() {
+ return byte_offset_of(Members, _fast_hashcode_offset);
+ }
+
+ static ByteSize fast_hashcode_shift_offset() {
+ return byte_offset_of(Members, _fast_hashcode_shift);
+ }
+
oop null_reset_value() const;
void set_null_reset_value(oop val);
diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp
index 3916b20f59ac..d2926f4ebb98 100644
--- a/src/hotspot/share/opto/callnode.cpp
+++ b/src/hotspot/share/opto/callnode.cpp
@@ -37,6 +37,7 @@
#include "opto/convertnode.hpp"
#include "opto/escape.hpp"
#include "opto/inlinetypenode.hpp"
+#include "opto/library_call.hpp"
#include "opto/locknode.hpp"
#include "opto/machnode.hpp"
#include "opto/matcher.hpp"
@@ -1195,14 +1196,22 @@ Node* CallStaticJavaNode::Ideal(PhaseGVN* phase, bool can_reshape) {
}
}
- // Try to replace the runtime call to the substitutability test emitted by acmp if we can reason
- // about the operands
- if (can_reshape && !control()->is_top() && !memory()->is_top() && method() != nullptr &&
- method()->holder() == phase->C->env()->ValueObjectMethods_klass() &&
- method()->name() == ciSymbols::isSubstitutable_name()) {
- Node* res = replace_is_substitutable(phase->is_IterGVN());
- if (res != nullptr) {
- return res;
+ if (can_reshape && !control()->is_top() && !memory()->is_top() && method() != nullptr) {
+ if (method()->holder() == phase->C->env()->ValueObjectMethods_klass() &&
+ method()->name() == ciSymbols::isSubstitutable_name()) {
+ // Try to replace the runtime call to the substitutability test emitted by acmp if we can reason
+ // about the operands
+ Node* res = replace_is_substitutable(phase->is_IterGVN());
+ if (res != nullptr) {
+ return res;
+ }
+ } else if (method()->holder() == phase->C->env()->System_klass() &&
+ method()->name() == ciSymbols::identityHashCode_name()) {
+ // Same with identityHashCode
+ Node* res = replace_identity_hash_code(phase->is_IterGVN());
+ if (res != nullptr) {
+ return res;
+ }
}
}
@@ -1398,6 +1407,70 @@ bool CallStaticJavaNode::remove_unknown_flat_array_load(PhaseIterGVN* igvn, Node
return true;
}
+Node* CallStaticJavaNode::replace_identity_hash_code(PhaseIterGVN* igvn) {
+ Node* arg = in(TypeFunc::Parms);
+ intptr_t klass_hash;
+ if (!InlineTypeNode::can_emit_identity_hash_code(*igvn, arg, klass_hash)) {
+ // We can't expand, but now, maybe we can also tell the fast path won't work
+ const Type* arg_type = igvn->type(arg);
+ if (UseHashcodeFastPath && igvn->type(arg)->is_inlinetypeptr()) {
+ ciInlineKlass* vk = arg_type->inline_klass();
+ bool fast_path_wont_work = false;
+ fast_path_wont_work = fast_path_wont_work || vk->number_of_oop_entries_in_acmp_map() > 0;
+ fast_path_wont_work = fast_path_wont_work || vk->number_of_nonoop_entries_in_acmp_map() > 1;
+ if (vk->number_of_nonoop_entries_in_acmp_map() == 1) {
+ int size = vk->get_nonoop_segment_of_acmp_map(0)._size;
+ fast_path_wont_work = fast_path_wont_work || (size != 1 && size != 2 && size != 4 && size != 8);
+ }
+ if (fast_path_wont_work) {
+ IfNode* fast_path_if = LibraryCallKit::hashcode_fast_path_if_from_identity_hash_code_call(igvn, this);
+ if (fast_path_if != nullptr) {
+ fast_path_if->set_req(1, igvn->intcon(1));
+ igvn->_worklist.push(fast_path_if);
+ return this;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ // Delay IGVN during macro expansion
+ assert(!igvn->delay_transform(), "must not delay during Ideal");
+ igvn->set_delay_transform(true);
+ GraphKit kit(this, *igvn);
+
+ Node* replace = InlineTypeNode::emit_identity_hash_code(&kit, arg, klass_hash);
+ igvn->set_delay_transform(false);
+ assert(replace != nullptr, "must succeed");
+
+ if (UseHashcodeFastPath) {
+ // Sabotage the fast hashcode path
+ IfNode* fast_path_if = LibraryCallKit::hashcode_fast_path_if_from_identity_hash_code_call(igvn, this);
+ if (fast_path_if != nullptr) {
+ fast_path_if->set_req(1, igvn->intcon(1));
+ igvn->_worklist.push(fast_path_if);
+ }
+ }
+
+ // Kill exception projections and return a tuple that will replace the call
+ CallProjections* projs = extract_projections(false /*separate_io_proj*/);
+ if (projs->fallthrough_catchproj != nullptr) {
+ igvn->replace_node(projs->fallthrough_catchproj, kit.control());
+ }
+ if (projs->catchall_memproj != nullptr) {
+ igvn->replace_node(projs->catchall_memproj, igvn->C->top());
+ }
+ if (projs->catchall_ioproj != nullptr) {
+ igvn->replace_node(projs->catchall_ioproj, igvn->C->top());
+ }
+ if (projs->catchall_catchproj != nullptr) {
+ igvn->replace_node(projs->catchall_catchproj, igvn->C->top());
+ }
+ Node* new_mem = kit.reset_memory();
+ assert(in(TypeFunc::Memory) == new_mem, "must not modify memory");
+ return TupleNode::make(tf()->range_cc(), igvn->C->top(), kit.i_o(), new_mem, kit.frameptr(), kit.returnadr(), replace);
+}
+
// Try to replace a runtime call to the substitutability test by either a simple pointer comparison
// if either operand is not a value object, or comparing their fields if either operand is an
// object of a known value type
diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp
index 465d1d429a26..229108614f36 100644
--- a/src/hotspot/share/opto/callnode.hpp
+++ b/src/hotspot/share/opto/callnode.hpp
@@ -905,6 +905,7 @@ class CallStaticJavaNode : public CallJavaNode {
bool remove_unknown_flat_array_load(PhaseIterGVN* igvn, Node* ctl, Node* mem, Node* unc_arg);
Node* replace_is_substitutable(PhaseIterGVN* igvn);
+ Node* replace_identity_hash_code(PhaseIterGVN* igvn);
public:
CallStaticJavaNode(Compile* C, const TypeFunc* tf, address addr, ciMethod* method)
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index 8e92471d4dfa..e12eec300899 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -1413,7 +1413,7 @@ const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const {
// Process weird unsafe references.
if (offset == Type::OffsetBot && (tj->isa_instptr() /*|| tj->isa_klassptr()*/)) {
- assert(InlineUnsafeOps || StressReflectiveCode || UseAcmpFastPath, "indeterminate pointers come only from unsafe ops");
+ assert(InlineUnsafeOps || StressReflectiveCode || UseAcmpFastPath || UseHashcodeFastPath, "indeterminate pointers come only from unsafe ops");
assert(!is_known_inst, "scalarizable allocation should not have unsafe references");
tj = TypeOopPtr::BOTTOM;
ptr = tj->ptr();
diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp
index 9310a721dc60..1c109cb56757 100644
--- a/src/hotspot/share/opto/graphKit.hpp
+++ b/src/hotspot/share/opto/graphKit.hpp
@@ -345,6 +345,7 @@ class GraphKit : public Phase {
Node* DivI(Node* ctl, Node* l, Node* r) { return _gvn.transform(new DivINode(ctl, l, r)); }
Node* AndI(Node* l, Node* r) { return _gvn.transform(new AndINode(l, r)); }
+ Node* AndL(Node* l, Node* r) { return _gvn.transform(new AndLNode(l, r)); }
Node* OrI(Node* l, Node* r) { return _gvn.transform(new OrINode(l, r)); }
Node* XorI(Node* l, Node* r) { return _gvn.transform(new XorINode(l, r)); }
@@ -352,13 +353,20 @@ class GraphKit : public Phase {
Node* MinI(Node* l, Node* r) { return _gvn.transform(new MinINode(l, r)); }
Node* LShiftI(Node* l, Node* r) { return _gvn.transform(new LShiftINode(l, r)); }
+ Node* LShiftL(Node* l, Node* r) { return _gvn.transform(new LShiftLNode(l, r)); }
Node* RShiftI(Node* l, Node* r) { return _gvn.transform(new RShiftINode(l, r)); }
+ Node* RShiftL(Node* l, Node* r) { return _gvn.transform(new RShiftLNode(l, r)); }
Node* URShiftI(Node* l, Node* r) { return _gvn.transform(new URShiftINode(l, r)); }
+ Node* URShiftL(Node* l, Node* r) { return _gvn.transform(new URShiftLNode(l, r)); }
+ Node* URShiftX(Node* l, Node* r) { return _gvn.transform(new URShiftXNode(l, r)); }
Node* CmpI(Node* l, Node* r) { return _gvn.transform(new CmpINode(l, r)); }
Node* CmpL(Node* l, Node* r) { return _gvn.transform(new CmpLNode(l, r)); }
Node* CmpP(Node* l, Node* r) { return _gvn.transform(new CmpPNode(l, r)); }
Node* Bool(Node* cmp, BoolTest::mask relop) { return _gvn.transform(new BoolNode(cmp, relop)); }
+ Node* BoolCmpI(Node* l, BoolTest::mask relop, Node* r) { return Bool(CmpI(l, r), relop); }
+ Node* BoolCmpL(Node* l, BoolTest::mask relop, Node* r) { return Bool(CmpL(l, r), relop); }
+ Node* BoolCmpP(Node* l, BoolTest::mask relop, Node* r) { return Bool(CmpP(l, r), relop); }
Node* AddP(Node* b, Node* a, Node* o) { return _gvn.transform(AddPNode::make_with_base(b, a, o)); }
diff --git a/src/hotspot/share/opto/inlinetypenode.cpp b/src/hotspot/share/opto/inlinetypenode.cpp
index 2f550fec89fe..48dc5ec1562e 100644
--- a/src/hotspot/share/opto/inlinetypenode.cpp
+++ b/src/hotspot/share/opto/inlinetypenode.cpp
@@ -1021,6 +1021,115 @@ Node* InlineTypeNode::emit_substitutability_check(GraphKit* kit, Node* lhs, Node
return result;
}
+// Check if identityHashCode of 'arg' can be implemented in IR.
+// Set klass_hash to be used as the seed of the hash. It might not be available later,
+// and we want emit_identity_hash_code not to fail on that.
+bool InlineTypeNode::can_emit_identity_hash_code(const PhaseIterGVN& igvn, Node* arg, intptr_t& klass_hash) {
+ const Type* arg_type = igvn.type(arg);
+ if (arg_type == TypePtr::NULL_PTR) {
+ return true;
+ }
+ if (!arg_type->is_inlinetypeptr()) {
+ return false;
+ }
+ ciInlineKlass* vk = arg_type->inline_klass();
+ if (vk->number_of_oop_entries_in_acmp_map() > 0) {
+ return false;
+ }
+ klass_hash = vk->java_mirror()->hash();
+ if (klass_hash == markWord::no_hash) {
+ return false;
+ }
+ return true;
+}
+
+Node* InlineTypeNode::emit_identity_hash_code(GraphKit* kit, Node* arg, intptr_t klass_hash) {
+ if (!kit->C->allow_macro_nodes()) {
+ // After macro expansion, InlineTypeNodes are also eliminated, creation of new ones then is not
+ // allowed
+ return nullptr;
+ }
+ PhaseIterGVN& igvn = *kit->gvn().is_IterGVN();
+
+ RegionNode* region = new RegionNode(1);
+ PhiNode* phi_result = new PhiNode(region, TypeInt::INT);
+ igvn.register_new_node_with_optimizer(region);
+ igvn.register_new_node_with_optimizer(phi_result);
+
+ Node* null_ctl = kit->top();
+ arg = kit->null_check_oop(arg, &null_ctl, false, false, false);
+ if (!null_ctl->is_top()) {
+ region->add_req(null_ctl);
+ phi_result->add_req(kit->intcon(0));
+ }
+ const Type* arg_type = igvn.type(arg);
+ if (arg_type->empty()) {
+ kit->set_control(region);
+ return phi_result;
+ }
+
+ assert(arg_type->is_inlinetypeptr(), "should be a value object at this point");
+ assert(!arg_type->maybe_null(), "must check null beforehand");
+ ciInlineKlass* vk = arg_type->inline_klass();
+ int number_of_nonoop_entries = vk->number_of_nonoop_entries_in_acmp_map();
+ assert(vk->number_of_oop_entries_in_acmp_map() == 0, "cannot have oops here");
+
+ auto make_load = [&](int offset, const Type* type, BasicType bt) -> Node* {
+ Node* adr = kit->basic_plus_adr(arg, offset);
+ ciField* field = vk->get_field_by_offset(offset, false);
+ // If the load is by chance not a mismatch, let's mark it so. This way, loading the field can be simplified
+ bool is_mismatch = field == nullptr || field->type()->basic_type() != bt;
+ if (bt == T_BYTE && field != nullptr && field->type()->basic_type() == T_BOOLEAN) {
+ is_mismatch = false;
+ bt = T_BOOLEAN;
+ type = TypeInt::BOOL;
+ }
+ return kit->make_load(kit->control(), adr, type, bt, MemNode::unordered, LoadNode::DependsOnlyOnTest, false, false, is_mismatch, is_mismatch);
+ };
+
+ Node* const thirty_one = kit->intcon(31);
+ Node* result = kit->intcon(checked_cast(klass_hash));
+ for (int i = 0; i < number_of_nonoop_entries; i++) {
+ AcmpMapSegment segment = vk->get_nonoop_segment_of_acmp_map(i);
+ int offset = segment._offset;
+ int size = segment._size;
+ int nlong = size / 8;
+ for (int j = 0; j < nlong; j++) {
+ Node* la = make_load(offset, TypeLong::LONG, T_LONG);
+ result = kit->AddI(kit->MulI(thirty_one, result), kit->ConvL2I(la));
+ result = kit->AddI(kit->MulI(thirty_one, result), kit->ConvL2I(kit->URShiftL(la, kit->intcon(32))));
+ offset += 8;
+ }
+ size -= nlong * 8;
+ int nint = size / 4;
+ for (int j = 0; j < nint; j++) {
+ Node* ia = make_load(offset, TypeInt::INT, T_INT);
+ result = kit->AddI(kit->MulI(thirty_one, result), ia);
+ offset += 4;
+ }
+ size -= nint * 4;
+ int nshort = size / 2;
+ for (int j = 0; j < nshort; j++) {
+ Node* sa = make_load(offset, TypeInt::SHORT, T_SHORT);
+ result = kit->AddI(kit->MulI(thirty_one, result), sa);
+ offset += 2;
+ }
+ size -= nshort * 2;
+ for (int j = 0; j < size; j++) {
+ Node* ba = make_load(offset, TypeInt::BYTE, T_BYTE);
+ result = kit->AddI(kit->MulI(thirty_one, result), ba);
+ offset++;
+ }
+ }
+ result = kit->AndI(result, kit->intcon(markWord::hash_mask));
+
+ region->add_req(kit->control());
+ phi_result->add_req(result);
+
+ kit->set_control(region);
+ return phi_result;
+}
+
InlineTypeNode* InlineTypeNode::buffer(GraphKit* kit, bool safe_for_replace) {
if (is_allocated(&kit->gvn())) {
// Already buffered
diff --git a/src/hotspot/share/opto/inlinetypenode.hpp b/src/hotspot/share/opto/inlinetypenode.hpp
index 3c1dea99ba60..1ef4ebd007b0 100644
--- a/src/hotspot/share/opto/inlinetypenode.hpp
+++ b/src/hotspot/share/opto/inlinetypenode.hpp
@@ -135,6 +135,10 @@ class InlineTypeNode : public TypeNode {
static bool can_emit_substitutability_check(PhaseGVN* phase, Node* lhs, Node* rhs);
static Node* emit_substitutability_check(GraphKit* kit, Node* lhs, Node* rhs);
+ // Implementation of identityHashCode for value classes with restrictions (e.g. no oops)
+ static bool can_emit_identity_hash_code(const PhaseIterGVN& igvn, Node* arg, intptr_t& klass_hash);
+ static Node* emit_identity_hash_code(GraphKit* kit, Node* arg, intptr_t klass_hash);
+
// Allocates the inline type (if not yet allocated)
InlineTypeNode* buffer(GraphKit* kit, bool safe_for_replace = true);
bool is_allocated(PhaseGVN* phase) const;
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index 0fa509d50529..5945f6f072ea 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -5495,11 +5495,67 @@ LibraryCallKit::generate_method_call(vmIntrinsicID method_id, bool is_virtual, b
* be virtual (invokevirtual) or bound (invokespecial). For each case we generate
* slightly different code.
*/
+Node* LibraryCallKit::get_hashcode_from_header(Node* header, RegionNode* unset_region) {
+ // Get the hash value and check to see that it has been properly assigned.
+ // We depend on hash_mask being at most 32 bits and avoid the use of
+ // hash_mask_in_place because it could be larger than 32 bits in a 64-bit
+ // vm: see markWord.hpp.
+ Node* hash_mask = _gvn.intcon(markWord::hash_mask);
+ Node* hash_shift = _gvn.intcon(markWord::hash_shift);
+ Node* hshifted_header = _gvn.transform(new URShiftXNode(header, hash_shift));
+ // This hack lets the hash bits live anywhere in the mark object now, as long
+ // as the shift drops the relevant bits into the low 32 bits. Note that
+ // Java spec says that HashCode is an int so there's no point in capturing
+ // an 'X'-sized hashcode (32 in 32-bit build or 64 in 64-bit build).
+ hshifted_header = ConvX2I(hshifted_header);
+ Node* hash_val = _gvn.transform(new AndINode(hshifted_header, hash_mask));
+
+ Node* no_hash_val = _gvn.intcon(markWord::no_hash);
+ Node* chk_assigned = _gvn.transform(new CmpINode( hash_val, no_hash_val));
+ Node* test_assigned = _gvn.transform(new BoolNode( chk_assigned, BoolTest::eq));
+
+ generate_slow_guard(test_assigned, unset_region);
+
+ return hash_val;
+}
+
+/* The overall logic is something like
+ *
+ * null_path:
+ * if receiver is null {
+ * if static { return 0 } else { null pointer exception }
+ * }
+ *
+ * cache_path:
+ * if header is not safe to read { goto inline_fast_path }
+ * hash = read_hash_from_header()
+ * if hash is empty { goto inline_fast_path }
+ * return hash
+ *
+ * inline_fast_path:
+ * if not static { goto slow }
+ * if not value object { goto slow }
+ * if value klass has no fast path { goto slow }
+ * if klass header is not safe to read { goto slow }
+ * k_hash = read_hash_from_klass_header()
+ * if k_hash is empty { goto slow }
+ * return fast_hashcode_path (see InlineKlass::Members::_fast_hashcode_offset et seqq. for details on how this is computed)
+ *
+ * slow:
+ * runtime call to hash function (this may be replaced with the expanded form during IGVN. See CallStaticJavaNode::replace_identity_hash_code)
+ *
+ */
bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
assert(is_static == callee()->is_static(), "correct intrinsic selection");
assert(!(is_virtual && is_static), "either virtual, special, or static");
- enum { _slow_path = 1, _fast_path, _null_path, PATH_LIMIT };
+ enum {
+ _slow_path = 1, // Actually perform the runtime call
+ _cache_path, // Get the hash from the header
+ _null_path, // If object is null, hash is 0.
+ _inline_fast_path, // Fast path for value objects only (see InlineKlass::Members::_fast_hashcode_offset et seqq.)
+ PATH_LIMIT,
+ };
RegionNode* result_reg = new RegionNode(PATH_LIMIT);
PhiNode* result_val = new PhiNode(result_reg, TypeInt::INT);
@@ -5507,10 +5563,11 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
PhiNode* result_mem = new PhiNode(result_reg, Type::MEMORY, TypePtr::BOTTOM);
Node* obj = argument(0);
- // Don't intrinsify hashcode on inline types for now.
- // The "is locked" runtime check also subsumes the inline type check (as inline types cannot be locked) and goes to the slow path.
- if (gvn().type(obj)->is_inlinetypeptr()) {
- return false;
+ if (obj->is_InlineType()) {
+ PreserveReexecuteState preexecs(this);
+ inc_sp(2);
+ jvms()->set_should_reexecute(true);
+ obj = obj->as_InlineType()->buffer(this);
}
if (!is_static) {
@@ -5536,10 +5593,13 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
return true;
}
- // We only go to the fast case code if we pass a number of guards. The
- // paths which do not pass are accumulated in the slow_region.
+ // We only go to the cache case code if we pass a number of guards. The paths which do
+ // not pass are accumulated in the inline_fast_path_region. The compute region tries
+ // to use the fast path for inline types. That also needs a lot of guards to be met.
+ // The paths which do not pass are accumulated in the slow_region, where we do the
+ // runtime call, which is the last resort.
+ RegionNode* inline_fast_path_region = new RegionNode(1);
RegionNode* slow_region = new RegionNode(1);
- record_for_igvn(slow_region);
// If this is a virtual call, we generate a funny guard. We pull out
// the vtable entry corresponding to hashCode() from the target object.
@@ -5547,9 +5607,9 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
// Object hashCode() method, we pass the guard. We do not need this
// guard for non-virtual calls -- the caller is known to be the native
// Object hashCode().
+ // After null check, get the object's klass.
+ Node* obj_klass = load_object_klass(obj);
if (is_virtual) {
- // After null check, get the object's klass.
- Node* obj_klass = load_object_klass(obj);
generate_virtual_guard(obj_klass, slow_region);
}
@@ -5560,35 +5620,94 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
Node* no_ctrl = nullptr;
Node* header = make_load(no_ctrl, header_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
- // Get the hash value and check to see that it has been properly assigned.
- // We depend on hash_mask being at most 32 bits and avoid the use of
- // hash_mask_in_place because it could be larger than 32 bits in a 64-bit
- // vm: see markWord.hpp.
- Node *hash_mask = _gvn.intcon(markWord::hash_mask);
- Node *hash_shift = _gvn.intcon(markWord::hash_shift);
- Node *hshifted_header= _gvn.transform(new URShiftXNode(header, hash_shift));
- // This hack lets the hash bits live anywhere in the mark object now, as long
- // as the shift drops the relevant bits into the low 32 bits. Note that
- // Java spec says that HashCode is an int so there's no point in capturing
- // an 'X'-sized hashcode (32 in 32-bit build or 64 in 64-bit build).
- hshifted_header = ConvX2I(hshifted_header);
- Node *hash_val = _gvn.transform(new AndINode(hshifted_header, hash_mask));
+ Node* hash_val = get_hashcode_from_header(header, inline_fast_path_region);
- Node *no_hash_val = _gvn.intcon(markWord::no_hash);
- Node *chk_assigned = _gvn.transform(new CmpINode( hash_val, no_hash_val));
- Node *test_assigned = _gvn.transform(new BoolNode( chk_assigned, BoolTest::eq));
+ result_val->init_req(_cache_path, hash_val);
+ result_reg->init_req(_cache_path, control());
- generate_slow_guard(test_assigned, slow_region);
+ set_control(_gvn.transform(inline_fast_path_region));
+ IfNode* fast_path_iff = nullptr;
+ if (!stopped()) {
+ if (UseHashcodeFastPath && is_static && !_gvn.type(obj)->is_inlinetypeptr()) {
+ Node* is_not_value = inline_type_test(obj, false);
+ generate_fair_guard(is_not_value, slow_region);
+ if (!stopped()) {
+ // See InlineKlass::Members::_fast_hashcode_offset et seqq. for details on the fast path logic
+ Node* members_addr = off_heap_plus_addr(obj_klass, in_bytes(InlineKlass::adr_members_offset()));
+ Node* members = make_load(control(), members_addr, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered);
+ Node* offset_addr = off_heap_plus_addr(members, in_bytes(InlineKlass::fast_hashcode_offset_offset()));
+ Node* offset = make_load(control(), offset_addr, TypeInt::INT, T_INT, MemNode::unordered);
+ Node* bol_no_fast_path = BoolCmpI(offset, BoolTest::lt, zerocon(T_INT));
+ generate_slow_guard(bol_no_fast_path, slow_region);
+ if (!stopped()) {
+ if (control()->is_IfFalse()) {
+ fast_path_iff = control()->in(0)->as_If();
+ }
+ Node* klass_header_addr = off_heap_plus_addr(load_mirror_from_klass(obj_klass), oopDesc::mark_offset_in_bytes());
+ Node* klass_header = make_load(no_ctrl, klass_header_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
+ Node* result_empty = get_hashcode_from_header(klass_header, slow_region);
+ if (!stopped()) {
+ // Now that we know fast path applies, there are 3 cases to distinguish here,
+ // that unmasked_region/unmasked_result merge:
+ // 1. the object has no segment, the hash is simply the hash of the class object
+ // 2. the object has one segment of size smaller than 8 (1, 2, 4)
+ // 3. the object has one segment of size 8 (long-sized)
+ // See inlineKlass.hpp on why and how to tell them apart.
+ RegionNode* unmasked_region = new RegionNode(4);
+ Node* unmasked_result = new PhiNode(unmasked_region, TypeInt::INT);
+
+ // Case 1. no segment
+ Node* bol_empty_object = BoolCmpI(offset, BoolTest::eq, zerocon(T_INT));
+ IfNode* iff_is_empty_object = create_and_map_if(control(), bol_empty_object, PROB_FAIR, COUNT_UNKNOWN);
+ unmasked_region->init_req(1, IfTrue(iff_is_empty_object));
+ unmasked_result->init_req(1, result_empty);
+
+ set_control(IfFalse(iff_is_empty_object));
+
+ Node* obj_payload_addr = basic_plus_adr(obj, ConvI2L(offset));
+ Node* obj_payload = make_load(control(), obj_payload_addr, TypeLong::LONG, T_LONG, MemNode::unordered, LoadNNode::DependsOnlyOnTest, false, true, true, true);
+
+ Node* shift_addr = off_heap_plus_addr(members, in_bytes(InlineKlass::fast_hashcode_shift_offset()));
+ Node* shift = make_load(control(), shift_addr, TypeInt::INT, T_INT, MemNode::unordered);
+#ifdef VM_LITTLE_ENDIAN
+ // *(obj + offset) >> shift
+ Node* obj_extracted = RShiftL(obj_payload, shift);
+#else
+ // (*(obj + offset) << shift) >> shift
+ Node* obj_payload_left_shifted = LShiftL(obj_payload, shift);
+ Node* obj_extracted = RShiftL(obj_payload_left_shifted, shift);
+#endif
+ Node* is_long_payload_bol = BoolCmpI(shift, BoolTest::eq, intcon(0));
+ IfNode* iff_is_long_payload = create_and_map_if(control(), is_long_payload_bol, PROB_FAIR, COUNT_UNKNOWN);
+
+ // Case 2. one segment, less than 8-byte long
+ Node* result_int = AddI(MulI(intcon(31), result_empty), ConvL2I(obj_extracted));
+ unmasked_region->init_req(2, IfFalse(iff_is_long_payload));
+ unmasked_result->init_req(2, result_int);
+
+ // Case 3. one segment, 8-byte long
+ Node* result_long = AddI(MulI(intcon(31), result_int), ConvL2I(URShiftL(obj_extracted, intcon(32))));
+ unmasked_region->init_req(3, IfTrue(iff_is_long_payload));
+ unmasked_result->init_req(3, result_long);
+
+ Node* fast_path_result = AndI(_gvn.transform(unmasked_result), intcon(markWord::hash_mask));
+ result_reg->init_req(_inline_fast_path, _gvn.transform(unmasked_region));
+ result_val->init_req(_inline_fast_path, fast_path_result);
+ }
+ }
+ }
+ } else {
+ slow_region->add_req(control());
+ }
+ }
Node* init_mem = reset_memory();
- // fill in the rest of the null path:
result_io ->init_req(_null_path, i_o());
result_mem->init_req(_null_path, init_mem);
-
- result_val->init_req(_fast_path, hash_val);
- result_reg->init_req(_fast_path, control());
- result_io ->init_req(_fast_path, i_o());
- result_mem->init_req(_fast_path, init_mem);
+ result_io ->init_req(_cache_path, i_o());
+ result_mem->init_req(_cache_path, init_mem);
+ result_io ->set_req(_inline_fast_path, i_o());
+ result_mem ->set_req(_inline_fast_path, init_mem);
// Generate code for the slow case. We make a call to hashCode().
set_control(_gvn.transform(slow_region));
@@ -5597,7 +5716,9 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
set_all_memory(init_mem);
vmIntrinsics::ID hashCode_id = is_static ? vmIntrinsics::_identityHashCode : vmIntrinsics::_hashCode;
CallJavaNode* slow_call = generate_method_call(hashCode_id, is_virtual, is_static, false);
+ slow_call->set_req(TypeFunc::Parms, obj); // This obj is not null
Node* slow_result = set_results_for_java_call(slow_call);
+ assert(hashcode_fast_path_if_from_identity_hash_code_call(&_gvn, slow_call) == fast_path_iff, "");
// this->control() comes from set_results_for_java_call
result_reg->init_req(_slow_path, control());
result_val->init_req(_slow_path, slow_result);
@@ -5612,6 +5733,73 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
set_result(result_reg, result_val);
return true;
}
+IfNode* LibraryCallKit::hashcode_fast_path_if_from_identity_hash_code_call(PhaseGVN* phase, CallJavaNode* call) {
+ auto is_con_offset = [](Node* node, ByteSize n) -> bool {
+ if (!node->is_Con()) return false;
+ TypeNode* con = node->as_Type();
+ assert(con->type()->is_intptr_t(), "");
+ return con->type()->is_intptr_t()->is_con(in_bytes(n));
+ };
+
+ assert(call->in(TypeFunc::Control) != nullptr, "");
+ if (!call->in(TypeFunc::Control)->is_Region()) return nullptr;
+ RegionNode* region = call->in(TypeFunc::Control)->as_Region();
+ for (uint i = 1; i < region->req(); i++) {
+ assert(region->in(i) != nullptr, "");
+ if (!region->in(i)->is_IfProj()) continue;
+ IfProjNode* if_proj = region->in(i)->as_IfProj();
+ if (if_proj->_con != 1) continue;
+
+ assert(if_proj->in(0) != nullptr, "");
+ assert(if_proj->in(0)->is_If(), "");
+ IfNode* iff = if_proj->in(0)->as_If();
+
+ assert(iff->in(1) != nullptr, "");
+ if (!iff->in(1)->is_Bool()) continue;
+ BoolNode* lt = iff->in(1)->as_Bool();
+ if (lt->_test._test != BoolTest::lt) continue;
+
+ assert(lt->in(1) != nullptr, "");
+ if (lt->in(1)->Opcode() != Op_CmpI) continue;
+ CmpNode* cmp_i = lt->in(1)->as_Cmp();
+
+ assert(cmp_i->in(1) != nullptr, "");
+ assert(cmp_i->in(2) != nullptr, "");
+
+ if (cmp_i->in(1)->Opcode() != Op_LoadI) continue;
+ LoadNode* load_offset = cmp_i->in(1)->as_Load();
+ if (!cmp_i->in(2)->is_ConI()) continue;
+ ConINode* zero_i = cmp_i->in(2)->as_ConI();
+ assert(zero_i->type()->is_int() != nullptr, "");
+ if (!zero_i->type()->is_int()->is_con(0)) continue;
+
+ assert(load_offset->in(2) != nullptr, "");
+ if (!load_offset->in(2)->is_AddP()) continue;
+ AddPNode* offset_addr_add = load_offset->in(2)->as_AddP();
+
+ assert(offset_addr_add->in(AddPNode::Base) != nullptr, "");
+ assert(offset_addr_add->in(AddPNode::Address) != nullptr, "");
+ assert(offset_addr_add->in(AddPNode::Offset) != nullptr, "");
+ if (!offset_addr_add->in(AddPNode::Base)->is_top()) continue;
+ if (offset_addr_add->in(AddPNode::Address)->Opcode() != Op_LoadP) continue;
+ LoadNode* load_members = offset_addr_add->in(AddPNode::Address)->as_Load();
+ if (!is_con_offset(offset_addr_add->in(AddPNode::Offset), InlineKlass::fast_hashcode_offset_offset())) continue;
+
+ assert(load_members->in(2) != nullptr, "");
+ if (!load_members->in(2)->is_AddP()) continue;
+ AddPNode* members_addr_add = load_members->in(2)->as_AddP();
+
+ assert(members_addr_add->in(AddPNode::Base) != nullptr, "");
+ assert(members_addr_add->in(AddPNode::Address) != nullptr, "");
+ assert(members_addr_add->in(AddPNode::Offset) != nullptr, "");
+ if (!members_addr_add->in(AddPNode::Base)->is_top()) continue;
+ if (!phase->type(members_addr_add->in(AddPNode::Address))->isa_instklassptr()) continue;
+ if (!is_con_offset(members_addr_add->in(AddPNode::Offset), InlineKlass::adr_members_offset())) continue;
+
+ return iff;
+ }
+ return nullptr;
+}
//---------------------------inline_native_getClass----------------------------
// public final native Class> java.lang.Object.getClass();
diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp
index 7e3d65060d90..ce9735cc1748 100644
--- a/src/hotspot/share/opto/library_call.hpp
+++ b/src/hotspot/share/opto/library_call.hpp
@@ -312,7 +312,11 @@ class LibraryCallKit : public GraphKit {
bool inline_native_clone(bool is_virtual);
bool inline_native_Reflection_getCallerClass();
// Helper function for inlining native object hash method
+ Node* get_hashcode_from_header(Node* header, RegionNode* unset_region);
bool inline_native_hashcode(bool is_virtual, bool is_static);
+public:
+ static IfNode* hashcode_fast_path_if_from_identity_hash_code_call(PhaseGVN* phase, CallJavaNode* call);
+private:
bool inline_native_getClass();
// Helper functions for inlining arraycopy
diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp
index 3b824d810871..9c194774421a 100644
--- a/src/hotspot/share/opto/parse2.cpp
+++ b/src/hotspot/share/opto/parse2.cpp
@@ -2338,11 +2338,10 @@ void Parse::do_acmp(BoolTest::mask btest, Node* left, Node* right) {
Node* offset_addr = off_heap_plus_addr(members, in_bytes(InlineKlass::fast_acmp_offset_offset()));
Node* offset = make_load(control(), offset_addr, TypeInt::INT, T_INT, MemNode::unordered);
- Node* offset_cmp = CmpI(offset, zerocon(T_INT));
- Node* offset_bol = _gvn.transform(new BoolNode(offset_cmp, BoolTest::lt));
+ Node* offset_bol = BoolCmpI(offset, BoolTest::lt, zerocon(T_INT));
mask_iff = create_and_map_if(control(), offset_bol, PROB_FAIR, COUNT_UNKNOWN);
- Node* slow_path_ctl = _gvn.transform(new IfTrueNode(mask_iff));
- Node* fast_path_ctl = _gvn.transform(new IfFalseNode(mask_iff));
+ Node* slow_path_ctl = IfTrue(mask_iff);
+ Node* fast_path_ctl = IfFalse(mask_iff);
set_control(slow_path_ctl);
{
@@ -2356,11 +2355,11 @@ void Parse::do_acmp(BoolTest::mask btest, Node* left, Node* right) {
// *(left + offset) & mask == *(right + offset) & mask
Node* left_payload_addr = basic_plus_adr(not_null_left, offset_l);
Node* left_payload = make_load(control(), left_payload_addr, TypeLong::LONG, T_LONG, MemNode::unordered, LoadNNode::DependsOnlyOnTest, false, true, true, true);
- Node* left_masked = _gvn.transform(new AndLNode(left_payload, fast_acmp_mask));
+ Node* left_masked = AndL(left_payload, fast_acmp_mask);
Node* right_payload_addr = basic_plus_adr(not_null_right, offset_l);
Node* right_payload = make_load(control(), right_payload_addr, TypeLong::LONG, T_LONG, MemNode::unordered, LoadNNode::DependsOnlyOnTest, false, true, true, true);
- Node* right_masked = _gvn.transform(new AndLNode(right_payload, fast_acmp_mask));
+ Node* right_masked = AndL(right_payload, fast_acmp_mask);
Node* masked_cmp = CmpL(left_masked, right_masked);
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index 2cd0c6bbdd27..003e2d0fbfa4 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -3518,6 +3518,7 @@ jint Arguments::apply_ergo() {
DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(UseNullFreeAtomicValueFlattening);
DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(UseNullableNonAtomicValueFlattening);
DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(UseAcmpFastPath);
+ DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(UseHashcodeFastPath);
DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(PrintInlineLayout);
DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(PrintFlatArrayLayout);
DISABLE_FLAG_AND_WARN_IF_NOT_DEFAULT(IgnoreAssertUnsetFields);
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 295e92018ee3..edfcf2e53629 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -2029,6 +2029,9 @@ const int ObjectAlignmentInBytes = 8;
\
product(bool, UseAcmpFastPath, true, DIAGNOSTIC, \
"Use fast path for acmp.") \
+ \
+ product(bool, UseHashcodeFastPath, true, DIAGNOSTIC, \
+ "Use fast path for identityHashCode.") \
// end of RUNTIME_FLAGS
diff --git a/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java
index 7c833ad633ba..80ce42a8814c 100644
--- a/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java
+++ b/src/java.base/share/classes/java/lang/runtime/ValueObjectMethods.java
@@ -127,7 +127,7 @@ private static int valueObjectHashCode(Object obj) {
if (VERBOSE) {
System.out.println("valueObjectHashCode: obj.getClass:" + obj.getClass().getName());
}
- // This method assumes a is not null and is an instance of a value class
+ // This method assumes obj is not null and is an instance of a value class
Class> type = obj.getClass();
final Unsafe U = UNSAFE;
int[] map = U.getFieldMap(type);
diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java
index 9c19ed4c8ef9..9f1687e8cfdf 100644
--- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java
+++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java
@@ -446,11 +446,15 @@ private boolean check(String flag, String value) {
TestFormat.failNoThrow("Provided empty flag" + failAt());
return false;
}
+ Object actualFlagValue = WHITE_BOX.getStringVMFlag(flag);
+ if (actualFlagValue != null) {
+ return value.equals(actualFlagValue);
+ }
if (value.isEmpty()) {
TestFormat.failNoThrow("Provided empty value for flag " + flag + failAt());
return false;
}
- Object actualFlagValue = WHITE_BOX.getBooleanVMFlag(flag);
+ actualFlagValue = WHITE_BOX.getBooleanVMFlag(flag);
if (actualFlagValue != null) {
return checkBooleanFlag(flag, value, (Boolean) actualFlagValue);
}
@@ -462,10 +466,6 @@ private boolean check(String flag, String value) {
if (actualFlagValue != null) {
return checkFlag(Double::parseDouble, "floating point", flag, value, (Double) actualFlagValue);
}
- actualFlagValue = WHITE_BOX.getStringVMFlag(flag);
- if (actualFlagValue != null) {
- return value.equals(actualFlagValue);
- }
if (flag.equals("enable-valhalla")) {
return checkBooleanFlag(flag, value, Integer.class.isValue());
}
diff --git a/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestHashcodeFastPath.java b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestHashcodeFastPath.java
new file mode 100644
index 000000000000..1238ccc37b6f
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/valhalla/inlinetypes/TestHashcodeFastPath.java
@@ -0,0 +1,719 @@
+/*
+ * 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 id=0-fast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 0:fast
+ */
+
+/*
+ * @test id=1-fast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 1:fast
+ */
+
+/*
+ * @test id=2-fast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 2:fast
+ */
+
+/*
+ * @test id=3-fast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 3:fast
+ */
+
+/*
+ * @test id=4-fast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 4:fast
+ */
+
+/*
+ * @test id=5-fast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 5:fast
+ */
+
+/*
+ * @test id=6-fast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 6:fast
+ */
+
+/*
+ * @test id=0-nofast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 0:nofast
+ */
+
+/*
+ * @test id=1-nofast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 1:nofast
+ */
+
+/*
+ * @test id=2-nofast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 2:nofast
+ */
+
+/*
+ * @test id=3-nofast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 3:nofast
+ */
+
+/*
+ * @test id=4-nofast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 4:nofast
+ */
+
+/*
+ * @test id=5-nofast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 5:nofast
+ */
+
+/*
+ * @test id=6-nofast
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 6:nofast
+ */
+
+/*
+ * @test id=0-fast-nointrinsics
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 0:fast:nointrinsics
+ */
+
+/*
+ * @test id=0-nofast-nointrinsics
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 0:nofast:nointrinsics
+ */
+
+/*
+ * @test id=1-fast-nointrinsics
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 1:fast:nointrinsics
+ */
+
+/*
+ * @test id=1-nofast-nointrinsics
+ * @summary Test hashcode fast path with value classes
+ * @library /test/lib /
+ * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64")
+ * @enablePreview
+ * @modules java.base/jdk.internal.value
+ * java.base/jdk.internal.vm.annotation
+ * @run main ${test.main.class} 1:nofast:nointrinsics
+ */
+
+package compiler.valhalla.inlinetypes;
+
+import static compiler.lib.generators.Generators.G;
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+
+import static compiler.lib.ir_framework.IRNode.*;
+
+public class TestHashcodeFastPath {
+ record RunSettings(int scenario, boolean useHashCodeFastPath, boolean disableIntrinsics) {}
+
+ static boolean parseBooleanSetting(String[] pieces, int idx, String false_str, String true_str, boolean def) {
+ if (pieces.length > idx) {
+ String piece = pieces[idx];
+ if(piece.equals(false_str)) {
+ return false;
+ } else if(piece.equals(true_str)) {
+ return true;
+ } else {
+ throw new RuntimeException("Unknown setting: " + piece);
+ }
+ } else {
+ return def;
+ }
+ }
+
+ static RunSettings parseSetting(String arg) {
+ String[] pieces = arg.split(":");
+
+ int scenario = Integer.parseInt(pieces[0]);
+
+ boolean useHashCodeFastPath = parseBooleanSetting(pieces, 1, "nofast", "fast", true);
+ boolean disableIntrinsics = parseBooleanSetting(pieces, 2, "intrinsics", "nointrinsics", false);
+
+ return new RunSettings(scenario, useHashCodeFastPath, disableIntrinsics);
+ }
+
+ public static void main(String[] args) {
+ Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
+ RunSettings settings = parseSetting(args[0]);
+ Scenario scenario = scenarios[settings.scenario];
+ if (!settings.useHashCodeFastPath) {
+ scenario.addFlags("-XX:-UseHashcodeFastPath");
+ }
+ if (settings.disableIntrinsics) {
+ scenario.addFlags("-XX:DisableIntrinsic=_identityHashCode");
+ }
+ scenario.addFlags("-XX:CompileCommand=exclude,*::h");
+ InlineTypes.getFramework()
+ .addScenarios(scenario)
+ .start();
+ }
+
+ static abstract value class UniquelyDerivedBase {
+ }
+ static value class UniqueDerived extends UniquelyDerivedBase {
+ byte b;
+ UniqueDerived(byte b) {
+ this.b = b;
+ }
+ }
+
+ static abstract value class MultiplyDerivedBase {
+ }
+ static value class Derived extends MultiplyDerivedBase {
+ byte b;
+ Derived(byte b) {
+ this.b = b;
+ }
+ }
+ // Prevents Derived from being the only concrete class under MultiplyDerivedBase
+ static value class EvilDerived extends MultiplyDerivedBase {
+ byte b;
+ EvilDerived(byte b) {
+ this.b = b;
+ }
+ }
+
+ static value class DerivedWrapper {
+ short s;
+ Derived b;
+ DerivedWrapper(byte b, short s) {
+ this.b = new Derived(b);
+ this.s = s;
+ }
+ }
+
+ value record ShortWrapper(short s) {}
+
+ static abstract value class AbstractShort {
+ ShortWrapper s;
+ AbstractShort(int s) {
+ this.s = new ShortWrapper((short)s);
+ }
+
+ public String toString() {
+ return "AbstractShort(" + s + ")";
+ }
+ }
+
+ static value class ShortWithInt extends AbstractShort {
+ int i;
+ ShortWithInt(int s, int i) {
+ this.i = i;
+ super(s);
+ }
+ public String toString() {
+ return "ShortWithInt(s=" + s.s + ", i=" + i + ")";
+ }
+ }
+
+ static value class Empty {}
+
+
+
+ static value class LongLong {
+ long s;
+ long b;
+ LongLong(long s, long b) {
+ this.s = s;
+ this.b = b;
+ }
+ }
+ static value class WithOop {
+ String s;
+ WithOop(String s) {
+ this.s = s;
+ }
+ }
+
+ int h(Object o) {
+ return System.identityHashCode(o);
+ }
+
+ @Run(test = {
+ "h_object",
+ "h_unique_derived",
+ "h_uniquely_derived_base",
+ "h_derived",
+ "h_base",
+ "h_derived_hidden_type",
+ "h_short_with_int",
+ "h_short_with_int_hidden_type",
+ "h_with_oop",
+ "h_with_oop_hidden_type",
+ })
+ @Warmup(0) // We want to prevent profiling
+ public void run() {
+ var wrapper = new DerivedWrapper((byte)0, (short)0xa2a1);
+ var wrapper_ = new DerivedWrapper((byte)0, (short)0xa2a1);
+
+ var derived1 = new Derived((byte)0);
+ var derived_ = new Derived((byte)0);
+ var evilDerived1 = new EvilDerived((byte)1); // Force class loading
+
+ Asserts.assertEQ(h_object(null), h(null));
+ Asserts.assertEQ(h_object(null), 0);
+ Asserts.assertEQ(h_object(wrapper), h(wrapper_));
+
+ Asserts.assertEQ(h_derived(derived1), h(derived_));
+ Asserts.assertEQ(h_base(derived1), h(derived_));
+ Asserts.assertEQ(h_derived_hidden_type(derived1), h(derived_));
+
+ Asserts.assertEQ(h_base(evilDerived1), h(evilDerived1));
+
+ var uniqueDerived = new UniqueDerived((byte)0);
+ var uniqueDerived_ = new UniqueDerived((byte)0);
+
+ Asserts.assertEQ(h_object(uniqueDerived), h(uniqueDerived_));
+ Asserts.assertEQ(h_unique_derived(uniqueDerived), h(uniqueDerived_));
+ Asserts.assertEQ(h_uniquely_derived_base(uniqueDerived), h(uniqueDerived_));
+
+ var swi = new ShortWithInt(0, 1);
+ var swi_ = new ShortWithInt(0, 1);
+ Asserts.assertEQ(h_object(swi), h(swi_));
+ Asserts.assertEQ(h_short_with_int(swi), h(swi_));
+ Asserts.assertEQ(h_short_with_int_hidden_type(swi), h(swi_));
+
+ var empty = new Empty();
+ var empty_ = new Empty();
+ Asserts.assertEQ(h_object(empty), h(empty_));
+
+ var with_oops = new WithOop("a");
+ var with_oops_ = new WithOop("a");
+ Asserts.assertEQ(h_with_oop(with_oops), h(with_oops_));
+ Asserts.assertEQ(h_with_oop_hidden_type(with_oops), h(with_oops_));
+ }
+
+ static final String IDENTITY_HASHCODE = "identityHashCode";
+
+ static final int URSHIFT_L_COUNT_FOR_CACHE_PATH = 1; // Shift object header
+ static final int URSHIFT_L_COUNT_FOR_FAST_PATH = 2; // Shift class header, maybe shift again for long payload
+ static final int RSHIFT_L_COUNT_FOR_FAST_PATH = 1; // Shift object payload
+ static final String CACHE_PATH_U = "" + URSHIFT_L_COUNT_FOR_CACHE_PATH;
+ static final String CACHE_AND_FAST_PATH_U = "" + (URSHIFT_L_COUNT_FOR_CACHE_PATH + URSHIFT_L_COUNT_FOR_FAST_PATH);
+ static final String FAST_PATH_S = "" + RSHIFT_L_COUNT_FOR_FAST_PATH;
+
+ // Get hashcode fast path
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"})
+ int h_object(Object a) {
+ return System.identityHashCode(a);
+ }
+
+ // No hashcode fast path: the type is precise, and the call will be intrinsified
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_unique_derived(UniqueDerived a) {
+ return System.identityHashCode(a);
+ }
+
+ // No hashcode fast path: single concrete derived, and the call will be intrinsified
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_uniquely_derived_base(UniquelyDerivedBase a) {
+ return System.identityHashCode(a);
+ }
+
+ // No hashcode fast path: the type is precise, and the call will be intrinsified
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_derived(Derived a) {
+ return System.identityHashCode(a);
+ }
+
+ // Hashcode fast path is generated, the type is not precise enough for intrinsifying
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"})
+ int h_base(MultiplyDerivedBase a) {
+ return System.identityHashCode(a);
+ }
+
+ // Hides the type during parsing when always incrementally inlining
+ @ForceInline
+ public Object getter(Object o) {
+ return o;
+ }
+
+ // With late inlining, type is hidden at first, and a fast path is generated.
+ // Later, type becomes precise, call is intrinsified and fast path is removed.
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_derived_hidden_type(Derived a) {
+ return System.identityHashCode(getter(a));
+ }
+
+ // No hashcode fast path: the type is precise, and the call will be intrinsified. Fast path wouldn't work anyway because it has a weird size.
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL},failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_short_with_int(ShortWithInt a) {
+ return System.identityHashCode(a);
+ }
+
+ // With late inlining, type is hidden at first, and a fast path is generated.
+ // Later, type becomes precise, call is intrinsified and fast path is removed.
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_short_with_int_hidden_type(ShortWithInt a) {
+ return System.identityHashCode(getter(a));
+ }
+
+ // No hashcode fast path: the type is precise, and the call will be intrinsified if possible. Fast path wouldn't work anyway because of the oop.
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_with_oop(WithOop a) {
+ return System.identityHashCode(a);
+ }
+
+ // With late inlining, type is hidden at first, and a fast path is generated.
+ // Later, type becomes precise, call would be intrinsified if possible. But it's not. Yet, we can also find out the fast path won't work, and it is removed.
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_with_oop_hidden_type(WithOop a) {
+ return System.identityHashCode(getter(a));
+ }
+
+ // Only null path should exist
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_null() {
+ return System.identityHashCode(null);
+ }
+
+ // Only null path should survive
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {URSHIFT_L, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_null_hidden_type() {
+ return System.identityHashCode(getter(null));
+ }
+
+ @Run(test = {
+ "h_byte",
+ "h_short",
+ "h_int",
+ "h_long",
+ "h_long_long",
+ "h_short_with_int2",
+ "h_short_with_int_hidden_type2",
+ "h_with_oop2",
+ })
+ public void run2() {
+ for (int i = Byte.MIN_VALUE; i<= Byte.MAX_VALUE; ++i) {
+ Asserts.assertEQ(h_byte(new Byte((byte)i)), h(new Byte((byte)i)), "i = " + i);
+ }
+ // -1000 and 1000 are here to have a "normal" range, but outside what Short,
+ // Integer or Long will cache, that is without cached hash in the header.
+ int HALF_WIDTH = 256;
+ for (short base : new short[]{0, -1000, 1000, Short.MIN_VALUE, Short.MAX_VALUE}) {
+ for (short k = 0; k < 2 * HALF_WIDTH + 1; ++k) {
+ short s = (short) (k + base - HALF_WIDTH);
+ Asserts.assertEQ(h_short(new Short(s)), h(new Short(s)), "s = " + s);
+ }
+ }
+ for (int base : new int[]{0, -1000, 1000, Short.MIN_VALUE, Short.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE}) {
+ for (int k = 0; k < 2 * HALF_WIDTH + 1; ++k) {
+ int i = k + base - HALF_WIDTH;
+ Asserts.assertEQ(h_int(new Integer(i)), h(new Integer(i)), "i = " + i);
+ Asserts.assertEQ(h_short_with_int2(new ShortWithInt(i, i)), h(new ShortWithInt(i, i)), "i = " + i);
+ Asserts.assertEQ(h_short_with_int_hidden_type2(new ShortWithInt(i, i)), h(new ShortWithInt(i, i)), "i = " + i);
+ }
+ }
+ for (long base : new long[]{0, -1000, 1000, Long.MIN_VALUE, Long.MAX_VALUE}) {
+ for (long k = 0; k < 2 * HALF_WIDTH + 1; ++k) {
+ long l = k + base - HALF_WIDTH;
+ Asserts.assertEQ(h_long(new Long(l)), h(new Long(l)), "l = " + l);
+ Asserts.assertEQ(h_long_long(new LongLong(l, l)), h(new LongLong(l, l)), "l = " + l);
+ Asserts.assertEQ(h_long_long(new LongLong((l << 32L) + l, Long.MAX_VALUE - l)), h(new LongLong((l << 32L) + l, Long.MAX_VALUE - l)), "l = " + l);
+ String str = String.valueOf(l);
+ Asserts.assertEQ(h_with_oop2(new WithOop(str)), h(new WithOop(str)), "l = " + l);
+
+ Long l_ = new Long(l);
+ int expected_hash = h(l_);
+ Asserts.assertEQ(h_long(l_), expected_hash, "l = " + l);
+ }
+ }
+
+ short s = G.ints().next().shortValue();
+ Asserts.assertEQ(h_short(new Short(s)), h(new Short(s)), "s = " + s);
+ int i = G.ints().next();
+ Asserts.assertEQ(h_int(new Integer(i)), h(new Integer(i)), "i = " + i);
+ long l = G.longs().next();
+ Asserts.assertEQ(h_long(new Long(l)), h(new Long(l)), "l = " + l);
+ Asserts.assertEQ(h_long_long(new LongLong(i, i)), h(new LongLong(i, i)), "i = " + i);
+ Asserts.assertEQ(h_short_with_int2(new ShortWithInt(i, i)), h(new ShortWithInt(i, i)), "i = " + i);
+ Asserts.assertEQ(h_short_with_int_hidden_type2(new ShortWithInt(i, i)), h(new ShortWithInt(i, i)), "i = " + i);
+ String str = String.valueOf(i);
+ Asserts.assertEQ(h_with_oop2(new WithOop(str)), h(new WithOop(str)), "i = " + i);
+
+ Long lon = new Long(i);
+ int expected_hash = h(lon);
+ Asserts.assertEQ(h_long(lon), expected_hash, "lon = " + lon);
+ }
+
+ // Statically expanded
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_byte(Byte a) {
+ return System.identityHashCode(a);
+ }
+ // Statically expanded
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_short(Short a) {
+ return System.identityHashCode(a);
+ }
+ // Statically expanded
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_int(Integer a) {
+ return System.identityHashCode(a);
+ }
+ // Statically expanded
+ public static final String ONE_LONG_IN_INTRINSIC = "" + 1;
+ public static final String CACHE_PATH_AND_ONE_LONG_IN_INTRINSIC = "" + (URSHIFT_L_COUNT_FOR_CACHE_PATH + 1);
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_AND_ONE_LONG_IN_INTRINSIC}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, ONE_LONG_IN_INTRINSIC}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_long(Long a) {
+ return System.identityHashCode(a);
+ }
+ // Statically expanded
+ public static final String TWO_LONG_IN_INTRINSIC = "" + 2;
+ public static final String CACHE_PATH_AND_TWO_LONG_IN_INTRINSIC = "" + (URSHIFT_L_COUNT_FOR_CACHE_PATH + 2);
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_AND_TWO_LONG_IN_INTRINSIC}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, TWO_LONG_IN_INTRINSIC}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_long_long(LongLong a) {
+ return System.identityHashCode(a);
+ }
+
+ // No hashcode fast path: the type is precise, and the call will be intrinsified. Fast path wouldn't work anyway because it has a weird size.
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_short_with_int2(ShortWithInt a) {
+ return System.identityHashCode(a);
+ }
+
+ // With late inlining, type is hidden at first, and a fast path is generated.
+ // Later, type becomes precise, call is intrinsified and fast path is removed.
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_AND_FAST_PATH_U, RSHIFT_L, FAST_PATH_S, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L, RSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "true", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIfAnd = {"AlwaysIncrementalInline", "true", "UseHashcodeFastPath", "false", "DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, failOn = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_short_with_int_hidden_type2(ShortWithInt a) {
+ return System.identityHashCode(getter(a));
+ }
+
+ // No hashcode fast path: the type is precise, and the call will be intrinsified if possible. Fast path wouldn't work anyway because of the oop.
+ @Test
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {URSHIFT_L, CACHE_PATH_U, STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, applyIf = {"DisableIntrinsic", ""})
+ @IR(phase = {CompilePhase.AFTER_PARSING}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ @IR(phase = {CompilePhase.PRINT_IDEAL}, counts = {STATIC_CALL_OF_METHOD, IDENTITY_HASHCODE, "1"}, failOn = {URSHIFT_L}, applyIf = {"DisableIntrinsic", "_identityHashCode"})
+ int h_with_oop2(WithOop a) {
+ return System.identityHashCode(a);
+ }
+}
diff --git a/test/micro/org/openjdk/bench/valhalla/hash/FastPath.java b/test/micro/org/openjdk/bench/valhalla/hash/FastPath.java
new file mode 100644
index 000000000000..9cd5b08cdf11
--- /dev/null
+++ b/test/micro/org/openjdk/bench/valhalla/hash/FastPath.java
@@ -0,0 +1,502 @@
+/*
+ * 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.valhalla.hash;
+
+import org.openjdk.jmh.annotations.*;
+
+import java.util.concurrent.TimeUnit;
+
+@Fork(value = 3, jvmArgsAppend = {"--enable-preview"})
+@Warmup(iterations = 3, time = 1)
+@Measurement(iterations = 5, time = 1)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@BenchmarkMode(Mode.AverageTime)
+@State(Scope.Thread)
+public class FastPath {
+ public static final int SIZE = 100;
+
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash(Object obj) {
+ return System.identityHashCode(obj);
+ }
+
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash_specialized(Empty obj) {
+ return System.identityHashCode(obj);
+ }
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash_specialized(Byte obj) {
+ return System.identityHashCode(obj);
+ }
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash_specialized(Short obj) {
+ return System.identityHashCode(obj);
+ }
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash_specialized(Integer obj) {
+ return System.identityHashCode(obj);
+ }
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash_specialized(Long obj) {
+ return System.identityHashCode(obj);
+ }
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash_specialized(MyIntInt obj) {
+ return System.identityHashCode(obj);
+ }
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash_specialized(MyLongInt obj) {
+ return System.identityHashCode(obj);
+ }
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash_specialized(MyLongLong obj) {
+ return System.identityHashCode(obj);
+ }
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash_specialized(WithOop obj) {
+ return System.identityHashCode(obj);
+ }
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ private static int hash_specialized(MyByteShort obj) {
+ return System.identityHashCode(obj);
+ }
+
+ // Homogeneous cases of null, all go to null path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_null() {
+ int s = 0;
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(null);
+ }
+ return s;
+ }
+
+ // Homogeneous cases of empty object, all go to fast path
+ @CompilerControl(CompilerControl.Mode.DONT_INLINE)
+ public int no_hoist() {
+ return hash(new Empty());
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_empty() {
+ int s = System.identityHashCode(Empty.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += no_hoist();
+ }
+ return s;
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_empty_static() {
+ int s = System.identityHashCode(Empty.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash_specialized(new Empty());
+ }
+ return s;
+ }
+
+ // Homogeneous cases of bytes, all go to fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_byte() {
+ int s = System.identityHashCode(Byte.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(new Byte((byte)i));
+ }
+ return s;
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_byte_static() {
+ int s = System.identityHashCode(Byte.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash_specialized(new Byte((byte)i));
+ }
+ return s;
+ }
+
+ // Homogeneous cases of shorts, all go to fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_short() {
+ int s = System.identityHashCode(Short.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(new Short((short)(i + 256)));
+ }
+ return s;
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_short_static() {
+ int s = System.identityHashCode(Short.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash_specialized(new Short((short)(i + 256)));
+ }
+ return s;
+ }
+
+ // Homogeneous cases of ints, all go to fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_int() {
+ int s = System.identityHashCode(Integer.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(new Integer(i + 256));
+ }
+ return s;
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_int_static() {
+ int s = System.identityHashCode(Integer.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash_specialized(new Integer(i + 256));
+ }
+ return s;
+ }
+
+ // Homogeneous cases of longs, all go to fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_long() {
+ int s = System.identityHashCode(Long.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(new Long(i + 256));
+ }
+ return s;
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_long_static() {
+ int s = System.identityHashCode(Long.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash_specialized(new Long(i + 256));
+ }
+ return s;
+ }
+
+ // Homogeneous cases of pairs of ints, all go to fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_int_int() {
+ int s = System.identityHashCode(MyIntInt.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(new MyIntInt(i, 2*i));
+ }
+ return s;
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_int_int_static() {
+ int s = System.identityHashCode(MyIntInt.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash_specialized(new MyIntInt(i, 2*i));
+ }
+ return s;
+ }
+
+ // Heterogeneous cases, all go to fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int heterogeneous_small() {
+ int s = System.identityHashCode(Empty.class);
+ s += System.identityHashCode(Byte.class);
+ s += System.identityHashCode(Short.class);
+ s += System.identityHashCode(Integer.class);
+ s += System.identityHashCode(Long.class);
+ s += System.identityHashCode(MyIntInt.class);
+ for (int i = 0; i < SIZE; i++) {
+ Object v = switch (i % 7) {
+ case 0 -> new Empty();
+ case 1 -> new Byte((byte) i);
+ case 2 -> new Short((short) (i + 256));
+ case 3 -> new Integer(i + 256);
+ case 4 -> new Long(i + 256);
+ case 5 -> new MyIntInt(i, 2 * i);
+ default -> null;
+ };
+ s += hash(v);
+ }
+ return s;
+ }
+
+ // Homogeneous cases of pairs of long and int, too big for fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_too_big_long_int() {
+ int s = System.identityHashCode(MyLongInt.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(new MyLongInt(i, 2*i));
+ }
+ return s;
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_too_big_long_int_static() {
+ int s = System.identityHashCode(MyLongInt.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash_specialized(new MyLongInt(i, 2*i));
+ }
+ return s;
+ }
+
+ // Homogeneous cases of pairs of long, too big for fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_too_big_long_long() {
+ int s = System.identityHashCode(MyLongLong.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(new MyLongLong(i, 2*i));
+ }
+ return s;
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_too_big_long_long_static() {
+ int s = System.identityHashCode(MyLongLong.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash_specialized(new MyLongLong(i, 2*i));
+ }
+ return s;
+ }
+
+ // Heterogeneous cases, too big for fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int heterogeneous_too_big() {
+ int s = System.identityHashCode(MyLongInt.class);
+ s += System.identityHashCode(MyLongLong.class);
+ for (int i = 0; i < SIZE; i++) {
+ Object v = switch (i % 2) {
+ case 0 -> new MyLongInt(i, 2*i);
+ default -> new MyLongLong(i, 2*i);
+ };
+ s += hash(v);
+ }
+ return s;
+ }
+
+ // Homogeneous cases, with oop, so no fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_with_oop() {
+ int s = System.identityHashCode(WithOop.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(new WithOop(i));
+ }
+ return s;
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_with_oop_static() {
+ int s = System.identityHashCode(WithOop.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash_specialized(new WithOop(i));
+ }
+ return s;
+ }
+
+ // Homogeneous cases, not a nice size, so no fast path
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_weird_size() {
+ int s = System.identityHashCode(MyByteShort.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(new MyByteShort((byte) i, (short) (2*i)));
+ }
+ return s;
+ }
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_weird_size_static() {
+ int s = System.identityHashCode(MyByteShort.class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash_specialized(new MyByteShort((byte) i, (short) (2*i)));
+ }
+ return s;
+ }
+
+ // Homogeneous cases of String, so fast path not taken
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_with_obj_string() {
+ int s = System.identityHashCode(String.class);
+ s += System.identityHashCode(int[].class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(String.valueOf(i));
+ }
+ return s;
+ }
+
+ // Homogeneous cases of arrays, so fast path not taken
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int homogeneous_with_obj_array() {
+ int s = System.identityHashCode(String.class);
+ s += System.identityHashCode(int[].class);
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(new int[]{i});
+ }
+ return s;
+ }
+
+ // Heterogeneous array, identity objects, so fast path not taken
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int heterogeneous_with_obj() {
+ int s = System.identityHashCode(String.class);
+ s += System.identityHashCode(int[].class);
+ for (int i = 0; i < SIZE; i++) {
+ Object v = switch (i % 2) {
+ case 0 -> String.valueOf(i);
+ default -> new int[]{i};
+ };
+ s += hash(v);
+ }
+ return s;
+ }
+
+ // Heterogeneous array, all of the above
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int big_mix() {
+ int s = System.identityHashCode(Empty.class);
+ s += System.identityHashCode(Byte.class);
+ s += System.identityHashCode(Short.class);
+ s += System.identityHashCode(Integer.class);
+ s += System.identityHashCode(Long.class);
+ s += System.identityHashCode(MyIntInt.class);
+ s += System.identityHashCode(MyLongInt.class);
+ s += System.identityHashCode(MyLongLong.class);
+ s += System.identityHashCode(WithOop.class);
+ s += System.identityHashCode(MyByteShort.class);
+ s += System.identityHashCode(String.class);
+ s += System.identityHashCode(int[].class);
+ for (int i = 0; i < SIZE; i++) {
+ Object v = switch (i % 13) {
+ case 0 -> new Empty();
+ case 1 -> new Byte((byte)i);
+ case 2 -> new Short((short)(i + 256));
+ case 3 -> new Integer((i + 256));
+ case 4 -> new Long((i + 256));
+ case 5 -> new MyIntInt(i, 2*i);
+ case 6 -> new MyLongInt(i, 2*i);
+ case 7 -> new MyLongLong(i, 2*i);
+ case 8 -> new WithOop(i);
+ case 9 -> new MyByteShort((byte) i, (short)(2*i));
+ case 10 -> String.valueOf(i);
+ case 11 -> new int[]{i};
+ default -> null;
+ };
+ s += hash(v);
+ }
+ return s;
+ }
+
+ // Array of pre-hashed values, should take the cache path.
+ @Benchmark
+ @OperationsPerInvocation(SIZE)
+ @CompilerControl(CompilerControl.Mode.INLINE)
+ public int pre_hashed(PreHashedCase st) {
+ int s = 0;
+ for (int i = 0; i < SIZE; i++) {
+ s += hash(st.arr[i]);
+ }
+ return s;
+ }
+
+ static value class Empty {}
+ static value class MyIntInt {
+ int fst;
+ int snd;
+ public MyIntInt (int fst, int snd) {this.fst = fst; this.snd = snd;}
+ }
+ static value class MyLongInt {
+ long fst;
+ int snd;
+ MyLongInt(long fst, int snd) { this.fst = fst; this.snd = snd; }
+ }
+ static value class MyLongLong {
+ long fst;
+ long snd;
+ MyLongLong(long fst, long snd) { this.fst = fst; this.snd = snd; }
+ }
+ static value class WithOop {
+ Integer[] s;
+ WithOop(int i) {
+ if (i % 4 == 0) {
+ this.s = null;
+ } else {
+ this.s = new Integer[]{i};
+ }
+ }
+ }
+ static value class MyByteShort {
+ byte fst;
+ short snd;
+ MyByteShort(byte fst, short snd) { this.fst = fst; this.snd = snd; }
+ }
+
+ @State(Scope.Thread)
+ public static class PreHashedCase {
+ Object[] arr;
+
+ @Setup
+ public void setup() {
+ arr = new Object[SIZE];
+
+ for (int i = 0; i < SIZE; i++) {
+ arr[i] = new MyLongLong(((long)i) << 32, i);
+ System.identityHashCode(arr[i]);
+ }
+ }
+ }
+}
From 4555cf21371723f8fc69b28d265b5e342b900f28 Mon Sep 17 00:00:00 2001
From: Dingli Zhang
Date: Mon, 17 Aug 2026 08:33:47 +0000
Subject: [PATCH 32/88] 8390101: RISC-V: Use vmandn.mm for vector mask and-not
Reviewed-by: fyang, gcao
---
src/hotspot/cpu/riscv/riscv_v.ad | 28 +++++++++++++++-
.../AllBitsSetVectorMatchRuleTest.java | 32 +++++++++----------
2 files changed, 42 insertions(+), 18 deletions(-)
diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad
index bf291adce59c..ca77e6ba3413 100644
--- a/src/hotspot/cpu/riscv/riscv_v.ad
+++ b/src/hotspot/cpu/riscv/riscv_v.ad
@@ -4482,7 +4482,7 @@ instruct vmaskAllL(vRegMask dst, iRegL src) %{
// ------------------------------ Vector mask basic OPs ------------------------
-// vector mask logical ops: and/or/xor
+// vector mask logical ops: and/and-not/or/xor
instruct vmask_and(vRegMask dst, vRegMask src1, vRegMask src2) %{
match(Set dst (AndVMask src1 src2));
@@ -4497,6 +4497,32 @@ instruct vmask_and(vRegMask dst, vRegMask src1, vRegMask src2) %{
ins_pipe(pipe_slow);
%}
+instruct vmask_and_notI(vRegMask dst, vRegMask src1, vRegMask src2, immI_M1 m1) %{
+ match(Set dst (AndVMask src1 (XorVMask src2 (MaskAll m1))));
+ format %{ "vmask_and_notI $dst, $src1, $src2" %}
+ ins_encode %{
+ BasicType bt = Matcher::vector_element_basic_type(this);
+ __ vsetvli_helper(bt, Matcher::vector_length(this));
+ __ vmandn_mm(as_VectorRegister($dst$$reg),
+ as_VectorRegister($src1$$reg),
+ as_VectorRegister($src2$$reg));
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+instruct vmask_and_notL(vRegMask dst, vRegMask src1, vRegMask src2, immL_M1 m1) %{
+ match(Set dst (AndVMask src1 (XorVMask src2 (MaskAll m1))));
+ format %{ "vmask_and_notL $dst, $src1, $src2" %}
+ ins_encode %{
+ BasicType bt = Matcher::vector_element_basic_type(this);
+ __ vsetvli_helper(bt, Matcher::vector_length(this));
+ __ vmandn_mm(as_VectorRegister($dst$$reg),
+ as_VectorRegister($src1$$reg),
+ as_VectorRegister($src2$$reg));
+ %}
+ ins_pipe(pipe_slow);
+%}
+
instruct vmask_or(vRegMask dst, vRegMask src1, vRegMask src2) %{
match(Set dst (OrVMask src1 src2));
format %{ "vmask_or $dst, $src1, $src2" %}
diff --git a/test/hotspot/jtreg/compiler/vectorapi/AllBitsSetVectorMatchRuleTest.java b/test/hotspot/jtreg/compiler/vectorapi/AllBitsSetVectorMatchRuleTest.java
index 2c5b74a078ea..b95771d98629 100644
--- a/test/hotspot/jtreg/compiler/vectorapi/AllBitsSetVectorMatchRuleTest.java
+++ b/test/hotspot/jtreg/compiler/vectorapi/AllBitsSetVectorMatchRuleTest.java
@@ -43,7 +43,7 @@
* @key randomness
* @library /test/lib /
* @requires vm.compiler2.enabled
- * @requires (os.simpleArch == "aarch64" & vm.cpu.features ~= ".*asimd.*") | (os.simpleArch == "riscv64" & vm.cpu.features ~= ".*zvbb.*")
+ * @requires (os.simpleArch == "aarch64" & vm.cpu.features ~= ".*asimd.*") | (os.simpleArch == "riscv64" & vm.cpu.features ~= ".*rvv.*")
* @summary AArch64: [vector] Make all bits set vector sharable for match rules
* @modules jdk.incubator.vector
*
@@ -94,7 +94,7 @@ public class AllBitsSetVectorMatchRuleTest {
// Tests of C2 match rules for vector ops containing an all-bits-set vector operand.
@Test
- @IR(counts = { IRNode.VAND_NOT_I, " >= 1" })
+ @IR(counts = { IRNode.VAND_NOT_I, " >= 1" }, applyIfCPUFeatureOr = {"asimd", "true", "zvbb", "true"})
public static void testAllBitsSetVector() {
IntVector av = IntVector.fromArray(I_SPECIES, ia, 0);
IntVector bv = IntVector.fromArray(I_SPECIES, ib, 0);
@@ -107,7 +107,7 @@ public static void testAllBitsSetVector() {
}
@Test
- @IR(counts = { IRNode.VAND_NOT_L, " >= 1" })
+ @IR(counts = { IRNode.VAND_NOT_L, " >= 1" }, applyIfCPUFeatureOr = {"asimd", "true", "zvbb", "true"})
public static void testVectorVAndNotL() {
LongVector av = LongVector.fromArray(L_SPECIES, la, 0);
LongVector bv = LongVector.fromArray(L_SPECIES, lb, 0);
@@ -120,8 +120,7 @@ public static void testVectorVAndNotL() {
}
@Test
- @IR(counts = { IRNode.VAND_NOT_I_MASKED, " >= 1" }, applyIfCPUFeature = {"sve", "true"})
- @IR(counts = { IRNode.VAND_NOT_I_MASKED, " >= 1" }, applyIfPlatform = {"riscv64", "true"})
+ @IR(counts = { IRNode.VAND_NOT_I_MASKED, " >= 1" }, applyIfCPUFeatureOr = {"sve", "true", "zvbb", "true"})
public static void testVectorVAndNotIMasked() {
VectorMask avm = VectorMask.fromArray(I_SPECIES, ma, 0);
IntVector av = IntVector.fromArray(I_SPECIES, ia, 0);
@@ -137,8 +136,7 @@ public static void testVectorVAndNotIMasked() {
}
@Test
- @IR(counts = { IRNode.VAND_NOT_L_MASKED, " >= 1" }, applyIfCPUFeature = {"sve", "true"})
- @IR(counts = { IRNode.VAND_NOT_L_MASKED, " >= 1" }, applyIfPlatform = {"riscv64", "true"})
+ @IR(counts = { IRNode.VAND_NOT_L_MASKED, " >= 1" }, applyIfCPUFeatureOr = {"sve", "true", "zvbb", "true"})
public static void testVectorVAndNotLMasked() {
VectorMask avm = VectorMask.fromArray(L_SPECIES, ma, 0);
LongVector av = LongVector.fromArray(L_SPECIES, la, 0);
@@ -154,7 +152,7 @@ public static void testVectorVAndNotLMasked() {
}
@Test
- @IR(counts = { IRNode.RISCV_VAND_NOTI_VX, " >= 1" }, applyIfPlatform = {"riscv64", "true"})
+ @IR(counts = { IRNode.RISCV_VAND_NOTI_VX, " >= 1" }, applyIfCPUFeature = {"zvbb", "true"})
public static void testAllBitsSetVectorRegI() {
IntVector av = IntVector.fromArray(I_SPECIES, ia, 0);
int bs = ib[0];
@@ -167,7 +165,7 @@ public static void testAllBitsSetVectorRegI() {
}
@Test
- @IR(counts = { IRNode.RISCV_VAND_NOTL_VX, " >= 1" }, applyIfPlatform = {"riscv64", "true"})
+ @IR(counts = { IRNode.RISCV_VAND_NOTL_VX, " >= 1" }, applyIfCPUFeature = {"zvbb", "true"})
public static void testAllBitsSetVectorRegL() {
LongVector av = LongVector.fromArray(L_SPECIES, la, 0);
long bs = lb[0];
@@ -180,7 +178,7 @@ public static void testAllBitsSetVectorRegL() {
}
@Test
- @IR(counts = { IRNode.RISCV_VAND_NOTI_VX_MASKED, " >= 1" }, applyIfPlatform = {"riscv64", "true"})
+ @IR(counts = { IRNode.RISCV_VAND_NOTI_VX_MASKED, " >= 1" }, applyIfCPUFeature = {"zvbb", "true"})
public static void testAllBitsSetVectorRegIMask() {
VectorMask avm = VectorMask.fromArray(I_SPECIES, ma, 0);
IntVector av = IntVector.fromArray(I_SPECIES, ia, 0);
@@ -196,7 +194,7 @@ public static void testAllBitsSetVectorRegIMask() {
}
@Test
- @IR(counts = { IRNode.RISCV_VAND_NOTL_VX_MASKED, " >= 1" }, applyIfPlatform = {"riscv64", "true"})
+ @IR(counts = { IRNode.RISCV_VAND_NOTL_VX_MASKED, " >= 1" }, applyIfCPUFeature = {"zvbb", "true"})
public static void testAllBitsSetVectorRegLMask() {
VectorMask avm = VectorMask.fromArray(L_SPECIES, ma, 0);
LongVector av = LongVector.fromArray(L_SPECIES, la, 0);
@@ -214,7 +212,7 @@ public static void testAllBitsSetVectorRegLMask() {
// Tests that VectorMask.andNot() chains match to VMASK_AND_NOT / VAND_NOT (two andNot ops).
@Test
@IR(counts = { IRNode.VAND_NOT_I, "2" }, applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"})
- @IR(counts = { IRNode.VMASK_AND_NOT_I, "2" }, applyIfCPUFeature = {"sve", "true"})
+ @IR(counts = { IRNode.VMASK_AND_NOT_I, "2" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"})
public static void testMaskAndNotI() {
VectorMask avm = VectorMask.fromArray(I_SPECIES, ma, 0);
VectorMask bvm = VectorMask.fromArray(I_SPECIES, mb, 0);
@@ -229,7 +227,7 @@ public static void testMaskAndNotI() {
@Test
@IR(counts = { IRNode.VAND_NOT_L, "2" }, applyIfCPUFeatureAnd = {"asimd", "true", "sve", "false"})
- @IR(counts = { IRNode.VMASK_AND_NOT_L, "2" }, applyIfCPUFeature = {"sve", "true"})
+ @IR(counts = { IRNode.VMASK_AND_NOT_L, "2" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"})
public static void testMaskAndNotL() {
VectorMask avm = VectorMask.fromArray(L_SPECIES, ma, 0);
VectorMask bvm = VectorMask.fromArray(L_SPECIES, mb, 0);
@@ -244,7 +242,7 @@ public static void testMaskAndNotL() {
// Tests that mask.not().and(other) matches to VMASK_AND_NOT (AndVMask commutative rule).
@Test
- @IR(counts = { IRNode.VMASK_AND_NOT_I, "1" }, applyIfCPUFeature = {"sve", "true"})
+ @IR(counts = { IRNode.VMASK_AND_NOT_I, "1" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"})
public static void testCommutativeAndVMaskI() {
VectorMask avm = VectorMask.fromArray(I_SPECIES, ma, 0);
VectorMask bvm = VectorMask.fromArray(I_SPECIES, mb, 0);
@@ -257,7 +255,7 @@ public static void testCommutativeAndVMaskI() {
}
@Test
- @IR(counts = { IRNode.VMASK_AND_NOT_L, "1" }, applyIfCPUFeature = {"sve", "true"})
+ @IR(counts = { IRNode.VMASK_AND_NOT_L, "1" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"})
public static void testCommutativeAndVMaskL() {
VectorMask avm = VectorMask.fromArray(L_SPECIES, ma, 0);
VectorMask bvm = VectorMask.fromArray(L_SPECIES, mb, 0);
@@ -271,7 +269,7 @@ public static void testCommutativeAndVMaskL() {
// Tests that mask.and(allTrue.xor(other)) matches to VMASK_AND_NOT (XorVMask commutative rule).
@Test
- @IR(counts = { IRNode.VMASK_AND_NOT_I, "1" }, applyIfCPUFeature = {"sve", "true"})
+ @IR(counts = { IRNode.VMASK_AND_NOT_I, "1" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"})
public static void testCommutativeXorVMaskI() {
VectorMask avm = VectorMask.fromArray(I_SPECIES, ma, 0);
VectorMask bvm = VectorMask.fromArray(I_SPECIES, mb, 0);
@@ -285,7 +283,7 @@ public static void testCommutativeXorVMaskI() {
}
@Test
- @IR(counts = { IRNode.VMASK_AND_NOT_L, "1" }, applyIfCPUFeature = {"sve", "true"})
+ @IR(counts = { IRNode.VMASK_AND_NOT_L, "1" }, applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"})
public static void testCommutativeXorVMaskL() {
VectorMask avm = VectorMask.fromArray(L_SPECIES, ma, 0);
VectorMask bvm = VectorMask.fromArray(L_SPECIES, mb, 0);
From 858880932a9d879c452a98abb4f2f39cb655e34b Mon Sep 17 00:00:00 2001
From: Ioi Lam
Date: Mon, 17 Aug 2026 16:17:08 +0000
Subject: [PATCH 33/88] 8387494: ResolvedMethodName::vmtarget is not updated
when class in AOT cache is redefined
Reviewed-by: kvn, asmehra
---
.../share/cds/aotLinkedClassBulkLoader.cpp | 3 +
src/hotspot/share/cds/heapShared.cpp | 29 +++-
src/hotspot/share/cds/heapShared.hpp | 1 +
src/hotspot/share/oops/trainingData.cpp | 2 +-
test/hotspot/jtreg/TEST.groups | 2 +
.../cds/appcds/agent/RedefineAllAgent.java | 76 ++++++++++
.../RedefineAllAgent.mf} | 4 +-
.../cds/appcds/agent/RedefineAllTest.java | 112 ++++++++++++++
.../appcds/agent/RedefineHotMethodTest.java | 138 ++++++++++++++++++
.../JavaAgent.java => agent/SimpleTest.java} | 72 +++++----
.../appcds/aotCache/JavaAgentTransformer.java | 71 ---------
test/lib/RedefineClassHelper.java | 38 ++++-
12 files changed, 434 insertions(+), 114 deletions(-)
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.java
rename test/hotspot/jtreg/runtime/cds/appcds/{aotCache/JavaAgentTransformer.mf => agent/RedefineAllAgent.mf} (53%)
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllTest.java
create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineHotMethodTest.java
rename test/hotspot/jtreg/runtime/cds/appcds/{aotCache/JavaAgent.java => agent/SimpleTest.java} (73%)
delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.java
diff --git a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp
index 572b973827bd..b6dad1e25137 100644
--- a/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp
+++ b/src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp
@@ -85,6 +85,9 @@ void AOTLinkedClassBulkLoader::preload_classes_impl(TRAPS) {
initiate_loading(THREAD, "app", h_system_loader, table->boot2());
initiate_loading(THREAD, "app", h_system_loader, table->platform());
preload_classes_in_table(table->app(), "app", h_system_loader, CHECK);
+
+ // Do this after all boot/platform/app classes are loaded, but before bytecode execution.
+ HeapShared::load_cached_resolved_methods();
}
void AOTLinkedClassBulkLoader::preload_classes_in_table(Array* classes,
diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp
index 4f92eb486ee0..2fc23700d828 100644
--- a/src/hotspot/share/cds/heapShared.cpp
+++ b/src/hotspot/share/cds/heapShared.cpp
@@ -35,7 +35,7 @@
#include "cds/aotStreamedHeapLoader.hpp"
#include "cds/aotStreamedHeapWriter.hpp"
#include "cds/archiveBuilder.hpp"
-#include "cds/archiveUtils.hpp"
+#include "cds/archiveUtils.inline.hpp"
#include "cds/cds_globals.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/cdsEnumKlass.hpp"
@@ -67,6 +67,7 @@
#include "oops/oopHandle.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
#include "prims/jvmtiExport.hpp"
+#include "prims/resolvedMethodTable.hpp"
#include "runtime/arguments.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "runtime/globals_extension.hpp"
@@ -142,6 +143,8 @@ ArchivedKlassSubGraphInfoRecord* HeapShared::_run_time_special_subgraph;
GrowableArrayCHeap* HeapShared::_pending_roots = nullptr;
OopHandle HeapShared::_scratch_basic_type_mirrors[T_VOID+1];
MetaspaceObjToOopHandleTable* HeapShared::_scratch_objects_table = nullptr;
+static GrowableArray* _dumptime_resolved_methods = nullptr;
+static Array* _runtime_resolved_methods = nullptr;
static bool is_subgraph_root_class_of(ArchivableStaticFieldInfo fields[], InstanceKlass* ik) {
for (int i = 0; fields[i].valid(); i++) {
@@ -640,6 +643,7 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra
m = RegeneratedClasses::maybe_get_regenerated_object(m);
InstanceKlass* method_holder = m->method_holder();
AOTArtifactFinder::add_cached_class(method_holder);
+ _dumptime_resolved_methods->append(HeapShared::append_root(obj));
}
}
}
@@ -712,6 +716,7 @@ void HeapShared::remove_scratch_resolved_references(ConstantPool* src) {
void HeapShared::init_dumping() {
_scratch_objects_table = new (mtClass)MetaspaceObjToOopHandleTable();
+ _dumptime_resolved_methods = new (mtClassShared) GrowableArray(100, mtClassShared);
_pending_roots = new GrowableArrayCHeap(500);
_pending_roots->append(nullptr); // root index 0 represents a null oop
DEBUG_ONLY(_dumptime_classes_with_cached_oops = new (mtClassShared)ArchivableKlassTable());
@@ -1024,6 +1029,10 @@ void HeapShared::write_heap(AOTMappedHeapInfo* mapped_heap_info, AOTStreamedHeap
delete _pending_roots;
_pending_roots = nullptr;
+ _runtime_resolved_methods = ArchiveUtils::archive_array(_dumptime_resolved_methods);
+ delete _dumptime_resolved_methods;
+ _dumptime_resolved_methods = nullptr;
+
make_archived_object_cache_gc_safe();
}
@@ -1313,9 +1322,27 @@ void HeapShared::write_subgraph_info_table() {
void HeapShared::serialize_tables(SerializeClosure* soc) {
_run_time_subgraph_info_table.serialize_header(soc);
soc->do_ptr(&_run_time_special_subgraph);
+ soc->do_ptr(&_runtime_resolved_methods);
DEBUG_ONLY(soc->do_ptr(&_runtime_classes_with_cached_oops));
}
+void HeapShared::load_cached_resolved_methods() {
+ precond(CDSConfig::is_using_aot_linked_classes());
+ if (_runtime_resolved_methods != nullptr) {
+ JavaThread* current = JavaThread::current();
+ HandleMark hm(current);
+ for (int i = 0; i < _runtime_resolved_methods->length(); i++) {
+ int root_index = _runtime_resolved_methods->at(i);
+ Handle mem_name(current, get_root(root_index, /*clear=*/true));
+ Method* method = java_lang_invoke_ResolvedMethodName::vmtarget(mem_name());
+ InstanceKlass* holder = method->method_holder();
+ holder->set_has_resolved_methods();
+ oop o = ResolvedMethodTable::add_method(method, mem_name);
+ precond(o == mem_name());
+ }
+ }
+}
+
static void verify_the_heap(Klass* k, const char* which) {
if (VerifyArchivedFields > 0) {
ResourceMark rm;
diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp
index 48ce3bc40dcd..ba7ec626d909 100644
--- a/src/hotspot/share/cds/heapShared.hpp
+++ b/src/hotspot/share/cds/heapShared.hpp
@@ -467,6 +467,7 @@ class HeapShared: AllStatic {
static void init_heap_writer() NOT_CDS_JAVA_HEAP_RETURN;
static void write_subgraph_info_table() NOT_CDS_JAVA_HEAP_RETURN;
static void serialize_tables(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
+ static void load_cached_resolved_methods() NOT_CDS_JAVA_HEAP_RETURN;
static void initialize_java_lang_invoke(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
static void init_classes_for_special_subgraph(Handle loader, TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
diff --git a/src/hotspot/share/oops/trainingData.cpp b/src/hotspot/share/oops/trainingData.cpp
index 8c8e8521fbb1..2b87c899fe16 100644
--- a/src/hotspot/share/oops/trainingData.cpp
+++ b/src/hotspot/share/oops/trainingData.cpp
@@ -120,7 +120,7 @@ void TrainingData::verify() {
}
static bool is_excluded(InstanceKlass* k) {
- if (!k->is_loaded() || k->has_been_redefined()) {
+ if (!k->is_loaded() || (TrainingData::need_data() && k->has_been_redefined())) {
return true;
}
if (CDSConfig::is_at_aot_safepoint()) {
diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups
index 78eee6addea3..8e30e73a1826 100644
--- a/test/hotspot/jtreg/TEST.groups
+++ b/test/hotspot/jtreg/TEST.groups
@@ -442,6 +442,7 @@ hotspot_cds_only = \
hotspot_appcds_dynamic = \
runtime/cds/appcds/ \
+ -runtime/cds/appcds/agent \
-runtime/cds/appcds/aotAnnotations \
-runtime/cds/appcds/aotCache \
-runtime/cds/appcds/aotClassLinking \
@@ -545,6 +546,7 @@ hotspot_cds_epsilongc = \
# test AOT class linking, so there's no need to run them again with -XX:+AOTClassLinking.
hotspot_aot_classlinking = \
runtime/cds \
+ -runtime/cds/appcds/agent \
-runtime/cds/appcds/aotAnnotations \
-runtime/cds/appcds/aotCache \
-runtime/cds/appcds/aotClassLinking \
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.java b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.java
new file mode 100644
index 000000000000..11ed8bad4cc9
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.java
@@ -0,0 +1,76 @@
+/*
+ * 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.lang.classfile.ClassFile;
+import java.lang.classfile.ClassModel;
+import java.lang.classfile.ClassTransform;
+import java.lang.classfile.constantpool.ConstantPoolBuilder;
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.security.ProtectionDomain;
+
+public class RedefineAllAgent implements ClassFileTransformer {
+ public static void premain(String agentArguments, Instrumentation inst) {
+ inst.addTransformer(new RedefineAllAgent(), /*canRetransform=*/true);
+
+ for (Class> c : inst.getAllLoadedClasses()) {
+ if (!c.isArray() && !c.isHidden() && inst.isModifiableClass(c)) {
+ try {
+ // Note: we cannot use test/lib/RedefineClassHelper.java because we may
+ // have a regenerated class (see regeneratedClasses.cpp) such as
+ // java/lang/invoke/DirectMethodHandle$Holder, whose bytecodes are different than
+ // the DirectMethodHandle$Holder.class file stored in the JDK's modules file.
+ //
+ // Therefore, we cannot use RedefineClassHelper.getBytecodes(). We must call into
+ // inst.retransformClasses(), which will give us the correct bytecodes using
+ // JvmtiClassFileReconstituter.
+ inst.retransformClasses(c);
+ System.out.println("========= Success: " + c.getName());
+ } catch (UnmodifiableClassException e) {
+ System.out.println("========== Failed: " + c.getName());
+ e.printStackTrace(System.out);
+ }
+ }
+ }
+ }
+
+ public byte[] transform(ClassLoader loader, String name, Class> classBeingRedefined,
+ ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException {
+ try {
+ System.out.println((classBeingRedefined == null ? "retransforming " : "redefining ") + name);
+ ClassFile cf = ClassFile.of();
+ ClassModel model = cf.parse(buffer);
+ ConstantPoolBuilder cp = ConstantPoolBuilder.of(model);
+ cp.utf8Entry("Hello");
+ buffer = cf.build(model.thisClass(), cp,
+ cb -> cb.transform(model, ClassTransform.ACCEPT_ALL));
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw new RuntimeException("Unexpected", t);
+ }
+ return buffer;
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.mf b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.mf
similarity index 53%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.mf
rename to test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.mf
index f0b185661430..8e077b3bd3ff 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.mf
+++ b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllAgent.mf
@@ -1,5 +1,5 @@
Manifest-Version: 1.0
-Premain-Class: JavaAgentTransformer
-Agent-Class: JavaAgentTransformer
+Premain-Class: RedefineAllAgent
+Agent-Class: RedefineAllAgent
Can-Retransform-Classes: true
Can-Redefine-Classes: true
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllTest.java b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllTest.java
new file mode 100644
index 000000000000..e2eb7bad139b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineAllTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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 The agent is loaded in production run. It redefines all classes, including those already loaded from the AOT cache.
+ * @requires vm.cds.supports.aot.class.linking
+ * @library /test/lib /test/setup_aot
+ * @build RedefineAllTest RedefineAllAgent JavacBenchApp
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
+ * JavacBenchApp
+ * JavacBenchApp$ClassFile
+ * JavacBenchApp$FileManager
+ * JavacBenchApp$SourceFile
+ * @run driver/timeout=240 RedefineAllTest AOT
+ */
+
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.helpers.ClassFileInstaller;
+
+public class RedefineAllTest {
+ static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+ static final String mainClass = "JavacBenchApp";
+
+ public static String agentClasses[] = {
+ "RedefineAllAgent",
+ };
+ static String agentJar;
+
+ public static void main(String... args) throws Exception {
+ agentJar = ClassFileInstaller.writeJar("agent.jar",
+ ClassFileInstaller.Manifest.fromSourceFile("RedefineAllAgent.mf"),
+ agentClasses);
+ run(args, false);
+ run(args, true);
+ }
+
+ static void run(String[] args, boolean compressedOops) throws Exception {
+ Tester t = new Tester(compressedOops);
+ t.run(args);
+ }
+
+ static class Tester extends CDSAppTester {
+ boolean compressedOops;
+
+ public Tester(boolean compressedOops) {
+ super(mainClass);
+ this.compressedOops = compressedOops;
+ }
+
+ @Override
+ public String classpath(RunMode runMode) {
+ return appJar;
+ }
+
+ @Override
+ public String[] vmArgs(RunMode runMode) {
+ String mode = "-XX:" + (compressedOops ? "+" : "-") + "UseCompressedOops";
+
+ if (runMode == RunMode.PRODUCTION) {
+ return new String[] {
+ mode,
+ "-javaagent:" + agentJar,
+ };
+ } else {
+ return new String[] {
+ mode,
+ // This is needed for using the agent in production run.
+ "--add-modules=java.instrument",
+ };
+ }
+ }
+
+ @Override
+ public String[] appCommandLine(RunMode runMode) {
+ return new String[] {
+ mainClass,
+ "2",
+ };
+ }
+
+ @Override
+ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
+ if (runMode.isApplicationExecuted()) {
+ out.shouldMatch("Generated source code for [0-9]+ classes and compiled them");
+ }
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineHotMethodTest.java b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineHotMethodTest.java
new file mode 100644
index 000000000000..fdb105b3f2c1
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/appcds/agent/RedefineHotMethodTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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 The agent is loaded in production run. It redefines RedefineHotMethodApp::increment() to return 34.
+ * @requires vm.cds.supports.aot.class.linking
+ * @library /test/lib /test/hotspot/jtreg/serviceability/jvmti/RedefineClasses
+ * @build RedefineHotMethodTest RedefineClassHelper
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar RedefineHotMethodApp RedefineClassHelper
+ * @run main RedefineClassHelper
+ * @run driver RedefineHotMethodTest AOT
+ */
+
+import java.lang.classfile.CodeBuilder;
+import java.lang.classfile.CodeElement;
+import java.lang.classfile.MethodModel;
+
+import jdk.test.lib.cds.CDSAppTester;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.helpers.ClassFileInstaller;
+
+public class RedefineHotMethodTest {
+ static final String appJar = ClassFileInstaller.getJarPath("app.jar");
+ static final String mainClass = RedefineHotMethodApp.class.getName();
+
+ public static String agentClasses[] = {
+ "RedefineHotMethodAgent",
+ };
+ static String agentJar = "redefineagent.jar";
+
+ public static void main(String... args) throws Exception {
+ Tester t = new Tester();
+ t.run(args);
+ }
+
+ static class Tester extends CDSAppTester {
+ public Tester() {
+ super(mainClass);
+ }
+
+ @Override
+ public String classpath(RunMode runMode) {
+ return appJar;
+ }
+
+ @Override
+ public String[] vmArgs(RunMode runMode) {
+ if (runMode == RunMode.PRODUCTION) {
+ return new String[] {
+ "-javaagent:" + agentJar,
+ };
+ } else {
+ return new String[] {
+ // This is needed for using the agent in production run.
+ "--add-modules=java.instrument",
+ };
+ }
+ }
+
+ @Override
+ public String[] appCommandLine(RunMode runMode) {
+ return new String[] {
+ mainClass,
+ runMode.toString(),
+ };
+ }
+
+ @Override
+ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
+ if (runMode == RunMode.TRAINING) {
+ out.shouldContain("counter = 120000001");
+ } else if (runMode == RunMode.PRODUCTION) {
+ out.shouldContain("counter = 340000001");
+ }
+ }
+ }
+}
+
+class RedefineHotMethodApp {
+ volatile static long counter;
+
+ public static void main(String args[]) throws Exception {
+ if (args[0].equals("PRODUCTION")) {
+ redefineIncrementMethod();
+ }
+ doLoop();
+ counter ++;
+ System.out.println("counter = " + counter);
+ }
+
+ static void doLoop() {
+ for (int i = 0; i < 1000 * 1000; i++) {
+ for (int j = 0; j < 10; j++) {
+ counter += increment();
+ }
+ }
+ }
+
+ static void redefineIncrementMethod() throws Exception {
+ RedefineClassHelper.redefineMethodBodies(RedefineHotMethodApp.class,
+ (MethodModel method) -> method.methodName().equalsString("increment"),
+ (CodeBuilder builder, CodeElement element) -> {
+ builder.loadConstant(34);
+ builder.ireturn();
+ });
+ }
+
+ // This method will be redefined in redefineIncrementMethod() to return 34 instead.
+ //
+ // Any AOT-compiled methods that use the original version of this method
+ // must not be used.
+ static int increment() {
+ return 12;
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgent.java b/test/hotspot/jtreg/runtime/cds/appcds/agent/SimpleTest.java
similarity index 73%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgent.java
rename to test/hotspot/jtreg/runtime/cds/appcds/agent/SimpleTest.java
index 758d252f18d5..2a8bb4b0d0ea 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgent.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/agent/SimpleTest.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
@@ -28,10 +28,11 @@
* @bug 8361725
* @summary -javaagent is not allowed when creating static CDS archive
* @requires vm.cds.supports.aot.class.linking
- * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
- * @build JavaAgent JavaAgentTransformer Util
- * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar JavaAgentApp JavaAgentApp$ShouldBeTransformed
- * @run driver JavaAgent STATIC
+ * @library /test/lib /test/hotspot/jtreg/serviceability/jvmti/RedefineClasses
+ * @build SimpleTest RedefineClassHelper
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar SimpleTestApp SimpleTestApp$ShouldBeTransformed
+ * @run main RedefineClassHelper
+ * @run driver SimpleTest STATIC
*/
/**
@@ -39,12 +40,13 @@
* @bug 8362561
* @summary -javaagent is not allowed when creating dynamic CDS archive
* @requires vm.cds.supports.aot.class.linking
- * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
- * @build JavaAgent JavaAgentTransformer Util
- * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar JavaAgentApp JavaAgentApp$ShouldBeTransformed
+ * @library /test/lib /test/hotspot/jtreg/serviceability/jvmti/RedefineClasses
+ * @build SimpleTest RedefineClassHelper
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar SimpleTestApp SimpleTestApp$ShouldBeTransformed
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
- * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. JavaAgent DYNAMIC
+ * @run main RedefineClassHelper
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. SimpleTest DYNAMIC
*/
/*
@@ -52,31 +54,32 @@
* @summary -javaagent should be allowed in AOT workflow. However, classes transformed/redefined by agents will not
* be cached.
* @requires vm.cds.supports.aot.class.linking
- * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
- * @build JavaAgent JavaAgentTransformer Util
- * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar JavaAgentApp JavaAgentApp$ShouldBeTransformed
- * @run driver JavaAgent AOT
+ * @library /test/lib /test/hotspot/jtreg/serviceability/jvmti/RedefineClasses
+ * @build SimpleTest RedefineClassHelper
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar SimpleTestApp SimpleTestApp$ShouldBeTransformed
+ * @run main RedefineClassHelper
+ * @run driver SimpleTest AOT
*/
+import java.lang.classfile.CodeBuilder;
+import java.lang.classfile.CodeElement;
+import java.lang.classfile.MethodModel;
+
import jdk.test.lib.cds.CDSAppTester;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.helpers.ClassFileInstaller;
-public class JavaAgent {
+public class SimpleTest {
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
- static final String mainClass = "JavaAgentApp";
+ static final String mainClass = "SimpleTestApp";
public static String agentClasses[] = {
- "JavaAgentTransformer",
+ "SimpleAgent",
"Util",
};
- static String agentJar;
+ static String agentJar = "redefineagent.jar";
public static void main(String... args) throws Exception {
- agentJar = ClassFileInstaller.writeJar("agent.jar",
- ClassFileInstaller.Manifest.fromSourceFile("JavaAgentTransformer.mf"),
- agentClasses);
-
Tester t = new Tester();
if (args[0].equals("STATIC") || args[0].equals("DYNAMIC")) {
// Some child processes may have non-zero exits. These are checked by
@@ -123,28 +126,18 @@ public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception
}
}
- static String agentLoadedMsg = "JavaAgentTransformer.premain() is called";
- static String agentPremainFinished = "JavaAgentTransformer::premain() is finished";
-
public void checkExecutionForAOTWorkflow(OutputAnalyzer out, RunMode runMode) throws Exception {
-
if (runMode.isApplicationExecuted()) {
- out.shouldContain(agentLoadedMsg);
- out.shouldContain("Transforming: JavaAgentApp$ShouldBeTransformed; Class> = null");
out.shouldContain("Result: YYYY"); // "XXXX" has been changed to "YYYY" by the agent
- } else {
- out.shouldNotContain(agentLoadedMsg);
}
switch (runMode) {
case RunMode.TRAINING:
- out.shouldContain(agentPremainFinished);
- out.shouldContain("Skipping JavaAgentApp$ShouldBeTransformed: From ClassFileLoadHook");
- out.shouldContain("Skipping JavaAgentTransformer: Unsupported location");
+ out.shouldContain("Skipping SimpleTestApp$ShouldBeTransformed: Has been redefined");
+ out.shouldContain("Skipping RedefineClassHelper: Unsupported location");
break;
case RunMode.ASSEMBLY:
out.shouldContain("Disabled all JVMTI agents during -XX:AOTMode=create");
- out.shouldNotContain(agentPremainFinished);
break;
}
@@ -153,7 +146,6 @@ public void checkExecutionForAOTWorkflow(OutputAnalyzer out, RunMode runMode) th
public void checkExecutionForStaticWorkflow(OutputAnalyzer out, RunMode runMode) throws Exception {
switch (runMode) {
case RunMode.TRAINING:
- out.shouldContain(agentPremainFinished);
out.shouldHaveExitValue(0);
break;
case RunMode.DUMP_STATIC:
@@ -182,14 +174,20 @@ public void checkExecutionForDynamicWorkflow(OutputAnalyzer out, RunMode runMode
}
}
-class JavaAgentApp {
- public static void main(String[] args) {
+class SimpleTestApp {
+ public static void main(String[] args) throws Exception {
+ RedefineClassHelper.redefineMethodBodies(ShouldBeTransformed.class,
+ (MethodModel method) -> method.methodName().equalsString("toString"),
+ (CodeBuilder builder, CodeElement element) -> {
+ builder.ldc("YYYY");
+ builder.areturn();
+ });
System.out.println("Result: " + (new ShouldBeTransformed()));
}
static class ShouldBeTransformed {
public String toString() {
- return "XXXX"; // Will be changed to YYYY by the agent
+ return "XXXX"; // Will be changed to "YYYY" with class redefinition
}
}
}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.java
deleted file mode 100644
index 123e4a0d72b0..000000000000
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JavaAgentTransformer.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-import java.lang.System.Logger.Level;
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.instrument.IllegalClassFormatException;
-import java.lang.instrument.Instrumentation;
-import java.security.ProtectionDomain;
-
-// This class is available on the classpath so it can be accessed by JavaAgentApp
-public class JavaAgentTransformer implements ClassFileTransformer {
- private static Instrumentation savedInstrumentation;
- private static final System.Logger LOGGER = System.getLogger(JavaAgentTransformer.class.getName());
-
- public static void premain(String agentArguments, Instrumentation instrumentation) {
- System.out.println("JavaAgentTransformer.premain() is called");
- instrumentation.addTransformer(new JavaAgentTransformer(), /*canRetransform=*/true);
- savedInstrumentation = instrumentation;
-
- LOGGER.log(Level.WARNING, "JavaAgentTransformer::premain() is finished");
- }
-
- public static Instrumentation getInstrumentation() {
- return savedInstrumentation;
- }
-
- public static void agentmain(String args, Instrumentation inst) throws Exception {
- premain(args, inst);
- }
-
- public byte[] transform(ClassLoader loader, String name, Class> classBeingRedefined,
- ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException {
- if (name.equals("JavaAgentApp$ShouldBeTransformed")) {
- System.out.println("Transforming: " + name + "; Class> = " + classBeingRedefined);
- try {
- replace(buffer, "XXXX", "YYYY");
- } catch (Throwable t) {
- t.printStackTrace();
- }
- Thread.dumpStack();
- return buffer;
- }
- return null;
- }
-
- static void replace(byte[] buffer, String from, String to) {
- int n = Util.replace(buffer, from, to);
- System.out.println("..... replaced " + n + " occurrence(s) of '" + from + "' to '" + to + "'");
- }
-}
diff --git a/test/lib/RedefineClassHelper.java b/test/lib/RedefineClassHelper.java
index 064778b3a2ab..4ee145f923be 100644
--- a/test/lib/RedefineClassHelper.java
+++ b/test/lib/RedefineClassHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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,10 +25,13 @@
import java.lang.classfile.ClassElement;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
+import java.lang.classfile.ClassTransform;
+import java.lang.classfile.CodeTransform;
+import java.lang.classfile.MethodModel;
import java.lang.constant.ClassDesc;
-
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
+import java.util.function.Predicate;
import jdk.test.lib.compiler.InMemoryJavaCompiler;
import jdk.test.lib.helpers.ClassFileInstaller;
@@ -68,6 +71,10 @@ public static void redefineClass(Class> clazz, byte[] bytecode) throws Excepti
instrumentation.redefineClasses(new ClassDefinition(clazz, bytecode));
}
+ private static byte[] getBytecodes(Class> clazz) throws Exception {
+ return getBytecodes(clazz.getClassLoader(), clazz.getName());
+ }
+
private static byte[] getBytecodes(ClassLoader loader, String name) throws Exception {
try (InputStream is = loader.getResourceAsStream(name + ".class")) {
byte[] buf = is.readAllBytes();
@@ -103,6 +110,33 @@ public static byte[] replaceClassName(ClassLoader loader, String oldClassName, S
return replaceClassName(buf, newClassName);
}
+ /*
+ * For the given clazz, use to replace the code body of the methods that are
+ * selected by filter.
+ *
+ * @param clazz the class to redefine
+ * @param filter the Predicate to choose the method(s) to redefine
+ * @param xform used for generating new method bodies
+ *
+ * Example:
+ *
+ * RedefineClassHelper.redefineMethodBodies(ShouldBeTransformed.class,
+ * (MethodModel method) -> method.methodName().equalsString("toString"),
+ * (CodeBuilder builder, CodeElement element) -> {
+ * builder.ldc("YYYY");
+ * builder.areturn();
+ * });
+ */
+ public static void redefineMethodBodies(Class> clazz, Predicate filter, CodeTransform xform) throws Exception {
+ byte[] bytecodes = RedefineClassHelper.getBytecodes(clazz);
+ ClassFile cf = ClassFile.of();
+ ClassModel model = cf.parse(bytecodes);
+
+ ClassTransform transform =
+ ClassTransform.transformingMethodBodies(filter, xform);
+ redefineClass(clazz, cf.transformClass(model, transform));
+ }
+
/**
* Main method to be invoked before test to create the redefineagent.jar
*/
From 3eefd7ab1bf855493e18a9234d5019ee01fb7e25 Mon Sep 17 00:00:00 2001
From: Ashay Rane
Date: Mon, 17 Aug 2026 16:20:45 +0000
Subject: [PATCH 34/88] 8389829: (fs) Copying symbolic link fails in Developer
Mode (win)
Reviewed-by: alanb
---
.../sun/nio/fs/WindowsFileAttributeViews.java | 6 +-
.../classes/sun/nio/fs/WindowsFileCopy.java | 50 ++++-
test/jdk/java/nio/file/Files/CopyAndMove.java | 210 +++++++++++++-----
3 files changed, 208 insertions(+), 58 deletions(-)
diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java b/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java
index 9a6c0cf37649..cd84103743f7 100644
--- a/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2024, 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
@@ -272,8 +272,8 @@ void setAttributes(WindowsFileAttributes attrs)
// as otherwise the last modified time may be wrong.
setFileTimes(
WindowsFileAttributes.toWindowsTime(attrs.creationTime()),
- WindowsFileAttributes.toWindowsTime(attrs.lastModifiedTime()),
- WindowsFileAttributes.toWindowsTime(attrs.lastAccessTime()));
+ WindowsFileAttributes.toWindowsTime(attrs.lastAccessTime()),
+ WindowsFileAttributes.toWindowsTime(attrs.lastModifiedTime()));
}
}
diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsFileCopy.java b/src/java.base/windows/classes/sun/nio/fs/WindowsFileCopy.java
index a561e81536ab..41557733c171 100644
--- a/src/java.base/windows/classes/sun/nio/fs/WindowsFileCopy.java
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsFileCopy.java
@@ -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
@@ -170,11 +170,15 @@ static void copy(final WindowsPath source,
}
}
+ if (!followLinks && sourceAttrs.isSymbolicLink()) {
+ copySymbolicLink(source, sourceAttrs, target, copyAttributes);
+ return;
+ }
+
// Use CopyFileEx if the file is not a directory or junction
if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
boolean isBuffering = sourceAttrs.size() <= UNBUFFERED_IO_THRESHOLD;
- final int flags = (followLinks ? 0 : COPY_FILE_COPY_SYMLINK) |
- (isBuffering ? 0 : COPY_FILE_NO_BUFFERING);
+ final int flags = isBuffering ? 0 : COPY_FILE_NO_BUFFERING;
if (interruptible) {
// interruptible copy
@@ -246,6 +250,7 @@ public void implRun() throws IOException {
RemoveDirectory(targetPath);
} catch (WindowsException ignore) { }
}
+ throw x;
}
// copy security attributes. If this fail it doesn't cause the move
@@ -256,6 +261,45 @@ public void implRun() throws IOException {
}
}
+ private static void copySymbolicLink(WindowsPath source,
+ WindowsFileAttributes sourceAttrs,
+ WindowsPath target,
+ boolean copyAttributes)
+ throws IOException
+ {
+ String targetPath = asWin32Path(target);
+ try {
+ String linkTarget = WindowsLinkSupport.readLink(source);
+ int flags = sourceAttrs.isDirectoryLink() ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
+ WindowsLinkSupport.createSymbolicLink(targetPath,
+ WindowsPath.addPrefixIfNeeded(linkTarget),
+ flags);
+ } catch (WindowsException x) {
+ x.rethrowAsIOException(target);
+ }
+
+ if (copyAttributes) {
+ WindowsFileAttributeViews.Dos view =
+ WindowsFileAttributeViews.createDosView(target, false);
+ try {
+ view.setAttributes(sourceAttrs);
+ } catch (IOException x) {
+ try {
+ if (sourceAttrs.isDirectoryLink()) {
+ RemoveDirectory(targetPath);
+ } else {
+ DeleteFile(targetPath);
+ }
+ } catch (WindowsException ignore) { }
+ throw x;
+ }
+
+ try {
+ copySecurityAttributes(source, target, false);
+ } catch (IOException ignore) { }
+ }
+ }
+
// throw a DirectoryNotEmpty exception if not empty
static void ensureEmptyDir(WindowsPath dir) throws IOException {
try (WindowsDirectoryStream dirStream =
diff --git a/test/jdk/java/nio/file/Files/CopyAndMove.java b/test/jdk/java/nio/file/Files/CopyAndMove.java
index f8bc9f997b75..ca4cc0aaf00c 100644
--- a/test/jdk/java/nio/file/Files/CopyAndMove.java
+++ b/test/jdk/java/nio/file/Files/CopyAndMove.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2024, 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
@@ -138,10 +138,10 @@ static void checkBasicAttributes(BasicFileAttributes attrs1,
BasicFileAttributes attrs2)
{
// check file type
- assertTrue(attrs1.isRegularFile() == attrs2.isRegularFile());
- assertTrue(attrs1.isDirectory() == attrs2.isDirectory());
- assertTrue(attrs1.isSymbolicLink() == attrs2.isSymbolicLink());
- assertTrue(attrs1.isOther() == attrs2.isOther());
+ assertEquals(attrs1.isRegularFile(), attrs2.isRegularFile());
+ assertEquals(attrs1.isDirectory(), attrs2.isDirectory());
+ assertEquals(attrs1.isSymbolicLink(), attrs2.isSymbolicLink());
+ assertEquals(attrs1.isOther(), attrs2.isOther());
// check last modified time if not a symbolic link
if (!attrs1.isSymbolicLink()) {
@@ -162,30 +162,43 @@ static void checkBasicAttributes(BasicFileAttributes attrs1,
// check size
if (attrs1.isRegularFile())
- assertTrue(attrs1.size() == attrs2.size());
+ assertEquals(attrs1.size(), attrs2.size());
+ }
+
+ static void setWindowsLinkAttributes(Path link) throws IOException {
+ // Set the timestamp to 2000-01-01T00:00:00Z.
+ FileTime time = FileTime.from(946684800, TimeUnit.SECONDS);
+
+ BasicFileAttributeView basicView =
+ getFileAttributeView(link, BasicFileAttributeView.class, NOFOLLOW_LINKS);
+ basicView.setTimes(/* mtime */ time, /* atime */ null, /* creation */ time);
+
+ DosFileAttributeView dosView =
+ getFileAttributeView(link, DosFileAttributeView.class, NOFOLLOW_LINKS);
+ dosView.setHidden(true);
}
static void checkPosixAttributes(PosixFileAttributes attrs1,
PosixFileAttributes attrs2)
{
- assertTrue(attrs1.permissions().equals(attrs2.permissions()),
+ assertEquals(attrs1.permissions(), attrs2.permissions(),
"permissions%n1 (%d): %s%n2 (%d): %s%n%n",
attrs1.permissions().size(), attrs1.permissions(),
attrs2.permissions().size(), attrs2.permissions());
- assertTrue(attrs1.owner().equals(attrs2.owner()),
+ assertEquals(attrs1.owner(), attrs2.owner(),
"owner%n1: %s%n2: %s%n%n", attrs1.owner(), attrs2.owner());
- assertTrue(attrs1.group().equals(attrs2.group()),
+ assertEquals(attrs1.group(), attrs2.group(),
"group%n1: %s%n2: %s%n%n", attrs1.group(), attrs2.group());
}
static void checkDosAttributes(DosFileAttributes attrs1,
DosFileAttributes attrs2)
{
- assertTrue(attrs1.isReadOnly() == attrs2.isReadOnly(),
+ assertEquals(attrs1.isReadOnly(), attrs2.isReadOnly(),
"isReadOnly%n1: %s%n2: %s%n%n", attrs1.isReadOnly(), attrs2.isReadOnly());
- assertTrue(attrs1.isHidden() == attrs2.isHidden(),
+ assertEquals(attrs1.isHidden(), attrs2.isHidden(),
"isHidden%n1: %s%n2: %s%n%n", attrs1.isHidden(), attrs2.isHidden());
- assertTrue(attrs1.isSystem() == attrs2.isSystem(),
+ assertEquals(attrs1.isSystem(), attrs2.isSystem(),
"isSystem%n1: %s%n2: %s%n%n", attrs1.isSystem(), attrs2.isSystem());
}
@@ -197,7 +210,7 @@ static void checkUserDefinedFileAttributes(Map attrs1,
ByteBuffer bb1 = attrs1.get(name);
ByteBuffer bb2 = attrs2.get(name);
assertTrue(bb2 != null);
- assertTrue(bb1.equals(bb2));
+ assertEquals(bb1, bb2);
}
}
@@ -211,7 +224,7 @@ static Map readUserDefinedFileAttributes(Path file)
int size = view.size(name);
ByteBuffer bb = ByteBuffer.allocate(size);
int n = view.read(name, bb);
- assertTrue(n == size);
+ assertEquals(n, size);
bb.flip();
result.put(name, bb);
}
@@ -256,7 +269,7 @@ static void moveAndVerify(Path source, Path target, CopyOption... options)
// move file
Path result = move(source, target, options);
- assertTrue(result == target);
+ assertEquals(result, target);
// verify source does not exist
assertTrue(notExists(source));
@@ -677,12 +690,74 @@ static void testMove(Path dir1, Path dir2, boolean supportsSymbolicLinks)
delete(source);
}
+ static void checkTargetAttributes(Path source, Path target,
+ BasicFileAttributes sourceAttrs)
+ throws IOException
+ {
+ checkBasicAttributes(sourceAttrs,
+ readAttributes(target, BasicFileAttributes.class));
+
+ // check POSIX attributes are copied
+ if (!Platform.isWindows() && testPosixAttributes) {
+ checkPosixAttributes(
+ readAttributes(source, PosixFileAttributes.class),
+ readAttributes(target, PosixFileAttributes.class));
+ }
+
+ // verify other attributes when same provider
+ if (source.getFileSystem().provider() == target.getFileSystem().provider()) {
+ // check DOS attributes are copied
+ if (Platform.isWindows()) {
+ checkDosAttributes(
+ readAttributes(source, DosFileAttributes.class),
+ readAttributes(target, DosFileAttributes.class));
+ }
+
+ // check named attributes are copied
+ if (getFileStore(source).supportsFileAttributeView("xattr") &&
+ getFileStore(target).supportsFileAttributeView("xattr"))
+ {
+ checkUserDefinedFileAttributes(readUserDefinedFileAttributes(source),
+ readUserDefinedFileAttributes(target));
+ }
+ }
+ }
+
+ static void checkSymLinkAttributes(Path source, Path target,
+ BasicFileAttributes sourceAttrs)
+ throws IOException
+ {
+ assertTrue(sourceAttrs.isSymbolicLink());
+
+ BasicFileAttributes targetAttrs =
+ readAttributes(target, BasicFileAttributes.class, NOFOLLOW_LINKS);
+ checkBasicAttributes(sourceAttrs, targetAttrs);
+
+ // verify other attributes when same provider and on Windows
+ if (Platform.isWindows() && source.getFileSystem().provider() ==
+ target.getFileSystem().provider()) {
+ // check that timestamps on the source are retained for the target
+ FileTime srcCreationTime = sourceAttrs.creationTime();
+ FileTime tgtCreationTime = targetAttrs.creationTime();
+ assertEquals(srcCreationTime, tgtCreationTime);
+
+ FileTime srcModTime = sourceAttrs.lastModifiedTime();
+ FileTime tgtModTime = targetAttrs.lastModifiedTime();
+ assertEquals(srcModTime, tgtModTime);
+
+ // check DOS attributes are copied
+ checkDosAttributes(
+ readAttributes(source, DosFileAttributes.class, NOFOLLOW_LINKS),
+ readAttributes(target, DosFileAttributes.class, NOFOLLOW_LINKS));
+ }
+ }
+
// copy source to target with verification
static void copyAndVerify(Path source, Path target, CopyOption... options)
throws IOException
{
Path result = copy(source, target, options);
- assertTrue(result == target);
+ assertEquals(result, target);
// get attributes of source and target file to verify copy
boolean followLinks = true;
@@ -701,41 +776,18 @@ static void copyAndVerify(Path source, Path target, CopyOption... options)
// check hash if regular file
if (basicAttributes.isRegularFile())
- assertTrue(computeHash(source) == computeHash(target));
+ assertEquals(computeHash(source), computeHash(target));
// check link target if symbolic link
if (basicAttributes.isSymbolicLink())
- assert(readSymbolicLink(source).equals(readSymbolicLink(target)));
+ assertEquals(readSymbolicLink(source), readSymbolicLink(target));
// check that attributes are copied
- if (copyAttributes && followLinks) {
- checkBasicAttributes(basicAttributes,
- readAttributes(source, BasicFileAttributes.class, linkOptions));
-
- // check POSIX attributes are copied
- if (!Platform.isWindows() && testPosixAttributes) {
- checkPosixAttributes(
- readAttributes(source, PosixFileAttributes.class, linkOptions),
- readAttributes(target, PosixFileAttributes.class, linkOptions));
- }
-
- // verify other attributes when same provider
- if (source.getFileSystem().provider() == target.getFileSystem().provider()) {
- // check DOS attributes are copied
- if (Platform.isWindows()) {
- checkDosAttributes(
- readAttributes(source, DosFileAttributes.class, linkOptions),
- readAttributes(target, DosFileAttributes.class, linkOptions));
- }
-
- // check named attributes are copied
- if (followLinks &&
- getFileStore(source).supportsFileAttributeView("xattr") &&
- getFileStore(target).supportsFileAttributeView("xattr"))
- {
- checkUserDefinedFileAttributes(readUserDefinedFileAttributes(source),
- readUserDefinedFileAttributes(target));
- }
+ if (copyAttributes) {
+ if (followLinks) {
+ checkTargetAttributes(source, target, basicAttributes);
+ } else if (basicAttributes.isSymbolicLink()) {
+ checkSymLinkAttributes(source, target, basicAttributes);
}
}
}
@@ -970,6 +1022,23 @@ static void testCopyFileToFile(Path dir1, Path dir2, boolean supportsSymbolicLin
delete(source);
}
+ /**
+ * Test: Copy link + attributes
+ */
+ if (supportsSymbolicLinks) {
+ source = createSourceFile(dir1);
+ link = dir1.resolve("link");
+ createSymbolicLink(link, source);
+ if (Platform.isWindows()) {
+ setWindowsLinkAttributes(link);
+ }
+
+ target = getTargetFile(dir2);
+ copyAndVerify(link, target, NOFOLLOW_LINKS, COPY_ATTRIBUTES);
+ delete(link);
+ delete(source);
+ }
+
/**
* Test: Copy link (to directory)
*/
@@ -984,6 +1053,24 @@ static void testCopyFileToFile(Path dir1, Path dir2, boolean supportsSymbolicLin
delete(source);
}
+ /**
+ * Test: Copy link to directory + attributes
+ */
+ if (supportsSymbolicLinks) {
+ source = dir1.resolve("mydir");
+ createDirectory(source);
+ link = dir1.resolve("link");
+ createSymbolicLink(link, source);
+ if (Platform.isWindows()) {
+ setWindowsLinkAttributes(link);
+ }
+
+ target = getTargetFile(dir2);
+ copyAndVerify(link, target, NOFOLLOW_LINKS, COPY_ATTRIBUTES);
+ delete(link);
+ delete(source);
+ }
+
/**
* Test: Copy broken link
*/
@@ -1121,9 +1208,9 @@ static void testCopyInputStreamToFile(int size) throws IOException {
} else {
n = copy(in, target);
}
- assertTrue(in.read() == -1); // EOF
- assertTrue(n == size);
- assertTrue(size(target) == size);
+ assertEquals(in.read(), -1); // EOF
+ assertEquals(n, size);
+ assertEquals(size(target), size);
} finally {
in.close();
}
@@ -1171,15 +1258,15 @@ static void testCopyFileToOuputStream(int size) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
long n = copy(source, out);
- assertTrue(n == size);
- assertTrue(out.size() == size);
+ assertEquals(n, size);
+ assertEquals(out.size(), size);
byte[] read = out.toByteArray();
assertTrue(Arrays.equals(read, b));
// check output stream is open
out.write(0);
- assertTrue(out.size() == size+1);
+ assertEquals(out.size(), size+1);
} finally {
delete(source);
}
@@ -1197,6 +1284,25 @@ static void assertTrue(boolean value, String format, Object... args) {
}
}
+ static void assertEquals(long lhs, long rhs) {
+ if (lhs != rhs) {
+ throw new RuntimeException("assertEquals: " + lhs + " != " + rhs);
+ }
+ }
+
+ static void assertEquals(Object lhs, Object rhs) {
+ assertEquals(lhs, rhs, null);
+ }
+
+ static void assertEquals(Object lhs, Object rhs, String format, Object... args) {
+ if (!Objects.equals(lhs, rhs)) {
+ String msg = format == null
+ ? "assertEquals: " + Objects.toString(lhs) + " != " + Objects.toString(rhs)
+ : String.format(format, args);
+ throw new RuntimeException(msg);
+ }
+ }
+
// computes simple hash of the given file
static int computeHash(Path file) throws IOException {
int h = 0;
From 134c07f4fa4a4cf71b0e96b40c372a96371616f1 Mon Sep 17 00:00:00 2001
From: Ioi Lam
Date: Mon, 17 Aug 2026 16:29:29 +0000
Subject: [PATCH 35/88] 8390447: Update ProblemList-Xcomp.txt entries for
8388438
Reviewed-by: dholmes
---
test/hotspot/jtreg/ProblemList-Xcomp.txt | 2 --
1 file changed, 2 deletions(-)
diff --git a/test/hotspot/jtreg/ProblemList-Xcomp.txt b/test/hotspot/jtreg/ProblemList-Xcomp.txt
index 1262c1698bd6..c6443fd3f932 100644
--- a/test/hotspot/jtreg/ProblemList-Xcomp.txt
+++ b/test/hotspot/jtreg/ProblemList-Xcomp.txt
@@ -62,5 +62,3 @@ gc/arguments/TestNewSizeFlags.java 8299116 macosx-aarch64
#############################################################################
# Value Objects failures start here:
-runtime/cds/appcds/aotCache/AOTMapTest.java#valhalla 8388438 generic-all
-runtime/cds/appcds/cacheObject/ArchivedFlatArrayTest.java 8388438 generic-all
From c328739ac02fe6d241a5810235cd512f52d5b248 Mon Sep 17 00:00:00 2001
From: Ioi Lam
Date: Mon, 17 Aug 2026 16:29:59 +0000
Subject: [PATCH 36/88] 8390357: AOTMapTest.java fails
--vmoptions:--enable-preview
Reviewed-by: thartmann, lfoltan
---
.../cds/appcds/aotCache/AOTMapTest.java | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java
index 5f9652dc7bec..61416929e6d4 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTMapTest.java
@@ -66,13 +66,12 @@
* AOTMapTestValhallaHelper$Wrapper
* AOTMapTestValhallaHelper$WrapperWrapper
* AOTMapTestValhallaHelper$ArchivedData
- * @run main/othervm/timeout=240 AOTMapTest AOT --two-step-training
+ * @run main/othervm/timeout=240 AOTMapTest AOT --two-step-training Valhalla
*/
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
-import jdk.internal.misc.PreviewFeatures;
import java.util.ArrayList;
import jdk.test.lib.cds.CDSAppTester;
import jdk.test.lib.helpers.ClassFileInstaller;
@@ -82,12 +81,9 @@ public class AOTMapTest {
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
static final String mainClass = "AOTMapTestApp";
static final String classLoadLogFile = "production.class.load.log";
-
+ static boolean testValhalla;
public static void main(String[] args) throws Exception {
- doTest(args);
- }
-
- public static void doTest(String[] args) throws Exception {
+ testValhalla = args.length >= 3 && args[2].equals("Valhalla");
Tester tester = new Tester();
tester.run(args);
@@ -137,7 +133,7 @@ public String[] vmArgs(RunMode runMode) {
vmArgs.add("--add-exports");
vmArgs.add("java.base/jdk.internal.misc=ALL-UNNAMED");
- if (PreviewFeatures.isEnabled()) {
+ if (testValhalla) {
vmArgs.add("--enable-preview");
vmArgs.add("--add-exports");
vmArgs.add("java.base/jdk.internal.value=ALL-UNNAMED");
@@ -165,6 +161,7 @@ public String[] vmArgs(RunMode runMode) {
public String[] appCommandLine(RunMode runMode) {
return new String[] {
mainClass,
+ testValhalla ? "Valhalla" : "none"
};
}
}
@@ -176,9 +173,10 @@ public static void main(String[] args) throws Exception {
System.out.println("Hello AOTMapTestApp");
testCustomLoader();
- if (PreviewFeatures.isEnabled()) {
+ if (args[0].equals("Valhalla")) {
Class> c = Class.forName("AOTMapTestValhallaHelper");
- c.newInstance();
+ Object o = c.newInstance();
+ System.out.println(o);
}
}
From f5a59340d990e3fc8cf5a7e50e096d3d37d926f5 Mon Sep 17 00:00:00 2001
From: Jaikiran Pai
Date: Mon, 17 Aug 2026 16:44:33 +0000
Subject: [PATCH 37/88] 8390202: Typos in the javadoc of various classes in
jdk.sctp module
Reviewed-by: dfuchs
---
.../share/classes/com/sun/nio/sctp/MessageInfo.java | 8 ++++----
.../share/classes/com/sun/nio/sctp/SctpChannel.java | 8 ++++----
.../share/classes/com/sun/nio/sctp/SctpMultiChannel.java | 4 ++--
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/jdk.sctp/share/classes/com/sun/nio/sctp/MessageInfo.java b/src/jdk.sctp/share/classes/com/sun/nio/sctp/MessageInfo.java
index 8fdd4e31bd9e..9440f15c02fe 100644
--- a/src/jdk.sctp/share/classes/com/sun/nio/sctp/MessageInfo.java
+++ b/src/jdk.sctp/share/classes/com/sun/nio/sctp/MessageInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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
@@ -142,7 +142,7 @@ public static MessageInfo createOutgoing(Association association,
* otherwise the preferred destination of the message to be sent.
*
* @return The socket address, or {@code null} if this instance is to be
- * used for sending a message and has been construced without
+ * used for sending a message and has been constructed without
* specifying a preferred destination address
*
*/
@@ -154,7 +154,7 @@ public static MessageInfo createOutgoing(Association association,
* sent on.
*
* @return The association, or {@code null} if this instance is to be
- * used for sending a message and has been construced using the
+ * used for sending a message and has been constructed using the
* the {@link #createOutgoing(SocketAddress,int)
* createOutgoing(SocketAddress,int)} static factory method
*/
@@ -163,7 +163,7 @@ public static MessageInfo createOutgoing(Association association,
/**
* Returns the number of bytes read for the received message.
*
- * This method is only appicable for received messages, it has no
+ *
This method is only applicable for received messages, it has no
* meaning for messages being sent.
*
* @return The number of bytes read, {@code -1} if the channel is an {@link
diff --git a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java
index 312a143a8e43..9e38f5fb7bda 100644
--- a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java
+++ b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpChannel.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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
@@ -438,7 +438,7 @@ public abstract SctpChannel unbindAddress(InetAddress address)
/**
* Connects this channel's socket.
*
- *
This is a convience method and is equivalent to evaluating the
+ *
This is a convenience method and is equivalent to evaluating the
* following expression:
*
* setOption(SctpStandardSocketOptions.SCTP_INIT_MAXSTREAMS, SctpStandardSocketOption.InitMaxStreams.create(maxInStreams, maxOutStreams))
@@ -723,7 +723,7 @@ public final int validOps() {
* does not contain the complete message, then an invocation of {@link
* MessageInfo#isComplete isComplete} on the returned {@code
* MessageInfo} will return {@code false}, and more invocations of this
- * method will be necessary to completely consume the messgae. Only
+ * method will be necessary to completely consume the message. Only
* one message at a time will be partially delivered in any stream. The
* socket option {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE
* SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
@@ -829,7 +829,7 @@ public abstract MessageInfo receive(ByteBuffer dst,
* output buffer
*
* @throws InvalidStreamException
- * If {@code streamNumner} is negative or greater than or equal to
+ * If {@code streamNumber} is negative or greater than or equal to
* the maximum number of outgoing streams
*
* @throws java.nio.channels.ClosedChannelException
diff --git a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java
index 54b5bb796b06..d877b5a5fefb 100644
--- a/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java
+++ b/src/jdk.sctp/share/classes/com/sun/nio/sctp/SctpMultiChannel.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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
@@ -544,7 +544,7 @@ public final int validOps() {
* contain the complete message, then an invocation of {@link
* MessageInfo#isComplete isComplete} on the returned {@code
* MessageInfo} will return {@code false}, and more invocations of this
- * method will be necessary to completely consume the messgae. Only
+ * method will be necessary to completely consume the message. Only
* one message at a time will be partially delivered in any stream. The
* socket option {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE
* SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
From 0d2320aea28c47106b08fbc701fc09250cd5b4dd Mon Sep 17 00:00:00 2001
From: Matias Saavedra Silva
Date: Mon, 17 Aug 2026 18:48:56 +0000
Subject: [PATCH 38/88] 8368350: Do not support -XX:+AOTClassLinking for static
CDS archive
Reviewed-by: iklam, lfoltan, asmehra
---
src/hotspot/share/cds/cdsConfig.cpp | 13 ++-
test/hotspot/jtreg/TEST.groups | 76 +-------------
.../appcds/LambdaWithUseImplMethodHandle.java | 9 +-
.../AOTClassLinkingVMOptions.java | 0
.../AOTClassLinkingVerification.java | 0
.../AOTLoaderConstraintsTest.java | 0
.../aotClassLinking/AddExports.java | 0
.../aotClassLinking/AddOpens.java | 0
.../aotClassLinking/AddReads.java | 0
.../aotClassLinking/BadNewClass.jasm | 0
.../aotClassLinking/BadNewClass2.jasm | 0
.../aotClassLinking/BadNewClass3.jasm | 0
.../aotClassLinking/BadNewClass4.jasm | 0
.../aotClassLinking/BadOldClass.jasm | 0
.../aotClassLinking/BadOldClass2.jasm | 0
.../aotClassLinking/BadOldClass3.jasm | 0
.../aotClassLinking/BadOldClass4.jasm | 0
.../aotClassLinking/BadOldClassA.jasm | 0
.../aotClassLinking/BadOldClassB.jasm | 0
.../aotClassLinking/BootClass.java | 0
.../aotClassLinking/BulkLoaderTest.java | 23 +++--
.../aotClassLinking/FakeCodeLocation.java | 0
.../GeneratedInternedString.java | 0
.../aotClassLinking/GoodOldClass.jasm | 0
.../InitiatingLoaderTester.jasm | 0
.../LambdaInExcludedClass.java | 12 +--
.../aotClassLinking/MethodHandleTest.java | 0
.../NonFinalStaticWithInitVal.java | 0
.../NonFinalStaticWithInitVal_Helper.jasm | 0
.../aotClassLinking/StringConcatStress.java | 4 +-
.../aotClassLinking/TestSetupAOTTest.java | 0
.../aotClassLinking/TrainingRun.java | 0
.../aotClassLinking/WeakReferenceTest.java | 0
...DynamicDumpWithAOTLinkedStaticArchive.java | 60 -----------
.../runtime/cds/appcds/aotFlags/AOTFlags.java | 14 ++-
.../jvmti/CFLH/ClassFileLoadHookTest.java | 99 ++++++++++++++-----
.../resolvedConstants/ResolvedConstants.java | 3 +-
37 files changed, 123 insertions(+), 190 deletions(-)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/AOTClassLinkingVMOptions.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/AOTClassLinkingVerification.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/AOTLoaderConstraintsTest.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/AddExports.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/AddOpens.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/AddReads.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BadNewClass.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BadNewClass2.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BadNewClass3.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BadNewClass4.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BadOldClass.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BadOldClass2.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BadOldClass3.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BadOldClass4.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BadOldClassA.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BadOldClassB.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BootClass.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/BulkLoaderTest.java (94%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/FakeCodeLocation.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/GeneratedInternedString.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/GoodOldClass.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/InitiatingLoaderTester.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/LambdaInExcludedClass.java (90%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/MethodHandleTest.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/NonFinalStaticWithInitVal.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/NonFinalStaticWithInitVal_Helper.jasm (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/StringConcatStress.java (99%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/TestSetupAOTTest.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/TrainingRun.java (100%)
rename test/hotspot/jtreg/runtime/cds/appcds/{ => aotCache}/aotClassLinking/WeakReferenceTest.java (100%)
delete mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java
diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp
index 3e51712c75e1..c07884a13cc8 100644
--- a/src/hotspot/share/cds/cdsConfig.cpp
+++ b/src/hotspot/share/cds/cdsConfig.cpp
@@ -767,11 +767,20 @@ void CDSConfig::setup_compiler_args() {
void CDSConfig::prepare_for_dumping() {
assert(CDSConfig::is_dumping_archive(), "sanity");
+ if (is_dumping_classic_static_archive() && AOTClassLinking) {
+ if (FLAG_IS_CMDLINE(AOTClassLinking)) {
+ log_warning(cds)("AOTClassLinking is not supported for classic CDS archive");
+ }
+ FLAG_SET_ERGO(AOTClassLinking, false);
+ FLAG_SET_ERGO(AOTInvokeDynamicLinking, false);
+ }
+
if (is_dumping_dynamic_archive() && AOTClassLinking) {
if (FLAG_IS_CMDLINE(AOTClassLinking)) {
log_warning(cds)("AOTClassLinking is not supported for dynamic CDS archive");
}
FLAG_SET_ERGO(AOTClassLinking, false);
+ FLAG_SET_ERGO(AOTInvokeDynamicLinking, false);
}
if (is_dumping_dynamic_archive() && !is_using_archive()) {
@@ -949,7 +958,7 @@ bool CDSConfig::is_preserving_verification_constraints() {
} else if (is_dumping_final_static_archive()) { // writing AOT cache
return is_dumping_aot_linked_classes();
} else if (is_dumping_classic_static_archive()) {
- return is_dumping_aot_linked_classes();
+ return false;
} else {
return false;
}
@@ -1055,7 +1064,7 @@ void CDSConfig::stop_using_full_module_graph(const char* reason) {
}
bool CDSConfig::is_dumping_aot_linked_classes() {
- if (is_dumping_classic_static_archive() || is_dumping_final_static_archive()) {
+ if (is_dumping_final_static_archive()) {
// FMG is required to guarantee that all cached boot/platform/app classes
// are visible in the production run, so they can be unconditionally
// loaded during VM bootstrap.
diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups
index 8e30e73a1826..0216f5f13550 100644
--- a/test/hotspot/jtreg/TEST.groups
+++ b/test/hotspot/jtreg/TEST.groups
@@ -188,7 +188,7 @@ tier1_compiler_1 = \
-compiler/classUnloading/methodUnloading/TestOverloadCompileQueues.java \
-compiler/codecache/stress \
-compiler/codegen/aes
-
+
tier1_compiler_2 = \
compiler/gcbarriers/ \
compiler/igvn/ \
@@ -445,7 +445,6 @@ hotspot_appcds_dynamic = \
-runtime/cds/appcds/agent \
-runtime/cds/appcds/aotAnnotations \
-runtime/cds/appcds/aotCache \
- -runtime/cds/appcds/aotClassLinking \
-runtime/cds/appcds/aotCode \
-runtime/cds/appcds/aotFlags \
-runtime/cds/appcds/aotProfile \
@@ -540,79 +539,6 @@ hotspot_cds_epsilongc = \
runtime/cds/appcds/jigsaw \
runtime/cds/appcds/loaderConstraints
-# Run "old" CDS tests with -XX:+AOTClassLinking. This should include most CDS tests, except for
-# those that rely on redefining classes that are already archived.
-# Note that appcds/aotXXX directories are excluded -- those tests already specifically
-# test AOT class linking, so there's no need to run them again with -XX:+AOTClassLinking.
-hotspot_aot_classlinking = \
- runtime/cds \
- -runtime/cds/appcds/agent \
- -runtime/cds/appcds/aotAnnotations \
- -runtime/cds/appcds/aotCache \
- -runtime/cds/appcds/aotClassLinking \
- -runtime/cds/appcds/aotCode \
- -runtime/cds/appcds/aotFlags \
- -runtime/cds/appcds/aotProfile \
- -runtime/cds/appcds/ArchivedFieldMetadataMismatchTest.java \
- -runtime/cds/appcds/BadBSM.java \
- -runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java \
- -runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java \
- -runtime/cds/appcds/CDSandJFR.java \
- -runtime/cds/appcds/LambdaContainsOldInf.java \
- -runtime/cds/appcds/customLoader/CustomClassListDump.java \
- -runtime/cds/appcds/customLoader/HelloCustom_JFR.java \
- -runtime/cds/appcds/customLoader/OldClassAndInf.java \
- -runtime/cds/appcds/customLoader/ParallelTestMultiFP.java \
- -runtime/cds/appcds/customLoader/ParallelTestSingleFP.java \
- -runtime/cds/appcds/customLoader/SameNameInTwoLoadersTest.java \
- -runtime/cds/appcds/DumpClassListWithLF.java \
- -runtime/cds/appcds/dynamicArchive \
- -runtime/cds/appcds/HelloExtTest.java \
- -runtime/cds/appcds/InlineFieldExclusionTest.java \
- -runtime/cds/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java \
- -runtime/cds/appcds/jigsaw/ExactOptionMatch.java \
- -runtime/cds/appcds/jigsaw/JigsawOptionsCombo.java \
- -runtime/cds/appcds/jigsaw/modulepath/AddModules.java \
- -runtime/cds/appcds/jigsaw/modulepath/JvmtiAddPath.java \
- -runtime/cds/appcds/jigsaw/modulepath/MainModuleOnly.java \
- -runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP.java \
- -runtime/cds/appcds/jigsaw/modulepath/ModulePathAndCP_JFR.java \
- -runtime/cds/appcds/jigsaw/modulepath/ModulePathAndFMG.java \
- -runtime/cds/appcds/jigsaw/overridetests/OverrideTests.java \
- -runtime/cds/appcds/jigsaw/RedefineClassesInModuleGraph.java \
- -runtime/cds/appcds/JvmtiAddPath.java \
- -runtime/cds/appcds/jvmti \
- -runtime/cds/appcds/LambdaProxyClasslist.java \
- -runtime/cds/appcds/loaderConstraints/LoaderConstraintsTest.java \
- -runtime/cds/appcds/methodHandles \
- -runtime/cds/appcds/NestHostOldInf.java \
- -runtime/cds/appcds/OldClassTest.java \
- -runtime/cds/appcds/OldClassWithjsr.java \
- -runtime/cds/appcds/OldInfExtendsInfDefMeth.java \
- -runtime/cds/appcds/OldSuperClass.java \
- -runtime/cds/appcds/OldSuperInfIndirect.java \
- -runtime/cds/appcds/OldSuperInf.java \
- -runtime/cds/appcds/redefineClass \
- -runtime/cds/appcds/resolvedConstants/AOTLinkedLambdas.java \
- -runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java \
- -runtime/cds/appcds/resolvedConstants/ResolvedConstants.java \
- -runtime/cds/appcds/RewriteBytecodesInlineTest.java \
- -runtime/cds/appcds/RewriteBytecodesTest.java \
- -runtime/cds/appcds/SignedJar.java \
- -runtime/cds/appcds/SpecifySysLoaderProp.java \
- -runtime/cds/appcds/StaticArchiveWithLambda.java \
- -runtime/cds/appcds/TestEpsilonGCWithCDS.java \
- -runtime/cds/appcds/TestParallelGCWithCDS.java \
- -runtime/cds/appcds/TestSerialGCWithCDS.java \
- -runtime/cds/appcds/TestZGCWithCDS.java \
- -runtime/cds/appcds/TestWithProfiler.java \
- -runtime/cds/appcds/VerifyObjArrayCloneTest.java \
- -runtime/cds/serviceability/ReplaceCriticalClassesForSubgraphs.java \
- -runtime/cds/serviceability/ReplaceCriticalClasses.java \
- -runtime/cds/serviceability/transformRelatedClasses/TransformInterfaceAndImplementor.java \
- -runtime/cds/serviceability/transformRelatedClasses/TransformSuperAndSubClasses.java \
- -runtime/cds/serviceability/transformRelatedClasses/TransformSuperSubTwoPckgs.java
-
# needs -nativepath:/images/test/hotspot/jtreg/native/
hotspot_metaspace = \
gtest/MetaspaceGtests.java \
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java b/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java
index 5870b4cc36c6..c1e21db4c69d 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/LambdaWithUseImplMethodHandle.java
@@ -27,7 +27,6 @@
* @bug 8290417
* @summary CDS cannot archive lambda proxy with useImplMethodHandle
* @requires vm.cds
- * @requires vm.cds.supports.aot.class.linking
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* @build pkg1.BaseWithProtectedMethod
* @build pkg2.Child
@@ -44,11 +43,10 @@ public class LambdaWithUseImplMethodHandle {
// See pkg2/Child.jcod for details about the condition that triggers JDK-8290417
public static void main(String[] args) throws Exception {
- test(false);
- test(true);
+ test();
}
- static void test(boolean aotClassLinking) throws Exception {
+ static void test() throws Exception {
String appJar = ClassFileInstaller.getJarPath("test.jar");
String mainClass = "LambdaWithUseImplMethodHandleApp";
String expectedMsg = "Called BaseWithProtectedMethod::protectedMethod";
@@ -63,9 +61,6 @@ static void test(boolean aotClassLinking) throws Exception {
.addPrefix("-XX:ExtraSharedClassListFile=" + classList,
"-cp", appJar)
.setArchiveName(archiveName);
- if (aotClassLinking) {
- opts.addPrefix("-XX:+AOTClassLinking");
- }
CDSTestUtils.createArchiveAndCheck(opts);
// run with archive
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVerification.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVerification.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVerification.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTLoaderConstraintsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTLoaderConstraintsTest.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTLoaderConstraintsTest.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AOTLoaderConstraintsTest.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddExports.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddExports.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddOpens.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AddReads.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass2.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass2.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass2.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass3.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass3.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass3.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass3.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass4.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass4.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadNewClass4.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadNewClass4.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass2.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass2.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass2.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass3.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass3.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass3.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass3.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass4.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass4.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClass4.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClass4.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClassA.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClassA.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClassA.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClassA.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClassB.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClassB.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BadOldClassB.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BadOldClassB.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BootClass.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BootClass.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BootClass.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BootClass.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BulkLoaderTest.java
similarity index 94%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BulkLoaderTest.java
index 482f8fb4ad00..9bebaa7a1522 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/BulkLoaderTest.java
@@ -91,15 +91,18 @@ public static void main(String[] args) throws Exception {
// Run without archived FMG -- fail to load
{
- final String archiveType = (args[0].equals("AOT")) ? "AOT cache" : "shared archive file";
String extraVmArgs[] = {
- "-Xlog:cds",
+ "-Xlog:aot,cds",
"-Djdk.module.showModuleResolution=true"
};
t.setCheckExitValue(false);
OutputAnalyzer out = t.productionRun(extraVmArgs);
- out.shouldHaveExitValue(1);
- out.shouldContain(archiveType + " has aot-linked classes. It cannot be used when archived full module graph is not used.");
+ if (args[0].equals("AOT")) {
+ out.shouldHaveExitValue(1);
+ out.shouldContain("AOT cache has aot-linked classes. It cannot be used when archived full module graph is not used.");
+ } else {
+ out.shouldHaveExitValue(0);
+ }
t.setCheckExitValue(true);
}
}
@@ -117,10 +120,14 @@ public String classpath(RunMode runMode) {
@Override
public String[] vmArgs(RunMode runMode) {
- return new String[] {
- "-Xlog:cds,aot,aot+load,cds+class=debug,aot+class=debug",
- "-XX:+AOTClassLinking",
- };
+ if (runMode == RunMode.DUMP_STATIC) {
+ return new String[] { "-Xlog:cds,aot,aot+load,cds+class=debug,aot+class=debug" };
+ } else {
+ return new String[] {
+ "-Xlog:cds,aot,aot+load,cds+class=debug,aot+class=debug",
+ "-XX:+AOTClassLinking",
+ };
+ }
}
@Override
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/FakeCodeLocation.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/FakeCodeLocation.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/FakeCodeLocation.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/FakeCodeLocation.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GeneratedInternedString.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/GeneratedInternedString.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GeneratedInternedString.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/GeneratedInternedString.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/GoodOldClass.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/GoodOldClass.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/GoodOldClass.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/InitiatingLoaderTester.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/InitiatingLoaderTester.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/InitiatingLoaderTester.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/InitiatingLoaderTester.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/LambdaInExcludedClass.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/LambdaInExcludedClass.java
similarity index 90%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/LambdaInExcludedClass.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/LambdaInExcludedClass.java
index c91e999c40fe..45bcad3f9b9e 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/LambdaInExcludedClass.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/LambdaInExcludedClass.java
@@ -30,11 +30,10 @@
* @library /test/jdk/lib/testlibrary /test/lib
* @build LambdaInExcludedClass
* @run driver jdk.test.lib.helpers.ClassFileInstaller LambdaInExcludedClassApp
- * @run driver LambdaInExcludedClass STATIC
+ * @run driver LambdaInExcludedClass AOT
*/
import jdk.test.lib.cds.CDSAppTester;
-import jdk.test.lib.helpers.ClassFileInstaller;
import jdk.test.lib.process.OutputAnalyzer;
public class LambdaInExcludedClass {
@@ -42,7 +41,7 @@ public class LambdaInExcludedClass {
public static void main(String[] args) throws Exception {
Tester t = new Tester();
- t.run(args);
+ t.runAOTWorkflow(args);
}
static class Tester extends CDSAppTester {
@@ -60,8 +59,8 @@ public String classpath(RunMode runMode) {
@Override
public String[] vmArgs(RunMode runMode) {
return new String[] {
+ "-Xlog:aot",
"-Xmx128m",
- "-XX:+AOTClassLinking",
"-XX:+UnlockExperimentalVMOptions",
"-XX:+UseEpsilonGC",
};
@@ -76,10 +75,9 @@ public String[] appCommandLine(RunMode runMode) {
@Override
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
- if (runMode == RunMode.DUMP_STATIC) {
+ if (runMode == RunMode.TRAINING) {
out.shouldContain("Skipping LambdaInExcludedClassApp: Unsupported location");
- out.shouldContain("Cannot aot-resolve constants for LambdaInExcludedClassApp because it is excluded");
- } else {
+ } else if (runMode == RunMode.PRODUCTION) {
out.shouldContain("Hello LambdaInExcludedClassApp");
}
}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/MethodHandleTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/MethodHandleTest.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/MethodHandleTest.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/MethodHandleTest.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/NonFinalStaticWithInitVal.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/NonFinalStaticWithInitVal.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/NonFinalStaticWithInitVal.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/NonFinalStaticWithInitVal.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/NonFinalStaticWithInitVal_Helper.jasm b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/NonFinalStaticWithInitVal_Helper.jasm
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/NonFinalStaticWithInitVal_Helper.jasm
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/NonFinalStaticWithInitVal_Helper.jasm
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/StringConcatStress.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/StringConcatStress.java
similarity index 99%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/StringConcatStress.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/StringConcatStress.java
index 81bf28010b1c..676a98ff387a 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/StringConcatStress.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/StringConcatStress.java
@@ -64,9 +64,7 @@ public String classpath(RunMode runMode) {
}
public String[] vmArgs(RunMode runMode) {
- return new String[] {
- "-XX:+AOTClassLinking", // by default enables AOTInvokeDynamicLinking
- };
+ return new String[0];
}
@Override
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TestSetupAOTTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TestSetupAOTTest.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TestSetupAOTTest.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TestSetupAOTTest.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TrainingRun.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TrainingRun.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/TrainingRun.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/TrainingRun.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/WeakReferenceTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/WeakReferenceTest.java
similarity index 100%
rename from test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/WeakReferenceTest.java
rename to test/hotspot/jtreg/runtime/cds/appcds/aotCache/aotClassLinking/WeakReferenceTest.java
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java
deleted file mode 100644
index 8014ed685ee1..000000000000
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/DynamicDumpWithAOTLinkedStaticArchive.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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
- * 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 8374639
- * @requires vm.cds.supports.aot.class.linking
- * @library /test/lib
- * @build DynamicDumpWithAOTLinkedStaticArchive jdk.test.whitebox.WhiteBox
- * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
- * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar TestApp
- * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. DynamicDumpWithAOTLinkedStaticArchive
- */
-
-import jdk.test.lib.cds.SimpleCDSAppTester;
-import jdk.test.lib.process.OutputAnalyzer;
-
-public class DynamicDumpWithAOTLinkedStaticArchive {
- public static void main(String... args) throws Exception {
- SimpleCDSAppTester.of("DynamicDumpWithAOTLinkedStaticArchive")
- .classpath("app.jar")
- .appCommandLine("TestApp")
- .setGenerateBaseArchive(true)
- .setBaseArchiveOptions("-XX:+AOTClassLinking")
- .setProductionChecker((OutputAnalyzer out) -> {
- out.shouldContain("HelloWorld");
- })
- .runDynamicWorkflow();
- }
-}
-
-class TestApp {
- public static void main(String[] args) {
- System.out.println("HelloWorld");
- System[][][] x = new System[0][0][0];
- System.out.println(x);
- }
-}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java
index f43155bf6d57..718793902390 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotFlags/AOTFlags.java
@@ -476,8 +476,20 @@ static void negativeTests() throws Exception {
out.shouldHaveExitValue(1);
//----------------------------------------------------------------------
- printTestCase("Cannot use a dynamic CDS archive for -XX:AOTCache");
+ printTestCase("Cannot use a classic CDS archive with -XX:+AOTClassLinking");
String staticArchive = "static.jsa";
+
+ pb = ProcessTools.createLimitedTestJavaProcessBuilder(
+ "-Xshare:dump",
+ "-XX:SharedArchiveFile=" + staticArchive,
+ "-XX:+AOTClassLinking");
+ out = CDSTestUtils.executeAndLog(pb, "static");
+ out.shouldContain("AOTClassLinking is not supported for classic CDS archive");
+ out.shouldHaveExitValue(0);
+
+ //----------------------------------------------------------------------
+ printTestCase("Cannot use a dynamic CDS archive for -XX:AOTCache");
+ staticArchive = "static.jsa";
String dynamicArchive = "dynamic.jsa";
pb = ProcessTools.createLimitedTestJavaProcessBuilder(
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java b/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java
index 2674793c04ac..d71a157fe576 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/jvmti/CFLH/ClassFileLoadHookTest.java
@@ -27,14 +27,18 @@
* @summary Test jvmti class file loader hook interaction with AppCDS
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
* @requires vm.cds
+ * @requires vm.cds.supports.aot.class.linking
* @requires vm.jvmti
* @build ClassFileLoadHook
+ * @build jdk.test.whitebox.WhiteBox
+ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm/native ClassFileLoadHookTest
*/
import jdk.test.lib.cds.CDSOptions;
import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.cds.CDSAppTester;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.helpers.ClassFileInstaller;
@@ -46,12 +50,15 @@ public class ClassFileLoadHookTest {
"java/sql/SQLException"
};
+ static final String mainClass = "ClassFileLoadHook";
+ static String wbJar;
+ static String appJar;
+ static String useWb;
+
public static void main(String[] args) throws Exception {
- String wbJar =
- ClassFileInstaller.writeJar("WhiteBox.jar", "jdk.test.whitebox.WhiteBox");
- String appJar =
- ClassFileInstaller.writeJar("ClassFileLoadHook.jar", sharedClasses);
- String useWb = "-Xbootclasspath/a:" + wbJar;
+ wbJar = ClassFileInstaller.writeJar("WhiteBox.jar", "jdk.test.whitebox.WhiteBox");
+ appJar = ClassFileInstaller.writeJar("ClassFileLoadHook.jar", sharedClasses);
+ useWb = "-Xbootclasspath/a:" + wbJar;
// First, run the test class directly, w/o sharing, as a baseline reference
CDSOptions opts = (new CDSOptions())
@@ -61,7 +68,7 @@ public static void main(String[] args) throws Exception {
"-XX:+WhiteBoxAPI",
useWb,
"-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
- "ClassFileLoadHook",
+ mainClass,
"" + ClassFileLoadHook.TestCaseId.SHARING_OFF_CFLH_ON);
CDSTestUtils.run(opts)
.assertNormalExit();
@@ -71,7 +78,7 @@ public static void main(String[] args) throws Exception {
OutputAnalyzer out = TestCommon.exec(appJar,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI", useWb,
- "ClassFileLoadHook",
+ mainClass,
"" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_OFF);
TestCommon.checkExec(out);
@@ -82,7 +89,7 @@ public static void main(String[] args) throws Exception {
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI", useWb,
"-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
- "ClassFileLoadHook",
+ mainClass,
"" + ClassFileLoadHook.TestCaseId.SHARING_AUTO_CFLH_ON);
opts = (new CDSOptions()).setXShareMode("auto");
@@ -93,27 +100,69 @@ public static void main(String[] args) throws Exception {
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI", useWb,
"-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
- "ClassFileLoadHook",
+ mainClass,
"" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON);
TestCommon.checkExec(out);
// JEP 483: if dumped with -XX:+AOTClassLinking, cannot use archive when CFLH is enabled
- TestCommon.testDump(appJar, sharedClasses, useWb, "-XX:+AOTClassLinking");
- out = TestCommon.exec(appJar,
- "-XX:+UnlockDiagnosticVMOptions",
- "-XX:+WhiteBoxAPI", useWb,
- "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
- "-Xlog:aot",
- "-Xlog:cds",
- "ClassFileLoadHook",
- "" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON);
- if (out.contains("Using AOT-linked classes: false (static archive: no aot-linked classes")) {
- // JTREG is executed with VM options that do not support -XX:+AOTClassLinking, so
- // the static archive was not created with aot-linked classes.
- out.shouldHaveExitValue(0);
- } else {
- out.shouldContain("shared archive file has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use.");
- out.shouldNotHaveExitValue(0);
+ Tester t = new Tester();
+ t.setCheckExitValue(false);
+ t.runAOTWorkflow();
+ }
+
+ static class Tester extends CDSAppTester {
+ public Tester() {
+ super(mainClass);
+ }
+
+ @Override
+ public String classpath(RunMode runMode) {
+ return appJar;
+ }
+
+ @Override
+ public String[] vmArgs(RunMode runMode) {
+ if (runMode == RunMode.TRAINING) {
+ return new String[] {
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI", useWb,
+ "-XX:+AOTClassLinking",
+ "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
+ "-Xlog:aot,cds"
+ };
+ } else if (runMode == RunMode.ASSEMBLY) {
+ return new String[] {
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI", useWb,
+ "-XX:+AOTClassLinking",
+ "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
+ "-Xlog:aot,cds"
+ };
+ } else {
+ return new String[] {
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI", useWb,
+ "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
+ "-Xlog:aot,cds"
+ };
+ }
+ }
+
+ @Override
+ public String[] appCommandLine(RunMode runMode) {
+ if (runMode == RunMode.TRAINING) {
+ return new String[] { mainClass, "" + ClassFileLoadHook.TestCaseId.SHARING_OFF_CFLH_ON };
+ } else {
+ return new String[] { mainClass, "" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON };
+ }
+ }
+
+ @Override
+ public void checkExecution(OutputAnalyzer out, RunMode runMode) {
+ if (runMode == RunMode.PRODUCTION) {
+ out.shouldContain("AOT cache has aot-linked classes. It cannot be used when JVMTI ClassFileLoadHook is in use.");
+ out.shouldNotHaveExitValue(0);
+ }
}
}
}
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java
index ea2c6fc88b49..2fc58d7fc0d4 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/resolvedConstants/ResolvedConstants.java
@@ -62,7 +62,6 @@
* @test id=aot
* @summary Dump time resolution of constant pool entries (AOT workflow).
* @requires vm.cds
- * @requires vm.cds.supports.aot.class.linking
* @requires vm.compMode != "Xcomp"
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes/
* @build OldProvider OldClass OldConsumer StringConcatTestOld
@@ -89,7 +88,7 @@ public class ResolvedConstants {
static boolean aotClassLinking;
public static void main(String[] args) throws Exception {
test(args, false);
- if (!args[0].equals("DYNAMIC")) {
+ if (args[0].equals("AOT")) {
test(args, true);
}
}
From 079d77cc05b56829742876cb6e944ca52478d510 Mon Sep 17 00:00:00 2001
From: Fairoz Matte
Date: Mon, 17 Aug 2026 18:52:46 +0000
Subject: [PATCH 39/88] 8390184: Some typos in the jdk.jdi module
Reviewed-by: cjplummer
---
.../classes/com/sun/jdi/InconsistentDebugInfoException.java | 4 ++--
.../share/classes/com/sun/jdi/connect/LaunchingConnector.java | 4 ++--
.../share/classes/com/sun/jdi/connect/spi/Connection.java | 4 ++--
.../classes/com/sun/jdi/request/ClassPrepareRequest.java | 4 ++--
.../com/sun/jdi/request/MonitorContendedEnterRequest.java | 2 +-
.../com/sun/jdi/request/MonitorContendedEnteredRequest.java | 2 +-
6 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/InconsistentDebugInfoException.java b/src/jdk.jdi/share/classes/com/sun/jdi/InconsistentDebugInfoException.java
index 45904e20a99c..17987fd4d7d4 100644
--- a/src/jdk.jdi/share/classes/com/sun/jdi/InconsistentDebugInfoException.java
+++ b/src/jdk.jdi/share/classes/com/sun/jdi/InconsistentDebugInfoException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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,7 @@
package com.sun.jdi;
/**
- * Thrown to indicate that there is an inconistency in the debug
+ * Thrown to indicate that there is an inconsistency in the debug
* information provided by the target VM. For example, this exception
* is thrown if there is a type mismatch between a retrieved value's
* runtime type and its declared type as reported by the target VM.
diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/connect/LaunchingConnector.java b/src/jdk.jdi/share/classes/com/sun/jdi/connect/LaunchingConnector.java
index bfe09a539cfa..0381e94469be 100644
--- a/src/jdk.jdi/share/classes/com/sun/jdi/connect/LaunchingConnector.java
+++ b/src/jdk.jdi/share/classes/com/sun/jdi/connect/LaunchingConnector.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2017, 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
@@ -54,7 +54,7 @@ public interface LaunchingConnector extends Connector {
* received.
*
* Important note: If a target VM is launched through this
- * funcctions, its output and error streams must be read as it
+ * function, its output and error streams must be read as it
* executes. These streams are available through the
* {@link java.lang.Process Process} object returned by
* {@link VirtualMachine#process}. If the streams are not periodically
diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/connect/spi/Connection.java b/src/jdk.jdi/share/classes/com/sun/jdi/connect/spi/Connection.java
index 0fe7bed9c095..a8758df40558 100644
--- a/src/jdk.jdi/share/classes/com/sun/jdi/connect/spi/Connection.java
+++ b/src/jdk.jdi/share/classes/com/sun/jdi/connect/spi/Connection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2020, 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
@@ -100,7 +100,7 @@ public Connection() {}
* the connection while the readPacket is in progress.
*
* @throws java.io.IOException
- * If the length of the packet (as indictaed by the first
+ * If the length of the packet (as indicated by the first
* 4 bytes) is less than 11 bytes, or an I/O error occurs.
*
*
diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/request/ClassPrepareRequest.java b/src/jdk.jdi/share/classes/com/sun/jdi/request/ClassPrepareRequest.java
index 079f993c019f..b2bdf2d5e7e6 100644
--- a/src/jdk.jdi/share/classes/com/sun/jdi/request/ClassPrepareRequest.java
+++ b/src/jdk.jdi/share/classes/com/sun/jdi/request/ClassPrepareRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2017, 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
@@ -105,7 +105,7 @@ public interface ClassPrepareRequest extends EventRequest {
* refType.availableStrata();
*
* such that a name on the list returned by
- * refType.sourceNames(someStratam)
+ * refType.sourceNames(someStratum)
*
* matches 'sourceNamePattern'.
* Regular expressions are limited
diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnterRequest.java b/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnterRequest.java
index a4bee836db2c..7c2af377e506 100644
--- a/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnterRequest.java
+++ b/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnterRequest.java
@@ -36,7 +36,7 @@
/**
* Request for notification of a thread in the target VM
* attempting to enter a monitor already acquired by another thread.
- * When an enabled MonitorContededEnterRequest is satisfied, an
+ * When an enabled MonitorContendedEnterRequest is satisfied, an
* {@link EventSet event set} containing a
* {@link MonitorContendedEnterEvent MonitorContendedEnterEvent}
* will be placed on the {@link EventQueue EventQueue}.
diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnteredRequest.java b/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnteredRequest.java
index b9f55df95506..ff24d8e8937a 100644
--- a/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnteredRequest.java
+++ b/src/jdk.jdi/share/classes/com/sun/jdi/request/MonitorContendedEnteredRequest.java
@@ -36,7 +36,7 @@
/**
* Request for notification of a thread in the target VM entering a monitor
* after waiting for it to be released by another thread.
- * When an enabled MonitorContededEnteredRequest is satisfied, an
+ * When an enabled MonitorContendedEnteredRequest is satisfied, an
* {@link EventSet event set} containing a
* {@link MonitorContendedEnteredEvent MonitorContendedEnteredEvent}
* will be placed on the {@link EventQueue EventQueue}.
From efd8ae1047cb87a96a2e355c8954c557337a1b96 Mon Sep 17 00:00:00 2001
From: Yunbo Zhang
Date: Mon, 17 Aug 2026 19:46:12 +0000
Subject: [PATCH 40/88] 8378896: A make target "clean-microbenchmark", for
micro development
Reviewed-by: erikj
---
make/Main.gmk | 6 +++++-
make/MainSupport.gmk | 8 ++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/make/Main.gmk b/make/Main.gmk
index 198403844c31..a78fc509ff98 100644
--- a/make/Main.gmk
+++ b/make/Main.gmk
@@ -1420,6 +1420,9 @@ clean: $(CLEAN_DIR_TARGETS)
clean-docs:
$(call CleanDocs)
+clean-microbenchmark:
+ $(call CleanMicrobenchmark)
+
clean-compile-commands:
$(call CleanMakeSupportDir,compile-commands)
@@ -1468,7 +1471,8 @@ dist-clean: clean
)
$(ECHO) Cleaned everything, you will have to re-run configure.
-ALL_TARGETS += clean clean-docs clean-compile-commands dist-clean $(CLEAN_DIR_TARGETS) \
+ALL_TARGETS += clean clean-docs clean-microbenchmark clean-compile-commands \
+ dist-clean $(CLEAN_DIR_TARGETS) \
$(CLEAN_SUPPORT_DIR_TARGETS) $(CLEAN_TEST_TARGETS) $(CLEAN_PHASE_TARGETS) \
$(CLEAN_MODULE_TARGETS) $(CLEAN_MODULE_PHASE_TARGETS)
diff --git a/make/MainSupport.gmk b/make/MainSupport.gmk
index 6025cc74a942..f4bb403ab602 100644
--- a/make/MainSupport.gmk
+++ b/make/MainSupport.gmk
@@ -65,6 +65,14 @@ define CleanDocs
@$(ECHO) " done"
endef
+define CleanMicrobenchmark
+ @$(PRINTF) "Cleaning microbenchmark build artifacts ..."
+ @$(ECHO) "" $(LOG_DEBUG)
+ $(RM) -r $(SUPPORT_OUTPUTDIR)/test/micro
+ $(RM) -r $(TEST_IMAGE_DIR)/micro
+ @$(ECHO) " done"
+endef
+
# Cleans the dir given as $1
define CleanDir
@$(PRINTF) "Cleaning %s build artifacts ..." "$(strip $1)"
From 9c924f938d51e1e78223e159a50cfa258b1fd8bf Mon Sep 17 00:00:00 2001
From: Weijun Wang
Date: Thu, 30 Apr 2026 17:46:10 +0000
Subject: [PATCH 41/88] 8382471: Improve Resource Resolving
Reviewed-by: rhalade, mschoene, jnimeh, mullan
---
.../utils/resolver/ResourceResolverSpi.java | 25 +++++++++++++++++++
.../implementations/ResolverDirectHTTP.java | 14 ++++++++---
.../ResolverLocalFilesystem.java | 17 +++++++------
test/lib/jdk/test/lib/security/XMLUtils.java | 11 +++++++-
4 files changed, 55 insertions(+), 12 deletions(-)
diff --git a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java
index 357088262605..1e7bd76413c9 100644
--- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java
+++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverSpi.java
@@ -51,4 +51,29 @@ public abstract XMLSignatureInput engineResolveURI(ResourceResolverContext conte
*/
public abstract boolean engineCanResolveURI(ResourceResolverContext context);
+ /**
+ * Returns the scheme for a URI.
+ *
+ * @param uri the URI
+ * @return the scheme, or {@code null} if none
+ */
+ protected static final String scheme(String uri) {
+ if (uri == null) {
+ return null;
+ }
+ char[] uriChars = uri.toCharArray();
+ // Similar to java.net.URI::parse. Find ':' before any of '/', '?',
+ // or '#', and treat the characters before it as scheme.
+ for (int i = 0; i < uriChars.length; i++) {
+ if (uriChars[i] == '/' || uriChars[i] == '?' || uriChars[i] == '#') {
+ return null;
+ }
+ if (uriChars[i] == ':') {
+ // No validation on the output since we only care if it's
+ // empty or equal to specific values.
+ return uri.substring(0, i);
+ }
+ }
+ return null;
+ }
}
diff --git a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java
index deda69e98b96..dafa851f3d0c 100644
--- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java
+++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java
@@ -207,6 +207,8 @@ private URLConnection openConnection(URL url, ResourceResolverContext context) t
*/
@Override
public boolean engineCanResolveURI(ResourceResolverContext context) {
+ LOG.debug("I was asked whether I can resolve {}", context.uriToResolve);
+
if (context.uriToResolve == null) {
LOG.debug("quick fail, uri == null");
return false;
@@ -217,11 +219,15 @@ public boolean engineCanResolveURI(ResourceResolverContext context) {
return false;
}
- LOG.debug("I was asked whether I can resolve {}", context.uriToResolve);
+ String uriToResolveScheme = scheme(context.uriToResolve);
- if (context.uriToResolve.startsWith("http:") ||
- context.uriToResolve.startsWith("https:") ||
- context.baseUri != null && (context.baseUri.startsWith("http:") || context.baseUri.startsWith("https:"))) {
+ if (uriToResolveScheme == null) {
+ String baseUriScheme = scheme(context.baseUri);
+ if ("http".equals(baseUriScheme) || "https".equals(baseUriScheme)) {
+ LOG.debug("I state that I can resolve {}", context.uriToResolve);
+ return true;
+ }
+ } else if (uriToResolveScheme.equals("http") || uriToResolveScheme.equals("https")) {
LOG.debug("I state that I can resolve {}", context.uriToResolve);
return true;
}
diff --git a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java
index d3970a3ea694..2a96866cf8be 100644
--- a/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java
+++ b/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java
@@ -72,20 +72,23 @@ public boolean engineCanResolveURI(ResourceResolverContext context) {
return false;
}
- if (context.uriToResolve.isEmpty() || context.uriToResolve.charAt(0) == '#' ||
- context.uriToResolve.startsWith("http:") || context.uriToResolve.startsWith("https:")) {
+ if (context.uriToResolve.isEmpty() || context.uriToResolve.charAt(0) == '#') {
return false;
}
- try {
- LOG.debug("I was asked whether I can resolve {}", context.uriToResolve);
+ LOG.debug("I was asked whether I can resolve {}", context.uriToResolve);
+
+ String uriToResolveScheme = scheme(context.uriToResolve);
- if (context.uriToResolve.startsWith("file:") || context.baseUri.startsWith("file:")) {
+ if (uriToResolveScheme == null) {
+ String baseUriScheme = scheme(context.baseUri);
+ if ("file".equals(baseUriScheme)) {
LOG.debug("I state that I can resolve {}", context.uriToResolve);
return true;
}
- } catch (Exception e) {
- LOG.debug(e.getMessage(), e);
+ } else if (uriToResolveScheme.equals("file")) {
+ LOG.debug("I state that I can resolve {}", context.uriToResolve);
+ return true;
}
LOG.debug("But I can't");
diff --git a/test/lib/jdk/test/lib/security/XMLUtils.java b/test/lib/jdk/test/lib/security/XMLUtils.java
index e70a30d9b3d2..62090c9c8619 100644
--- a/test/lib/jdk/test/lib/security/XMLUtils.java
+++ b/test/lib/jdk/test/lib/security/XMLUtils.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
@@ -187,6 +187,7 @@ public static Signer signer(PrivateKey privateKey)
public static class Signer {
+ private String baseURI = null;
final PrivateKey privateKey; // signer key, never null
X509Certificate cert; // certificate, optional
@@ -253,6 +254,11 @@ public Signer prop(String name, Object o) {
return this;
}
+ public Signer baseURI(String base) {
+ this.baseURI = base;
+ return this;
+ }
+
// Signs different sources
// Signs an XML file in detached mode
@@ -341,6 +347,9 @@ private DOMSignContext withProps(DOMSignContext ctxt) {
for (var e : props.entrySet()) {
ctxt.setProperty(e.getKey(), e.getValue());
}
+ if (baseURI != null) {
+ ctxt.setBaseURI(baseURI);
+ }
return ctxt;
}
From d8605e35a34e45481daed913f8f9c3bda843be7e Mon Sep 17 00:00:00 2001
From: Daniel Fuchs
Date: Fri, 12 Jun 2026 12:35:01 +0000
Subject: [PATCH 42/88] 8384708: Enhance HTTP Connections
Co-authored-by: Michael McMahon
Reviewed-by: rhalade, djelinski, aefimov, michaelm, vyazici
---
.../classes/sun/net/www/http/HttpClient.java | 8 +
.../www/protocol/http/HttpURLConnection.java | 16 +-
.../HTTPSetAuthenticatorTest.java | 6 +-
.../SetAuthenticator/HTTPTestServer.java | 487 +++++++++++-------
4 files changed, 315 insertions(+), 202 deletions(-)
diff --git a/src/java.base/share/classes/sun/net/www/http/HttpClient.java b/src/java.base/share/classes/sun/net/www/http/HttpClient.java
index ffc5946b0b33..4db6c24ee1ef 100644
--- a/src/java.base/share/classes/sun/net/www/http/HttpClient.java
+++ b/src/java.base/share/classes/sun/net/www/http/HttpClient.java
@@ -27,6 +27,7 @@
import java.io.*;
import java.net.*;
+import java.net.Proxy.Type;
import java.util.Locale;
import java.util.Objects;
import java.util.OptionalInt;
@@ -174,6 +175,13 @@ int getKeepAliveTimeout() {
return keepAliveTimeout;
}
+ public Proxy getHttpProxy() {
+ if (proxy != null && proxy.type() == Type.HTTP) {
+ return proxy;
+ }
+ return null;
+ }
+
static String normalizeCBT(String s) {
if (s == null || s.equals("never")) {
return "never";
diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
index 45e641f11eee..7b7506420f72 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
@@ -366,6 +366,9 @@ private static Set schemesListToSet(String list) {
private boolean tryTransparentNTLMProxy = true;
private boolean useProxyResponseCode = false;
+ // used when redirecting to compare current and previous proxies
+ private Proxy lastProxy;
+
/* Used by Windows specific code */
private Object authObj;
@@ -1376,7 +1379,6 @@ private InputStream getInputStream0() throws IOException {
// If the user has set either of these headers then do not remove them
isUserServerAuth = requests.getKey("Authorization") != -1;
isUserProxyAuth = requests.getKey("Proxy-Authorization") != -1;
-
try {
do {
if (!checkReuseConnection())
@@ -1386,6 +1388,14 @@ private InputStream getInputStream0() throws IOException {
return cachedInputStream;
}
+ // we may need to remove proxy-authorization
+ Proxy p = http.getHttpProxy();
+ // if we're not using a proxy or if the proxy to be used is not
+ // the same as the originally set one, then remove it
+ if (p == null || (lastProxy != null && !lastProxy.equals(p))) {
+ requests.remove("Proxy-Authorization");
+ lastProxy = null;
+ }
/* REMIND: This exists to fix the HttpsURLConnection subclass.
* Hotjava needs to run on JDK1.1FCS. Do proper fix once a
* proper solution for SSL can be found.
@@ -1416,7 +1426,7 @@ private InputStream getInputStream0() throws IOException {
disconnectInternal();
throw new IOException ("Invalid Http response");
}
- if (respCode == HTTP_PROXY_AUTH) {
+ if (respCode == HTTP_PROXY_AUTH && tunnelState() != TunnelState.TUNNELING) {
if (streaming()) {
disconnectInternal();
throw new HttpRetryException (
@@ -1999,6 +2009,7 @@ private void doTunneling0() throws IOException {
if (respCode == HTTP_OK) {
setTunnelState(TunnelState.TUNNELING);
+ savedRequests.remove("Proxy-Authorization");
break;
}
// we don't know how to deal with other response code
@@ -2552,6 +2563,7 @@ private boolean followRedirect0(String loc, int stat, URL locUrl)
{
assert isLockHeldByCurrentThread();
+ lastProxy = http.getHttpProxy();
disconnectInternal();
if (streaming()) {
throw new HttpRetryException (RETRY_MSG3, stat, loc);
diff --git a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java
index 4d6a74e760b0..723d203c93ad 100644
--- a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java
+++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPSetAuthenticatorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2023, 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
@@ -68,13 +68,11 @@
* @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST PROXY305
* @run main/othervm -Dhttp.auth.digest.reEnabledAlgorithms=MD5 HTTPSetAuthenticatorTest DIGEST SERVER307
* @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER
- * @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY
+ * @run main/othervm -Djdk.http.auth.tunneling.disabledSchemes= HTTPSetAuthenticatorTest BASIC PROXY
* @run main/othervm HTTPSetAuthenticatorTest BASIC PROXY305
* @run main/othervm HTTPSetAuthenticatorTest BASIC SERVER307
* @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER
* @run main/othervm HTTPSetAuthenticatorTest BASICSERVER SERVER307
- *
- * @author danielfuchs
*/
public class HTTPSetAuthenticatorTest extends HTTPTest {
diff --git a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java
index aa158c3b6678..6ceb281e364c 100644
--- a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java
+++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2023, 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
@@ -53,6 +53,7 @@
import java.util.Arrays;
import java.util.Base64;
import java.util.HexFormat;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Random;
@@ -60,6 +61,7 @@
import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;
import sun.net.www.HeaderParser;
+import sun.net.www.MessageHeader;
/**
* A simple HTTP server that supports Digest authentication.
@@ -344,10 +346,11 @@ public static HTTPTestServer createServer(HttpProtocolType protocol,
Objects.requireNonNull(auth);
HttpServer impl = createHttpServer(protocol);
+ AuthResponder authResponder = createAuthResponder(schemeType, auth, authType, algorithm);
final HTTPTestServer server = new HTTPTestServer(impl, null, delegate);
final HttpHandler hh = server.createHandler(schemeType, auth, authType);
HttpContext ctxt = impl.createContext(path, hh);
- server.configureAuthentication(ctxt, schemeType, auth, authType, algorithm);
+ server.configureAuthentication(ctxt, schemeType, authResponder, authType);
impl.start();
return server;
}
@@ -363,12 +366,19 @@ public static HTTPTestServer createProxy(HttpProtocolType protocol,
Objects.requireNonNull(auth);
HttpServer impl = createHttpServer(protocol);
+ AuthResponder authResponder = createAuthResponder(schemeType, auth, authType, null);
final HTTPTestServer server = protocol == HttpProtocolType.HTTPS
- ? new HttpsProxyTunnel(impl, null, delegate)
+ ? new HttpsProxyTunnel(impl, null, delegate, authResponder)
: new HTTPTestServer(impl, null, delegate);
final HttpHandler hh = server.createHandler(schemeType, auth, authType);
HttpContext ctxt = impl.createContext(path, hh);
- server.configureAuthentication(ctxt, schemeType, auth, authType, null);
+ if (protocol == HttpProtocolType.HTTPS) {
+ server.configureAuthentication(ctxt, HttpSchemeType.NONE,
+ new NoAuthResponder(auth, HttpAuthType.SERVER),
+ HttpAuthType.SERVER);
+ } else {
+ server.configureAuthentication(ctxt, schemeType, authResponder, authType);
+ }
impl.start();
return server;
@@ -441,16 +451,16 @@ private HttpHandler createHandler(HttpSchemeType schemeType,
private void configureAuthentication(HttpContext ctxt,
HttpSchemeType schemeType,
- HttpTestAuthenticator auth,
- HttpAuthType authType, String algorithm) {
+ AuthResponder authResponder,
+ HttpAuthType authType) {
switch(schemeType) {
case DIGEST:
// DIGEST authentication is handled by the handler.
- ctxt.getFilters().add(new HttpDigestFilter(auth, authType, algorithm));
+ ctxt.getFilters().add(new HttpDigestFilter(authResponder));
break;
case BASIC:
// BASIC authentication is handled by the filter.
- ctxt.getFilters().add(new HttpBasicFilter(auth, authType));
+ ctxt.getFilters().add(new HttpBasicFilter(authResponder));
break;
case BASICSERVER:
switch(authType) {
@@ -458,14 +468,14 @@ private void configureAuthentication(HttpContext ctxt,
// HttpServer can't support Proxy-type authentication
// => we do as if BASIC had been specified, and we will
// handle authentication in the handler.
- ctxt.getFilters().add(new HttpBasicFilter(auth, authType));
+ ctxt.getFilters().add(new HttpBasicFilter(authResponder));
break;
case SERVER: case SERVER307:
// Basic authentication is handled by HttpServer
// directly => the filter should not perform
// authentication again.
- setContextAuthenticator(ctxt, auth);
- ctxt.getFilters().add(new HttpNoAuthFilter(authType));
+ setContextAuthenticator(ctxt, authResponder.authenticator);
+ ctxt.getFilters().add(new HttpNoAuthFilter(authResponder));
break;
default:
throw new InternalError("Invalid combination scheme="
@@ -473,7 +483,7 @@ private void configureAuthentication(HttpContext ctxt,
}
case NONE:
// No authentication at all.
- ctxt.getFilters().add(new HttpNoAuthFilter(authType));
+ ctxt.getFilters().add(new HttpNoAuthFilter(authResponder));
break;
default:
throw new InternalError("No such scheme: " + schemeType);
@@ -485,38 +495,230 @@ private HttpHandler create300Handler(URL proxyURL,
return new Http3xxHandler(proxyURL, type, code300);
}
- // Abstract HTTP filter class.
- private abstract static class AbstractHttpFilter extends Filter {
-
+ private static abstract class AuthResponder {
final HttpAuthType authType;
+ final HttpTestAuthenticator authenticator;
final String type;
- public AbstractHttpFilter(HttpAuthType authType, String type) {
+
+ AuthResponder(HttpTestAuthenticator authenticator,
+ HttpAuthType authType,
+ String scheme) {
+ this.authenticator = authenticator;
this.authType = authType;
- this.type = type;
+ this.type = authType == HttpAuthType.PROXY
+ ? scheme + " Proxy"
+ : scheme + " Server";
}
- String getLocation() {
- return "Location";
- }
- String getAuthenticate() {
+ final String authenticateHeader() {
return authType == HttpAuthType.PROXY
- ? "Proxy-Authenticate" : "WWW-Authenticate";
+ ? "Proxy-Authenticate"
+ : "WWW-Authenticate";
}
- String getAuthorization() {
+ final String authorizationHeader() {
return authType == HttpAuthType.PROXY
- ? "Proxy-Authorization" : "Authorization";
+ ? "Proxy-Authorization"
+ : "Authorization";
}
- int getUnauthorizedCode() {
+ int unauthorizedCode() {
return authType == HttpAuthType.PROXY
? HttpURLConnection.HTTP_PROXY_AUTH
: HttpURLConnection.HTTP_UNAUTHORIZED;
}
- String getKeepAlive() {
- return "keep-alive";
- }
- String getConnection() {
+ String unauthorizedString() {
return authType == HttpAuthType.PROXY
- ? "Proxy-Connection" : "Connection";
+ ? "Proxy Authentication Required"
+ : "Unauthorized";
+ }
+ String type() { return type;}
+ abstract String generateAuthenticateChallenge();
+ abstract boolean isAuthentified(String method, Iterator authValues);
+ }
+
+ private static final class BasicAuthResponder extends AuthResponder {
+ BasicAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType) {
+ super(authenticator, authType, "Basic");
+ }
+
+ @Override
+ String generateAuthenticateChallenge() {
+ return "Basic realm=\"" + authenticator.getRealm() + "\"";
+ }
+
+ @Override
+ boolean isAuthentified(String method, Iterator authValues) {
+ while(authValues.hasNext()) {
+ String a = authValues.next();
+ System.out.println(type + ": processing " + a);
+ int sp = a.indexOf(' ');
+ if (sp < 0) return false;
+ String scheme = a.substring(0, sp);
+ if (!"Basic".equalsIgnoreCase(scheme)) {
+ System.out.println(type + ": Unsupported scheme '"
+ + scheme +"'");
+ return false;
+ }
+ if (a.length() <= sp+1) {
+ System.out.println(type + ": value too short for '"
+ + scheme +"'");
+ return false;
+ }
+ a = a.substring(sp+1);
+ return validate(a);
+ }
+ return false;
+ }
+
+ boolean validate(String a) {
+ byte[] b = Base64.getDecoder().decode(a);
+ String userpass = new String (b);
+ int colon = userpass.indexOf (':');
+ String uname = userpass.substring (0, colon);
+ String pass = userpass.substring (colon+1);
+ return authenticator.getUserName().equals(uname) &&
+ new String(authenticator.getPassword(uname)).equals(pass);
+ }
+
+ }
+
+ private static final class DigestAuthResponder extends AuthResponder {
+ // This is a very basic DIGEST - used only for the purpose of testing
+ // the client implementation. Therefore we can get away with never
+ // updating the server nonce as it makes the implementation of the
+ // server side digest simpler.
+ private final byte[] nonce;
+ private final String ns;
+ private final String algorithm;
+ DigestAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType, String algorithm) {
+ super(authenticator, authType, "Digest");
+ nonce = new byte[16];
+ new Random(Instant.now().toEpochMilli()).nextBytes(nonce);
+ ns = new BigInteger(1, nonce).toString(16);
+ this.algorithm = (algorithm == null) ? "MD5" : algorithm;
+ }
+
+ @Override
+ String generateAuthenticateChallenge() {
+ return "Digest realm=\"" + authenticator.getRealm() + "\","
+ + "\r\n qop=\"auth\", " + "algorithm=\"" + algorithm + "\", "
+ + "\r\n nonce=\"" + ns +"\"";
+ }
+
+ @Override
+ boolean isAuthentified(String method, Iterator authValues) {
+ while(authValues.hasNext()) {
+ String a = authValues.next();
+ System.out.println(type + ": processing " + a);
+ int sp = a.indexOf(' ');
+ if (sp < 0) return false;
+ String scheme = a.substring(0, sp);
+ if (!"Digest".equalsIgnoreCase(scheme)) {
+ System.out.println(type + ": Unsupported scheme '" + scheme +"'");
+ return false;
+ }
+ if (a.length() <= sp+1) {
+ System.out.println(type + ": value too short for '" + scheme +"'");
+ return false;
+ }
+ a = a.substring(sp+1);
+ DigestResponse dgr = DigestResponse.create(a);
+ return validate(method, dgr);
+ }
+ return false;
+ }
+
+ boolean validate(String reqMethod, DigestResponse dg) {
+ if (!this.algorithm.equalsIgnoreCase(dg.getAlgorithm("MD5"))) {
+ System.out.println(type + ": Unsupported algorithm "
+ + dg.algorithm);
+ return false;
+ }
+ if (!"auth".equalsIgnoreCase(dg.getQoP("auth"))) {
+ System.out.println(type + ": Unsupported qop "
+ + dg.qop);
+ return false;
+ }
+ try {
+ if (!dg.nonce.equals(ns)) {
+ System.out.println(type + ": bad nonce returned by client: "
+ + nonce + " expected " + ns);
+ return false;
+ }
+ if (dg.response == null) {
+ System.out.println(type + ": missing digest response.");
+ return false;
+ }
+ char[] pa = authenticator.getPassword(dg.username);
+ return verify(reqMethod, dg, pa);
+ } catch(IllegalArgumentException | SecurityException
+ | NoSuchAlgorithmException e) {
+ System.out.println(type + ": " + e.getMessage());
+ return false;
+ }
+ }
+
+ boolean verify(String reqMethod, DigestResponse dg, char[] pw)
+ throws NoSuchAlgorithmException {
+ String response = DigestResponse.computeDigest(true, reqMethod, pw, algorithm, dg);
+ if (!dg.response.equals(response)) {
+ System.out.println(type + ": bad response returned by client: "
+ + dg.response + " expected " + response);
+ return false;
+ } else {
+ System.out.println(type + ": verified response " + response);
+ }
+ return true;
+ }
+
+ }
+
+ private static final class NoAuthResponder extends AuthResponder {
+ NoAuthResponder(HttpTestAuthenticator authenticator, HttpAuthType authType) {
+ super(authenticator, authType, "NoAuth");
+ }
+
+ @Override
+ String generateAuthenticateChallenge() {
+ throw new InternalError("Should not reach here");
+ }
+
+ @Override
+ boolean isAuthentified(String method, Iterator authValues) {
+ return true;
+ }
+ }
+
+ private static AuthResponder createAuthResponder(HttpSchemeType schemeType,
+ HttpTestAuthenticator authenticator,
+ HttpAuthType authType,
+ String algorithm) {
+ switch (schemeType) {
+ case BASIC, BASICSERVER: return new BasicAuthResponder(authenticator, authType);
+ case DIGEST: return new DigestAuthResponder(authenticator, authType, algorithm);
+ case NONE: return new NoAuthResponder(authenticator, authType);
+ default: throw new IllegalArgumentException(
+ "Unknown authentication scheme: " + schemeType);
+ }
+ }
+
+ // Abstract HTTP filter class.
+ private abstract static class AbstractHttpFilter extends Filter {
+
+ final AuthResponder authResponder;
+ final String type;
+ public AbstractHttpFilter(AuthResponder authResponder) {
+ this.authResponder = authResponder;
+ this.type = authResponder.type();
+ }
+
+ final String getAuthenticate() {
+ return authResponder.authenticateHeader();
+ }
+ final String getAuthorization() {
+ return authResponder.authorizationHeader();
+ }
+ final int getUnauthorizedCode() {
+ return authResponder.unauthorizedCode();
}
protected abstract boolean isAuthentified(HttpExchange he) throws IOException;
protected abstract void requestAuthentication(HttpExchange he) throws IOException;
@@ -694,11 +896,10 @@ public static DigestResponse create(String raw) {
}
- private class HttpNoAuthFilter extends AbstractHttpFilter {
+ private static final class HttpNoAuthFilter extends AbstractHttpFilter {
- public HttpNoAuthFilter(HttpAuthType authType) {
- super(authType, authType == HttpAuthType.SERVER
- ? "NoAuth Server" : "NoAuth Proxy");
+ public HttpNoAuthFilter(AuthResponder authResponder) {
+ super(authResponder);
}
@Override
@@ -720,19 +921,15 @@ public String description() {
// An HTTP Filter that performs Basic authentication
private class HttpBasicFilter extends AbstractHttpFilter {
-
- private final HttpTestAuthenticator auth;
- public HttpBasicFilter(HttpTestAuthenticator auth, HttpAuthType authType) {
- super(authType, authType == HttpAuthType.SERVER
- ? "Basic Server" : "Basic Proxy");
- this.auth = auth;
+ public HttpBasicFilter(AuthResponder authResponder) {
+ super(authResponder);
}
@Override
protected void requestAuthentication(HttpExchange he)
throws IOException {
- he.getResponseHeaders().add(getAuthenticate(),
- "Basic realm=\"" + auth.getRealm() + "\"");
+ String challenge = authResponder.generateAuthenticateChallenge();
+ he.getResponseHeaders().add(getAuthenticate(), challenge);
System.out.println(type + ": Requesting Basic Authentication "
+ he.getResponseHeaders().getFirst(getAuthenticate()));
}
@@ -742,39 +939,12 @@ protected boolean isAuthentified(HttpExchange he) {
if (he.getRequestHeaders().containsKey(getAuthorization())) {
List authorization =
he.getRequestHeaders().get(getAuthorization());
- for (String a : authorization) {
- System.out.println(type + ": processing " + a);
- int sp = a.indexOf(' ');
- if (sp < 0) return false;
- String scheme = a.substring(0, sp);
- if (!"Basic".equalsIgnoreCase(scheme)) {
- System.out.println(type + ": Unsupported scheme '"
- + scheme +"'");
- return false;
- }
- if (a.length() <= sp+1) {
- System.out.println(type + ": value too short for '"
- + scheme +"'");
- return false;
- }
- a = a.substring(sp+1);
- return validate(a);
- }
- return false;
+ return authResponder.isAuthentified(he.getRequestMethod(),
+ authorization.iterator());
}
return false;
}
- boolean validate(String a) {
- byte[] b = Base64.getDecoder().decode(a);
- String userpass = new String (b);
- int colon = userpass.indexOf (':');
- String uname = userpass.substring (0, colon);
- String pass = userpass.substring (colon+1);
- return auth.getUserName().equals(uname) &&
- new String(auth.getPassword(uname)).equals(pass);
- }
-
@Override
public String description() {
return "Filter for " + type;
@@ -786,31 +956,14 @@ public String description() {
// An HTTP Filter that performs Digest authentication
private class HttpDigestFilter extends AbstractHttpFilter {
- // This is a very basic DIGEST - used only for the purpose of testing
- // the client implementation. Therefore we can get away with never
- // updating the server nonce as it makes the implementation of the
- // server side digest simpler.
- private final HttpTestAuthenticator auth;
- private final byte[] nonce;
- private final String ns;
- private final String algorithm;
- public HttpDigestFilter(HttpTestAuthenticator auth, HttpAuthType authType, String algorithm) {
- super(authType, authType == HttpAuthType.SERVER
- ? "Digest Server" : "Digest Proxy");
- this.auth = auth;
- nonce = new byte[16];
- new Random(Instant.now().toEpochMilli()).nextBytes(nonce);
- ns = new BigInteger(1, nonce).toString(16);
- this.algorithm = (algorithm == null) ? "MD5" : algorithm;
+ public HttpDigestFilter(AuthResponder authResponder) {
+ super(authResponder);
}
@Override
protected void requestAuthentication(HttpExchange he)
throws IOException {
- he.getResponseHeaders().add(getAuthenticate(),
- "Digest realm=\"" + auth.getRealm() + "\","
- + "\r\n qop=\"auth\", " + "algorithm=\"" + algorithm + "\", "
- + "\r\n nonce=\"" + ns +"\"");
+ he.getResponseHeaders().add(getAuthenticate(), authResponder.generateAuthenticateChallenge());
System.out.println(type + ": Requesting Digest Authentication "
+ he.getResponseHeaders().getFirst(getAuthenticate()));
}
@@ -819,71 +972,11 @@ protected void requestAuthentication(HttpExchange he)
protected boolean isAuthentified(HttpExchange he) {
if (he.getRequestHeaders().containsKey(getAuthorization())) {
List authorization = he.getRequestHeaders().get(getAuthorization());
- for (String a : authorization) {
- System.out.println(type + ": processing " + a);
- int sp = a.indexOf(' ');
- if (sp < 0) return false;
- String scheme = a.substring(0, sp);
- if (!"Digest".equalsIgnoreCase(scheme)) {
- System.out.println(type + ": Unsupported scheme '" + scheme +"'");
- return false;
- }
- if (a.length() <= sp+1) {
- System.out.println(type + ": value too short for '" + scheme +"'");
- return false;
- }
- a = a.substring(sp+1);
- DigestResponse dgr = DigestResponse.create(a);
- return validate(he.getRequestMethod(), dgr);
- }
- return false;
+ return authResponder.isAuthentified(he.getRequestMethod(), authorization.iterator());
}
return false;
}
- boolean validate(String reqMethod, DigestResponse dg) {
- if (!this.algorithm.equalsIgnoreCase(dg.getAlgorithm("MD5"))) {
- System.out.println(type + ": Unsupported algorithm "
- + dg.algorithm);
- return false;
- }
- if (!"auth".equalsIgnoreCase(dg.getQoP("auth"))) {
- System.out.println(type + ": Unsupported qop "
- + dg.qop);
- return false;
- }
- try {
- if (!dg.nonce.equals(ns)) {
- System.out.println(type + ": bad nonce returned by client: "
- + nonce + " expected " + ns);
- return false;
- }
- if (dg.response == null) {
- System.out.println(type + ": missing digest response.");
- return false;
- }
- char[] pa = auth.getPassword(dg.username);
- return verify(reqMethod, dg, pa);
- } catch(IllegalArgumentException | SecurityException
- | NoSuchAlgorithmException e) {
- System.out.println(type + ": " + e.getMessage());
- return false;
- }
- }
-
- boolean verify(String reqMethod, DigestResponse dg, char[] pw)
- throws NoSuchAlgorithmException {
- String response = DigestResponse.computeDigest(true, reqMethod, pw, algorithm, dg);
- if (!dg.response.equals(response)) {
- System.out.println(type + ": bad response returned by client: "
- + dg.response + " expected " + response);
- return false;
- } else {
- System.out.println(type + ": verified response " + response);
- }
- return true;
- }
-
@Override
public String description() {
return "Filter for DIGEST authentication";
@@ -979,22 +1072,23 @@ public void configure (HttpsParameters params) {
}
}
- // This is a bit hacky: HttpsProxyTunnel is an HTTPTestServer hidden
- // behind a fake proxy that only understands CONNECT requests.
- // The fake proxy is just a server socket that intercept the
- // CONNECT and then redirect streams to the real server.
+ // The HttpsProxyTunnel is a proxy that only understands
+ // CONNECT requests. It is only used for tunnelling, but
+ // supports Proxy Authentication with the help of an
+ // AuthResponder
static class HttpsProxyTunnel extends HTTPTestServer
implements Runnable {
final ServerSocket ss;
+ final AuthResponder authResponder;
private volatile boolean stop;
public HttpsProxyTunnel(HttpServer server, HTTPTestServer target,
- HttpHandler delegate)
+ HttpHandler delegate, AuthResponder authResponder)
throws IOException {
super(server, target, delegate);
System.out.flush();
- System.err.println("WARNING: HttpsProxyTunnel is an experimental test class");
+ this.authResponder = authResponder;
ss = ServerSocketFactory.create();
start();
}
@@ -1048,28 +1142,6 @@ public InetSocketAddress getProxyAddress() {
return new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort());
}
- // This is a bit shaky. It doesn't handle continuation
- // lines, but our client shouldn't send any.
- // Read a line from the input stream, swallowing the final
- // \r\n sequence. Stops at the first \n, doesn't complain
- // if it wasn't preceded by '\r'.
- //
- String readLine(InputStream r) throws IOException {
- StringBuilder b = new StringBuilder();
- int c;
- while ((c = r.read()) != -1) {
- if (c == '\n') break;
- b.appendCodePoint(c);
- }
- if (b.length() == 0) {
- return "";
- }
- if (b.codePointAt(b.length() -1) == '\r') {
- b.delete(b.length() -1, b.length());
- }
- return b.toString();
- }
-
@Override
public void run() {
Socket clientConnection = null;
@@ -1137,6 +1209,37 @@ public void run() {
}
}
+ private boolean isAuthentified(MessageHeader request) {
+ String requestLine = request.getValue(0);
+ String method = requestLine.substring(0, requestLine.indexOf(' '));
+ assert "CONNECT".equals(method);
+ return authResponder.isAuthentified(method,
+ request.multiValueIterator(authResponder.authorizationHeader()));
+ }
+
+ private String challengeResponse() {
+ return "HTTP/1.1 " + authResponder.unauthorizedCode() + " "
+ + authResponder.unauthorizedString()
+ + "\r\nContent-Length: 0\r\n"
+ + authResponder.authenticateHeader() + ": "
+ + authResponder.generateAuthenticateChallenge()
+ + "\r\n\r\n";
+ }
+
+ private String okResponse() {
+ return "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n";
+ }
+
+ private String badGatewayResponse() {
+ return "HTTP/1.1 502 Bad Gateway\r\nContent-Length: 0\r\n\r\n";
+ }
+
+ private void sendResponse(PrintWriter pw, String response) {
+ System.out.println("Tunnel: Sending " + response);
+ pw.print(response);
+ pw.flush();
+ }
+
private void processRequestAndWaitToComplete(final Socket clientConnection)
throws IOException, InterruptedException {
final Socket targetConnection;
@@ -1146,32 +1249,24 @@ private void processRequestAndWaitToComplete(final Socket clientConnection)
clientConnection.getOutputStream(), "UTF-8");
PrintWriter pw = new PrintWriter(w);
System.out.println("Tunnel: Reading request line");
- String requestLine = readLine(ccis);
+ MessageHeader request = new MessageHeader(ccis);
+ String requestLine = request.getValue(0);
System.out.println("Tunnel: Request line: " + requestLine);
- if (requestLine.startsWith("CONNECT ")) {
- // We should probably check that the next word following
- // CONNECT is the host:port of our HTTPS serverImpl.
- // Some improvement for a followup!
-
- // Read all headers until we find the empty line that
- // signals the end of all headers.
- while(!requestLine.equals("")) {
- System.out.println("Tunnel: Reading header: "
- + (requestLine = readLine(ccis)));
+ if (requestLine != null && requestLine.startsWith("CONNECT ")) {
+ if (!isAuthentified(request)) {
+ sendResponse(pw, challengeResponse());
+ return;
}
-
targetConnection = new Socket(
serverImpl.getAddress().getAddress(),
serverImpl.getAddress().getPort());
// Then send the 200 OK response to the client
- System.out.println("Tunnel: Sending "
- + "HTTP/1.1 200 OK\r\n\r\n");
- pw.print("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
- pw.flush();
+ sendResponse(pw, okResponse());
} else {
// This should not happen. If it does then consider it a
// client error and throw an IOException
+ sendResponse(pw, badGatewayResponse());
System.out.println("Tunnel: Throwing an IOException due to unexpected" +
" request line: " + requestLine);
throw new IOException("Client request error - Unexpected request line");
From b2d3948239d46f9e862b38b4db54e4d8675f1895 Mon Sep 17 00:00:00 2001
From: Phil Race
Date: Wed, 17 Jun 2026 17:08:29 +0000
Subject: [PATCH 43/88] 8386298: Improve font loading
Reviewed-by: rhalade, kizune, jdv
---
src/java.desktop/share/classes/sun/font/HBShaper.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/java.desktop/share/classes/sun/font/HBShaper.java b/src/java.desktop/share/classes/sun/font/HBShaper.java
index e7f3e6178fae..8652388edb16 100644
--- a/src/java.desktop/share/classes/sun/font/HBShaper.java
+++ b/src/java.desktop/share/classes/sun/font/HBShaper.java
@@ -387,7 +387,9 @@ private static int get_glyph_v_advance(
*/
private static class IntPtr {
MemorySegment seg;
+ @SuppressWarnings("restricted")
IntPtr(MemorySegment seg) {
+ this.seg = seg.reinterpret(4);
}
void set(int i) {
From 44e6f0470b1840663180affda166c1e9c110c129 Mon Sep 17 00:00:00 2001
From: Hai-May Chao
Date: Wed, 24 Jun 2026 22:30:20 +0000
Subject: [PATCH 44/88] 8386205: Enhance TLS server
Reviewed-by: rhalade, ahgross, jnimeh, ascarpino
---
.../sun/security/ssl/ServerHandshakeContext.java | 4 ++--
.../share/classes/sun/security/ssl/ServerHello.java | 10 ++++++++++
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java
index 8bb7def0f575..5d203b5c6ccd 100644
--- a/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshakeContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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,7 @@ class ServerHandshakeContext extends HandshakeContext {
private static final long DEFAULT_STATUS_RESP_DELAY = 5000L;
final long statusRespTimeout;
boolean acceptCliHelloFragments = false;
-
+ boolean sentHRR = false;
ServerHandshakeContext(SSLContextImpl sslContext,
TransportContext conContext) throws IOException {
diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHello.java b/src/java.base/share/classes/sun/security/ssl/ServerHello.java
index 4bd2b0a059f5..360699280512 100644
--- a/src/java.base/share/classes/sun/security/ssl/ServerHello.java
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHello.java
@@ -805,6 +805,15 @@ private T13HelloRetryRequestProducer() {
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
ServerHandshakeContext shc = (ServerHandshakeContext) context;
+
+
+ if (shc.sentHRR) {
+ throw shc.conContext.fatal(
+ Alert.HANDSHAKE_FAILURE,
+ "TLS 1.3 server MUST NOT send a second HelloRetryRequest " +
+ "in the same connection");
+ }
+
ClientHelloMessage clientHello = (ClientHelloMessage) message;
// negotiate the cipher suite.
@@ -840,6 +849,7 @@ public byte[] produce(ConnectionContext context,
// Output the handshake message.
hhrm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
+ shc.sentHRR = true;
// In TLS1.3 middlebox compatibility mode the server sends a
// dummy change_cipher_spec record immediately after its
From 9139138393cd643d28e64bc756b99f32dc12cbc5 Mon Sep 17 00:00:00 2001
From: Shiv Shah
Date: Mon, 17 Aug 2026 22:55:51 +0000
Subject: [PATCH 45/88] 8390429: Enable suspend001 jdb test to run with virtual
threads
Reviewed-by: cjplummer, dholmes
---
test/hotspot/jtreg/ProblemList-Virtual.txt | 3 +++
.../nsk/jdb/suspend/suspend001/suspend001.java | 7 ++++---
.../jdb/suspend/suspend001/suspend001a.java | 18 ++++++++++++------
3 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt
index e25d3d644a17..53bb850a6ecc 100644
--- a/test/hotspot/jtreg/ProblemList-Virtual.txt
+++ b/test/hotspot/jtreg/ProblemList-Virtual.txt
@@ -60,6 +60,9 @@ vmTestbase/nsk/jdb/where/where005/where005.java 8278470 generic-all
vmTestbase/nsk/jdb/list/list003/list003.java 8300707 generic-all
vmTestbase/nsk/jdb/repeat/repeat001/repeat001.java 8300707 generic-all
+###
+# suspend001 hangs with virtual threads until the suspended-successor issue is fixed.
+vmTestbase/nsk/jdb/suspend/suspend001/suspend001.java 8390031 generic-all
####
## NSK JDI tests failing with wrapper
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001.java
index 20c9778f2510..90982a167bc5 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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,6 +80,7 @@ public static void main (String argv[]) {
static final String DEBUGGEE_CLASS = TEST_CLASS + "a";
static final String FIRST_BREAK = DEBUGGEE_CLASS + ".main";
static final String LAST_BREAK = DEBUGGEE_CLASS + ".breakHere";
+ static final String THREAD_STARTED_BREAK = DEBUGGEE_CLASS + ".threadStarted";
static final String SUSPENDED = "Suspended";
static final String DEBUGGEE_THREAD = PACKAGE_NAME + "." + SUSPENDED;
@@ -94,9 +95,9 @@ protected void runCases() {
String[] threads;
jdb.setBreakpointInMethod(LAST_BREAK);
- reply = jdb.receiveReplyFor(JdbCommand.cont);
+ waitForTestedThreadStarts(THREAD_STARTED_BREAK, 2);
while (true) {
- threads = jdb.getThreadIds(DEBUGGEE_THREAD);
+ threads = jdb.getThreadIdsByName(SUSPENDED);
if (threads.length != 1) {
log.complain("jdb should report 1 instance of " + DEBUGGEE_THREAD);
log.complain("Found: " + threads.length);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001a.java b/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001a.java
index 35a4d4258260..dc518cfdbd63 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001a.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdb/suspend/suspend001/suspend001a.java
@@ -23,6 +23,8 @@
package nsk.jdb.suspend.suspend001;
+import jdk.test.lib.thread.ThreadWrapper;
+
import nsk.share.*;
import nsk.share.jpda.*;
import nsk.share.jdb.*;
@@ -42,6 +44,8 @@ public static void main(String args[]) {
static void breakHere () {}
+ static void threadStarted () {}
+
static Object lock = new Object();
static Object waitnotify = new Object();
public static volatile int notSuspended = 0;
@@ -50,8 +54,8 @@ public int runIt(String args[], PrintStream out) {
argumentHandler = new JdbArgumentHandler(args);
log = new Log(out, argumentHandler);
- Thread suspended = new Suspended("Suspended");
- Thread myThread = new MyThread("MyThread");
+ Thread suspended = new Suspended("Suspended").getThread();
+ Thread myThread = new MyThread("MyThread").getThread();
// lock monitor to prevent threads from finishing after they started
synchronized (lock) {
@@ -103,16 +107,16 @@ public static int getResult() {
}
}
-// This test uses a platform thread because suspending the tested virtual thread
-// and continuing causes jdb to stop responding.
-class Suspended extends Thread {
+class Suspended extends ThreadWrapper {
String name;
public Suspended (String n) {
+ super(n);
name = n;
}
public void run() {
+ suspend001a.threadStarted();
// Concatenate strings in advance to avoid lambda calculations later
final String ThreadFinished = "Thread finished: " + this.name;
suspend001a.log.display("Thread started: " + this.name);
@@ -129,14 +133,16 @@ public void run() {
}
}
-class MyThread extends Thread {
+class MyThread extends ThreadWrapper {
String name;
public MyThread (String n) {
+ super(n);
name = n;
}
public void run() {
+ suspend001a.threadStarted();
// Concatenate strings in advance to avoid lambda calculations later
final String ThreadFinished = "Thread finished: " + this.name;
suspend001a.log.display("Thread started: " + this.name);
From f3cf3e2036a4453d08fc5b39c3792d67e10eb66f Mon Sep 17 00:00:00 2001
From: Matias Saavedra Silva
Date: Mon, 17 Aug 2026 23:08:25 +0000
Subject: [PATCH 46/88] 8390484: Problem list appcds/aotCache/ tests
Reviewed-by: lmesnik, iklam
---
test/hotspot/jtreg/ProblemList.txt | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
index ccc9a9dac567..5e4f0fd8921d 100644
--- a/test/hotspot/jtreg/ProblemList.txt
+++ b/test/hotspot/jtreg/ProblemList.txt
@@ -92,6 +92,12 @@ gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#default 8386964
# :hotspot_runtime
+runtime/cds/appcds/aotCache/aotClassLinking/AOTClassLinkingVMOptions.java 8390485 generic-all
+runtime/cds/appcds/aotCache/aotClassLinking/AddExports.java 8390485 generic-all
+runtime/cds/appcds/aotCache/aotClassLinking/AddOpens.java 8390485 generic-all
+runtime/cds/appcds/aotCache/aotClassLinking/AddReads.java 8390485 generic-all
+runtime/cds/appcds/resolvedConstants/AOTLinkedLambdas.java 8390485 generic-all
+runtime/cds/appcds/resolvedConstants/AOTLinkedVarHandles.java 8390485 generic-all
runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64
runtime/Monitor/SyncOnValueBasedClassTest.java 8340995 linux-s390x
runtime/os/TestTracePageSizes.java#no-options 8267460 linux-aarch64
From 4d812a64865ef250bd81705ae0c0a18675e4b378 Mon Sep 17 00:00:00 2001
From: Dingli Zhang
Date: Mon, 17 Aug 2026 23:15:14 +0000
Subject: [PATCH 47/88] 8390452: RISC-V: gc/shenandoah/TestSieveObjects.java
fails with "assert(UseZba) failed: must be"
Reviewed-by: shade, fyang
---
.../riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
index 6c39c8e456f1..1de4ff2b93de 100644
--- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
@@ -854,7 +854,7 @@ void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) {
// Save the result where needed. Narrow entries return narrowOop (32 bits)
// we need to zero the upper 32 bits of x10.
if (_narrow) {
- __ zext_w(_obj, x10);
+ __ zext(_obj, x10, 32);
} else {
__ mv(_obj, x10);
}
From 92e236a361497b0895d42a6d6650eb503d6851dc Mon Sep 17 00:00:00 2001
From: David Holmes
Date: Tue, 18 Aug 2026 01:16:16 +0000
Subject: [PATCH 48/88] 8390493: Remove
vmTestbase/nsk/jvmti/IterateOverReachableObjects/iterreachobj002/TestDescription.java
from the ProblemList
Reviewed-by: liach
---
test/hotspot/jtreg/ProblemList.txt | 2 --
1 file changed, 2 deletions(-)
diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
index 5e4f0fd8921d..d41a77a1b850 100644
--- a/test/hotspot/jtreg/ProblemList.txt
+++ b/test/hotspot/jtreg/ProblemList.txt
@@ -210,8 +210,6 @@ compiler/vectorapi/TestVectorReassociations.java 8388927 generic-all
serviceability/sa/TestJhsdbJstackMixedWithXComp.java#xcomp-disable-tiered-compilation 8386674 linux-aarch64
-vmTestbase/nsk/jvmti/IterateOverReachableObjects/iterreachobj002/TestDescription.java 8384882 linux-all
-
# The following test failure(s) DID NOT reproduce during Tier[1-8] testing
# done for 8377828. Further analysis was done with 8376235 and just one
# sub-test needed to remain on the ProblemList via 8361089.
From 764e83c46c839164c137cd2f058c7cc2b6519dc6 Mon Sep 17 00:00:00 2001
From: Gui Cao
Date: Tue, 18 Aug 2026 01:44:20 +0000
Subject: [PATCH 49/88] 8390441: RISC-V: Fix C2 stack-to-stack spill copies
with large offsets
Reviewed-by: fyang, dzhang
---
src/hotspot/cpu/riscv/riscv.ad | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad
index 43590e89c521..3be8416d8b48 100644
--- a/src/hotspot/cpu/riscv/riscv.ad
+++ b/src/hotspot/cpu/riscv/riscv.ad
@@ -1553,6 +1553,27 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
int src_offset = ra_->reg2offset(src_lo);
int dst_offset = ra_->reg2offset(dst_lo);
+ // Stack-to-stack copies use t0 for the value. Bail out if a destination
+ // address also needs t0 to materialize an offset outside the 12-bit range.
+ if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) {
+ int last_dst_offset = dst_offset;
+ if (bottom_type()->isa_pvectmask()) {
+ int vmask_size_in_bytes = Matcher::scalable_predicate_reg_slots() * 32 / 8;
+ last_dst_offset += vmask_size_in_bytes - 4;
+ } else if (ideal_reg() == Op_VecA) {
+ int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE);
+ last_dst_offset += vector_reg_size_in_bytes - 8;
+ }
+
+ if (masm != nullptr && !Assembler::is_simm12(last_dst_offset)) {
+ // size() emits into a scratch buffer where recording a failure is not allowed.
+ if (!C->output()->in_scratch_emit_size()) {
+ C->record_method_not_compilable("unsupported large stack-to-stack spill copy");
+ }
+ return 0;
+ }
+ }
+
if (bottom_type()->isa_vect() != nullptr) {
uint ireg = ideal_reg();
if (ireg == Op_VecA && masm) {
From 4a653f843988c287df8ac9d4e8b299fa5fc52ea0 Mon Sep 17 00:00:00 2001
From: Prasanta Sadhukhan
Date: Tue, 18 Aug 2026 08:27:12 +0000
Subject: [PATCH 50/88] 8390455: Fix more typos in java.desktop module
Reviewed-by: jdv, azvegint
---
src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java | 2 +-
.../macosx/native/libawt_lwawt/awt/ImageSurfaceData.h | 6 +++---
.../classes/com/sun/imageio/plugins/png/PNGMetadata.java | 4 ++--
.../com/sun/imageio/plugins/tiff/TIFFDecompressor.java | 4 ++--
.../share/classes/com/sun/media/sound/DLSModulator.java | 4 ++--
.../share/classes/com/sun/media/sound/SoftSynthesizer.java | 4 ++--
.../share/classes/com/sun/media/sound/WaveFileReader.java | 4 ++--
src/java.desktop/share/classes/java/awt/Dialog.java | 2 +-
.../share/classes/javax/swing/AbstractButton.java | 2 +-
.../share/classes/javax/swing/ProgressMonitor.java | 4 ++--
.../classes/javax/swing/plaf/basic/BasicComboBoxUI.java | 2 +-
.../share/classes/javax/swing/plaf/basic/BasicListUI.java | 2 +-
.../classes/javax/swing/plaf/synth/SynthComboBoxUI.java | 4 ++--
.../share/classes/javax/swing/text/html/StyleSheet.java | 4 ++--
src/java.desktop/share/classes/sun/awt/UngrabEvent.java | 2 +-
.../share/classes/sun/font/FontDesignMetrics.java | 2 +-
.../share/classes/sun/font/FontManagerNativeLibrary.java | 4 ++--
src/java.desktop/share/classes/sun/font/SunFontManager.java | 2 +-
.../share/classes/sun/swing/plaf/DesktopProperty.java | 2 +-
src/java.desktop/share/native/libjavajpeg/imageioJPEG.c | 4 ++--
src/java.desktop/unix/classes/sun/awt/X11/XAtom.java | 6 +++---
21 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java
index 634f578df025..61f9d543394e 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java
@@ -1359,7 +1359,7 @@ protected void changeFocusedWindow(boolean becomesFocused, Window opposite) {
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
focusLog.fine("ungrabbing on " + grabbingWindow);
}
- // ungrab a simple window if its owner looses activation.
+ // ungrab a simple window if its owner loses activation.
grabbingWindow.ungrab();
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/ImageSurfaceData.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/ImageSurfaceData.h
index e71224a6ff3f..ecd716ca06c5 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/ImageSurfaceData.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/ImageSurfaceData.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -101,8 +101,8 @@ struct _ImageSDOps
CGDataProviderRef dataProvider;
// Pointer in memory that is used for create the CGBitmapContext and the CGDataProvider (used for imgRef). This is a native
- // copy of the pixels for the Image. There is a spearate copy of the pixels that lives in Java heap. There are two main
- // reasons why we keep those pixels spearate: 1) CG doesn't support all the Java pixel formats 2) The Garbage collector can
+ // copy of the pixels for the Image. There is a separate copy of the pixels that lives in Java heap. There are two main
+ // reasons why we keep those pixels separate: 1) CG doesn't support all the Java pixel formats 2) The Garbage collector can
// move the java pixels at any time. There are possible workarounds for both problems. Number 2) seems to be a more serious issue, since
// we can solve 1) by only supporting certain image types.
void * nativePixels;
diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java
index 730294c9f017..7424e74f1d8b 100644
--- a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java
+++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGMetadata.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1810,7 +1810,7 @@ private boolean isValidKeyword(String s) {
* Latin-1 [ISO-8859-1] characters and spaces; that is, only
* character codes 32-126 and 161-255 decimal are allowed.
* For Latin-1 value fields the 0x10 (linefeed) control
- * character is aloowed too.
+ * character is allowed too.
*
* See: http://www.w3.org/TR/PNG/#11keywords
*/
diff --git a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java
index c5f8a0991745..6e21ce3ed854 100644
--- a/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java
+++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFDecompressor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2023, 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
@@ -377,7 +377,7 @@ public abstract class TIFFDecompressor {
/**
* The width of the source region that will actually be copied
* into the destination image, taking into account all
- * susbampling, offsetting, and clipping.
+ * subsampling, offsetting, and clipping.
*
* The active source width will always be equal to
* {@code (dstWidth - 1)*subsampleX + 1}.
diff --git a/src/java.desktop/share/classes/com/sun/media/sound/DLSModulator.java b/src/java.desktop/share/classes/com/sun/media/sound/DLSModulator.java
index 257777628b29..f22a48d3fdaa 100644
--- a/src/java.desktop/share/classes/com/sun/media/sound/DLSModulator.java
+++ b/src/java.desktop/share/classes/com/sun/media/sound/DLSModulator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 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,7 @@
package com.sun.media.sound;
/**
- * This class is used to store modulator/artiuclation data.
+ * This class is used to store modulator/articulation data.
* A modulator connects one synthesizer source to
* a destination. For example a note on velocity
* can be mapped to the gain of the synthesized voice.
diff --git a/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java b/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java
index 4f4b82164868..b52251ad7e1e 100644
--- a/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java
+++ b/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2024, 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
@@ -1221,7 +1221,7 @@ public AudioInputStream openStream(AudioFormat targetFormat,
// Always create external_channels array
// with 16 or more channels
// so getChannels works correctly
- // when the synhtesizer is closed.
+ // when the synthesizer is closed.
if (channels.length < 16)
external_channels = new SoftChannelProxy[16];
else
diff --git a/src/java.desktop/share/classes/com/sun/media/sound/WaveFileReader.java b/src/java.desktop/share/classes/com/sun/media/sound/WaveFileReader.java
index 7295b180ed13..c4bdebbc8f82 100644
--- a/src/java.desktop/share/classes/com/sun/media/sound/WaveFileReader.java
+++ b/src/java.desktop/share/classes/com/sun/media/sound/WaveFileReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -48,7 +48,7 @@ public final class WaveFileReader extends SunFileReader {
StandardFileFormat getAudioFileFormatImpl(final InputStream stream)
throws UnsupportedAudioFileException, IOException {
- // assumes sream is rewound
+ // assumes stream is rewound
int nread = 0;
int fmt;
diff --git a/src/java.desktop/share/classes/java/awt/Dialog.java b/src/java.desktop/share/classes/java/awt/Dialog.java
index 038aa5b65e32..3230d94228c8 100644
--- a/src/java.desktop/share/classes/java/awt/Dialog.java
+++ b/src/java.desktop/share/classes/java/awt/Dialog.java
@@ -908,7 +908,7 @@ private boolean conditionalShow(Component toFocus, AtomicLong time) {
}
// This call is required as the show() method of the Dialog class
- // does not invoke the super.show(). So wried... :(
+ // does not invoke the super.show(). So weird... :(
mixOnShowing();
peer.setVisible(true); // now guaranteed never to block
diff --git a/src/java.desktop/share/classes/javax/swing/AbstractButton.java b/src/java.desktop/share/classes/javax/swing/AbstractButton.java
index ad5f0eba3de8..0361c5efc613 100644
--- a/src/java.desktop/share/classes/javax/swing/AbstractButton.java
+++ b/src/java.desktop/share/classes/javax/swing/AbstractButton.java
@@ -417,7 +417,7 @@ public void setMargin(Insets m) {
* the label.
*
* @return an Insets object specifying the margin
- * between the botton's border and the label
+ * between the button's border and the label
* @see #setMargin
*/
public Insets getMargin() {
diff --git a/src/java.desktop/share/classes/javax/swing/ProgressMonitor.java b/src/java.desktop/share/classes/javax/swing/ProgressMonitor.java
index cea800d42f2c..96fa68fef944 100644
--- a/src/java.desktop/share/classes/javax/swing/ProgressMonitor.java
+++ b/src/java.desktop/share/classes/javax/swing/ProgressMonitor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2023, 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
@@ -534,7 +534,7 @@ protected class AccessibleProgressMonitor extends AccessibleContext
* AccessibleJLabel
* AccessibleJProgressBar
*
- * The abstraction presented to assitive technologies by
+ * The abstraction presented to assistive technologies by
* the AccessibleProgressMonitor is that a dialog contains a
* progress monitor with three children: a message, a note
* label and a progress bar.
diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java
index de3cb1adf752..34829b1ac58f 100644
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java
@@ -532,7 +532,7 @@ protected LayoutManager createLayoutManager() {
}
/**
- * Creates the default renderer that will be used in a non-editiable combo
+ * Creates the default renderer that will be used in a non-editable combo
* box. A default renderer will used only if a renderer has not been
* explicitly set with setRenderer.
*
diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java
index 37bcbec21567..8eb7f11be3f7 100644
--- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java
+++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicListUI.java
@@ -747,7 +747,7 @@ InputMap getInputMap(int condition) {
/**
* Unregisters keyboard actions installed from
* installKeyboardActions.
- * This method is called at uninstallUI() time - subclassess should
+ * This method is called at uninstallUI() time - subclasses should
* ensure that all of the keyboard actions registered at installUI
* time are removed here.
*
diff --git a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java
index 0c373483153d..821a31854179 100644
--- a/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java
+++ b/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthComboBoxUI.java
@@ -105,7 +105,7 @@ public class SynthComboBoxUI extends BasicComboBoxUI implements
private ButtonHandler buttonHandler;
/**
- * Handler for repainting combo when editor component gains/looses focus
+ * Handler for repainting combo when editor component gains/loses focus
*/
private EditorFocusHandler editorFocusHandler;
@@ -766,7 +766,7 @@ public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {}
}
/**
- * Handler for repainting combo when editor component gains/looses focus
+ * Handler for repainting combo when editor component gains/loses focus
*/
private static class EditorFocusHandler implements FocusListener,
PropertyChangeListener {
diff --git a/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java b/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java
index 64d2e0e3bbf8..5e4fef910170 100644
--- a/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java
+++ b/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java
@@ -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
@@ -3164,7 +3164,7 @@ public ChangeListener[] getChangeListeners() {
/**
- * SelectorMapping contains a specifitiy, as an integer, and an associated
+ * SelectorMapping contains a specificity, as an integer, and an associated
* Style. It can also reference children SelectorMappings,
* so that it behaves like a tree.
*
diff --git a/src/java.desktop/share/classes/sun/awt/UngrabEvent.java b/src/java.desktop/share/classes/sun/awt/UngrabEvent.java
index c9d97b8ed951..2c4ea174e826 100644
--- a/src/java.desktop/share/classes/sun/awt/UngrabEvent.java
+++ b/src/java.desktop/share/classes/sun/awt/UngrabEvent.java
@@ -30,7 +30,7 @@
/**
* Sent when one of the following events occur on the grabbed window:
- * it looses focus, but not to one of the owned windows
+ * it loses focus, but not to one of the owned windows
* mouse click on the outside area happens (except for one of the owned windows)
* switch to another application or desktop happens
* click in the non-client area of the owning window or this window happens
diff --git a/src/java.desktop/share/classes/sun/font/FontDesignMetrics.java b/src/java.desktop/share/classes/sun/font/FontDesignMetrics.java
index 66ccf7499c54..95657fbb5fd9 100644
--- a/src/java.desktop/share/classes/sun/font/FontDesignMetrics.java
+++ b/src/java.desktop/share/classes/sun/font/FontDesignMetrics.java
@@ -68,7 +68,7 @@
* The FontDesignMetrics class expresses font metrics in terms of arbitrary
* typographic units (not points) chosen by the font supplier
* and used in the underlying platform font representations. These units are
- * defined by dividing the em-square into a grid. The em-sqaure is the
+ * defined by dividing the em-square into a grid. The em-square is the
* theoretical square whose dimensions are the full body height of the
* font. A typographic unit is the smallest measurable unit in the
* em-square. The number of units-per-em is determined by the font
diff --git a/src/java.desktop/share/classes/sun/font/FontManagerNativeLibrary.java b/src/java.desktop/share/classes/sun/font/FontManagerNativeLibrary.java
index df67a40fd141..589e24327d56 100644
--- a/src/java.desktop/share/classes/sun/font/FontManagerNativeLibrary.java
+++ b/src/java.desktop/share/classes/sun/font/FontManagerNativeLibrary.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 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
@@ -39,7 +39,7 @@ public class FontManagerNativeLibrary {
top of freetype library (that is used in binary form).
This wrapper is compiled into fontmanager and this make
- fontmanger library depending on freetype library.
+ font manager library depending on freetype library.
On Windows DLL's in the JRE's BIN directory cannot be
found by windows DLL loading as that directory is not
diff --git a/src/java.desktop/share/classes/sun/font/SunFontManager.java b/src/java.desktop/share/classes/sun/font/SunFontManager.java
index f9808530866a..ffd22f7aa42e 100644
--- a/src/java.desktop/share/classes/sun/font/SunFontManager.java
+++ b/src/java.desktop/share/classes/sun/font/SunFontManager.java
@@ -2617,7 +2617,7 @@ public boolean registerFont(Font font) {
* - family name is not the same as the full name of an installed font
* - full name is not the same as the family name of an installed font
* The last two of these may initially look odd but the reason is
- * that (unfortunately) Font constructors do not distinuguish these.
+ * that (unfortunately) Font constructors do not distinguish these.
* An extreme example of such a problem would be a font which has
* family name "Dialog.Plain" and full name of "Dialog".
* The one arguably overly stringent restriction here is that if an
diff --git a/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java b/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java
index 6b33f0786a8d..11586c67ff50 100644
--- a/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java
+++ b/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java
@@ -75,7 +75,7 @@ public class DesktopProperty implements UIDefaults.ActiveValue {
/**
- * Cleans up any lingering state held by unrefeernced
+ * Cleans up any lingering state held by unreferenced
* DesktopProperties.
*/
public static void flushUnreferencedProperties() {
diff --git a/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c b/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c
index ac37ad8eab6f..49520b45e312 100644
--- a/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c
+++ b/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c
@@ -519,7 +519,7 @@ static int GET_ARRAYS(JNIEnv *env, imageIODataPtr data, const JOCTET **next_byte
/*
* Set up error handling to use setjmp/longjmp. This is the third such
* setup, as both the AWT jpeg decoder and the com.sun... JPEG classes
- * setup thier own. Ultimately these should be integrated, as they all
+ * setup their own. Ultimately these should be integrated, as they all
* do pretty much the same thing.
*/
@@ -2358,7 +2358,7 @@ imageio_term_destination (j_compress_ptr cinfo)
JNIEnv *env = (JNIEnv *)JNU_GetEnv(the_jvm, JNI_VERSION_1_2);
/* find out how much needs to be written */
- /* this conversion from size_t to jint is safe, because the lenght of the buffer is limited by jint */
+ /* this conversion from size_t to jint is safe, because the length of the buffer is limited by jint */
jint datacount = (jint)(sb->bufferLength - dest->free_in_buffer);
if (datacount != 0) {
diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java b/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java
index 3a9855420588..558807403752 100644
--- a/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java
+++ b/src/java.desktop/unix/classes/sun/awt/X11/XAtom.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -31,10 +31,10 @@
* Standard X Atom are defined by X11 and these atoms are defined in this class
* for convenience. Common X Atoms like {@code XA_WM_NAME} are used to communicate with the
* Window manager to let it know the Window name. The use and protocol for these
- * atoms are defined in the Inter client communications converntions manual.
+ * atoms are defined in the Inter client communications conventions manual.
* User specified XAtoms are defined by specifying a name that gets Interned
* by the XServer and an {@code XAtom} object is returned. An {@code XAtom} can also be created
- * by using a pre-exisiting atom like {@code XA_WM_CLASS}. A {@code display} has to be specified
+ * by using a pre-existing atom like {@code XA_WM_CLASS}. A {@code display} has to be specified
* in order to create an {@code XAtom}.
*
* Once an {@code XAtom} instance is created, you can call get and set property methods to
From 64e0cf4162e7512c987154c127677f3331c3bb9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Du=C5=A1an=20B=C3=A1lek?=
Date: Tue, 18 Aug 2026 10:14:53 +0000
Subject: [PATCH 51/88] 8389859: Missing TYPE_USE annotations on type variable
uses
Reviewed-by: jlahoda
---
.../sun/tools/javac/code/TypeAnnotations.java | 2 +-
.../processing/model/type/BasicAnnoTests.java | 41 +++++++++++++++++--
2 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java
index e49ffa921281..b065bcc75e98 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java
@@ -485,7 +485,7 @@ private Type typeWithAnnotations(final JCTree typetree, final Type type,
if (type.hasTag(TypeTag.ARRAY)) {
ret = rewriteArrayType(typetree, (ArrayType)type, annotations, onlyTypeAnnotations, pos);
} else if (type.hasTag(TypeTag.TYPEVAR)) {
- ret = type.annotatedType(onlyTypeAnnotations);
+ ret = type.annotatedType(annotations);
} else if (type.getKind() == TypeKind.UNION) {
// There is a TypeKind, but no TypeTag.
UnionClassType ut = (UnionClassType) type;
diff --git a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java
index f94ba461a450..9b4ff972ba74 100644
--- a/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java
+++ b/test/langtools/tools/javac/processing/model/type/BasicAnnoTests.java
@@ -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
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8013852 8031744 8225377 8323684
+ * @bug 8013852 8031744 8225377 8323684 8389859
* @summary Annotations on types
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
@@ -103,7 +103,9 @@ public class BasicAnnoTests extends JavacTestingAbstractProcessor {
new NameToAnnotationEntry("BasicAnnoTests.TB", BasicAnnoTests.TB.class),
new NameToAnnotationEntry("BasicAnnoTests.TC", BasicAnnoTests.TC.class),
new NameToAnnotationEntry("BasicAnnoTests.TCs", BasicAnnoTests.TCs.class),
- new NameToAnnotationEntry("BasicAnnoTests.TD", BasicAnnoTests.TD.class));
+ new NameToAnnotationEntry("BasicAnnoTests.TD", BasicAnnoTests.TD.class),
+ new NameToAnnotationEntry("BasicAnnoTests.DTF", BasicAnnoTests.DTF.class),
+ new NameToAnnotationEntry("BasicAnnoTests.DTP", BasicAnnoTests.DTP.class));
static class NameToAnnotationEntry extends AbstractMap.SimpleEntry> {
public NameToAnnotationEntry(String key, Class extends Annotation> entry) {
@@ -531,6 +533,16 @@ R scan(Iterable extends TypeMirror> iter, P p) {
int value();
}
+ @Target({ElementType.TYPE_USE, ElementType.FIELD})
+ public @interface DTF {
+ int value();
+ }
+
+ @Target({ElementType.TYPE_USE, ElementType.PARAMETER})
+ public @interface DTP {
+ int value();
+ }
+
// Test cases
// TODO: add more cases for arrays
@@ -697,6 +709,29 @@ class Inner8<@TA(50) T> {
@Test(posn=6, annoType = TB.class, expect = "61")
void m60(@TA(60) @TB(61) String t) { }
+ // Test dual target annotations on uses of type variables
+ @Test(posn=6, annoType = DTP.class, expect = "61")
+ void m61(@DTP(61) T t) { }
+
+ @Test(posn=7, annoType = DTP.class, expect = "62")
+ void m62(@DTP(62) T[] t) { }
+
+ class Inner63 {
+ @Test(posn=0, annoType = DTF.class, expect = "63")
+ @DTF(63) T f;
+ }
+ // Test dual target annotations on uses of Class types
+ @Test(posn=6, annoType = DTP.class, expect = "64")
+ void m64(@DTP(64) String t) { }
+
+ @Test(posn=7, annoType = DTP.class, expect = "65")
+ void m65(@DTP(65) String[] t) { }
+
+ class Inner66 {
+ @Test(posn=0, annoType = DTF.class, expect = "66")
+ @DTF(66) String f;
+ }
+
class Inner70 {
@Test(posn=0, annoType = TA.class, expect = "70")
@Test(posn=0, annoType = TB.class, expect = "71")
From c2d84f0d7d7944f4b0498c9f06283dd3517d9fcc Mon Sep 17 00:00:00 2001
From: Daniel Skantz
Date: Tue, 18 Aug 2026 12:50:39 +0000
Subject: [PATCH 52/88] 8362117: C2:
compiler/stringopts/TestStackedConcatsAppendUncommonTrap.java fails with a
wrong result due to invalidated liveness assumptions for data phis
Co-authored-by: Emanuel Peter
Reviewed-by: rcastanedalo, dlong, thartmann
---
src/hotspot/share/opto/stringopts.cpp | 94 +++++++++++-
.../TestStackedConcatsSharedTest.java | 62 +++++++-
.../stringopts/TestStringConcatIR.java | 76 ++++++++++
.../TestStringConcatValidateMerge.java | 142 ++++++++++++++++++
.../TestStringConcatValidateMergeXcomp.java | 68 +++++++++
5 files changed, 433 insertions(+), 9 deletions(-)
create mode 100644 test/hotspot/jtreg/compiler/stringopts/TestStringConcatIR.java
create mode 100644 test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMerge.java
create mode 100644 test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMergeXcomp.java
diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp
index 45a1bfddc81e..5437f4e9a689 100644
--- a/src/hotspot/share/opto/stringopts.cpp
+++ b/src/hotspot/share/opto/stringopts.cpp
@@ -52,6 +52,9 @@ class StringConcat : public ResourceObj {
Node_List _control; // List of control nodes that will be deleted
Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten
// to restart at the initial JVMState.
+ Unique_Node_List _allowed_compares; // validate_control_flow() needs to know which compare nodes are
+ // accepted users of call results. In case of stacked concats,
+ // these need to be persisted across merges for validation.
static constexpr uint STACKED_CONCAT_UPPER_BOUND = 256; // argument limit for a merged concat.
// The value 256 was derived by measuring
@@ -286,6 +289,10 @@ void StringConcat::eliminate_unneeded_control() {
StringConcat* StringConcat::merge(StringConcat* other, Node* arg) {
StringConcat* result = new StringConcat(_stringopts, _end);
+
+ Unique_Node_List null_check_ifs;
+ Unique_Node_List skipped_phis;
+
for (uint x = 0; x < _control.size(); x++) {
Node* n = _control.at(x);
if (n->is_Call()) {
@@ -311,6 +318,17 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) {
result->append(other->argument(y), other->mode(y));
}
arguments_appended += other->num_arguments();
+ // Cache elements for later verification.
+ if (argument(x)->is_Phi()) {
+ Node* phi = argument(x);
+ assert(phi->as_Phi()->is_diamond_phi() > 0, "must be a diamond phi (ref. skip_string_null_check).");
+ Node* iff = phi->in(0)->in(1)->in(0);
+ Node* bol = iff->in(1);
+ Node* cmpp = bol->as_Bool()->in(1);
+ null_check_ifs.push(iff);
+ skipped_phis.push(phi);
+ result->_allowed_compares.push(cmpp);
+ }
} else {
result->append(argx, mode(x));
arguments_appended++;
@@ -327,13 +345,55 @@ StringConcat* StringConcat::merge(StringConcat* other, Node* arg) {
return nullptr;
}
}
+
+ // Verify that the diamond region isn't shared with non-null check phis;
+ // and that the associated bool doesn't have external uses.
+ for (uint i = 0; i < skipped_phis.size(); i++) {
+ Node* n = skipped_phis.at(i);
+ Node* r = n->in(0);
+ for (SimpleDUIterator j(r); j.has_next(); j.next()) {
+ Node* n2 = j.get();
+ if (n2->is_Phi() && !n2->is_memory_phi() && !skipped_phis.member(n2)) {
+#ifndef PRODUCT
+ if (PrintOptimizeStringConcat) {
+ tty->print_cr("null-check diamond region has external phi uses");
+ }
+#endif
+ return nullptr;
+ }
+ }
+ Node* iff = n->in(0)->in(1)->in(0);
+ Node* bol = iff->in(1);
+ for (SimpleDUIterator j(bol); j.has_next(); j.next()) {
+ if (!null_check_ifs.member(j.get())) {
+#ifndef PRODUCT
+ if (PrintOptimizeStringConcat) {
+ tty->print_cr("null-check diamond bool has external uses.");
+ }
+#endif
+ return nullptr;
+ }
+ }
+ }
+
result->set_allocation(other->_begin);
for (uint i = 0; i < _constructors.size(); i++) {
result->add_constructor(_constructors.at(i));
}
+
for (uint i = 0; i < other->_constructors.size(); i++) {
result->add_constructor(other->_constructors.at(i));
}
+
+ // We add previous _allowed_compares in case of repeated stacked concatenation.
+ for (uint i = 0; i < _allowed_compares.size(); i++) {
+ result->_allowed_compares.push(_allowed_compares.at(i));
+ }
+
+ for (uint i = 0; i < other->_allowed_compares.size(); i++) {
+ result->_allowed_compares.push(other->_allowed_compares.at(i));
+ }
+
result->_multiple = true;
return result;
}
@@ -936,6 +996,10 @@ bool StringConcat::validate_control_flow() {
int null_check_count = 0;
Unique_Node_List ctrl_path;
+ // Local version of _allowed_compares that stores allowed comparisons discovered during traversal
+ // but that we won't persist across merges.
+ Unique_Node_List local_allowed_compares;
+
assert(_control.contains(_begin), "missing");
assert(_control.contains(_end), "missing");
@@ -993,11 +1057,31 @@ bool StringConcat::validate_control_flow() {
Node* v2 = cmp->in(2);
Node* otherproj = iff->proj_out(1 - ptr->as_Proj()->_con);
- // Null check of the return of append which can simply be eliminated
+ // Either a null check of the return of append which can simply be eliminated,
+ // or possibly of a toString during stacked concats.
if (b->_test._test == BoolTest::ne &&
v2->bottom_type() == TypePtr::NULL_PTR &&
v1->is_Proj() && ctrl_path.member(v1->in(0))) {
- // null check of the return value of the append
+ if (!is_SB_toString(v1->in(0))) {
+ // append type
+ assert(v1->in(0)->as_CallStaticJava()->method()->name() == ciSymbols::append_name(), "must be");
+ local_allowed_compares.push(cmp);
+ } else {
+ // toString
+ assert(_multiple, "if not _multiple, we should not have a toString on this control path");
+ if (!_allowed_compares.member(cmp)) {
+ // Should have been populated during merge if valid.
+ // This should also be caught in result use verification later but we can fail early here.
+ fail = true;
+#ifndef PRODUCT
+ if (PrintOptimizeStringConcat) {
+ tty->print_cr("Failing as toString()-dependent compare is not part of a recognized string null check.");
+ cmp->dump();
+ }
+#endif
+ break;
+ }
+ }
null_check_count++;
if (otherproj->outcnt() == 1) {
CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava();
@@ -1022,6 +1106,8 @@ bool StringConcat::validate_control_flow() {
((v1->is_Proj() && is_SB_toString(v1->in(0)) && ctrl_path.member(v1->in(0))) ||
(v2->is_Proj() && is_SB_toString(v2->in(0)) && ctrl_path.member(v2->in(0))))) {
// iftrue -> if -> bool -> cmpp -> resproj -> tostring
+ assert(!_allowed_compares.member(cmp) && !local_allowed_compares.member(cmp), "Unsound dependency on intermediate values");
+ // Would be caught by containment analysis later but we can fail early here.
fail = true;
break;
}
@@ -1147,7 +1233,9 @@ bool StringConcat::validate_control_flow() {
continue;
}
int opc = use->Opcode();
- if (opc == Op_CmpP || opc == Op_Node) {
+ if (opc == Op_Node ||
+ (opc == Op_CmpP && (use->outcnt() == 1) // The cmpp validation assumes a unique use.
+ && (local_allowed_compares.member(use) || _allowed_compares.member(use)))) {
ctrl_path.push(use);
continue;
}
diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsSharedTest.java b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsSharedTest.java
index 15fc03036749..1e6fd434804d 100644
--- a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsSharedTest.java
+++ b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsSharedTest.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
@@ -23,13 +23,15 @@
/*
* @test
- * @bug 8356246
+ * @bug 8356246 8362117
* @summary Test stacked string concatenations where the toString of the first StringBuilder
* is used as a shared test by two diamond Ifs in the second StringBuilder.
- * @run main/othervm compiler.stringopts.TestStackedConcatsSharedTest
- * @run main/othervm -XX:-TieredCompilation -Xcomp
- * -XX:CompileOnly=compiler.stringopts.TestStackedConcatsSharedTest::*
- * compiler.stringopts.TestStackedConcatsSharedTest
+ * (f): make sure we don't crash outright
+ * (g): external null checks depending on the same test/removed call should not give a wrong result.
+ * (h): multiple phis attached to the same diamond region; only one is a proper null check phi.
+ * (i): non-null check phi reused after intermediate stacked concat: check for correct result
+ * @run main/othervm ${test.main.class}
+ * @run main/othervm -XX:-TieredCompilation -Xcomp -XX:CompileOnly=${test.main.class}::* ${test.main.class}
*/
package compiler.stringopts;
@@ -42,6 +44,21 @@ public static void main(String... args) {
if (!s.equals("")) {
throw new RuntimeException("wrong result");
}
+ s = g();
+ if (!s.equals("abcabcabc")) {
+ System.out.println(s);
+ throw new RuntimeException("wrong result");
+ }
+ s = h();
+ if (!s.equals("abcabcnotnull")) {
+ System.out.println(s);
+ throw new RuntimeException("wrong result");
+ }
+ s = i();
+ if (!s.equals("abcabcnotnull")) {
+ System.out.println(s);
+ throw new RuntimeException("wrong result");
+ }
}
static String f() {
@@ -52,4 +69,37 @@ static String f() {
s = new StringBuilder(String.valueOf(s)).append(String.valueOf(s)).toString();
return s;
}
+
+ static String g() {
+ String s = "abc";
+ s = new StringBuilder(s).toString();
+ s = new StringBuilder(String.valueOf(s)).append(String.valueOf(s)).toString() + (s == null ? "def" : "abc");
+ return s;
+ }
+
+ static String h() {
+ String s1 = new String("abc");
+ String s2 = new StringBuilder(s1).append(s1).toString();
+ String arg2 = "";
+ if (s2 == null) {
+ arg2 = "null";
+ } else {
+ arg2 = "notnull";
+ }
+ return new StringBuilder(s2).append(arg2).toString();
+ }
+
+ static String i() {
+ String s1 = new String("abc");
+ String s2 = new StringBuilder(s1).append(s1).toString();
+ String arg2 = "";
+ if (s2 == null) {
+ arg2 = "null";
+ } else {
+ arg2 = "notnull";
+ }
+ String s3 = new StringBuilder(s2).toString();
+ String s4 = new StringBuilder(s3).append(arg2).toString();
+ return s4;
+ }
}
diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStringConcatIR.java b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatIR.java
new file mode 100644
index 000000000000..0ead4d2661cd
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatIR.java
@@ -0,0 +1,76 @@
+/*
+ * 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 8362117
+ * @summary Basic IR checks to verify that merge validation does not break concat optimizations.
+ * @library /test/lib /
+ * @run driver ${test.main.class}
+ */
+
+package compiler.stringopts;
+
+import compiler.lib.ir_framework.*;
+
+public class TestStringConcatIR {
+
+ public static void main(String[] args) {
+ TestFramework.runWithFlags();
+ }
+
+ @Run(test = {"stackedConcat", "stackedConcatNullCheck"})
+ public void runMethodA() {
+ stackedConcat();
+ stackedConcatNullCheck();
+ }
+
+ @Test
+ @IR(counts = {IRNode.CALL, ">= 9"}, phase = {CompilePhase.BEFORE_STRINGOPTS}) // at least init, append, tostring x 3
+ @IR(counts = {IRNode.ALLOC, "= 3"}, phase = {CompilePhase.BEFORE_STRINGOPTS})
+ @IR(counts = {IRNode.CALL, "= 0"}, phase = {CompilePhase.ITER_GVN1})
+ @IR(counts = {IRNode.ALLOC, "= 1"}, phase = {CompilePhase.ITER_GVN1})
+ @IR(counts = {IRNode.STORE_B, "= 16"}, phase = {CompilePhase.ITER_GVN1})
+ static String stackedConcat() {
+ String s = "ab";
+ s = new StringBuilder(s).append(s).toString();
+ s = new StringBuilder(s).append(s).toString();
+ s = new StringBuilder(s).append(s).toString();
+ return s;
+ }
+
+ @Test
+ @IR(applyIf = {"TieredCompilation", "true"},
+ counts = {IRNode.CALL, ">= 9", IRNode.ALLOC, "= 3"},
+ phase = {CompilePhase.BEFORE_STRINGOPTS})
+ @IR(applyIf = {"TieredCompilation", "true"},
+ counts = {IRNode.CALL, "= 0", IRNode.ALLOC, "= 1", IRNode.STORE_B, "= 24"},
+ phase = {CompilePhase.ITER_GVN1})
+ static String stackedConcatNullCheck() {
+ String s = "abc";
+ s = new StringBuilder(String.valueOf(s)).append(s).toString();
+ s = new StringBuilder(String.valueOf(s)).append(String.valueOf(s)).toString();
+ s = new StringBuilder(s).append(String.valueOf(s)).toString();
+ return s;
+ }
+}
diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMerge.java b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMerge.java
new file mode 100644
index 000000000000..b539c0e7cb5d
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMerge.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
+ * @bug 8362117
+ * @summary Prevent crashes and miscompilations when external constructs
+ * could be confused for StringConcat append/toString-null checks.
+ * @run main/othervm ${test.main.class}
+ * @run main/othervm -Xbatch
+ * -XX:CompileOnly=${test.main.class}::test* ${test.main.class}
+ * @run main/othervm -Xbatch
+ * -XX:CompileThreshold=500
+ * -XX:CompileOnly=${test.main.class}::test* ${test.main.class}
+ * @run main/othervm -Xbatch
+ * -XX:-TieredCompilation
+ * -XX:CompileOnly=${test.main.class}::test* ${test.main.class}
+ */
+
+package compiler.stringopts;
+
+public class TestStringConcatValidateMerge {
+
+ public static void main (String... args) {
+
+ String gold = test1(false);
+ for (int i = 0; i < 10_000; i++) {
+ test1((i & 1) == 0);
+ }
+ String val = test1(false);
+ if (!val.equals(gold)) {
+ throw new RuntimeException("wrong value: " + val + " vs " + gold);
+ }
+
+ for (int t = 0; t < 10_000; t++) {
+ // The following line is probably important for profiling.
+ try { new String((String) null); } catch (NullPointerException e) {}
+ test2();
+ }
+
+ for (int t = 0; t < 10_000; t++) {
+ try {
+ if (t % 2 != 0) {
+ test3(null, "B");
+ } else {
+ test3("A", null);
+ }
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ for (int i = 0; i < 100_000; i++) {
+ test4();
+ }
+
+ for (int i = 0; i < 100_000; i++) {
+ test5(i % 2 == 0);
+ }
+
+ gold = test6(new StringBuilder(" "));
+ for (int i = 0; i < 100_000; i++) {
+ val = test6(new StringBuilder(" "));
+ }
+ if (!val.equals(gold)) {
+ throw new RuntimeException("wrong result.");
+ }
+
+ }
+
+ // test1-3: StringOpts can't stack as SB1 is used in a compare in SB2 (previously confused as a valid string null check).
+ // test4: can't remove SB1's toString as it's used in an external comparison that needs it -> reject stacking
+ // test5: hand-written branching that changes return value (previously mistaken to be a valid string null check).
+ // test6: merge an unresolved stringbuilder with the intermediate value used in a compare: reject single concat.
+
+ // JDK-8385429
+ static String test1(boolean flag) {
+ String s = new StringBuilder("ABC").toString();
+ return new StringBuilder().append(s).append(s == null ? "x" : "y").append(flag ? "z" : s).toString();
+ }
+
+ // JDK-8385428
+ static int test2() {
+ String s1 = (("a" == null) ? "b" : "c") + 'd';
+ String s2 = new StringBuilder(s1).toString();
+ String s3 = new StringBuilder(s2).append(s1).append(s2 == null ? "" : s2).toString();
+ return s3.length();
+ }
+
+ // JDK-8385415
+ static Object test3(String a, String b) {
+ String s1 = new String(b);
+ String s2 = new StringBuffer(s1).append(s1).toString();
+ return new StringBuffer(s2).append(a).append(s2 == null ? "" : s2).toString();
+ }
+
+ // JDK-8384130
+ static String test4() {
+ String s = new StringBuilder().toString();
+ return new StringBuilder(s).toString() == s ? "a" : "b";
+ }
+
+ static String test5(boolean test) {
+ String s1 = new String("b");
+ String s2 = new StringBuilder(s1).append(s1).toString();
+ String arg1 = "";
+ String arg2 = "";
+ if (s2 == null) {
+ arg2 = "null";
+ } else {
+ arg2 = "Some other string";
+ }
+ return new StringBuffer(s2).append(arg2).toString();
+ }
+
+ static String test6(StringBuilder c) {
+ StringBuilder s = new StringBuilder().append(" ");
+ String ret = s.append(s == c ? "abc" : " ").toString();
+ return ret;
+ }
+
+}
diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMergeXcomp.java b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMergeXcomp.java
new file mode 100644
index 000000000000..ca75545092bb
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/stringopts/TestStringConcatValidateMergeXcomp.java
@@ -0,0 +1,68 @@
+/*
+ * 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 8362117
+ * @summary Similar type of test scenarios as in TestStringConcatValidateMerge.java
+ * but for problems which manifested with -Xcomp
+ * (f): stringopts shouldn't confuse ternary expression with string null check
+ * and fold away diamond phi arbitrarily leading to wrong result when depending on
+ * toString of SB1.
+ * (g): variant of (f) with append instead of toString
+ * @library /test/lib /
+ * @run main/othervm ${test.main.class}
+ * @run main/othervm -XX:-TieredCompilation -Xcomp
+ * -XX:CompileOnly=${test.main.class}::* ${test.main.class}
+ */
+
+package compiler.stringopts;
+
+import jdk.test.lib.Asserts;
+
+public class TestStringConcatValidateMergeXcomp {
+
+ public static void main (String... args) {
+ new StringBuilder(); // load the class
+ f();
+ g();
+ }
+
+ static String f() {
+ String s = "a";
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append((s == "xx") ? s : "aa").toString();
+ Asserts.assertEQ(s, "aaaa"); // in particular, we should not have s.equals("aaxx");
+ return s;
+ }
+
+ static String g() {
+ String s = "a";
+ StringBuilder sb0 = new StringBuilder();
+ s = new StringBuilder().append(s).append(s).toString();
+ StringBuilder sb2 = new StringBuilder().append(s);
+ s = sb2.append((sb2 == sb0) ? "xx" : "aa").toString();
+ Asserts.assertEQ(s, "aaaa"); // in particular, we should not have s.equals("aaxx").
+ return s;
+ }
+}
From 0741f25d3d66cca73328faf2683f5e65c4278837 Mon Sep 17 00:00:00 2001
From: Roland Westrelin
Date: Tue, 18 Aug 2026 13:46:17 +0000
Subject: [PATCH 53/88] 8390467: C2: _map != nullptr assert failure in
LibraryCallKit::inline_Class_cast()
Reviewed-by: qamai, thartmann
---
src/hotspot/share/opto/library_call.cpp | 5 +-
.../intrinsics/TestClassCastDeadPath.java | 67 +++++++++++++++++++
2 files changed, 69 insertions(+), 3 deletions(-)
create mode 100644 test/hotspot/jtreg/compiler/intrinsics/TestClassCastDeadPath.java
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index 5945f6f072ea..ddd59936f4da 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -4631,7 +4631,7 @@ bool LibraryCallKit::inline_Class_cast() {
}
// Not-subtype or the mirror's klass ptr is nullptr (in case it is a primitive).
- enum { _bad_type_path = 1, _prim_path = 2, _npe_path = 3, PATH_LIMIT };
+ enum { _bad_type_path = 1, _prim_path = 2, PATH_LIMIT };
RegionNode* region = new RegionNode(PATH_LIMIT);
record_for_igvn(region);
@@ -4653,8 +4653,7 @@ bool LibraryCallKit::inline_Class_cast() {
region->init_req(_bad_type_path, bad_type_ctrl);
}
if (region->in(_prim_path) != top() ||
- region->in(_bad_type_path) != top() ||
- region->in(_npe_path) != top()) {
+ region->in(_bad_type_path) != top()) {
// Let Interpreter throw ClassCastException.
PreserveJVMState pjvms(this);
if (new_cast_failure_map != nullptr) {
diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestClassCastDeadPath.java b/test/hotspot/jtreg/compiler/intrinsics/TestClassCastDeadPath.java
new file mode 100644
index 000000000000..fe310bb9f46e
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/intrinsics/TestClassCastDeadPath.java
@@ -0,0 +1,67 @@
+/*
+ * 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 8390467
+ * @summary C2: _map != nullptr assert failure in LibraryCallKit::inline_Class_cast()
+ * @run main/othervm -XX:CompileOnly=${test.main.class}::test1 -Xcomp ${test.main.class}
+ */
+
+package compiler.intrinsics;
+
+public class TestClassCastDeadPath {
+ public static void main(String[] args) {
+ B b = new B();
+ C c = new C();
+ A.class.cast(b);
+ try {
+ test1(c);
+ } catch (ClassCastException cce) {
+ }
+ }
+
+ private static void test1(Object o) {
+ if (!(o instanceof I)) {
+ throw new RuntimeException("never taken");
+ }
+ A.class.cast(o);
+ }
+
+ static abstract class A {
+
+ }
+
+ static class B extends A {
+
+ }
+
+ interface I {
+
+ }
+
+ static class C implements I {
+
+ }
+
+}
From c2e49da8f6391a0a3447fc5b1349bebd3b7f2e23 Mon Sep 17 00:00:00 2001
From: Ivan Bereziuk
Date: Tue, 18 Aug 2026 13:52:39 +0000
Subject: [PATCH 54/88] 8390065: TestDockerMemoryMetrics.java fails in
MetricsMemoryTester failcount
Reviewed-by: cnorrbin
---
.../platform/docker/MetricsMemoryTester.java | 83 +++++++++++--------
.../docker/TestDockerMemoryMetrics.java | 37 ++++++---
2 files changed, 73 insertions(+), 47 deletions(-)
diff --git a/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java
index c27a1c7480fb..53924b6abadb 100644
--- a/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java
+++ b/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -65,43 +65,58 @@ private static void testMemoryLimit(String value) {
}
private static void testMemoryFailCount() {
- long memAndSwapLimit = Metrics.systemMetrics().getMemoryAndSwapLimit();
- long memLimit = Metrics.systemMetrics().getMemoryLimit();
+ final Metrics metrics = Metrics.systemMetrics();
+ final long memAndSwapLimit = metrics.getMemoryAndSwapLimit();
+ final long memLimit = metrics.getMemoryLimit();
+
+ final int M = 1024 * 1024;
+
+ // We need swap to execute this test. Otherwise OOM killer acts with
+ // SIGKILL before we read the fail counter.
+
+ final long maxHeapSize = Runtime.getRuntime().maxMemory();
+ if (maxHeapSize <= memLimit || maxHeapSize >= memAndSwapLimit) {
+ throw new RuntimeException(
+ "Expected memory limit < maximum heap < memory-and-swap limit: "
+ + "memory=" + memLimit / M + "M, "
+ + "heap=" + maxHeapSize / M + "M, "
+ + "memory-and-swap=" + memAndSwapLimit / M + "M");
+ }
- // We need swap to execute this test or will SEGV
- if (memAndSwapLimit <= memLimit) {
- System.out.println("No swap memory limits. Ignoring test!");
- } else {
- long count = Metrics.systemMetrics().getMemoryFailCount();
-
- // Allocate 512M of data in 1M chunks per iteration
- byte[][] bytes = new byte[64 * 8][];
- boolean atLeastOneAllocationWorked = false;
- for (int i = 0; i < 64 * 8; i++) {
- try {
- bytes[i] = new byte[1024 * 1024];
- atLeastOneAllocationWorked = true;
- // Break out as soon as we see an increase in failcount
- // to avoid getting killed by the OOM killer.
- if (Metrics.systemMetrics().getMemoryFailCount() > count) {
- break;
- }
- } catch (Error e) { // OOM error
- break;
- }
- }
- if (!atLeastOneAllocationWorked) {
- System.out.println("Allocation failed immediately. Ignoring test!");
- return;
+ final long initialFailCount = metrics.getMemoryFailCount();
+
+ System.out.println("Initial memory fail count: " + initialFailCount);
+
+ // Allocate 512M of data in 1M chunks per iteration
+ byte[][] bytes = new byte[512][];
+
+ for (int i = 0; i < 512; i++) {
+ if (i % 8 == 0) {
+ System.out.printf("Allocated: %3dM, Memory usage: %3dM, Memory and swap: %3dM\n",
+ i,
+ metrics.getMemoryUsage() / M,
+ metrics.getMemoryAndSwapUsage() / M);
+ } else {
+ System.out.print(".");
}
- // Be sure bytes allocations don't get optimized out
- System.out.println("DEBUG: Bytes allocation length 1: " + bytes[0].length);
- if (Metrics.systemMetrics().getMemoryFailCount() <= count) {
- throw new RuntimeException("Memory fail count : new : ["
- + Metrics.systemMetrics().getMemoryFailCount() + "]"
- + ", old : [" + count + "]");
+ bytes[i] = new byte[M];
+ Arrays.fill(bytes[i], (byte) 1); // dirty every page
+ // Break out as soon as we see an increase in failcount
+ if (metrics.getMemoryFailCount() > initialFailCount) {
+ break;
}
}
+
+ // Be sure bytes allocations don't get optimized out
+ System.out.println("\nDEBUG: Bytes allocation length 1: " + bytes[0].length);
+ final long newCount = metrics.getMemoryFailCount();
+ System.out.println("Final memory fail count: " + newCount);
+
+ if (newCount <= initialFailCount) {
+ throw new RuntimeException("Memory fail count did not increase: initial="
+ + initialFailCount + ", final=" + newCount);
+ }
+
System.out.println("TEST PASSED!!!");
}
diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java
index 12f90d655169..78878879783a 100644
--- a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java
+++ b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -76,7 +76,7 @@ public static void main(String[] args) throws Exception {
}
testOomKillFlag("100m", true);
- testMemoryFailCount("128m");
+ testMemoryFailCount("128m" /*memory*/, "768m" /*max_heap*/, "1024m" /*memory_n_swap*/);
testMemorySoftLimit("500m","200m");
@@ -105,36 +105,47 @@ private static void testMemoryLimit(String value, boolean addCgroupMount) throws
DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!");
}
- private static void testMemoryFailCount(String value) throws Exception {
- Common.logNewTestCase("testMemoryFailCount" + value);
+ private static void testMemoryFailCount(String memory, String heap, String memoryAndSwap) throws Exception {
+ Common.logNewTestCase("testMemoryFailCount, memory = " + memory
+ + ", heap = " + heap
+ + ", memory + swap = " + memoryAndSwap);
// Check whether swapping really works for this test
// On some systems there is no swap space enabled. And running
- // 'java -Xms{mem-limit} -Xmx{mem-limit} -XX:+AlwaysPreTouch -version'
+ // 'java -Xms{heap} -Xmx{heap} -XX:+AlwaysPreTouch -version'
// would fail due to swap space size being 0. Note that when swap is
- // properly enabled on the system the container gets the same amount
- // of swap as is configured for memory. Thus, 2x{mem-limit} is the actual
- // memory and swap bound for this pre-test.
+ // properly enabled, the explicit memory-and-swap limit gives the JVM
+ // enough headroom to exceed the physical memory limit without being
+ // killed by the OOM killer.
DockerRunOptions preOpts =
new DockerRunOptions(imageName, "/jdk/bin/java", "-version");
preOpts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/")
- .addDockerOpts("--memory=" + value)
+ .addDockerOpts("--memory=" + memory)
+ .addDockerOpts("--memory-swap=" + memoryAndSwap)
.addJavaOpts("-XX:+AlwaysPreTouch")
- .addJavaOpts("-Xms" + value)
- .addJavaOpts("-Xmx" + value);
+ .addJavaOptsAppended("-XX:InitialHeapSize=" + heap)
+ .addJavaOptsAppended("-XX:MaxHeapSize=" + heap);
OutputAnalyzer oa = DockerTestUtils.dockerRunJava(preOpts);
String output = oa.getOutput();
if (!output.contains("version")) {
throw new SkippedException("Swapping doesn't work for this test.");
}
+ // 0 128 1024
+ // |---o----------------|---------------------------X--------------)-------------|
+ // START memory.max growth target MaxHeapSize memory+swap limit
+ // o~~~~~>~>~>~>~>~>~>~>~>~>~>~>~>~>~>~>~> (growth) OOM
+ // failcount: 0 1 2 3 . . . N
+ //
DockerRunOptions opts =
new DockerRunOptions(imageName, "/jdk/bin/java", "MetricsMemoryTester");
opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/")
- .addDockerOpts("--memory=" + value)
- .addJavaOpts("-Xmx" + value)
+ .addDockerOpts("--memory=" + memory)
+ .addDockerOpts("--memory-swap=" + memoryAndSwap)
.addJavaOpts("-cp", "/test-classes/")
.addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED")
+ // set the required heap size *after* inherited jtreg options
+ .addJavaOptsAppended("-XX:MaxHeapSize=" + heap)
.addClassOptions("failcount");
oa = DockerTestUtils.dockerRunJava(opts);
output = oa.getOutput();
From 3be31d10b55a54199be1aa629a510d28c3d7f169 Mon Sep 17 00:00:00 2001
From: Erik Gahlin
Date: Tue, 18 Aug 2026 13:53:01 +0000
Subject: [PATCH 55/88] 8390133: JFR: OngoingStream reads stale header
Reviewed-by: mgronlun
---
.../jdk/jfr/internal/consumer/OngoingStream.java | 11 ++++++-----
.../jdk/jfr/internal/consumer/RecordingInput.java | 6 +++++-
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java
index 83d8b8c5f17d..a60117fe316c 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 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,10 +167,11 @@ private byte[] readWithHeader(int size) throws IOException {
byte[] bytes = new byte[Math.max(HEADER_SIZE, size)];
for (int attempts = 0; attempts < 25; attempts++) {
// read twice and check files state to avoid simultaneous change by JVM
- input.position(0);
- input.readFully(bytes, 0, HEADER_SIZE);
- input.position(0);
- input.readFully(headerBytes);
+ input.positionPhysical(0);
+ input.readPhysicalFully(bytes, 0, HEADER_SIZE);
+ input.positionPhysical(0);
+ input.readPhysicalFully(headerBytes, 0, HEADER_SIZE);
+ input.position(HEADER_SIZE);
if (bytes[HEADER_FILE_STATE_POSITION] != MODIFYING_STATE) {
if (bytes[HEADER_FILE_STATE_POSITION] == headerBytes[HEADER_FILE_STATE_POSITION]) {
ByteBuffer buffer = ByteBuffer.wrap(bytes);
diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java
index 33cb928bbbfd..001bd39896e3 100644
--- a/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java
+++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java
@@ -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
@@ -110,6 +110,10 @@ long readPhysicalLong() throws IOException {
return file.readLong();
}
+ void readPhysicalFully(byte[] dest, int offset, int length) throws IOException {
+ file.readFully(dest, offset, length);
+ }
+
@Override
public final byte readByte() throws IOException {
if (!currentBlock.contains(position)) {
From 8a507d85e9b7c7a0acfd7fa72b898b9a9ce2d5ee Mon Sep 17 00:00:00 2001
From: Peijun Xu
Date: Tue, 18 Aug 2026 13:55:24 +0000
Subject: [PATCH 56/88] 8390102: RISC-V: Support AOT Code Cache
Reviewed-by: adinn, fyang, kvn
---
.../cpu/riscv/c1_LIRAssembler_riscv.cpp | 15 +
.../cpu/riscv/c1_LIRAssembler_riscv.hpp | 2 +-
.../gc/g1/g1BarrierSetAssembler_riscv.cpp | 20 +-
.../shenandoahBarrierSetAssembler_riscv.cpp | 20 +-
.../cpu/riscv/macroAssembler_riscv.cpp | 96 +-
.../cpu/riscv/macroAssembler_riscv.hpp | 6 +-
src/hotspot/cpu/riscv/riscv.ad | 31 +-
src/hotspot/cpu/riscv/runtime_riscv.cpp | 25 +-
src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp | 40 +-
src/hotspot/cpu/riscv/stubGenerator_riscv.cpp | 999 ++++++++++++++----
src/hotspot/cpu/riscv/stubRoutines_riscv.cpp | 12 +-
src/hotspot/cpu/riscv/vm_version_riscv.cpp | 60 ++
src/hotspot/cpu/riscv/vm_version_riscv.hpp | 21 +
.../os_cpu/linux_riscv/os_linux_riscv.cpp | 12 +-
src/hotspot/share/code/aotCodeCache.cpp | 12 +-
src/hotspot/share/code/aotCodeCache.hpp | 17 +-
.../AOTCodeCPUFeatureIncompatibilityTest.java | 20 +-
test/jtreg-ext/requires/VMProps.java | 2 +-
18 files changed, 1153 insertions(+), 257 deletions(-)
diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
index 5d174e77f37a..496e26d3c0b5 100644
--- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.cpp
@@ -36,6 +36,7 @@
#include "ci/ciInlineKlass.hpp"
#include "ci/ciInstance.hpp"
#include "ci/ciObjArrayKlass.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/compiledIC.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "nativeInst_riscv.hpp"
@@ -43,6 +44,7 @@
#include "oops/oop.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/sharedRuntime.hpp"
+#include "runtime/threadIdentifier.hpp"
#include "utilities/powerOfTwo.hpp"
#include "vmreg_riscv.inline.hpp"
@@ -441,6 +443,19 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod
case T_LONG:
assert(patch_code == lir_patch_none, "no patching handled here");
+#if INCLUDE_CDS
+ if (AOTCodeCache::is_on_for_dump()) {
+ address b = c->as_pointer();
+ if (b == (address)ThreadIdentifier::unsafe_offset()) {
+ __ la(dest->as_register_lo(), ExternalAddress(b));
+ break;
+ }
+ if (AOTRuntimeConstants::contains(b)) {
+ __ load_aotrc_address(dest->as_register_lo(), b);
+ break;
+ }
+ }
+#endif
__ mv(dest->as_register_lo(), (intptr_t)c->as_jlong());
break;
diff --git a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp
index 4a915c727ae3..85e2e2866e9e 100644
--- a/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/c1_LIRAssembler_riscv.hpp
@@ -69,7 +69,7 @@ friend class ArrayCopyStub;
_call_stub_size = 11 * MacroAssembler::instruction_size +
1 * MacroAssembler::instruction_size + wordSize,
// See emit_exception_handler for detail
- _exception_handler_size = DEBUG_ONLY(256) NOT_DEBUG(32), // or smaller
+ _exception_handler_size = DEBUG_ONLY(1*K) NOT_DEBUG(175), // or smaller
// See emit_deopt_handler for detail
// far_call (2) + j (1)
_deopt_handler_size = 1 * MacroAssembler::instruction_size +
diff --git a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp
index fa236bf8eadf..abcf69e2df09 100644
--- a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp
@@ -24,6 +24,7 @@
*/
#include "asm/macroAssembler.inline.hpp"
+#include "code/aotCodeCache.hpp"
#include "gc/g1/g1BarrierSet.hpp"
#include "gc/g1/g1BarrierSetAssembler.hpp"
#include "gc/g1/g1BarrierSetRuntime.hpp"
@@ -257,9 +258,22 @@ static void generate_post_barrier(MacroAssembler* masm,
assert(thread == xthread, "must be");
assert_different_registers(store_addr, new_val, thread, tmp1, tmp2, noreg);
// Does store cross heap regions?
- __ xorr(tmp1, store_addr, new_val); // tmp1 := store address ^ new value
- __ srli(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes)
- __ beqz(tmp1, done);
+#if INCLUDE_CDS
+ // AOT code needs to load the barrier grain shift from the aot
+ // runtime constants area in the code cache otherwise we can compile
+ // it as an immediate operand
+ if (AOTCodeCache::is_on_for_dump()) {
+ __ xorr(tmp1, store_addr, new_val);
+ __ lwu(tmp2, ExternalAddress(AOTRuntimeConstants::grain_shift_address()));
+ __ srl(tmp1, tmp1, tmp2);
+ __ beqz(tmp1, done);
+ } else
+#endif
+ {
+ __ xorr(tmp1, store_addr, new_val); // tmp1 := store address ^ new value
+ __ srli(tmp1, tmp1, G1HeapRegion::LogOfHRGrainBytes); // tmp1 := ((store address ^ new value) >> LogOfHRGrainBytes)
+ __ beqz(tmp1, done);
+ }
// Crosses regions, storing null?
if (new_val_may_be_null) {
diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
index 1de4ff2b93de..647846d523b2 100644
--- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp
@@ -219,8 +219,14 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm,
// Test for in-cset
if (is_strong) {
- __ mv(t1, ShenandoahHeap::in_cset_fast_test_addr());
- __ srli(t0, x10, ShenandoahHeapRegion::region_size_bytes_shift_jint());
+ if (AOTCodeCache::is_on_for_dump()) {
+ __ ld(t1, ExternalAddress(AOTRuntimeConstants::cset_base_address()));
+ __ lwu(t0, ExternalAddress(AOTRuntimeConstants::grain_shift_address()));
+ __ srl(t0, x10, t0);
+ } else {
+ __ mv(t1, ShenandoahHeap::in_cset_fast_test_addr());
+ __ srli(t0, x10, ShenandoahHeapRegion::region_size_bytes_shift_jint());
+ }
__ add(t1, t1, t0);
__ lbu(t1, Address(t1));
__ test_bit(t0, t1, 0);
@@ -815,8 +821,14 @@ void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) {
__ mv(_tmp2, _obj);
}
- __ mv(_tmp1, ShenandoahHeap::in_cset_fast_test_addr());
- __ srli(_tmp2, _tmp2, ShenandoahHeapRegion::region_size_bytes_shift_jint());
+ if (AOTCodeCache::is_on_for_dump()) {
+ __ lwu(_tmp1, ExternalAddress(AOTRuntimeConstants::grain_shift_address()));
+ __ srl(_tmp2, _tmp2, _tmp1);
+ __ ld(_tmp1, ExternalAddress(AOTRuntimeConstants::cset_base_address()));
+ } else {
+ __ mv(_tmp1, ShenandoahHeap::in_cset_fast_test_addr());
+ __ srli(_tmp2, _tmp2, ShenandoahHeapRegion::region_size_bytes_shift_jint());
+ }
__ add(_tmp1, _tmp1, _tmp2);
__ lbu(_tmp1, Address(_tmp1, 0));
maybe_far_jump_if_zero(masm, _tmp1);
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
index 166915ba018e..15d853f919b5 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp
@@ -26,6 +26,7 @@
#include "asm/assembler.hpp"
#include "asm/assembler.inline.hpp"
+#include "cds/archiveBuilder.hpp"
#include "ci/ciInlineKlass.hpp"
#include "code/compiledIC.hpp"
#include "compiler/disassembler.hpp"
@@ -879,9 +880,13 @@ void MacroAssembler::resolve_global_jobject(Register value, Register tmp1, Regis
}
void MacroAssembler::stop(const char* msg) {
- BLOCK_COMMENT(msg);
+ // Skip AOT caching C strings in scratch buffer.
+ const char* str = (code_section()->scratch_emit()) ? msg : AOTCodeCache::add_C_string(msg);
+ BLOCK_COMMENT(str);
+ // load msg into a0 so we can access it from the signal handler
+ // ExternalAddress enables saving and restoring via the code cache
+ la(c_rarg0, ExternalAddress((address) str));
illegal_instruction(Assembler::csr::time);
- emit_int64((uintptr_t)msg);
}
void MacroAssembler::unimplemented(const char* what) {
@@ -911,10 +916,8 @@ void MacroAssembler::emit_static_call_stub() {
void MacroAssembler::call_VM_leaf_base(address entry_point,
int number_of_arguments,
Label *retaddr) {
- int32_t offset = 0;
push_reg(RegSet::of(t1, xmethod), sp); // push << t1 & xmethod >> to sp
- movptr(t1, entry_point, offset, t0);
- jalr(t1, offset);
+ rt_call(entry_point, t1, t0);
if (retaddr != nullptr) {
bind(*retaddr);
}
@@ -1164,16 +1167,17 @@ void MacroAssembler::jalr(Register Rs, int32_t offset) {
Assembler::jalr(x1, Rs, offset);
}
-void MacroAssembler::rt_call(address dest, Register tmp) {
- assert(tmp != x5, "tmp register must not be x5.");
+void MacroAssembler::rt_call(address dest, Register tmp1, Register tmp2) {
+ assert_different_registers(tmp1, x5);
+ assert_different_registers(tmp1, tmp2);
RuntimeAddress target(dest);
if (CodeCache::contains(dest)) {
- far_call(target, tmp);
+ far_call(target, tmp1);
} else {
relocate(target.rspec(), [&] {
int32_t offset;
- movptr(tmp, target.target(), offset);
- jalr(tmp, offset);
+ movptr(tmp1, target.target(), offset, tmp2);
+ jalr(tmp1, offset);
});
}
}
@@ -3054,7 +3058,7 @@ int MacroAssembler::patch_oop(address insn_addr, address o) {
void MacroAssembler::reinit_heapbase() {
if (UseCompressedOops) {
- if (Universe::is_fully_initialized()) {
+ if (Universe::is_fully_initialized() && !AOTCodeCache::is_on_for_dump()) {
mv(xheapbase, CompressedOops::base());
} else {
ld(xheapbase, ExternalAddress(CompressedOops::base_addr()));
@@ -3925,19 +3929,28 @@ void MacroAssembler::decode_klass_not_null(Register dst, Register src, Register
assert_different_registers(dst, tmp);
assert_different_registers(src, tmp);
- if (CompressedKlassPointers::base() == nullptr) {
+ Register xbase = tmp;
+
+ if (AOTCodeCache::is_on_for_dump()) {
+ // We are generating code during AOT buildup that will run in *future* processes
+ // with likely different encoding settings. Therefore, we have to load the
+ // encoding base dynamically, we cannot just bake it in as immediate.
+ // Note that we only need to do this for base. The encoding shift would be the
+ // same between build time and runtime: the standard precomputed shift.
+ assert(CompressedKlassPointers::shift() == ArchiveBuilder::precomputed_narrow_klass_shift(),
+ "unexpected compressed klass shift!");
+ ld(xbase, ExternalAddress(CompressedKlassPointers::base_addr()));
+ } else if (CompressedKlassPointers::base() == nullptr) {
if (CompressedKlassPointers::shift() != 0) {
slli(dst, src, CompressedKlassPointers::shift());
} else {
mv(dst, src);
}
return;
+ } else {
+ mv(xbase, (uintptr_t)CompressedKlassPointers::base());
}
- Register xbase = tmp;
-
- mv(xbase, (uintptr_t)CompressedKlassPointers::base());
-
if (CompressedKlassPointers::shift() != 0) {
// dst = (src << shift) + xbase
shadd(dst, src, xbase, dst /* temporary, dst != xbase */, CompressedKlassPointers::shift());
@@ -3952,6 +3965,28 @@ void MacroAssembler::encode_klass_not_null(Register r, Register tmp) {
}
void MacroAssembler::encode_klass_not_null(Register dst, Register src, Register tmp) {
+ Register xbase = dst;
+ if (dst == src) {
+ xbase = tmp;
+ }
+
+ if (AOTCodeCache::is_on_for_dump()) {
+ // We are generating code during AOT buildup that will run in *future* processes
+ // with likely different encoding settings. Therefore, we have to load the
+ // encoding base dynamically and must not take the base-value dependent zext
+ // short cut below. Note that we only need to do this for base; the encoding
+ // shift is the same at build and run time: the standard precomputed shift.
+ assert(CompressedKlassPointers::shift() == ArchiveBuilder::precomputed_narrow_klass_shift(),
+ "unexpected compressed klass shift!");
+ assert_different_registers(src, xbase);
+ ld(xbase, ExternalAddress(CompressedKlassPointers::base_addr()));
+ sub(dst, src, xbase);
+ if (CompressedKlassPointers::shift() != 0) {
+ srli(dst, dst, CompressedKlassPointers::shift());
+ }
+ return;
+ }
+
if (CompressedKlassPointers::base() == nullptr) {
if (CompressedKlassPointers::shift() != 0) {
srli(dst, src, CompressedKlassPointers::shift());
@@ -3967,11 +4002,6 @@ void MacroAssembler::encode_klass_not_null(Register dst, Register src, Register
return;
}
- Register xbase = dst;
- if (dst == src) {
- xbase = tmp;
- }
-
assert_different_registers(src, xbase);
mv(xbase, (uintptr_t)CompressedKlassPointers::base());
sub(dst, src, xbase);
@@ -5353,8 +5383,7 @@ void MacroAssembler::get_thread(Register thread) {
RegSet::range(x28, x31) + ra - thread;
push_reg(saved_regs, sp);
- mv(t1, CAST_FROM_FN_PTR(address, Thread::current));
- jalr(t1);
+ rt_call(CAST_FROM_FN_PTR(address, Thread::current), t1, t0);
if (thread != c_rarg0) {
mv(thread, c_rarg0);
}
@@ -5364,12 +5393,33 @@ void MacroAssembler::get_thread(Register thread) {
}
void MacroAssembler::load_byte_map_base(Register reg) {
+#if INCLUDE_CDS
+ if (AOTCodeCache::is_on_for_dump()) {
+ address byte_map_base_adr = AOTRuntimeConstants::card_table_base_address();
+ ld(reg, ExternalAddress(byte_map_base_adr));
+ return;
+ }
+#endif
CardTableBarrierSet* ctbs = CardTableBarrierSet::barrier_set();
// Strictly speaking the card table base isn't an address at all, and it might
// even be negative. It is thus materialised as a constant.
mv(reg, (uint64_t)ctbs->card_table_base_const());
}
+void MacroAssembler::load_aotrc_address(Register reg, address a) {
+#if INCLUDE_CDS
+ assert(AOTRuntimeConstants::contains(a), "address out of range for data area");
+ if (AOTCodeCache::is_on_for_dump()) {
+ // all aotrc field addresses should be registered in the AOTCodeCache address table
+ la(reg, ExternalAddress(a));
+ } else {
+ mv(reg, (intptr_t)a);
+ }
+#else
+ ShouldNotReachHere();
+#endif
+}
+
void MacroAssembler::build_frame(int framesize) {
assert(framesize >= 2, "framesize must include space for FP/RA");
assert(framesize % (2*wordSize) == 0, "must preserve 2*wordSize alignment");
diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
index b7d493fe8908..dffb85455f0b 100644
--- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
+++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp
@@ -28,6 +28,7 @@
#define CPU_RISCV_MACROASSEMBLER_RISCV_HPP
#include "asm/assembler.inline.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/vmreg.hpp"
#include "metaprogramming/enableIf.hpp"
#include "oops/compressedOops.hpp"
@@ -770,7 +771,7 @@ class MacroAssembler: public Assembler {
// is used to keep the entry address for jalr/movptr.
// Uses call() for intra code cache, else movptr + jalr.
// Clobebrs t1
- void rt_call(address dest, Register tmp = t1);
+ void rt_call(address dest, Register tmp1 = t1, Register tmp2 = noreg);
// ret: jalr x0, 0(x1)
inline void ret() {
@@ -1291,6 +1292,9 @@ class MacroAssembler: public Assembler {
void load_byte_map_base(Register reg);
+ // Load a constant address in the AOT Runtime Constants area
+ void load_aotrc_address(Register reg, address a);
+
void bang_stack_with_offset(int offset) {
// stack grows down, caller passes positive offset
assert(offset > 0, "must bang with negative offset");
diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad
index 3be8416d8b48..6f9508865115 100644
--- a/src/hotspot/cpu/riscv/riscv.ad
+++ b/src/hotspot/cpu/riscv/riscv.ad
@@ -2421,10 +2421,7 @@ encode %{
// Make the anchor frame walkable
__ la(t0, retaddr);
__ sd(t0, Address(xthread, JavaThread::last_Java_pc_offset()));
- int32_t offset = 0;
- // No relocation needed
- __ movptr(t1, entry, offset, t0); // lui + lui + slli + add
- __ jalr(t1, offset);
+ __ rt_call(entry, t1, t0);
__ bind(retaddr);
__ post_call_nop();
}
@@ -2821,6 +2818,18 @@ operand immP_1()
interface(CONST_INTER);
%}
+// AOT Runtime Constants Address
+operand immAOTRuntimeConstantsAddress()
+%{
+ // Check if the address is in the range of AOT Runtime Constants
+ predicate(AOTRuntimeConstants::contains((address)(n->get_ptr())));
+ match(ConP);
+
+ op_cost(0);
+ format %{ %}
+ interface(CONST_INTER);
+%}
+
// Int Immediate: low 16-bit mask
operand immI_16bits()
%{
@@ -4782,6 +4791,20 @@ instruct loadConP1(iRegPNoSp dst, immP_1 con)
ins_pipe(ialu_imm);
%}
+instruct loadAOTRCAddress(iRegPNoSp dst, immAOTRuntimeConstantsAddress con)
+%{
+ match(Set dst con);
+
+ ins_cost(ALU_COST);
+ format %{ "la $dst, $con\t# aotrc, #@loadAOTRCAddress" %}
+
+ ins_encode %{
+ __ load_aotrc_address($dst$$Register, (address)$con$$constant);
+ %}
+
+ ins_pipe(ialu_imm);
+%}
+
// Load Narrow Pointer Constant
instruct loadConN(iRegNNoSp dst, immN con)
%{
diff --git a/src/hotspot/cpu/riscv/runtime_riscv.cpp b/src/hotspot/cpu/riscv/runtime_riscv.cpp
index c52d5a31066a..5a1fdbe773a1 100644
--- a/src/hotspot/cpu/riscv/runtime_riscv.cpp
+++ b/src/hotspot/cpu/riscv/runtime_riscv.cpp
@@ -26,6 +26,7 @@
#ifdef COMPILER2
#include "asm/macroAssembler.hpp"
#include "asm/macroAssembler.inline.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/vmreg.hpp"
#include "interpreter/interpreter.hpp"
#include "opto/runtime.hpp"
@@ -58,10 +59,15 @@ class SimpleRuntimeFrame {
//------------------------------generate_uncommon_trap_blob--------------------
UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() {
+ const char* name = OptoRuntime::stub_name(StubId::c2_uncommon_trap_id);
+ CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::C2Blob, BlobId::c2_uncommon_trap_id);
+ if (blob != nullptr) {
+ return blob->as_uncommon_trap_blob();
+ }
+
// Allocate space for the code
ResourceMark rm;
// Setup code generation tools
- const char* name = OptoRuntime::stub_name(StubId::c2_uncommon_trap_id);
CodeBuffer buffer(name, 2048, 1024);
if (buffer.blob() == nullptr) {
return nullptr;
@@ -243,8 +249,10 @@ UncommonTrapBlob* OptoRuntime::generate_uncommon_trap_blob() {
// Make sure all code is generated
masm->flush();
- return UncommonTrapBlob::create(&buffer, oop_maps,
- SimpleRuntimeFrame::framesize >> 1);
+ UncommonTrapBlob* ut_blob = UncommonTrapBlob::create(&buffer, oop_maps,
+ SimpleRuntimeFrame::framesize >> 1);
+ AOTCodeCache::store_code_blob(*ut_blob, AOTCodeEntry::C2Blob, BlobId::c2_uncommon_trap_id);
+ return ut_blob;
}
//------------------------------generate_exception_blob---------------------------
@@ -278,10 +286,15 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() {
assert(SimpleRuntimeFrame::framesize % 4 == 0, "sp not 16-byte aligned");
+ const char* name = OptoRuntime::stub_name(StubId::c2_exception_id);
+ CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::C2Blob, BlobId::c2_exception_id);
+ if (blob != nullptr) {
+ return blob->as_exception_blob();
+ }
+
// Allocate space for the code
ResourceMark rm;
// Setup code generation tools
- const char* name = OptoRuntime::stub_name(StubId::c2_exception_id);
CodeBuffer buffer(name, 2048, 1024);
if (buffer.blob() == nullptr) {
return nullptr;
@@ -380,6 +393,8 @@ ExceptionBlob* OptoRuntime::generate_exception_blob() {
masm->flush();
// Set exception blob
- return ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1);
+ ExceptionBlob* ex_blob = ExceptionBlob::create(&buffer, oop_maps, SimpleRuntimeFrame::framesize >> 1);
+ AOTCodeCache::store_code_blob(*ex_blob, AOTCodeEntry::C2Blob, BlobId::c2_exception_id);
+ return ex_blob;
}
#endif // COMPILER2
diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
index eee5184dfcac..ec0a5f5d9b3a 100644
--- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
+++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp
@@ -27,6 +27,7 @@
#include "asm/macroAssembler.hpp"
#include "asm/macroAssembler.inline.hpp"
#include "classfile/symbolTable.hpp"
+#include "code/aotCodeCache.hpp"
#include "code/compiledIC.hpp"
#include "code/debugInfoRec.hpp"
#include "code/vtableStubs.hpp"
@@ -2123,6 +2124,12 @@ void SharedRuntime::generate_deopt_blob() {
// Setup code generation tools
int pad = 0;
const char* name = SharedRuntime::stub_name(StubId::shared_deopt_id);
+ CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, BlobId::shared_deopt_id);
+ if (blob != nullptr) {
+ _deopt_blob = blob->as_deoptimization_blob();
+ return;
+ }
+
CodeBuffer buffer(name, 2048 + pad, 1024);
MacroAssembler* masm = new MacroAssembler(&buffer);
int frame_size_in_words = -1;
@@ -2427,6 +2434,8 @@ void SharedRuntime::generate_deopt_blob() {
_deopt_blob = DeoptimizationBlob::create(&buffer, oop_maps, 0, exception_offset, reexecute_offset, frame_size_in_words);
assert(_deopt_blob != nullptr, "create deoptimization blob fail!");
_deopt_blob->set_unpack_with_exception_in_tls_offset(exception_in_tls_offset);
+
+ AOTCodeCache::store_code_blob(*_deopt_blob, AOTCodeEntry::SharedBlob, BlobId::shared_deopt_id);
}
// Number of stack slots between incoming argument block and the start of
@@ -2453,13 +2462,18 @@ VMReg SharedRuntime::thread_register() {
SafepointBlob* SharedRuntime::generate_handler_blob(StubId id, address call_ptr) {
assert(is_polling_page_id(id), "expected a polling page stub id");
+ const char* name = SharedRuntime::stub_name(id);
+ CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, StubInfo::blob(id));
+ if (blob != nullptr) {
+ return blob->as_safepoint_blob();
+ }
+
ResourceMark rm;
OopMapSet *oop_maps = new OopMapSet();
assert_cond(oop_maps != nullptr);
OopMap* map = nullptr;
// Allocate space for the code. Setup code generation tools.
- const char* name = SharedRuntime::stub_name(id);
CodeBuffer buffer(name, 2048, 1024);
MacroAssembler* masm = new MacroAssembler(&buffer);
assert_cond(masm != nullptr);
@@ -2564,7 +2578,10 @@ SafepointBlob* SharedRuntime::generate_handler_blob(StubId id, address call_ptr)
masm->flush();
// Fill-out other meta info
- return SafepointBlob::create(&buffer, oop_maps, frame_size_in_words);
+ SafepointBlob* sp_blob = SafepointBlob::create(&buffer, oop_maps, frame_size_in_words);
+
+ AOTCodeCache::store_code_blob(*sp_blob, AOTCodeEntry::SharedBlob, StubInfo::blob(id));
+ return sp_blob;
}
//
@@ -2579,10 +2596,15 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(StubId id, address destination
assert(StubRoutines::forward_exception_entry() != nullptr, "must be generated before");
assert(is_resolve_id(id), "expected a resolve stub id");
+ const char* name = SharedRuntime::stub_name(id);
+ CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, StubInfo::blob(id));
+ if (blob != nullptr) {
+ return blob->as_runtime_stub();
+ }
+
// allocate space for the code
ResourceMark rm;
- const char* name = SharedRuntime::stub_name(id);
CodeBuffer buffer(name, 1000, 512);
MacroAssembler* masm = new MacroAssembler(&buffer);
assert_cond(masm != nullptr);
@@ -2653,7 +2675,10 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(StubId id, address destination
masm->flush();
// return the blob
- return RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true);
+ RuntimeStub* rs_blob = RuntimeStub::new_runtime_stub(name, &buffer, frame_complete, frame_size_in_words, oop_maps, true);
+
+ AOTCodeCache::store_code_blob(*rs_blob, AOTCodeEntry::SharedBlob, StubInfo::blob(id));
+ return rs_blob;
}
// Continuation point for throwing of implicit exceptions that are
@@ -2698,6 +2723,11 @@ RuntimeStub* SharedRuntime::generate_throw_exception(StubId id, address runtime_
const char* timer_msg = "SharedRuntime generate_throw_exception";
TraceTime timer(timer_msg, TRACETIME_LOG(Info, startuptime));
+ CodeBlob* blob = AOTCodeCache::load_code_blob(AOTCodeEntry::SharedBlob, StubInfo::blob(id));
+ if (blob != nullptr) {
+ return blob->as_runtime_stub();
+ }
+
CodeBuffer code(name, insts_size, locs_size);
OopMapSet* oop_maps = new OopMapSet();
MacroAssembler* masm = new MacroAssembler(&code);
@@ -2756,6 +2786,8 @@ RuntimeStub* SharedRuntime::generate_throw_exception(StubId id, address runtime_
(framesize >> (LogBytesPerWord - LogBytesPerInt)),
oop_maps, false);
assert(stub != nullptr, "create runtime stub fail!");
+
+ AOTCodeCache::store_code_blob(*stub, AOTCodeEntry::SharedBlob, StubInfo::blob(id));
return stub;
}
diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp
index 7f43a1dba690..260d31fc7cdc 100644
--- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp
+++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp
@@ -67,6 +67,109 @@
#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
+alignas(64) static const char _encodeBlock_toBase64[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+alignas(64) static const char _encodeBlock_toBase64URL[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
+};
+
+static const uint8_t _decodeBlock_fromBase64[256] = {
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u,
+ 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u,
+ 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u,
+ 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+};
+
+static const uint8_t _decodeBlock_fromBase64URL[256] = {
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u,
+ 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u,
+ 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 63u,
+ 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u,
+ 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+ 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
+};
+
+alignas(64) static const uint32_t _sha256_round_consts[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
+};
+
+alignas(64) static const uint64_t _sha512_round_consts[80] = {
+ 0x428a2f98d728ae22l, 0x7137449123ef65cdl, 0xb5c0fbcfec4d3b2fl,
+ 0xe9b5dba58189dbbcl, 0x3956c25bf348b538l, 0x59f111f1b605d019l,
+ 0x923f82a4af194f9bl, 0xab1c5ed5da6d8118l, 0xd807aa98a3030242l,
+ 0x12835b0145706fbel, 0x243185be4ee4b28cl, 0x550c7dc3d5ffb4e2l,
+ 0x72be5d74f27b896fl, 0x80deb1fe3b1696b1l, 0x9bdc06a725c71235l,
+ 0xc19bf174cf692694l, 0xe49b69c19ef14ad2l, 0xefbe4786384f25e3l,
+ 0x0fc19dc68b8cd5b5l, 0x240ca1cc77ac9c65l, 0x2de92c6f592b0275l,
+ 0x4a7484aa6ea6e483l, 0x5cb0a9dcbd41fbd4l, 0x76f988da831153b5l,
+ 0x983e5152ee66dfabl, 0xa831c66d2db43210l, 0xb00327c898fb213fl,
+ 0xbf597fc7beef0ee4l, 0xc6e00bf33da88fc2l, 0xd5a79147930aa725l,
+ 0x06ca6351e003826fl, 0x142929670a0e6e70l, 0x27b70a8546d22ffcl,
+ 0x2e1b21385c26c926l, 0x4d2c6dfc5ac42aedl, 0x53380d139d95b3dfl,
+ 0x650a73548baf63del, 0x766a0abb3c77b2a8l, 0x81c2c92e47edaee6l,
+ 0x92722c851482353bl, 0xa2bfe8a14cf10364l, 0xa81a664bbc423001l,
+ 0xc24b8b70d0f89791l, 0xc76c51a30654be30l, 0xd192e819d6ef5218l,
+ 0xd69906245565a910l, 0xf40e35855771202al, 0x106aa07032bbd1b8l,
+ 0x19a4c116b8d2d0c8l, 0x1e376c085141ab53l, 0x2748774cdf8eeb99l,
+ 0x34b0bcb5e19b48a8l, 0x391c0cb3c5c95a63l, 0x4ed8aa4ae3418acbl,
+ 0x5b9cca4f7763e373l, 0x682e6ff3d6b2b8a3l, 0x748f82ee5defb2fcl,
+ 0x78a5636f43172f60l, 0x84c87814a1f0ab72l, 0x8cc702081a6439ecl,
+ 0x90befffa23631e28l, 0xa4506cebde82bde9l, 0xbef9a3f7b2c67915l,
+ 0xc67178f2e372532bl, 0xca273eceea26619cl, 0xd186b8c721c0c207l,
+ 0xeada7dd6cde0eb1el, 0xf57d4f7fee6ed178l, 0x06f067aa72176fbal,
+ 0x0a637dc5a2c898a6l, 0x113f9804bef90dael, 0x1b710b35131c471bl,
+ 0x28db77f523047d84l, 0x32caab7b40c72493l, 0x3c9ebe0a15c9bebcl,
+ 0x431d67c49c100d4cl, 0x4cc5d4becb3e42b6l, 0x597f299cfc657e2al,
+ 0x5fcb6fab3ad6faecl, 0x6c44198c4a475817l
+};
+
// Stub Code definitions
class StubGenerator: public StubCodeGenerator {
@@ -208,8 +311,17 @@ class StubGenerator: public StubCodeGenerator {
"adjust this code");
StubId stub_id = StubId::stubgen_call_stub_id;
+ GrowableArray entries;
+ int entry_count = StubInfo::entry_count(stub_id);
+ assert(entry_count == 2, "sanity check");
+ address start = load_archive_data(stub_id, &entries);
+ if (start != nullptr) {
+ assert(entries.length() == 1, "expected 1 extra entry");
+ return_address = entries.at(0);
+ return start;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
const Address sp_after_call (fp, sp_after_call_off * wordSize);
@@ -357,6 +469,7 @@ class StubGenerator: public StubCodeGenerator {
// save current address for use by exception handling code
return_address = __ pc();
+ entries.append(return_address);
// store result depending on type (everything that is not
// T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT)
@@ -460,6 +573,9 @@ class StubGenerator: public StubCodeGenerator {
__ fsd(j_farg0, Address(j_rarg2, 0), t0);
__ j(exit);
+ // record the stub entry and end plus the auxiliary entry
+ store_archive_data(stub_id, start, __ pc(), &entries);
+
return start;
}
@@ -477,8 +593,14 @@ class StubGenerator: public StubCodeGenerator {
address generate_catch_exception() {
StubId stub_id = StubId::stubgen_catch_exception_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;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
// same as in generate_call_stub():
const Address thread(fp, thread_off * wordSize);
@@ -501,7 +623,9 @@ class StubGenerator: public StubCodeGenerator {
__ verify_oop(x10);
__ sd(x10, Address(xthread, Thread::pending_exception_offset()));
- __ mv(t0, (address)__FILE__);
+ // special case -- add file name string to AOT address table
+ address file = (address)AOTCodeCache::add_C_string(__FILE__);
+ __ la(t0, ExternalAddress(file));
__ sd(t0, Address(xthread, Thread::exception_file_offset()));
__ mv(t0, (int)__LINE__);
__ sw(t0, Address(xthread, Thread::exception_line_offset()));
@@ -511,6 +635,9 @@ class StubGenerator: public StubCodeGenerator {
"_call_stub_return_address must have been generated before");
__ j(RuntimeAddress(StubRoutines::_call_stub_return_address));
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -530,8 +657,14 @@ class StubGenerator: public StubCodeGenerator {
address generate_forward_exception() {
StubId stub_id = StubId::stubgen_forward_exception_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;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
// Upon entry, RA points to the return address returning into
// Java (interpreted or compiled) code; i.e., the return address
@@ -598,6 +731,9 @@ class StubGenerator: public StubCodeGenerator {
__ verify_oop(x10);
__ jr(x9);
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -617,8 +753,14 @@ class StubGenerator: public StubCodeGenerator {
address generate_verify_oop() {
StubId stub_id = StubId::stubgen_verify_oop_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;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
Label exit, error;
@@ -658,6 +800,9 @@ class StubGenerator: public StubCodeGenerator {
__ rt_call(CAST_FROM_FN_PTR(address, MacroAssembler::debug64));
__ ebreak();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -673,14 +818,20 @@ class StubGenerator: public StubCodeGenerator {
// x29 < MacroAssembler::zero_words_block_size.
address generate_zero_blocks() {
+ StubId stub_id = StubId::stubgen_zero_blocks_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);
Label done;
const Register base = x28, cnt = x29, tmp1 = x30, tmp2 = x31;
- __ align(CodeEntryAlignment);
- StubId stub_id = StubId::stubgen_zero_blocks_id;
- StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
if (UseBlockZeroing) {
int zicboz_block_size = VM_Version::zicboz_block_size.value();
@@ -711,6 +862,9 @@ class StubGenerator: public StubCodeGenerator {
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -733,6 +887,12 @@ class StubGenerator: public StubCodeGenerator {
// s and d are adjusted to point to the remaining words to copy
//
address generate_copy_longs(StubId stub_id, Register s, Register d, Register count) {
+ 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;
+ }
BasicType type;
copy_direction direction;
switch (stub_id) {
@@ -762,7 +922,7 @@ class StubGenerator: public StubCodeGenerator {
Label again, drain;
StubCodeMark mark(this, stub_id);
__ align(CodeEntryAlignment);
- address start = __ pc();
+ start = __ pc();
if (direction == copy_forwards) {
__ sub(s, s, bias);
@@ -879,6 +1039,9 @@ class StubGenerator: public StubCodeGenerator {
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -1196,18 +1359,43 @@ class StubGenerator: public StubCodeGenerator {
break;
}
+ // all stubs provide a 2nd entry which omits the frame push for
+ // use when bailing out from a conjoint copy. However we may also
+ // need some extra addressses for memory access protection.
+ int entry_count = StubInfo::entry_count(stub_id);
+ assert(entry_count == 2, "sanity check");
+ assert(nopush_entry != nullptr, "all disjoint copy stubs export a nopush entry");
+
+ bool add_extras = !is_oop && (!aligned || sizeof(jlong) == size);
+ int extra_count = ((add_extras ? 1 : 0) * UnsafeMemoryAccess::COLUMN_COUNT);
+ GrowableArray entries;
+ GrowableArray extras;
+ GrowableArray *extras_ptr = (extra_count > 0 ? &extras : nullptr);
+ address start = load_archive_data(stub_id, &entries, extras_ptr);
+ if (start != nullptr) {
+ assert(entries.length() == entry_count - 1,
+ "unexpected entries count %d", entries.length());
+ *nopush_entry = entries.at(0);
+ assert(extras.length() == extra_count,
+ "unexpected extra count %d", extras.length());
+ if (add_extras) {
+ // register one handler at offset 0
+ register_unsafe_access_handlers(extras, 0, 1);
+ }
+ return start;
+ }
+
const Register s = c_rarg0, d = c_rarg1, count = c_rarg2;
RegSet saved_reg = RegSet::of(s, d, count);
__ align(CodeEntryAlignment);
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
__ enter();
- if (nopush_entry != nullptr) {
- *nopush_entry = __ pc();
- // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
- BLOCK_COMMENT("Entry:");
- }
+ *nopush_entry = __ pc();
+ entries.append(*nopush_entry);
+ // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
+ BLOCK_COMMENT("Entry:");
DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT;
if (dest_uninitialized) {
@@ -1227,8 +1415,7 @@ class StubGenerator: public StubCodeGenerator {
{
// UnsafeMemoryAccess page error: continue after unsafe access
- bool add_entry = !is_oop && (!aligned || sizeof(jlong) == size);
- UnsafeMemoryAccessMark umam(this, add_entry, true);
+ UnsafeMemoryAccessMark umam(this, add_extras, true);
copy_memory(decorators, is_oop ? T_OBJECT : T_BYTE, aligned, s, d, count, size);
}
@@ -1244,6 +1431,20 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ mv(x10, zr); // return 0
__ ret();
+
+ address end = __ pc();
+
+ if (add_extras) {
+ // retrieve the registered handler addresses
+ retrieve_unsafe_access_handlers(start, end, extras);
+ assert(extras.length() == extra_count,
+ "incorrect handlers count %d", extras.length());
+ }
+
+ // record the stub entry and end plus the no_push entry and any
+ // extra handler addresses
+ store_archive_data(stub_id, start, end, &entries, extras_ptr);
+
return start;
}
@@ -1272,8 +1473,6 @@ class StubGenerator: public StubCodeGenerator {
// used by some other conjoint copy method
//
address generate_conjoint_copy(StubId stub_id, address nooverlap_target, address *nopush_entry) {
- const Register s = c_rarg0, d = c_rarg1, count = c_rarg2;
- RegSet saved_regs = RegSet::of(s, d, count);
int size;
bool aligned;
bool is_oop;
@@ -1354,12 +1553,45 @@ class StubGenerator: public StubCodeGenerator {
ShouldNotReachHere();
}
+ // only some conjoint stubs generate a 2nd entry
+ int entry_count = StubInfo::entry_count(stub_id);
+ int expected_entry_count = (nopush_entry == nullptr ? 1 : 2);
+ assert(entry_count == expected_entry_count,
+ "expected entry count %d does not match declared entry count %d for stub %s",
+ expected_entry_count, entry_count, StubInfo::name(stub_id));
+
+ // We need to protect memory accesses in certain cases
+ bool add_extras = !is_oop && (!aligned || sizeof(jlong) == size);
+ int extra_count = ((add_extras ? 1 : 0) * UnsafeMemoryAccess::COLUMN_COUNT);
+ GrowableArray entries;
+ GrowableArray extras;
+ GrowableArray *entries_ptr = (nopush_entry != nullptr ? &entries : nullptr);
+ GrowableArray *extras_ptr = (extra_count > 0 ? &extras : nullptr);
+ address start = load_archive_data(stub_id, entries_ptr, extras_ptr);
+ if (start != nullptr) {
+ assert(entries.length() == expected_entry_count - 1,
+ "unexpected entries count %d", entries.length());
+ assert(extras.length() == extra_count,
+ "unexpected extra count %d", extras.length());
+ if (nopush_entry != nullptr) {
+ *nopush_entry = entries.at(0);
+ }
+ if (add_extras) {
+ // register one handler at offset 0
+ register_unsafe_access_handlers(extras, 0, 1);
+ }
+ return start;
+ }
+
+ const Register s = c_rarg0, d = c_rarg1, count = c_rarg2;
+ RegSet saved_regs = RegSet::of(s, d, count);
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
__ enter();
if (nopush_entry != nullptr) {
*nopush_entry = __ pc();
+ entries.append(*nopush_entry);
// caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
BLOCK_COMMENT("Entry:");
}
@@ -1390,8 +1622,7 @@ class StubGenerator: public StubCodeGenerator {
{
// UnsafeMemoryAccess page error: continue after unsafe access
- bool add_entry = !is_oop && (!aligned || sizeof(jlong) == size);
- UnsafeMemoryAccessMark umam(this, add_entry, true);
+ UnsafeMemoryAccessMark umam(this, add_extras, true);
copy_memory(decorators, is_oop ? T_OBJECT : T_BYTE, aligned, s, d, count, -size);
}
@@ -1405,6 +1636,20 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ mv(x10, zr); // return 0
__ ret();
+
+ address end = __ pc();
+
+ if (add_extras) {
+ // retrieve the registered handler addresses
+ retrieve_unsafe_access_handlers(start, end, extras);
+ assert(extras.length() == extra_count,
+ "incorrect handlers count %d", extras.length());
+ }
+
+ // record the stub entry and end plus any no_push entry and/or
+ // extra handler addresses
+ store_archive_data(stub_id, start, end, entries_ptr, extras_ptr);
+
return start;
}
@@ -1457,6 +1702,27 @@ class StubGenerator: public StubCodeGenerator {
ShouldNotReachHere();
}
+ // The normal stub provides a 2nd entry which omits the frame push
+ // for use when bailing out from a disjoint copy.
+ // Only some conjoint stubs generate a 2nd entry
+ int entry_count = StubInfo::entry_count(stub_id);
+ int expected_entry_count = (nopush_entry == nullptr ? 1 : 2);
+ GrowableArray entries;
+ GrowableArray *entries_ptr = (expected_entry_count == 1 ? nullptr : &entries);
+ assert(entry_count == expected_entry_count,
+ "expected entry count %d does not match declared entry count %d for stub %s",
+ expected_entry_count, entry_count, StubInfo::name(stub_id));
+ address start = load_archive_data(stub_id, entries_ptr);
+ if (start != nullptr) {
+ assert(entries.length() + 1 == expected_entry_count,
+ "expected entry count %d does not match return entry count %d for stub %s",
+ expected_entry_count, entries.length() + 1, StubInfo::name(stub_id));
+ if (nopush_entry != nullptr) {
+ *nopush_entry = entries.at(0);
+ }
+ return start;
+ }
+
Label L_load_element, L_store_element, L_do_card_marks, L_done, L_done_pop;
// Input registers (after setup_arg_regs)
@@ -1489,13 +1755,14 @@ class StubGenerator: public StubCodeGenerator {
__ align(CodeEntryAlignment);
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
__ enter(); // required for proper stackwalking of RuntimeStub frame
// Caller of this entry point must set up the argument registers.
if (nopush_entry != nullptr) {
*nopush_entry = __ pc();
+ entries.append(*nopush_entry);
BLOCK_COMMENT("Entry:");
}
@@ -1598,6 +1865,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end plus any no_push entry
+ store_archive_data(stub_id, start, __ pc(), entries_ptr);
+
return start;
}
@@ -1633,10 +1903,23 @@ class StubGenerator: public StubCodeGenerator {
}
address generate_unsafecopy_common_error_exit() {
- address start = __ pc();
+ StubId stub_id = StubId::stubgen_unsafecopy_common_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();
__ mv(x10, 0);
__ leave();
__ ret();
+
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -1651,10 +1934,22 @@ class StubGenerator: public StubCodeGenerator {
// c_rarg2 - byte value
//
address generate_unsafe_setmemory() {
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_unsafe_setmemory_id;
+ int entry_count = StubInfo::entry_count(stub_id);
+ assert(entry_count == 1, "sanity check");
+ // we expect one set of extra unsafememory access handler entries
+ GrowableArray extras;
+ int extra_count = 1 * UnsafeMemoryAccess::COLUMN_COUNT;
+ address start = load_archive_data(stub_id, nullptr, &extras);
+ if (start != nullptr) {
+ assert(extras.length() == extra_count,
+ "unexpected extra entry count %d", extras.length());
+ register_unsafe_access_handlers(extras, 0, 1);
+ return start;
+ }
+ __ align(CodeEntryAlignment);
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
// bump this on entry, not on exit:
// inc_counter_np(SharedRuntime::_unsafe_set_memory_ctr);
@@ -1668,6 +1963,7 @@ class StubGenerator: public StubCodeGenerator {
const Register tmp_reg = x29; // temp register
// Mark remaining code as such which performs Unsafe accesses.
+ {
UnsafeMemoryAccessMark umam(this, true, false);
__ enter(); // required for proper stackwalking of RuntimeStub frame
@@ -1748,6 +2044,17 @@ class StubGenerator: public StubCodeGenerator {
__ bind(L_exit);
__ leave();
__ ret();
+ // have to exit the block and destroy the UnsafeMemoryAccessMark
+ // in order to retrieve the handler end address
+ }
+
+ // install saved handler addresses in extras
+ address end = __ pc();
+ retrieve_unsafe_access_handlers(start, end, extras);
+ assert(extras.length() == extra_count,
+ "incorrect handlers count %d", extras.length());
+ // record the stub entry and end plus the extras
+ store_archive_data(stub_id, start, end, nullptr, &extras);
return start;
}
@@ -1771,13 +2078,19 @@ class StubGenerator: public StubCodeGenerator {
address long_copy_entry) {
assert_cond(byte_copy_entry != nullptr && short_copy_entry != nullptr &&
int_copy_entry != nullptr && long_copy_entry != nullptr);
+ StubId stub_id = StubId::stubgen_unsafe_arraycopy_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;
+ }
Label L_long_aligned, L_int_aligned, L_short_aligned;
const Register s = c_rarg0, d = c_rarg1, count = c_rarg2;
__ align(CodeEntryAlignment);
- StubId stub_id = StubId::stubgen_unsafe_arraycopy_id;
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
__ enter(); // required for proper stackwalking of RuntimeStub frame
// bump this on entry, not on exit:
@@ -1804,6 +2117,9 @@ class StubGenerator: public StubCodeGenerator {
__ srli(count, count, LogBytesPerLong); // size => long_count
__ j(RuntimeAddress(long_copy_entry));
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -1827,6 +2143,13 @@ class StubGenerator: public StubCodeGenerator {
assert_cond(byte_copy_entry != nullptr && short_copy_entry != nullptr &&
int_copy_entry != nullptr && oop_copy_entry != nullptr &&
long_copy_entry != nullptr && checkcast_copy_entry != nullptr);
+ StubId stub_id = StubId::stubgen_generic_arraycopy_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;
+ }
Label L_failed, L_failed_0, L_objArray;
Label L_copy_bytes, L_copy_shorts, L_copy_ints, L_copy_longs;
@@ -1842,10 +2165,9 @@ class StubGenerator: public StubCodeGenerator {
__ align(CodeEntryAlignment);
- StubId stub_id = StubId::stubgen_generic_arraycopy_id;
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
__ enter(); // required for proper stackwalking of RuntimeStub frame
@@ -2095,6 +2417,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave(); // required for proper stackwalking of RuntimeStub frame
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -2140,9 +2465,15 @@ class StubGenerator: public StubCodeGenerator {
ShouldNotReachHere();
};
+ 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);
- address start = __ pc();
+ start = __ pc();
BLOCK_COMMENT("Entry:");
@@ -2296,6 +2627,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -2476,8 +2810,14 @@ class StubGenerator: public StubCodeGenerator {
address generate_aescrypt_encryptBlock() {
assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support");
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_aescrypt_encryptBlock_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);
Label L_aes128, L_aes192;
@@ -2493,7 +2833,7 @@ class StubGenerator: public StubCodeGenerator {
};
const VectorRegister res = v19;
- address start = __ pc();
+ start = __ pc();
__ enter();
__ lwu(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)));
@@ -2532,6 +2872,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -2555,8 +2898,14 @@ class StubGenerator: public StubCodeGenerator {
address generate_aescrypt_decryptBlock() {
assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support");
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_aescrypt_decryptBlock_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);
Label L_aes128, L_aes192;
@@ -2572,7 +2921,7 @@ class StubGenerator: public StubCodeGenerator {
};
const VectorRegister res = v19;
- address start = __ pc();
+ start = __ pc();
__ enter(); // required for proper stackwalking of RuntimeStub frame
__ lwu(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)));
@@ -2611,6 +2960,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -2664,8 +3016,14 @@ class StubGenerator: public StubCodeGenerator {
//
address generate_cipherBlockChaining_encryptAESCrypt() {
assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support");
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_cipherBlockChaining_encryptAESCrypt_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);
const Register from = c_rarg0;
@@ -2676,7 +3034,7 @@ class StubGenerator: public StubCodeGenerator {
const Register keylen = x28;
- address start = __ pc();
+ start = __ pc();
__ enter();
Label L_aes128, L_aes192;
@@ -2698,6 +3056,9 @@ class StubGenerator: public StubCodeGenerator {
__ bind(L_aes192);
cipherBlockChaining_encryptAESCrypt(13, from, to, key, rvec, input_len);
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -2753,8 +3114,14 @@ class StubGenerator: public StubCodeGenerator {
//
address generate_cipherBlockChaining_decryptAESCrypt() {
assert(UseAESIntrinsics, "need AES instructions (Zvkned extension) support");
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_cipherBlockChaining_decryptAESCrypt_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);
const Register from = c_rarg0;
@@ -2765,7 +3132,7 @@ class StubGenerator: public StubCodeGenerator {
const Register keylen = x28;
- address start = __ pc();
+ start = __ pc();
__ enter();
Label L_aes128, L_aes192, L_aes128_loop, L_aes192_loop, L_aes256_loop;
@@ -2787,6 +3154,9 @@ class StubGenerator: public StubCodeGenerator {
__ bind(L_aes192);
cipherBlockChaining_decryptAESCrypt(13, from, to, key, rvec, input_len);
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -2958,8 +3328,14 @@ class StubGenerator: public StubCodeGenerator {
address generate_counterMode_AESCrypt() {
assert(UseAESCTRIntrinsics, "need AES instructions (Zvkned extension) and Zbb extension support");
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_counterMode_AESCrypt_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);
const Register in = c_rarg0;
@@ -2972,7 +3348,7 @@ class StubGenerator: public StubCodeGenerator {
const Register keylen = c_rarg7; // temporary register
- const address start = __ pc();
+ start = __ pc();
__ enter();
Label L_exit;
@@ -3002,6 +3378,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -3048,11 +3427,17 @@ class StubGenerator: public StubCodeGenerator {
address generate_ghash_processBlocks() {
assert(UseGHASHIntrinsics, "need GHASH instructions (Zvkg extension) and Zvbb support");
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_ghash_processBlocks_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);
- address start = __ pc();
+ start = __ pc();
__ enter();
Register state = c_rarg0;
@@ -3069,6 +3454,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -3133,8 +3521,14 @@ class StubGenerator: public StubCodeGenerator {
assert(UseGHASHIntrinsics, "need GHASH instructions (Zvkg extension) and Zvbb support");
assert(UseAESCTRIntrinsics, "need AES instructions (Zvkned extension) and Zbb extension support");
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_galoisCounterMode_AESCrypt_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);
const Register in = c_rarg0;
@@ -3158,7 +3552,7 @@ class StubGenerator: public StubCodeGenerator {
VectorRegister vtmp2 = v17;
VectorRegister vtmp3 = v18;
- const address start = __ pc();
+ start = __ pc();
__ enter();
Label L_exit;
@@ -3193,6 +3587,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -3251,6 +3648,12 @@ class StubGenerator: public StubCodeGenerator {
default:
ShouldNotReachHere();
};
+ 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);
address entry = __ pc();
@@ -3335,17 +3738,27 @@ class StubGenerator: public StubCodeGenerator {
__ sub(result, tmp1, tmp2);
__ bind(DONE);
__ ret();
+
+ // record the stub entry and end
+ store_archive_data(stub_id, entry, __ pc());
+
return entry;
}
address generate_method_entry_barrier() {
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_method_entry_barrier_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);
Label deoptimize_label;
- address start = __ pc();
+ start = __ pc();
BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
@@ -3399,6 +3812,9 @@ class StubGenerator: public StubCodeGenerator {
__ mv(sp, t0);
__ jr(t1);
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -3423,6 +3839,12 @@ class StubGenerator: public StubCodeGenerator {
default:
ShouldNotReachHere();
};
+ 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);
address entry = __ pc();
@@ -3519,6 +3941,10 @@ class StubGenerator: public StubCodeGenerator {
__ bind(LENGTH_DIFF);
__ pop_reg(spilled_regs, sp);
__ ret();
+
+ // record the stub entry and end
+ store_archive_data(stub_id, entry, __ pc());
+
return entry;
}
@@ -3555,6 +3981,12 @@ class StubGenerator: public StubCodeGenerator {
ShouldNotReachHere();
};
+ 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);
address entry = __ pc();
@@ -3778,6 +4210,10 @@ class StubGenerator: public StubCodeGenerator {
__ bind(DONE);
__ pop_reg(spilled_regs, sp);
__ ret();
+
+ // record the stub entry and end
+ store_archive_data(stub_id, entry, __ pc());
+
return entry;
}
@@ -3791,6 +4227,20 @@ class StubGenerator: public StubCodeGenerator {
#ifdef COMPILER2
void generate_lookup_secondary_supers_table_stub() {
StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_id;
+ GrowableArray entries;
+ int entry_count = StubInfo::entry_count(stub_id);
+ assert(entry_count == Klass::SECONDARY_SUPERS_TABLE_SIZE, "sanity check");
+ address start = load_archive_data(stub_id, &entries);
+ if (start != nullptr) {
+ assert(entries.length() == Klass::SECONDARY_SUPERS_TABLE_SIZE - 1,
+ "unexpected extra entry count %d", entries.length());
+ StubRoutines::_lookup_secondary_supers_table_stubs[0] = start;
+ for (int slot = 1; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) {
+ StubRoutines::_lookup_secondary_supers_table_stubs[slot] = entries.at(slot - 1);
+ }
+ return;
+ }
+
StubCodeMark mark(this, stub_id);
const Register
@@ -3803,7 +4253,13 @@ class StubGenerator: public StubCodeGenerator {
r_bitmap = x16;
for (int slot = 0; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) {
- StubRoutines::_lookup_secondary_supers_table_stubs[slot] = __ pc();
+ address next_entry = __ pc();
+ StubRoutines::_lookup_secondary_supers_table_stubs[slot] = next_entry;
+ if (slot == 0) {
+ start = next_entry;
+ } else {
+ entries.append(next_entry);
+ }
Label L_success;
__ enter();
__ lookup_secondary_supers_table_const(r_sub_klass, r_super_klass, result,
@@ -3812,14 +4268,22 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
}
+ // record the stub entry and end plus all the auxiliary entries
+ store_archive_data(stub_id, start, __ pc(), &entries);
}
// Slow path implementation for UseSecondarySupersTable.
address generate_lookup_secondary_supers_table_slow_path_stub() {
StubId stub_id = StubId::stubgen_lookup_secondary_supers_table_slow_path_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;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
const Register
r_super_klass = x10, // argument
r_array_base = x11, // argument
@@ -3832,13 +4296,22 @@ class StubGenerator: public StubCodeGenerator {
__ lookup_secondary_supers_table_slow_path(r_super_klass, r_array_base, r_array_index, r_bitmap, result, temp1);
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
address generate_mulAdd()
{
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_mulAdd_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);
address entry = __ pc();
@@ -3856,6 +4329,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, entry, __ pc());
+
return entry;
}
@@ -3871,8 +4347,14 @@ class StubGenerator: public StubCodeGenerator {
*/
address generate_multiplyToLen()
{
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_multiplyToLen_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);
address entry = __ pc();
@@ -3897,13 +4379,22 @@ class StubGenerator: public StubCodeGenerator {
__ leave(); // required for proper stackwalking of RuntimeStub frame
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, entry, __ pc());
+
return entry;
}
address generate_squareToLen()
{
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_squareToLen_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);
address entry = __ pc();
@@ -3930,6 +4421,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, entry, __ pc());
+
return entry;
}
@@ -3943,8 +4437,14 @@ class StubGenerator: public StubCodeGenerator {
// c_rarg4 - numIter
//
address generate_bigIntegerLeftShift() {
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_bigIntegerLeftShiftWorker_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);
address entry = __ pc();
@@ -3982,6 +4482,9 @@ class StubGenerator: public StubCodeGenerator {
__ bind(exit);
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, entry, __ pc());
+
return entry;
}
@@ -3995,8 +4498,14 @@ class StubGenerator: public StubCodeGenerator {
// c_rarg4 - numIter
//
address generate_bigIntegerRightShift() {
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_bigIntegerRightShiftWorker_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);
address entry = __ pc();
@@ -4037,6 +4546,9 @@ class StubGenerator: public StubCodeGenerator {
__ bind(exit);
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, entry, __ pc());
+
return entry;
}
#endif
@@ -4824,9 +5336,19 @@ class StubGenerator: public StubCodeGenerator {
if (!Continuations::enabled()) return nullptr;
StubId stub_id = StubId::stubgen_cont_thaw_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;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
generate_cont_thaw(Continuation::thaw_top);
+
+ // record the stub start and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -4835,11 +5357,20 @@ class StubGenerator: public StubCodeGenerator {
// TODO: will probably need multiple return barriers depending on return type
StubId stub_id = StubId::stubgen_cont_returnBarrier_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;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
generate_cont_thaw(Continuation::thaw_return_barrier);
+ // record the stub start and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -4847,19 +5378,34 @@ class StubGenerator: public StubCodeGenerator {
if (!Continuations::enabled()) return nullptr;
StubId stub_id = StubId::stubgen_cont_returnBarrierExc_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;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
generate_cont_thaw(Continuation::thaw_return_barrier_exception);
+ // record the stub start and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
address generate_cont_preempt_stub() {
if (!Continuations::enabled()) return nullptr;
StubId stub_id = StubId::stubgen_cont_preempt_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;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
__ reset_last_Java_frame(true);
@@ -4883,6 +5429,9 @@ class StubGenerator: public StubCodeGenerator {
__ ld(t1, Address(t1));
__ jr(t1);
+ // record the stub start and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -5033,53 +5582,6 @@ class StubGenerator: public StubCodeGenerator {
// c_rarg3 - int limit
//
address generate_sha2_implCompress(Assembler::SEW vset_sew, StubId stub_id) {
- alignas(64) static const uint32_t round_consts_256[64] = {
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
- 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
- 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
- 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
- 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
- 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
- 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
- 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
- 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
- };
- alignas(64) static const uint64_t round_consts_512[80] = {
- 0x428a2f98d728ae22l, 0x7137449123ef65cdl, 0xb5c0fbcfec4d3b2fl,
- 0xe9b5dba58189dbbcl, 0x3956c25bf348b538l, 0x59f111f1b605d019l,
- 0x923f82a4af194f9bl, 0xab1c5ed5da6d8118l, 0xd807aa98a3030242l,
- 0x12835b0145706fbel, 0x243185be4ee4b28cl, 0x550c7dc3d5ffb4e2l,
- 0x72be5d74f27b896fl, 0x80deb1fe3b1696b1l, 0x9bdc06a725c71235l,
- 0xc19bf174cf692694l, 0xe49b69c19ef14ad2l, 0xefbe4786384f25e3l,
- 0x0fc19dc68b8cd5b5l, 0x240ca1cc77ac9c65l, 0x2de92c6f592b0275l,
- 0x4a7484aa6ea6e483l, 0x5cb0a9dcbd41fbd4l, 0x76f988da831153b5l,
- 0x983e5152ee66dfabl, 0xa831c66d2db43210l, 0xb00327c898fb213fl,
- 0xbf597fc7beef0ee4l, 0xc6e00bf33da88fc2l, 0xd5a79147930aa725l,
- 0x06ca6351e003826fl, 0x142929670a0e6e70l, 0x27b70a8546d22ffcl,
- 0x2e1b21385c26c926l, 0x4d2c6dfc5ac42aedl, 0x53380d139d95b3dfl,
- 0x650a73548baf63del, 0x766a0abb3c77b2a8l, 0x81c2c92e47edaee6l,
- 0x92722c851482353bl, 0xa2bfe8a14cf10364l, 0xa81a664bbc423001l,
- 0xc24b8b70d0f89791l, 0xc76c51a30654be30l, 0xd192e819d6ef5218l,
- 0xd69906245565a910l, 0xf40e35855771202al, 0x106aa07032bbd1b8l,
- 0x19a4c116b8d2d0c8l, 0x1e376c085141ab53l, 0x2748774cdf8eeb99l,
- 0x34b0bcb5e19b48a8l, 0x391c0cb3c5c95a63l, 0x4ed8aa4ae3418acbl,
- 0x5b9cca4f7763e373l, 0x682e6ff3d6b2b8a3l, 0x748f82ee5defb2fcl,
- 0x78a5636f43172f60l, 0x84c87814a1f0ab72l, 0x8cc702081a6439ecl,
- 0x90befffa23631e28l, 0xa4506cebde82bde9l, 0xbef9a3f7b2c67915l,
- 0xc67178f2e372532bl, 0xca273eceea26619cl, 0xd186b8c721c0c207l,
- 0xeada7dd6cde0eb1el, 0xf57d4f7fee6ed178l, 0x06f067aa72176fbal,
- 0x0a637dc5a2c898a6l, 0x113f9804bef90dael, 0x1b710b35131c471bl,
- 0x28db77f523047d84l, 0x32caab7b40c72493l, 0x3c9ebe0a15c9bebcl,
- 0x431d67c49c100d4cl, 0x4cc5d4becb3e42b6l, 0x597f299cfc657e2al,
- 0x5fcb6fab3ad6faecl, 0x6c44198c4a475817l
- };
const int const_add = vset_sew == Assembler::e32 ? 16 : 32;
bool multi_block;
@@ -5103,9 +5605,15 @@ class StubGenerator: public StubCodeGenerator {
default:
ShouldNotReachHere();
};
+ int entry_count = StubInfo::entry_count(stub_id);
+ assert(entry_count == 1, "sanity check");
+ address start = _cgen->load_archive_data(stub_id);
+ if (start != nullptr) {
+ return start;
+ }
__ align(CodeEntryAlignment);
StubCodeMark mark(_cgen, stub_id);
- address start = __ pc();
+ start = __ pc();
Register buf = c_rarg0;
Register state = c_rarg1;
@@ -5129,7 +5637,7 @@ class StubGenerator: public StubCodeGenerator {
__ enter();
- address constant_table = vset_sew == Assembler::e32 ? (address)round_consts_256 : (address)round_consts_512;
+ address constant_table = vset_sew == Assembler::e32 ? (address)_sha256_round_consts : (address)_sha512_round_consts;
la(consts, ExternalAddress(constant_table));
// Register use in this function:
@@ -5280,6 +5788,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ _cgen->store_archive_data(stub_id, start, __ pc());
+
return start;
}
};
@@ -5452,7 +5963,6 @@ class StubGenerator: public StubCodeGenerator {
// x30 t5 buf6
// x31 t6 buf7
address generate_md5_implCompress(StubId stub_id) {
- __ align(CodeEntryAlignment);
bool multi_block;
switch (stub_id) {
case StubId::stubgen_md5_implCompress_id:
@@ -5464,8 +5974,15 @@ class StubGenerator: public StubCodeGenerator {
default:
ShouldNotReachHere();
};
+ 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);
- address start = __ pc();
+ start = __ pc();
// rotation constants
const int S11 = 7;
@@ -5668,6 +6185,9 @@ class StubGenerator: public StubCodeGenerator {
__ pop_reg(saved_regs, sp);
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return (address) start;
}
@@ -5715,14 +6235,19 @@ class StubGenerator: public StubCodeGenerator {
* N depends on single vector register length.
*/
address generate_chacha20Block() {
- Label L_Rounds;
-
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_chacha20Block_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);
- address start = __ pc();
+ start = __ pc();
__ enter();
+ Label L_Rounds;
const int states_len = 16;
const int step = 4;
const Register state = c_rarg0;
@@ -5805,6 +6330,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return (address) start;
}
@@ -6030,10 +6558,16 @@ class StubGenerator: public StubCodeGenerator {
default:
ShouldNotReachHere();
};
+ 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);
- address start = __ pc();
+ start = __ pc();
__ enter();
RegSet saved_regs = RegSet::range(x18, x27);
@@ -6156,6 +6690,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return (address) start;
}
@@ -6224,26 +6761,16 @@ class StubGenerator: public StubCodeGenerator {
* c_rarg5 - isURL, Base64 or URL character set
*/
address generate_base64_encodeBlock() {
- alignas(64) static const char toBase64[64] = {
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
- 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
- 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
- };
-
- alignas(64) static const char toBase64URL[64] = {
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
- 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
- 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
- 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
- };
-
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_base64_encodeBlock_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);
- address start = __ pc();
+ start = __ pc();
__ enter();
Register src = c_rarg0;
@@ -6265,9 +6792,9 @@ class StubGenerator: public StubCodeGenerator {
__ add(dst, dst, doff);
// load the codec base address
- __ la(codec, ExternalAddress((address) toBase64));
+ __ la(codec, ExternalAddress((address) _encodeBlock_toBase64));
__ beqz(isURL, ProcessData);
- __ la(codec, ExternalAddress((address) toBase64URL));
+ __ la(codec, ExternalAddress((address) _encodeBlock_toBase64URL));
__ BIND(ProcessData);
// vector version
@@ -6376,6 +6903,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return (address) start;
}
@@ -6459,48 +6989,16 @@ class StubGenerator: public StubCodeGenerator {
*/
address generate_base64_decodeBlock() {
- static const uint8_t fromBase64[256] = {
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u, 255u, 63u,
- 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u,
- 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 255u,
- 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u,
- 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- };
-
- static const uint8_t fromBase64URL[256] = {
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 62u, 255u, 255u,
- 52u, 53u, 54u, 55u, 56u, 57u, 58u, 59u, 60u, 61u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u,
- 15u, 16u, 17u, 18u, 19u, 20u, 21u, 22u, 23u, 24u, 25u, 255u, 255u, 255u, 255u, 63u,
- 255u, 26u, 27u, 28u, 29u, 30u, 31u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 40u,
- 41u, 42u, 43u, 44u, 45u, 46u, 47u, 48u, 49u, 50u, 51u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u, 255u,
- };
-
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_base64_decodeBlock_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);
- address start = __ pc();
+ start = __ pc();
__ enter();
Register src = c_rarg0;
@@ -6530,9 +7028,9 @@ class StubGenerator: public StubCodeGenerator {
__ mv(dstBackup, dst);
// load the codec base address
- __ la(codec, ExternalAddress((address) fromBase64));
+ __ la(codec, ExternalAddress((address) _decodeBlock_fromBase64));
__ beqz(isURL, ProcessData);
- __ la(codec, ExternalAddress((address) fromBase64URL));
+ __ la(codec, ExternalAddress((address) _decodeBlock_fromBase64URL));
__ BIND(ProcessData);
// vector version
@@ -6654,6 +7152,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave();
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return (address) start;
}
@@ -6742,10 +7243,16 @@ class StubGenerator: public StubCodeGenerator {
* c_rarg0 - int adler result
*/
address generate_updateBytesAdler32() {
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_updateBytesAdler32_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);
- address start = __ pc();
+ start = __ pc();
Label L_nmax, L_nmax_loop, L_nmax_loop_entry, L_by16, L_by16_loop,
L_by16_loop_unroll, L_by1_loop, L_do_mod, L_combine, L_by1;
@@ -6911,6 +7418,9 @@ class StubGenerator: public StubCodeGenerator {
__ leave(); // Required for proper stackwalking of RuntimeStub frame
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -6920,8 +7430,14 @@ class StubGenerator: public StubCodeGenerator {
// f10 = result (float)
// t1 = temporary register
address generate_float16ToFloat() {
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_hf2f_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);
address entry = __ pc();
BLOCK_COMMENT("float16ToFloat:");
@@ -6963,6 +7479,10 @@ class StubGenerator: public StubCodeGenerator {
__ fmv_w_x(dst, t1);
__ ret();
+
+ // record the stub entry and end
+ store_archive_data(stub_id, entry, __ pc());
+
return entry;
}
@@ -6971,8 +7491,14 @@ class StubGenerator: public StubCodeGenerator {
// f11 = temporary float register
// t1 = temporary register
address generate_floatToFloat16() {
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_f2hf_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);
address entry = __ pc();
BLOCK_COMMENT("floatToFloat16:");
@@ -7001,6 +7527,10 @@ class StubGenerator: public StubCodeGenerator {
__ float_to_float16_NaN(dst, src, t0, t1);
__ ret();
+
+ // record the stub entry and end
+ store_archive_data(stub_id, entry, __ pc());
+
return entry;
}
@@ -7089,10 +7619,16 @@ static const int64_t right_3_bits = right_n_bits(3);
// computation.
address generate_poly1305_processBlocks() {
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_poly1305_processBlocks_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);
- address start = __ pc();
+ start = __ pc();
__ enter();
Label here;
@@ -7208,6 +7744,9 @@ static const int64_t right_3_bits = right_n_bits(3);
__ leave(); // Required for proper stackwalking
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -7215,9 +7754,16 @@ static const int64_t right_3_bits = right_n_bits(3);
assert(UseRVV, "sanity");
const int lmul = 2;
const int stride = MaxVectorSize / sizeof(jint) * lmul;
+ StubId stub_id = StubId::stubgen_arrays_hashcode_powers_of_31_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, "StubRoutines", "arrays_hashcode_powers_of_31");
- address start = __ pc();
+ StubCodeMark mark(this, stub_id);
+ start = __ pc();
for (int i = stride; i >= 0; i--) {
jint power_of_31 = 1;
for (int j = i; j > 0; j--) {
@@ -7226,6 +7772,9 @@ static const int64_t right_3_bits = right_n_bits(3);
__ emit_int32(power_of_31);
}
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -7245,11 +7794,17 @@ static const int64_t right_3_bits = right_n_bits(3);
address generate_updateBytesCRC32() {
assert(UseCRC32Intrinsics, "what are we doing here?");
- __ align(CodeEntryAlignment);
StubId stub_id = StubId::stubgen_updateBytesCRC32_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);
- address start = __ pc();
+ start = __ pc();
// input parameters
const Register crc = c_rarg0; // crc
@@ -7266,14 +7821,23 @@ static const int64_t right_3_bits = right_n_bits(3);
__ leave(); // required for proper stackwalking of RuntimeStub frame
__ ret();
+ // record the stub entry and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
// exception handler for upcall stubs
address generate_upcall_stub_exception_handler() {
StubId stub_id = StubId::stubgen_upcall_stub_exception_handler_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;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
// Native caller has no idea how to handle exceptions,
// so we just crash here. Up to callee to catch exceptions.
@@ -7281,6 +7845,9 @@ static const int64_t right_3_bits = right_n_bits(3);
__ rt_call(CAST_FROM_FN_PTR(address, UpcallLinker::handle_uncaught_exception));
__ should_not_reach_here();
+ // record the stub start and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -7290,8 +7857,14 @@ static const int64_t right_3_bits = right_n_bits(3);
address generate_upcall_stub_load_target() {
StubId stub_id = StubId::stubgen_upcall_stub_load_target_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;
+ }
StubCodeMark mark(this, stub_id);
- address start = __ pc();
+ start = __ pc();
__ resolve_global_jobject(j_rarg0, t0, t1);
// Load target method from receiver
@@ -7305,6 +7878,9 @@ static const int64_t right_3_bits = right_n_bits(3);
__ ret();
+ // record the stub start and end
+ store_archive_data(stub_id, start, __ pc());
+
return start;
}
@@ -7397,16 +7973,30 @@ static const int64_t right_3_bits = right_n_bits(3);
if (UseMontgomeryMultiplyIntrinsic) {
StubId stub_id = StubId::stubgen_montgomeryMultiply_id;
- StubCodeMark mark(this, stub_id);
- MontgomeryMultiplyGenerator g(_masm, /*squaring*/false);
- StubRoutines::_montgomeryMultiply = g.generate_multiply();
+ address start = load_archive_data(stub_id);
+ if (start == nullptr) {
+ // we have to generate it
+ StubCodeMark mark(this, stub_id);
+ MontgomeryMultiplyGenerator g(_masm, /*squaring*/false);
+ start = g.generate_multiply();
+ // record the stub start and end
+ store_archive_data(stub_id, start, _masm->pc());
+ }
+ StubRoutines::_montgomeryMultiply = start;
}
if (UseMontgomerySquareIntrinsic) {
StubId stub_id = StubId::stubgen_montgomerySquare_id;
- StubCodeMark mark(this, stub_id);
- MontgomeryMultiplyGenerator g(_masm, /*squaring*/true);
- StubRoutines::_montgomerySquare = g.generate_square();
+ address start = load_archive_data(stub_id);
+ if (start == nullptr) {
+ // we have to generate it
+ StubCodeMark mark(this, stub_id);
+ MontgomeryMultiplyGenerator g(_masm, /*squaring*/true);
+ start = g.generate_square();
+ // record the stub start and end
+ store_archive_data(stub_id, start, _masm->pc());
+ }
+ StubRoutines::_montgomerySquare = start;
}
if (UseAESIntrinsics) {
@@ -7506,8 +8096,27 @@ static const int64_t right_3_bits = right_n_bits(3);
break;
};
}
+#if INCLUDE_CDS
+ static void init_AOTAddressTable(GrowableArray& external_addresses) {
+ // external data defined in this file
+#define ADD(addr) external_addresses.append((address)(addr));
+ ADD(_encodeBlock_toBase64);
+ ADD(_encodeBlock_toBase64URL);
+ ADD(_decodeBlock_fromBase64);
+ ADD(_decodeBlock_fromBase64URL);
+ ADD(_sha256_round_consts);
+ ADD(_sha512_round_consts);
+#undef ADD
+ }
+#endif // INCLUDE_CDS
}; // end class declaration
void StubGenerator_generate(CodeBuffer* code, BlobId blob_id, AOTStubData* stub_data) {
StubGenerator g(code, blob_id, stub_data);
}
+
+#if INCLUDE_CDS
+void StubGenerator_init_AOTAddressTable(GrowableArray& addresses) {
+ StubGenerator::init_AOTAddressTable(addresses);
+}
+#endif // INCLUDE_CDS
diff --git a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp
index b7f69eff9fa3..bedb67fdda6a 100644
--- a/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp
+++ b/src/hotspot/cpu/riscv/stubRoutines_riscv.cpp
@@ -507,7 +507,17 @@ ATTRIBUTE_ALIGNED(4096) juint StubRoutines::riscv::_crc_table[] =
};
#if INCLUDE_CDS
-// nothing to do for riscv
+extern void StubGenerator_init_AOTAddressTable(GrowableArray& external_addresses);
+
void StubRoutines::init_AOTAddressTable() {
+ ResourceMark rm;
+ GrowableArray external_addresses;
+ // publish static addresses referred to by riscv generator
+ // n.b. we have to use an extern call here because class
+ // StubGenerator, which provides the static method that knows how to
+ // add the relevant addresses, is declared in a source file rather
+ // than in a separately includeable header.
+ StubGenerator_init_AOTAddressTable(external_addresses);
+ AOTCodeCache::publish_external_addresses(external_addresses);
}
#endif // INCLUDE_CDS
diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp
index 22fd66a8da32..35fbf2c1c913 100644
--- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp
+++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp
@@ -30,6 +30,7 @@
#include "runtime/vm_version.hpp"
#include "utilities/formatBuffer.hpp"
#include "utilities/macros.hpp"
+#include "utilities/ostream.hpp"
#include
@@ -509,3 +510,62 @@ bool VM_Version::is_intrinsic_supported(vmIntrinsicID id) {
}
return true;
}
+
+int VM_Version::cpu_features_size() {
+ return sizeof(RVExtFeatures);
+}
+
+void VM_Version::store_cpu_features(void* buf) {
+ memcpy(buf, RVExtFeatures::current(), sizeof(RVExtFeatures));
+}
+
+bool VM_Version::verify_aot_code_cache_features(void* features_buffer) {
+ RVExtFeatures* features_to_test = (RVExtFeatures*)features_buffer;
+ return RVExtFeatures::current()->verify_aot_code_cache_features(features_to_test);
+}
+
+// Print one feature using the same spelling as features_string(): single letter
+// extensions appear as "rvc"/"rvv" and multi-character extensions with a lower
+// case leading character ("Zba" -> "zba"). Must stay in sync with the feature
+// string built in VM_Version::setup_cpu_available_features().
+void VM_Version::print_feature_name(stringStream& ss, RVFeatureValue* feature) {
+ const char* pretty = feature->pretty();
+ if (strlen(pretty) == 1) {
+ ss.print("rv%s", pretty);
+ } else {
+ ss.print("%c%s", (char)tolower(pretty[0]), &pretty[1]);
+ }
+}
+
+void VM_Version::insert_features_names(RVExtFeatures* features, stringStream& ss) {
+ const char* sep = "";
+ int i = 0;
+ while (i < RVExtFeatures::MAX_CPU_FEATURE_INDEX) {
+ if (features->support_feature(i)) {
+ ss.print("%s", sep);
+ print_feature_name(ss, _feature_list[i]);
+ sep = ", ";
+ }
+ i += 1;
+ }
+}
+
+void VM_Version::get_cpu_features_name(void* features_buffer, stringStream& ss) {
+ RVExtFeatures* features = (RVExtFeatures*)features_buffer;
+ insert_features_names(features, ss);
+}
+
+void VM_Version::get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss) {
+ RVExtFeatures* rv_ext_features_set1 = (RVExtFeatures*)features_set1;
+ RVExtFeatures* rv_ext_features_set2 = (RVExtFeatures*)features_set2;
+ const char* sep = "";
+ int i = 0;
+ while (i < RVExtFeatures::MAX_CPU_FEATURE_INDEX) {
+ if (rv_ext_features_set1->support_feature(i) && !rv_ext_features_set2->support_feature(i)) {
+ ss.print("%s", sep);
+ print_feature_name(ss, _feature_list[i]);
+ sep = ", ";
+ }
+ i += 1;
+ }
+}
diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp
index 8e61ff550ece..7a1ec2ba22c3 100644
--- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp
+++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp
@@ -36,6 +36,7 @@
#include "utilities/sizes.hpp"
class RiscvHwprobe;
+class stringStream;
class VM_Version : public Abstract_VM_Version {
friend RiscvHwprobe;
@@ -396,6 +397,15 @@ class VM_Version : public Abstract_VM_Version {
int idx = element_index(f);
return (_features_bitmap[idx] & feature_bit(f)) != 0;
}
+
+ bool verify_aot_code_cache_features(RVExtFeatures* features_to_test) const {
+ for (int i = 0; i < element_count(); i++) {
+ if (_features_bitmap[i] != features_to_test->_features_bitmap[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
};
// enable extensions based on profile, current supported profiles:
@@ -523,6 +533,17 @@ class VM_Version : public Abstract_VM_Version {
// Check intrinsic support
static bool is_intrinsic_supported(vmIntrinsicID id);
+
+ // AOT Code Cache support
+ static int cpu_features_size();
+ static void store_cpu_features(void* buf);
+ static bool verify_aot_code_cache_features(void* features_buffer);
+ static void get_cpu_features_name(void* features_buffer, stringStream& ss);
+ static void get_missing_features_name(void* features_set1, void* features_set2, stringStream& ss);
+
+ private:
+ static void print_feature_name(stringStream& ss, RVFeatureValue* feature);
+ static void insert_features_names(RVExtFeatures* features, stringStream& ss);
};
#endif // CPU_RISCV_VM_VERSION_RISCV_HPP
diff --git a/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp b/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp
index d4a306d35b18..7634ac0fd379 100644
--- a/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp
+++ b/src/hotspot/os_cpu/linux_riscv/os_linux_riscv.cpp
@@ -77,6 +77,11 @@
#define REG_LR 1
#define REG_FP 8
+// First argument register (x10), used to pass the stop() message
+// to the signal handler.
+#ifndef REG_A0
+#define REG_A0 10
+#endif
#define REG_BCP 22
NOINLINE address os::current_stack_pointer() {
@@ -240,11 +245,8 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info,
stub = SharedRuntime::handle_unsafe_access(thread, next_pc);
}
} else if (sig == SIGILL && nativeInstruction_at(pc)->is_stop()) {
- // Pull a pointer to the error message out of the instruction
- // stream.
- const uint64_t *detail_msg_ptr
- = (uint64_t*)(pc + NativeInstruction::instruction_size);
- const char *detail_msg = (const char *)*detail_msg_ptr;
+ // A pointer to the message will have been placed in a0
+ const char *detail_msg = (const char *)(uc->uc_mcontext.__gregs[REG_A0]);
const char *msg = "stop";
if (TraceTraps) {
tty->print_cr("trap: %s: (SIGILL)", msg);
diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp
index 662eab3311f6..c29f5726236b 100644
--- a/src/hotspot/share/code/aotCodeCache.cpp
+++ b/src/hotspot/share/code/aotCodeCache.cpp
@@ -204,7 +204,7 @@ uint AOTCodeCache::max_aot_code_size() {
// At this point all AOT class linking seetings are finilized
// and AOT cache is open so we can map AOT code region.
void AOTCodeCache::initialize() {
-#if defined(ZERO) || !(defined(AMD64) || defined(AARCH64))
+#if defined(ZERO) || !(defined(AMD64) || defined(AARCH64) || defined(RISCV64))
log_info(aot, codecache, init)("AOT Code Cache is not supported on this platform.");
disable_caching();
return;
@@ -263,7 +263,7 @@ void AOTCodeCache::initialize() {
FLAG_SET_DEFAULT(ForceUnreachable, true);
}
FLAG_SET_DEFAULT(DelayCompilerStubsGeneration, false);
-#endif // defined(AMD64) || defined(AARCH64)
+#endif // defined(AMD64) || defined(AARCH64) || defined(RISCV64)
}
static AOTCodeCache* opened_cache = nullptr; // Use this until we verify the cache
@@ -471,7 +471,7 @@ void AOTCodeCache::Config::record(uint cpu_features_offset) {
_useUnalignedLoadStores = UseUnalignedLoadStores;
#endif
-#if defined(AARCH64) && !defined(ZERO)
+#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
_avoidUnalignedAccesses = AvoidUnalignedAccesses;
#endif
@@ -601,14 +601,14 @@ bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const {
}
#endif // defined(X86) && !defined(ZERO)
-#if defined(AARCH64) && !defined(ZERO)
+#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
// switching on AvoidUnalignedAccesses may affect validity of array
// copy stubs and nmethods
if (!_avoidUnalignedAccesses && AvoidUnalignedAccesses) {
log_config_mismatch(_avoidUnalignedAccesses, AvoidUnalignedAccesses, "AvoidUnalignedAccesses");
return false;
}
-#endif // defined(AARCH64) && !defined(ZERO)
+#endif // (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
return true;
}
@@ -1941,6 +1941,8 @@ void AOTCodeAddressTable::init_extrs() {
ADD_EXTERNAL_ADDRESS(SharedRuntime::allocate_inline_types);
#if defined(AARCH64) && !defined(ZERO)
ADD_EXTERNAL_ADDRESS(JavaThread::aarch64_get_thread_helper);
+#endif
+#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
ADD_EXTERNAL_ADDRESS(BarrierSetAssembler::patching_epoch_addr());
#endif
diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp
index 640e73e68270..bd5a872e181c 100644
--- a/src/hotspot/share/code/aotCodeCache.hpp
+++ b/src/hotspot/share/code/aotCodeCache.hpp
@@ -352,11 +352,26 @@ class AOTStubData : public StackObj {
#define AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun)
#endif
+#if defined(RISCV64) && !defined(ZERO)
+#define AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun) \
+ do_var(intx, BlockZeroingLowLimit) /* zero blocks stub */ \
+ do_var(bool, UseBlockZeroing) /* zero blocks stub and nmethods */ \
+ do_var(bool, UseConservativeFence) /* fence encoding in stubs and nmethods */ \
+ do_var(bool, UseCtxFencei) /* method entry barrier stub */ \
+ do_var(bool, UseSecondarySupersCache) /* secondary supers cache in nmethods */ \
+ do_var(bool, UseZabha) /* narrow cmpxchg selection in nmethods */ \
+ do_fun(int, RVZicbozBlockSize, (int)VM_Version::zicboz_block_size.value()) \
+ // END
+#else
+#define AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun)
+#endif
+
#define AOTCODECACHE_CONFIGS_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_GENERIC_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_COMPILER2_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_AARCH64_DO(do_var, do_fun) \
AOTCODECACHE_CONFIGS_X86_DO(do_var, do_fun) \
+ AOTCODECACHE_CONFIGS_RISCV_DO(do_var, do_fun) \
// END
#define AOTCODECACHE_DECLARE_VAR(type, name) type _saved_ ## name;
@@ -377,7 +392,7 @@ class AOTCodeCache : public CHeapObj {
bool _useUnalignedLoadStores;
#endif
-#if defined(AARCH64) && !defined(ZERO)
+#if (defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
bool _avoidUnalignedAccesses;
#endif
diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java
index eebace23feaf..990a5e81e127 100644
--- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java
+++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCPUFeatureIncompatibilityTest.java
@@ -27,7 +27,7 @@
* @summary CPU feature compatibility test for AOT Code Cache
* @requires vm.cds.supports.aot.code.caching
* @requires vm.compMode != "Xcomp" & vm.compMode != "Xint"
- * @requires os.simpleArch == "x64" | os.simpleArch == "aarch64"
+ * @requires os.simpleArch == "x64" | os.simpleArch == "aarch64" | os.simpleArch == "riscv64"
* @comment The test verifies AOT checks during VM startup and not code generation.
* No need to run it with -Xcomp.
* @library /test/lib /test/setup_aot
@@ -74,6 +74,11 @@ public static void main(String... args) throws Exception {
testIncompatibleFeature("-XX:-UseCRC32", "crc32", IncompatibilityMode.MISSING);
testIncompatibleFeature("-XX:-UseCRC32", "crc32", IncompatibilityMode.ADDITIONAL);
}
+ } else if (Platform.isRISCV64()) {
+ if (isZbaSupported(cpuFeatures)) {
+ testIncompatibleFeature("-XX:-UseZba", "zba", IncompatibilityMode.MISSING);
+ testIncompatibleFeature("-XX:-UseZba", "zba", IncompatibilityMode.ADDITIONAL);
+ }
}
}
@@ -87,12 +92,14 @@ public String[] vmArgs(RunMode runMode) {
if (mode == IncompatibilityMode.MISSING) {
return new String[] {"-Xlog:aot+codecache*=debug"};
} else {
- return new String[] {vmOption, "-Xlog:aot+codecache*=debug"};
+ // UnlockDiagnosticVMOptions must precede vmOption because
+ // some tested CPU feature flags are diagnostic (e.g. UseZba on riscv).
+ return new String[] {"-XX:+UnlockDiagnosticVMOptions", vmOption, "-Xlog:aot+codecache*=debug"};
}
} else if (runMode == RunMode.PRODUCTION) {
if (mode == IncompatibilityMode.MISSING) {
- return new String[] {vmOption,
- "-XX:+UnlockDiagnosticVMOptions",
+ return new String[] {"-XX:+UnlockDiagnosticVMOptions",
+ vmOption,
// Prevent exiting VM on failure
"-XX:-AbortVMOnAOTCodeFailure",
"-Xlog:aot+codecache*=debug"};
@@ -149,4 +156,9 @@ static boolean isAVXSupported(List cpuFeatures) {
static boolean isCRC32Supported(List cpuFeatures) {
return cpuFeatures.contains("crc32");
}
+
+ // Only used on riscv64 platform
+ static boolean isZbaSupported(List cpuFeatures) {
+ return cpuFeatures.contains("zba");
+ }
}
diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java
index e4e0d5f3bfd7..7c8cb9b4b556 100644
--- a/test/jtreg-ext/requires/VMProps.java
+++ b/test/jtreg-ext/requires/VMProps.java
@@ -477,7 +477,7 @@ protected String vmCDSSupportsAOTClassLinking() {
protected String vmCDSSupportsAOTCodeCaching() {
if ("true".equals(vmCDSSupportsAOTClassLinking()) &&
!"zero".equals(vmFlavor()) &&
- (Platform.isX64() || Platform.isAArch64())) {
+ (Platform.isX64() || Platform.isAArch64() || Platform.isRISCV64())) {
return "true";
} else {
return "false";
From 60140b6e2ba04674ed6eca9755bf3ab0a0d69ef7 Mon Sep 17 00:00:00 2001
From: Christian Hagedorn
Date: Tue, 18 Aug 2026 15:59:46 +0000
Subject: [PATCH 57/88] 8381564: [IR Framework] Fix race between accept thread
and Driver VM thread
Reviewed-by: mchevalier, thartmann
---
.../ir_framework/driver/TestVMProcess.java | 186 ++++++++++++------
.../shared/TestFrameworkSocket.java | 98 +++++----
.../tests/TestNoMainExecution.java | 85 ++++++++
3 files changed, 280 insertions(+), 89 deletions(-)
create mode 100644 test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestNoMainExecution.java
diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java
index bfffbdbec7ea..baed93594597 100644
--- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java
+++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java
@@ -36,6 +36,8 @@
import jdk.test.lib.process.ProcessTools;
import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -49,12 +51,15 @@
* @see TestFrameworkSocket
*/
public class TestVMProcess {
- private static final boolean VERBOSE = Boolean.getBoolean("Verbose");
private static final boolean PREFER_COMMAND_LINE_FLAGS = Boolean.getBoolean("PreferCommandLineFlags");
private static final int WARMUP_ITERATIONS = Integer.getInteger("Warmup", -1);
private static final boolean VERIFY_VM = Boolean.getBoolean("VerifyVM") && Platform.isDebugBuild();
- private static final boolean REPORT_STDOUT = Boolean.getBoolean("ReportStdout");
+ private static final boolean VERBOSE = Boolean.getBoolean("Verbose");
private static final boolean EXCLUDE_RANDOM = Boolean.getBoolean("ExcludeRandom");
+ private static final boolean REPORT_STDOUT = Boolean.getBoolean("ReportStdout");
+ private static final boolean DUMP_OUTPUT = VERBOSE || EXCLUDE_RANDOM || REPORT_STDOUT;
+
+ private static final String FATAL_ERROR_MARKER = "# A fatal error has been detected by the Java Runtime Environment:";
private static String lastTestVMOutput = "";
@@ -72,11 +77,131 @@ public TestVMProcess(List additionalFlags, Class> testClass, Set>> No tests run due to empty set specified with -DTest and/or -DExclude. " +
+ "Make sure to define a set of at least one @Test method");
+ }
+
+ private TestVMException createTestVMExceptionForNonZeroExit(TestFrameworkSocket socket, boolean allowNotCompilable) {
+ String secondaryException = "";
+ try {
+ readAndDumpTestVmData(socket, allowNotCompilable);
+ } catch (RuntimeException e) {
+ // We observed a message processing exception. We treat it as secondary failure because messages could be
+ // incomplete when the VM crashed or not even sent by the Test VM when it exits early on start-up
+ // (e.g. passing in an unknown VM flag).
+ secondaryException = buildSecondaryExceptionInfo(e);
+ }
+ // Primary exception: non-zero Test VM exit.
+ return new TestVMException(buildExceptionInfo() + secondaryException);
+ }
+
+ private String buildSecondaryExceptionInfo(RuntimeException e) {
+ String secondaryException;
+ StringWriter stringWriter = new StringWriter();
+ e.printStackTrace(new PrintWriter(stringWriter));
+
+ secondaryException = System.lineSeparator() +
+ "Secondary Message-Processing Exception" + System.lineSeparator() +
+ "--------------------------------------" + System.lineSeparator() +
+ stringWriter;
+ return secondaryException;
+ }
+
+ /**
+ * Get more detailed information about the exception in a pretty format.
+ */
+ private String buildExceptionInfo() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Test VM exited with code ").append(oa.getExitValue()).append(System.lineSeparator());
+ if (hasFatalErrorMarker() || DUMP_OUTPUT) {
+ // Also dump the Test VM output if we experience a JVM error to show assertion failures etc.
+ builder.append(System.lineSeparator())
+ .append(System.lineSeparator())
+ .append("Test VM - Standard Output").append(System.lineSeparator())
+ .append("-------------------------").append(System.lineSeparator())
+ .append(oa.getStdout());
+ }
+ builder.append(System.lineSeparator())
+ .append(commandLine)
+ .append(System.lineSeparator())
+ .append(System.lineSeparator())
+ .append("Test VM - Error Output").append(System.lineSeparator())
+ .append("----------------------").append(System.lineSeparator())
+ .append(oa.getStderr())
+ .append(System.lineSeparator())
+ .append(System.lineSeparator());
+ return builder.toString();
+ }
+
+ /**
+ * Best-effort VM crash detection by matching the start of the fatal error message. This covers most of the crashes
+ * but fails when the Test VM was killed externally or when the output is unavailable or truncated which could
+ * happen in native stack overflow cases.
+ */
+ private boolean hasFatalErrorMarker() {
+ return oa.getExitValue() != 0 && oa.getOutput().contains(FATAL_ERROR_MARKER);
}
public String getCommandLine() {
@@ -173,55 +298,4 @@ private void start() {
+ System.lineSeparator();
lastTestVMOutput = oa.getOutput();
}
-
- private void checkTestVMExitCode() {
- final int exitCode = oa.getExitValue();
- if (EXCLUDE_RANDOM || REPORT_STDOUT || (VERBOSE && exitCode == 0)) {
- System.out.println("--- OUTPUT TestFramework Test VM ---");
- System.out.println(oa.getOutput());
- }
-
- if (exitCode != 0) {
- throwTestVMException();
- }
- }
-
- /**
- * Exit code was non-zero of Test VM. Check the stderr to determine what kind of exception that should be thrown to
- * react accordingly later.
- */
- private void throwTestVMException() {
- String stdErr = oa.getStderr();
- if (stdErr.contains("TestFormat.throwIfAnyFailures")) {
- Pattern pattern = Pattern.compile("Violations \\(\\d+\\)[\\s\\S]*(?=/============/)");
- Matcher matcher = pattern.matcher(stdErr);
- TestFramework.check(matcher.find(), "Must find violation matches");
- throw new TestFormatException(System.lineSeparator() + System.lineSeparator() + matcher.group());
- } else if (stdErr.contains("NoTestsRunException")) {
- throw new NoTestsRunException(">>> No tests run due to empty set specified with -DTest and/or -DExclude. " +
- "Make sure to define a set of at least one @Test method");
- } else {
- throw new TestVMException(getExceptionInfo());
- }
- }
-
- /**
- * Get more detailed information about the exception in a pretty format.
- */
- private String getExceptionInfo() {
- int exitCode = oa.getExitValue();
- String stdErr = oa.getStderr();
- String stdOut = "";
- boolean osIsWindows = Platform.isWindows();
- boolean JVMHadError = (!osIsWindows && exitCode == 134) || (osIsWindows && exitCode == -1);
- if (JVMHadError) {
- // Also dump the stdout if we experience a JVM error (e.g. to show hit assertions etc.).
- stdOut = System.lineSeparator() + System.lineSeparator() + "Standard Output" + System.lineSeparator()
- + "---------------" + System.lineSeparator() + oa.getOutput();
- }
- return "TestFramework Test VM exited with code " + exitCode + System.lineSeparator() + stdOut
- + System.lineSeparator() + commandLine + System.lineSeparator() + System.lineSeparator()
- + "Error Output" + System.lineSeparator() + "------------" + System.lineSeparator() + stdErr
- + System.lineSeparator() + System.lineSeparator();
- }
}
diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java
index 05359d0d789e..bb7765c5bc49 100644
--- a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java
+++ b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java
@@ -30,6 +30,8 @@
import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessages;
import compiler.lib.ir_framework.test.network.TestVmSocket;
+import jdk.test.lib.Utils;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -41,16 +43,36 @@
*/
public class TestFrameworkSocket implements AutoCloseable {
private static final String SERVER_PORT_PROPERTY = "ir.framework.server.port";
+ private static final int SOCKET_TIMEOUT_IN_MS = (int)Utils.adjustTimeout(10_000L);
private final int serverSocketPort;
private final ServerSocket serverSocket;
private final ExecutorService acceptExecutor;
private final ExecutorService clientExecutor;
- // Make these volatile such that the main thread can observe an update written by the worker threads in the executor
- // services to avoid stale values.
+ /*
+ * CompletableFuture shared by the Driver VM and the accept/reader threads.
+ *
+ * Lifecycle:
+ * 1. The future is created before the accept loop task is submitted.
+ * 2. The Driver VM starts the Test VM. The socket and the executors remain open while the Driver VM waits on the
+ * future to be completed.
+ * 3. The accept thread accepts the Test VM connection and reads the identity handshake.
+ * 4. The accept thread now schedules a reader for incoming Test VM messages.
+ * 5. During normal execution, the Test VM closes the connection before exiting. The reader completes the future
+ * with the parsed Test VM messages.
+ * 6. Accepting, identity handshake, task submission, or message reading failures complete the future exceptionally.
+ * 7. The Driver VM obtains the result, observes a failure, or times out before the socket and executors are closed.
+ *
+ * Note: The future must be created eagerly such that the Driver VM can wait on it even before the accept thread
+ * has accepted the Test VM connection. The accept thread might only be scheduled after the Test VM has exited.
+ * This is possible because the server socket is already listening and the OS can queue the connection until
+ * the accept thread processes it.
+ */
+ private final CompletableFuture javaMessagesFuture;
+
+ // Written by the Driver VM thread and read by the accept thread.
private volatile boolean running;
- private volatile Future javaFuture;
public TestFrameworkSocket() {
try {
@@ -62,6 +84,7 @@ public TestFrameworkSocket() {
serverSocketPort = serverSocket.getLocalPort();
acceptExecutor = Executors.newSingleThreadExecutor();
clientExecutor = Executors.newCachedThreadPool();
+ javaMessagesFuture = new CompletableFuture<>();
if (TestFramework.VERBOSE) {
System.out.println("TestFramework server socket uses port " + serverSocketPort);
}
@@ -73,50 +96,40 @@ public String getPortPropertyFlag() {
public void start() {
running = true;
- CountDownLatch calledAcceptLoopLatch = new CountDownLatch(1);
- startAcceptLoop(calledAcceptLoopLatch);
- }
-
- private void startAcceptLoop(CountDownLatch calledAcceptLoopLatch) {
- acceptExecutor.submit(() -> acceptLoop(calledAcceptLoopLatch));
- waitUntilAcceptLoopRuns(calledAcceptLoopLatch);
- }
-
- private void waitUntilAcceptLoopRuns(CountDownLatch calledAcceptLoopLatch) {
- try {
- if (!calledAcceptLoopLatch.await(10, TimeUnit.SECONDS)) {
- throw new IllegalStateException("acceptLoop did not start in time");
- }
- } catch (Exception e) {
- throw new TestFrameworkException("Could not start TestFrameworkSocket", e);
- }
+ acceptExecutor.submit(this::acceptLoop);
}
/**
* Main loop to wait for new client connections and handling them upon connection request.
*/
- private void acceptLoop(CountDownLatch calledAcceptLoopLatch) {
- calledAcceptLoopLatch.countDown();
+ private void acceptLoop() {
while (running) {
try {
acceptNewClientConnection();
- } catch (SocketException e) {
+ } catch (SocketException e) {
if (!running || serverSocket.isClosed()) {
// Normal shutdown
return;
}
- running = false;
- throw new TestFrameworkException("Server socket error", e);
+ throwServerSocketError(e);
} catch (TestFrameworkException e) {
- running = false;
- throw e;
+ throwTestFrameworkException(e);
} catch (Exception e) {
- running = false;
- throw new TestFrameworkException("Server socket error", e);
+ throwServerSocketError(e);
}
}
}
+ private void throwServerSocketError(Exception e) {
+ throwTestFrameworkException(new TestFrameworkException("Server socket error", e));
+ }
+
+ private void throwTestFrameworkException(TestFrameworkException testFrameworkException) {
+ running = false;
+ javaMessagesFuture.completeExceptionally(testFrameworkException);
+ throw testFrameworkException;
+ }
+
/**
* Accept new client connection by first reading the identity of the connection (either coming from Java or C2)
* and then submitting a task accordingly to manage incoming messages on that connection/socket.
@@ -137,11 +150,11 @@ private void acceptNewClientConnection() throws IOException {
private String readIdentity(Socket client, BufferedReader reader) throws IOException {
String identity;
try {
- client.setSoTimeout(10000);
+ client.setSoTimeout(SOCKET_TIMEOUT_IN_MS);
identity = reader.readLine();
TestFramework.check(identity != null, "end of stream has been reached without reading the identity");
} catch (SocketTimeoutException e) {
- throw new TestFrameworkException("Did not receive initial identity message after 10s", e);
+ throw new TestFrameworkException("Timed out while waiting for initial identity message", e);
} finally {
client.setSoTimeout(0);
}
@@ -154,7 +167,9 @@ private String readIdentity(Socket client, BufferedReader reader) throws IOExcep
*/
private void submitTask(String identity, Socket client, BufferedReader reader) {
if (identity.equals(TestVmSocket.IDENTITY)) {
- javaFuture = clientExecutor.submit(new TestVmMessageReader<>(client, reader, new JavaMessageParser()));
+ TestVmMessageReader messageReader =
+ new TestVmMessageReader<>(client, reader, new JavaMessageParser());
+ javaMessagesFuture.completeAsync(messageReader::call, clientExecutor);
} else {
throw new TestFrameworkException("Unrecognized identity: " + identity);
}
@@ -179,9 +194,26 @@ public TestVMData testVmData(String hotspotPidFileName, boolean allowNotCompilab
private JavaMessages testVmMessages() {
try {
- return javaFuture.get();
+ // Note: The Test VM may have already exited while the accept and message reader thread are still processing
+ // the connection. Let's wait until they are finished.
+ return javaMessagesFuture.get(SOCKET_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
throw new TestFrameworkException("No test VM messages were received", e);
+ } catch (TimeoutException e) {
+ throw new RuntimeException("Timed out while waiting for Test VM messages." + System.lineSeparator() +
+ System.lineSeparator() +
+ "Did any of the following happen?" + System.lineSeparator() +
+ "(1) TestFramework.addFlags(-DReproduce=true)" + System.lineSeparator() +
+ "(2) TestFramework.addFlags(--version) or any other VM flag that prevents " +
+ " TestVM.main() from being called?" + System.lineSeparator() +
+ "(3) The Test VM crashed before calling TestVM.main()" + System.lineSeparator() +
+ System.lineSeparator() +
+ "(1) and (2) are unsupported and are expected to fail." + System.lineSeparator() +
+ "-> Please change your test!" + System.lineSeparator() +
+ "(3) The IR Framework cannot handle early VM crashes." + System.lineSeparator() +
+ "-> Please change your test if such a crash was anticipated!" +
+ System.lineSeparator() + System.lineSeparator() +
+ "In all other cases, please file an IR Framework bug!", e);
} catch (Exception e) {
throw new TestFrameworkException("Error while fetching Test VM Future", e);
}
diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestNoMainExecution.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestNoMainExecution.java
new file mode 100644
index 000000000000..8e9410cdb8d9
--- /dev/null
+++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestNoMainExecution.java
@@ -0,0 +1,85 @@
+/*
+ * 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 8381564
+ * @requires vm.debug == true & vm.compMode != "Xint" & vm.compiler2.enabled & vm.flagless
+ * @summary Test that different ways to avoid executing main() are reported as correctly as failure.
+ * @library /test/lib /testlibrary_tests /
+ * @run driver ${test.main.class}
+ */
+
+package testlibrary_tests.ir_framework.tests;
+
+import compiler.lib.ir_framework.*;
+import jdk.test.lib.Asserts;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+public class TestNoMainExecution {
+ public static void main(String[] args) {
+ // We do not handshake because this flag disables socket communication
+ run("-DReproduce=true");
+
+ // We do not reach main() because there is no source file involved.
+ run("--version");
+
+ // We do not reach main() because we crash already at start-up.
+ runWithVmCrash();
+ }
+
+ private static void run(String... flags) {
+ try {
+ TestFramework.runWithFlags(flags);
+ Asserts.fail("should throw");
+ } catch (RuntimeException e) {
+ String errorMessage = e.getMessage();
+ // We expect a useful help message - match its header.
+ Asserts.assertTrue(errorMessage.contains("Did any of the following happen?"), errorMessage);
+ }
+ }
+
+ private static void runWithVmCrash() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream oldErr = System.err;
+
+ try (PrintStream ps = new PrintStream(baos)) {
+ System.setErr(ps);
+
+ try {
+ TestFramework.runWithFlags("-Xcomp", "-XX:+CICountNative", "-XX:CICrashAt=1");
+ Asserts.fail("should throw");
+ } catch (RuntimeException e) {
+ // With a VM crash, the message is found on the normal stderr instead.
+ System.setErr(oldErr);
+ String output = baos.toString();
+ Asserts.assertTrue(output.contains("Did any of the following happen?"));
+ }
+ }
+ }
+
+ @Test
+ public void test() {}
+}
From 29c7198bbbc6d3da01bd1a1b8b7d51d6fb1cd2ec Mon Sep 17 00:00:00 2001
From: Aleksey Shipilev
Date: Tue, 18 Aug 2026 16:14:13 +0000
Subject: [PATCH 58/88] 8390307: Shenandoah: Print region age in region
printouts
Reviewed-by: wkemper, ruili
---
src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp | 6 ++----
src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp | 1 +
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
index 026ba8681e6b..de660badd9ac 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
@@ -1465,10 +1465,8 @@ void ShenandoahHeap::print_heap_regions_on(outputStream* st) const {
st->print_cr("Heap Regions:");
st->print_cr("Region state: EU=empty-uncommitted, EC=empty-committed, R=regular, H=humongous start, HP=pinned humongous start");
st->print_cr(" HC=humongous continuation, CS=collection set, TR=trash, P=pinned, CSP=pinned collection set");
- st->print_cr("BTE=bottom/top/end, TAMS=top-at-mark-start");
- st->print_cr("UWM=update watermark, U=used");
- st->print_cr("T=TLAB allocs, G=GCLAB allocs");
- st->print_cr("S=shared allocs, L=live data");
+ st->print_cr("A=age, BTE=bottom/top/end, TAMS=top-at-mark-start, UWM=update watermark, U=used");
+ st->print_cr("T=TLAB allocs, G=GCLAB allocs, S=shared allocs, L=live data");
st->print_cr("CP=critical pins");
for (size_t i = 0; i < num_regions(); i++) {
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
index a37fd34238e0..84582ce07c30 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp
@@ -424,6 +424,7 @@ void ShenandoahHeapRegion::print_on(outputStream* st) const {
}
st->print("|%s", shenandoah_affiliation_code(affiliation()));
+ st->print("|A %2d", age());
#define SHR_PTR_FORMAT "%12" PRIxPTR
From 57649b487e560a0721ff04d80b365f5b1f80866c Mon Sep 17 00:00:00 2001
From: Cesar Soares Lucas
Date: Tue, 18 Aug 2026 16:14:57 +0000
Subject: [PATCH 59/88] 8389873: C2 crashes in ConnectionGraph::can_reduce_phi
during escape analysis
Reviewed-by: thartmann, qamai
---
src/hotspot/share/opto/escape.cpp | 4 +-
...stMergeStoresAndAllocationElimination.java | 10 +-
.../TestReduceAllocationAndHeapDump.java | 8 +-
.../TestReduceAllocationAndMemoryLoop.java | 4 +-
...stReduceAllocationAndNonExactAllocate.java | 4 +-
.../TestReduceAllocationAndNullableLoads.java | 4 +-
...ReduceAllocationAndPointerComparisons.java | 6 +-
.../TestReduceAllocationOptimizedOutPhi.java | 92 +++++++++++++++++++
...TestReducePhiOnCmpWithNoOptPtrCompare.java | 6 +-
.../TestScalarReplacementMaxLiveNodes.java | 8 +-
10 files changed, 119 insertions(+), 27 deletions(-)
rename test/hotspot/jtreg/compiler/{c2 => escapeAnalysis}/TestMergeStoresAndAllocationElimination.java (92%)
rename test/hotspot/jtreg/compiler/{c2 => escapeAnalysis}/TestReduceAllocationAndHeapDump.java (91%)
rename test/hotspot/jtreg/compiler/{c2 => escapeAnalysis}/TestReduceAllocationAndMemoryLoop.java (95%)
rename test/hotspot/jtreg/compiler/{c2 => escapeAnalysis}/TestReduceAllocationAndNonExactAllocate.java (95%)
rename test/hotspot/jtreg/compiler/{c2 => escapeAnalysis}/TestReduceAllocationAndNullableLoads.java (95%)
rename test/hotspot/jtreg/compiler/{c2 => escapeAnalysis}/TestReduceAllocationAndPointerComparisons.java (91%)
create mode 100644 test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationOptimizedOutPhi.java
rename test/hotspot/jtreg/compiler/{c2 => escapeAnalysis}/TestReducePhiOnCmpWithNoOptPtrCompare.java (96%)
rename test/hotspot/jtreg/compiler/{c2 => escapeAnalysis}/TestScalarReplacementMaxLiveNodes.java (95%)
diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp
index cb66ba0bdb64..957b79caf1fe 100644
--- a/src/hotspot/share/opto/escape.cpp
+++ b/src/hotspot/share/opto/escape.cpp
@@ -399,7 +399,7 @@ bool ConnectionGraph::compute_escape() {
if (VerifyReduceAllocationMerges) {
for (uint i = 0; i < reducible_merges.size(); i++ ) {
Node* n = reducible_merges.at(i);
- if (!can_reduce_phi(n->as_Phi())) {
+ if (n->outcnt() > 0 && !can_reduce_phi(n->as_Phi())) {
TraceReduceAllocationMerges = true;
n->dump(2);
n->dump(-2);
@@ -666,7 +666,7 @@ bool ConnectionGraph::can_reduce_phi(PhiNode* ophi) const {
// If there was an error attempting to reduce allocation merges for this
// method we might have disabled the compilation and be retrying with RAM
// disabled.
- if (!_compile->do_reduce_allocation_merges() || ophi->region()->Opcode() != Op_Region) {
+ if (!_compile->do_reduce_allocation_merges() || ophi->region() == nullptr || ophi->region()->Opcode() != Op_Region) {
return false;
}
diff --git a/test/hotspot/jtreg/compiler/c2/TestMergeStoresAndAllocationElimination.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestMergeStoresAndAllocationElimination.java
similarity index 92%
rename from test/hotspot/jtreg/compiler/c2/TestMergeStoresAndAllocationElimination.java
rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestMergeStoresAndAllocationElimination.java
index 605f65eb00c0..55b07f1347a6 100644
--- a/test/hotspot/jtreg/compiler/c2/TestMergeStoresAndAllocationElimination.java
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestMergeStoresAndAllocationElimination.java
@@ -21,7 +21,7 @@
* questions.
*/
-package compiler.c2;
+package compiler.escapeAnalysis;
/*
* @test
@@ -29,12 +29,12 @@
* @summary Test case where we had escape analysis tell us that we can possibly eliminate
* the array allocation, then MergeStores introduces a mismatched store, which
* the actual elimination does not verify for. That led to wrong results.
- * @run main/othervm -XX:CompileCommand=compileonly,compiler.c2.TestMergeStoresAndAllocationElimination::test
- * -XX:CompileCommand=exclude,compiler.c2.TestMergeStoresAndAllocationElimination::dontinline
+ * @run main/othervm -XX:CompileCommand=compileonly,compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination::test
+ * -XX:CompileCommand=exclude,compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination::dontinline
* -XX:-TieredCompilation -Xbatch
* -XX:+IgnoreUnrecognizedVMOptions -XX:-CICompileOSR
- * compiler.c2.TestMergeStoresAndAllocationElimination
- * @run main compiler.c2.TestMergeStoresAndAllocationElimination
+ * compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination
+ * @run main compiler.escapeAnalysis.TestMergeStoresAndAllocationElimination
*/
public class TestMergeStoresAndAllocationElimination {
diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndHeapDump.java
similarity index 91%
rename from test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java
rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndHeapDump.java
index f65643f8d870..17f92f593908 100644
--- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndHeapDump.java
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndHeapDump.java
@@ -27,10 +27,10 @@
* @summary Check that the JVM is able to dump the heap even when there are ReduceAllocationMerge in the scope.
* @library /test/lib /
* @requires vm.flavor == "server"
- * @run main/othervm compiler.c2.TestReduceAllocationAndHeapDump
+ * @run main/othervm compiler.escapeAnalysis.TestReduceAllocationAndHeapDump
*/
-package compiler.c2;
+package compiler.escapeAnalysis;
import java.io.File;
import jdk.test.lib.process.OutputAnalyzer;
@@ -49,8 +49,8 @@ public static void main(String[] args) throws Exception {
"-XX:CompileThresholdScaling=0.01",
"-XX:+HeapDumpAfterFullGC",
"-XX:HeapDumpPath=" + dumpDirectory.getAbsolutePath(),
- "-XX:CompileCommand=compileonly,compiler.c2.HeapDumper::testIt",
- "-XX:CompileCommand=exclude,compiler.c2.HeapDumper::dummy",
+ "-XX:CompileCommand=compileonly,compiler.escapeAnalysis.HeapDumper::testIt",
+ "-XX:CompileCommand=exclude,compiler.escapeAnalysis.HeapDumper::dummy",
HeapDumper.class.getName()
};
diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndMemoryLoop.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndMemoryLoop.java
similarity index 95%
rename from test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndMemoryLoop.java
rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndMemoryLoop.java
index 765dcee7c5b3..740ce5792cef 100644
--- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndMemoryLoop.java
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndMemoryLoop.java
@@ -29,10 +29,10 @@
* @requires vm.compiler2.enabled
* @run main/othervm -XX:CompileCommand=compileonly,*TestReduceAllocationAndMemoryLoop*::test*
* -XX:-TieredCompilation -Xbatch
- * compiler.c2.TestReduceAllocationAndMemoryLoop
+ * compiler.escapeAnalysis.TestReduceAllocationAndMemoryLoop
*/
-package compiler.c2;
+package compiler.escapeAnalysis;
public class TestReduceAllocationAndMemoryLoop {
public static void main(String[] args) throws Exception {
diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNonExactAllocate.java
similarity index 95%
rename from test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java
rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNonExactAllocate.java
index ccb00c635c52..5043e68f7fe0 100644
--- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNonExactAllocate.java
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNonExactAllocate.java
@@ -35,10 +35,10 @@
* -XX:-TieredCompilation
* -Xbatch
* -Xcomp
- * compiler.c2.TestReduceAllocationAndNonExactAllocate
+ * compiler.escapeAnalysis.TestReduceAllocationAndNonExactAllocate
*/
-package compiler.c2;
+package compiler.escapeAnalysis;
import jdk.internal.misc.Unsafe;
diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNullableLoads.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNullableLoads.java
similarity index 95%
rename from test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNullableLoads.java
rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNullableLoads.java
index e5df54994499..353e1d67b2b5 100644
--- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndNullableLoads.java
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndNullableLoads.java
@@ -31,10 +31,10 @@
* @run main/othervm -XX:CompileCommand=compileonly,*TestReduceAllocationAndNullableLoads*::*
* -XX:CompileCommand=dontinline,*TestReduceAllocationAndNullableLoads*::*
* -XX:-TieredCompilation -Xcomp
- * compiler.c2.TestReduceAllocationAndNullableLoads
+ * compiler.escapeAnalysis.TestReduceAllocationAndNullableLoads
*/
-package compiler.c2;
+package compiler.escapeAnalysis;
public class TestReduceAllocationAndNullableLoads {
public static void main(String[] args) {
diff --git a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndPointerComparisons.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndPointerComparisons.java
similarity index 91%
rename from test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndPointerComparisons.java
rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndPointerComparisons.java
index e6309b50f56e..e829a05a7243 100644
--- a/test/hotspot/jtreg/compiler/c2/TestReduceAllocationAndPointerComparisons.java
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationAndPointerComparisons.java
@@ -29,11 +29,11 @@
* @run main/othervm -XX:CompileCommand=compileonly,*TestReduceAllocationAndPointerComparisons*::*
* -XX:CompileCommand=dontinline,*TestReduceAllocationAndPointerComparisons*::*
* -XX:-TieredCompilation -Xcomp
- * compiler.c2.TestReduceAllocationAndPointerComparisons
- * @run main compiler.c2.TestReduceAllocationAndPointerComparisons
+ * compiler.escapeAnalysis.TestReduceAllocationAndPointerComparisons
+ * @run main compiler.escapeAnalysis.TestReduceAllocationAndPointerComparisons
*/
-package compiler.c2;
+package compiler.escapeAnalysis;
public class TestReduceAllocationAndPointerComparisons {
public static void main(String[] args) {
diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationOptimizedOutPhi.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationOptimizedOutPhi.java
new file mode 100644
index 000000000000..25660912fac7
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReduceAllocationOptimizedOutPhi.java
@@ -0,0 +1,92 @@
+/*
+ * 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 8389873
+ * @summary Check that Reduce Allocation Merges correctly handle the situation
+ * where the Phi was optimized out by IGVN during CG construction.
+ * @run main/othervm -XX:CompileCommand=compileonly,*${test.main.class}*::*
+ * -Xcomp -XX:-TieredCompilation ${test.main.class}
+ * @run main ${test.main.class}
+ */
+
+package compiler.escapeAnalysis;
+
+public class TestReduceAllocationOptimizedOutPhi {
+ static int var_369;
+
+ public static void main(String[] args) {
+ for (int i = 0; i < 100; i++) {
+ test();
+ }
+ }
+
+ static void test() {
+ switch (new Foo().var_2) {
+ case 3:
+ var_369 = (1.0002043F == new Foo().var_2 ? new Bar() : new Bar()).var_151;
+ var_369 = 4;
+ }
+ for (short var_480 = 0; var_480 < 1; var_480++) {
+ var_369 = new Bar().var_151;
+ }
+ }
+}
+
+class Foo {
+ long var_1 = 9;
+ char var_2 = 'B';
+
+ Foo() {
+ int var_121 = 9;
+ for (var_121 = 9; var_121 >= 0; var_121--) {
+ double var_122 = 2.2250738585072014E-308 - (Integer) 0;
+ float var_123 = '@' * (Integer) (Byte.valueOf((byte) 9) * var_121);
+ }
+ long var_124 = (Float.intBitsToFloat(608) == '(' ? 7L : 5);
+ byte var_127 = 5;
+ for (var_127 = 55; var_127 >= 0; var_127--) {
+ if (370 <= -Byte.valueOf((byte) 14)) {
+ long var_131 = 71L + (Integer) (-Character.valueOf('d'));
+ ++var_131;
+ var_131--;
+ var_131 >>>= var_131;
+ }
+ }
+ byte var_132 = 0;
+ for (var_132 = 0; var_132 < 6; var_132++) {
+ byte var_134 = ++var_127;
+ short var_135 = 2046;
+ double var_136 = -var_132 < 0.18568599F ? 0.44624205067529954 * Long.valueOf(1048575) + '~' : Short.valueOf((short) 4085);
+ byte var_137 = 3;
+ double var_138 = -Integer.valueOf(6) - 0.9994681372166424 * Long.valueOf(3) + (Integer) (+Short.valueOf((short) 512));
+ int var_141 = (Integer) (Byte.valueOf((byte) 7) * Character.valueOf('U')) - 2;
+ }
+ }
+}
+
+class Bar {
+ Byte var_151 = 3;
+}
+
diff --git a/test/hotspot/jtreg/compiler/c2/TestReducePhiOnCmpWithNoOptPtrCompare.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReducePhiOnCmpWithNoOptPtrCompare.java
similarity index 96%
rename from test/hotspot/jtreg/compiler/c2/TestReducePhiOnCmpWithNoOptPtrCompare.java
rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestReducePhiOnCmpWithNoOptPtrCompare.java
index 1247effd46d3..f56881ef3fbf 100644
--- a/test/hotspot/jtreg/compiler/c2/TestReducePhiOnCmpWithNoOptPtrCompare.java
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReducePhiOnCmpWithNoOptPtrCompare.java
@@ -26,10 +26,10 @@
* @bug 8361140
* @summary Test ConnectionGraph::reduce_phi_on_cmp when OptimizePtrCompare is disabled
* @library /test/lib /
- * @run driver compiler.c2.TestReducePhiOnCmpWithNoOptPtrCompare
+ * @run driver compiler.escapeAnalysis.TestReducePhiOnCmpWithNoOptPtrCompare
*/
-package compiler.c2;
+package compiler.escapeAnalysis;
import java.util.Random;
import jdk.test.lib.Asserts;
@@ -87,4 +87,4 @@ public boolean equals(Object o) {
return (p.x == x) && (p.y == y);
}
}
-}
\ No newline at end of file
+}
diff --git a/test/hotspot/jtreg/compiler/c2/TestScalarReplacementMaxLiveNodes.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestScalarReplacementMaxLiveNodes.java
similarity index 95%
rename from test/hotspot/jtreg/compiler/c2/TestScalarReplacementMaxLiveNodes.java
rename to test/hotspot/jtreg/compiler/escapeAnalysis/TestScalarReplacementMaxLiveNodes.java
index b5e349307c36..56f74d2285b3 100644
--- a/test/hotspot/jtreg/compiler/c2/TestScalarReplacementMaxLiveNodes.java
+++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestScalarReplacementMaxLiveNodes.java
@@ -28,10 +28,10 @@
* @library /test/lib /
* @requires vm.debug & vm.compiler2.enabled
* @compile -XDstringConcat=inline TestScalarReplacementMaxLiveNodes.java
- * @run main/othervm/timeout=480 compiler.c2.TestScalarReplacementMaxLiveNodes
+ * @run main/othervm/timeout=480 compiler.escapeAnalysis.TestScalarReplacementMaxLiveNodes
* @run main/othervm/timeout=480 -Xbatch -XX:-OptimizeStringConcat -XX:-TieredCompilation
* -XX:+UnlockDiagnosticVMOptions -XX:+ReduceAllocationMerges
- * -XX:CompileCommand=dontinline,compiler.c2.TestScalarReplacementMaxLiveNodes::test
+ * -XX:CompileCommand=dontinline,compiler.escapeAnalysis.TestScalarReplacementMaxLiveNodes::test
* -XX:CompileCommand=compileonly,*TestScalarReplacementMaxLiveNodes*::*test*
* -XX:CompileCommand=inline,*String*::*
* -XX:CompileCommand=dontinline,*StringBuilder*::ensureCapacityInternal
@@ -39,9 +39,9 @@
* -XX:NodeCountInliningCutoff=220000
* -XX:DesiredMethodLimit=100000
* -XX:+IgnoreUnrecognizedVMOptions -XX:CompileTaskTimeout=0
- * compiler.c2.TestScalarReplacementMaxLiveNodes
+ * compiler.escapeAnalysis.TestScalarReplacementMaxLiveNodes
*/
-package compiler.c2;
+package compiler.escapeAnalysis;
public class TestScalarReplacementMaxLiveNodes {
public static void main(String[] args) {
From c82f3b8fdf466499e5174c07345db714ab70e445 Mon Sep 17 00:00:00 2001
From: Aleksey Shipilev
Date: Tue, 18 Aug 2026 16:17:47 +0000
Subject: [PATCH 60/88] 8388292: Shenandoah: Micro-optimize CompressedOops uses
Reviewed-by: wkemper, ruili
---
.../gc/shenandoah/shenandoahBarrierSet.inline.hpp | 2 +-
.../share/gc/shenandoah/shenandoahHeap.inline.hpp | 12 ++++++------
.../share/gc/shenandoah/shenandoahRuntime.cpp | 2 +-
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
index 92f360216f40..b2f5fbad5cf0 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp
@@ -187,7 +187,7 @@ inline void ShenandoahBarrierSet::satb_barrier(T *field) {
if (ShenandoahSATBBarrier && _heap->is_concurrent_mark_in_progress()) {
T heap_oop = RawAccess<>::oop_load(field);
if (!CompressedOops::is_null(heap_oop)) {
- enqueue(CompressedOops::decode(heap_oop));
+ enqueue(CompressedOops::decode_not_null(heap_oop));
}
}
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
index a34a91c6d867..ff0271db8fda 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.inline.hpp
@@ -139,7 +139,7 @@ inline void ShenandoahHeap::conc_update_with_forwarded(T* p) {
// Either we succeed in updating the reference, or something else gets in our way.
// We don't care if that is another concurrent GC update, or another mutator update.
- atomic_update_oop(fwd, p, obj);
+ atomic_update_oop(fwd, p, o);
}
}
}
@@ -196,14 +196,14 @@ inline void ShenandoahHeap::atomic_update_oop(oop update, oop* addr, oop compare
inline void ShenandoahHeap::atomic_update_oop(oop update, narrowOop* addr, narrowOop compare) {
assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr));
- narrowOop u = CompressedOops::encode(update);
+ narrowOop u = CompressedOops::encode_not_null(update);
AtomicAccess::cmpxchg(addr, compare, u, memory_order_release);
}
inline void ShenandoahHeap::atomic_update_oop(oop update, narrowOop* addr, oop compare) {
assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr));
narrowOop c = CompressedOops::encode(compare);
- narrowOop u = CompressedOops::encode(update);
+ narrowOop u = CompressedOops::encode_not_null(update);
AtomicAccess::cmpxchg(addr, c, u, memory_order_release);
}
@@ -214,14 +214,14 @@ inline bool ShenandoahHeap::atomic_update_oop_check(oop update, oop* addr, oop c
inline bool ShenandoahHeap::atomic_update_oop_check(oop update, narrowOop* addr, narrowOop compare) {
assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr));
- narrowOop u = CompressedOops::encode(update);
+ narrowOop u = CompressedOops::encode_not_null(update);
return (narrowOop) AtomicAccess::cmpxchg(addr, compare, u, memory_order_release) == compare;
}
inline bool ShenandoahHeap::atomic_update_oop_check(oop update, narrowOop* addr, oop compare) {
assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr));
narrowOop c = CompressedOops::encode(compare);
- narrowOop u = CompressedOops::encode(update);
+ narrowOop u = CompressedOops::encode_not_null(update);
return CompressedOops::decode(AtomicAccess::cmpxchg(addr, c, u, memory_order_release)) == compare;
}
@@ -236,7 +236,7 @@ inline void ShenandoahHeap::atomic_clear_oop(oop* addr, oop compare) {
inline void ShenandoahHeap::atomic_clear_oop(narrowOop* addr, oop compare) {
assert(is_aligned(addr, sizeof(narrowOop)), "Address should be aligned: " PTR_FORMAT, p2i(addr));
- narrowOop cmp = CompressedOops::encode(compare);
+ narrowOop cmp = CompressedOops::encode_not_null(compare);
AtomicAccess::cmpxchg(addr, cmp, narrowOop(), memory_order_relaxed);
}
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp
index 77c1bab4c3ea..00910d3035ed 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp
@@ -63,7 +63,7 @@ JRT_LEAF(narrowOop, ShenandoahRuntime::load_reference_barrier_strong_narrow_narr
assert(!CompressedOops::is_null(src), "Filtered by caller");
oop s = CompressedOops::decode_not_null(src);
oop r = ShenandoahBarrierSet::barrier_set()->load_reference_barrier_mutator(s, load_addr);
- return CompressedOops::encode(r);
+ return CompressedOops::encode_not_null(r);
JRT_END
JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak(oopDesc* src, oop* load_addr))
From 8ac703a8d093f049e051d5a5b6346bccdd9d7e47 Mon Sep 17 00:00:00 2001
From: Daisuke Yamazaki
Date: Tue, 18 Aug 2026 16:21:06 +0000
Subject: [PATCH 61/88] 8386861: [Valhalla] emit_opSubstitutabilityCheck
speedup
Reviewed-by: chagedorn, mdoerr
---
src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp | 10 +++-------
src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp | 8 ++------
src/hotspot/share/c1/c1_LIRGenerator.cpp | 5 +++++
3 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
index 1d31777ad0cb..8f2eb05cbd5a 100644
--- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
@@ -3180,13 +3180,9 @@ void LIR_Assembler::emit_opSubstitutabilityCheck(LIR_OpSubstitutabilityCheck* op
} else {
Register tmp1 = op->tmp1()->as_register();
Register tmp2 = op->tmp2()->as_register();
- if (left == right) { // same operand, so clearly the same klasses, let's save the check
- __ b(*op->stub()->entry()); // -> do slow check
- } else {
- __ cmp_klasses_from_objects(CR0, left, right, tmp1, tmp2);
- __ bc_far_optimized(Assembler::bcondCRbiIs1, __ bi0(CR0, Assembler::equal),
- *op->stub()->entry()); // same klass -> do slow check
- }
+ __ cmp_klasses_from_objects(CR0, left, right, tmp1, tmp2);
+ __ bc_far_optimized(Assembler::bcondCRbiIs1, __ bi0(CR0, Assembler::equal),
+ *op->stub()->entry()); // same klass -> do slow check
// fall through to L_oops_not_equal
}
diff --git a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
index f745d80f4c52..84f99215f156 100644
--- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
@@ -1628,12 +1628,8 @@ void LIR_Assembler::emit_opSubstitutabilityCheck(LIR_OpSubstitutabilityCheck* op
} else {
Register tmp1 = op->tmp1()->as_register();
Register tmp2 = op->tmp2()->as_register();
- if (left == right) { // same operand, so clearly the same klasses, let's save the check
- __ jmp (*op->stub()->entry()); // -> do slow check
- } else {
- __ cmp_klasses_from_objects(left, right, tmp1, tmp2);
- __ jcc(Assembler::equal, *op->stub()->entry()); // same klass -> do slow check
- }
+ __ cmp_klasses_from_objects(left, right, tmp1, tmp2);
+ __ jcc(Assembler::equal, *op->stub()->entry()); // same klass -> do slow check
// fall through to L_oops_not_equal
}
diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp
index 52bb4e224b41..19b4d9ae203c 100644
--- a/src/hotspot/share/c1/c1_LIRGenerator.cpp
+++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp
@@ -3418,6 +3418,11 @@ void LIRGenerator::substitutability_check(If* x, LIRItem& left, LIRItem& right)
void LIRGenerator::substitutability_check_common(Value left_val, Value right_val, LIRItem& left, LIRItem& right,
LIR_Opr equal_result, LIR_Opr not_equal_result, LIR_Opr result,
CodeEmitInfo* info) {
+ if (left.result() == right.result()) {
+ __ move(equal_result, result);
+ return;
+ }
+
LIR_Opr tmp1 = LIR_OprFact::illegalOpr;
LIR_Opr tmp2 = LIR_OprFact::illegalOpr;
From 87e206b81e80215562bd0e7bba52efdf5ccff17c Mon Sep 17 00:00:00 2001
From: Jorn Vernee
Date: Tue, 18 Aug 2026 17:21:04 +0000
Subject: [PATCH 62/88] 8388792: False positives in jdk_foreign test suite
Reviewed-by: liach
---
test/jdk/java/foreign/TestByteBuffer.java | 25 +++++++++++++------
.../java/foreign/TestFunctionDescriptor.java | 15 +----------
test/jdk/java/foreign/TestLayouts.java | 21 ++++++++++------
.../jdk/java/foreign/TestMemoryAlignment.java | 18 -------------
test/jdk/java/foreign/TestSpliterator.java | 14 +++++++----
5 files changed, 42 insertions(+), 51 deletions(-)
diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java
index 3a5d1ef7c970..e45bb3fdbf0d 100644
--- a/test/jdk/java/foreign/TestByteBuffer.java
+++ b/test/jdk/java/foreign/TestByteBuffer.java
@@ -53,9 +53,7 @@
import java.nio.MappedByteBuffer;
import java.nio.ShortBuffer;
import java.nio.channels.FileChannel;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
+import java.nio.file.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -603,11 +601,24 @@ public void testMapZeroSize() throws IOException {
}
}
- @Test(expectedExceptions = UnsupportedOperationException.class)
+ @Test
public void testMapCustomPath() throws IOException {
- Path path = Path.of(URI.create("jrt:/"));
- try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
- fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, Arena.ofAuto());
+ // Zip file systems do support creating file channels
+ // but do not support memory mapping those files
+ Path scratch = Path.of("testMapCustomPath");
+ Files.createDirectories(scratch);
+ Path zipFile = scratch.resolve("test.zip");
+
+ try (FileSystem zipFs = FileSystems.newFileSystem(zipFile, Map.of("create", true))) {
+ // create test file
+ Path testFile = zipFs.getPath("/test_file.txt");
+ Files.writeString(testFile, "testing", StandardOpenOption.CREATE_NEW);
+
+ // now try to map it
+ try (FileChannel fileChannel = FileChannel.open(testFile, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
+ assertThrows(UnsupportedOperationException.class,
+ () -> fileChannel.map(FileChannel.MapMode.READ_WRITE, 0L, 0L, Arena.ofAuto()));
+ }
}
}
diff --git a/test/jdk/java/foreign/TestFunctionDescriptor.java b/test/jdk/java/foreign/TestFunctionDescriptor.java
index f4f9290c97d1..ef06f1048b20 100644
--- a/test/jdk/java/foreign/TestFunctionDescriptor.java
+++ b/test/jdk/java/foreign/TestFunctionDescriptor.java
@@ -34,10 +34,7 @@
import java.util.Optional;
import org.testng.annotations.Test;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotEquals;
-import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.*;
public class TestFunctionDescriptor extends NativeTestHelper {
@@ -116,16 +113,6 @@ public void testCarrierMethodType() {
assertEquals(cmt, MethodType.methodType(int.class, int.class, MemorySegment.class, MemorySegment.class));
}
- @Test(expectedExceptions = IllegalArgumentException.class)
- public void testBadCarrierMethodType() {
- FunctionDescriptor fd = FunctionDescriptor.of(C_INT,
- C_INT,
- MemoryLayout.structLayout(C_INT, C_INT),
- MemoryLayout.sequenceLayout(3, C_INT),
- MemoryLayout.paddingLayout(4));
- fd.toMethodType(); // should throw
- }
-
@Test(expectedExceptions = IllegalArgumentException.class)
public void testIllegalInsertArgNegIndex() {
FunctionDescriptor fd = FunctionDescriptor.of(C_INT);
diff --git a/test/jdk/java/foreign/TestLayouts.java b/test/jdk/java/foreign/TestLayouts.java
index 2c826ec9e213..2606e0481b32 100644
--- a/test/jdk/java/foreign/TestLayouts.java
+++ b/test/jdk/java/foreign/TestLayouts.java
@@ -307,10 +307,12 @@ public void testBadByteAlignment(MemoryLayout layout, long byteAlign) {
}
}
- @Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class)
+ @Test(dataProvider="layoutsAndAlignments")
public void testBadSequenceElementAlignmentTooBig(MemoryLayout layout, long byteAlign) {
- layout = layout.withByteAlignment(layout.byteSize() * 2); // hyper-align
- MemoryLayout.sequenceLayout(1, layout);
+ MemoryLayout elementLayout = layout.withByteAlignment(nextPowerOfTwo(layout.byteSize() * 2)); // hyper-align
+ IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
+ () -> MemoryLayout.sequenceLayout(1, elementLayout));
+ assertEquals(iae.getMessage(), "Element layout size is not multiple of alignment");
}
@Test(dataProvider="layoutsAndAlignments")
@@ -348,15 +350,16 @@ public void testBadElementsElementSizeNotMultipleOfAlignment(MemoryLayout layout
}
}
- @Test(dataProvider="layoutsAndAlignments", expectedExceptions = IllegalArgumentException.class)
+ @Test(dataProvider="layoutsAndAlignments")
public void testBadStruct(MemoryLayout layout, long byteAlign) {
- layout = layout.withByteAlignment(layout.byteSize() * 2); // hyper-align
- MemoryLayout.structLayout(layout, layout);
+ MemoryLayout elementLayout = layout.withByteAlignment(nextPowerOfTwo(layout.byteSize() * 2)); // hyper-align
+ IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
+ () -> MemoryLayout.structLayout(elementLayout, elementLayout));
+ assertTrue(iae.getMessage().contains("Invalid alignment constraint for member layout"));
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testSequenceElement() {
- SequenceLayout layout = MemoryLayout.sequenceLayout(10, JAVA_INT);
// Step must be != 0
PathElement.sequenceElement(3, 0);
}
@@ -543,4 +546,8 @@ static Stream groupLayoutStream() {
ValueLayout.JAVA_LONG,
ValueLayout.JAVA_DOUBLE,
};
+
+ private static long nextPowerOfTwo(long input) {
+ return 1L << -Long.numberOfLeadingZeros(input - 1);
+ }
}
diff --git a/test/jdk/java/foreign/TestMemoryAlignment.java b/test/jdk/java/foreign/TestMemoryAlignment.java
index 10916b731d5c..19f9f576ab2e 100644
--- a/test/jdk/java/foreign/TestMemoryAlignment.java
+++ b/test/jdk/java/foreign/TestMemoryAlignment.java
@@ -73,24 +73,6 @@ public void testAlignedAccess(long align) {
}
}
- @Test(dataProvider = "alignments")
- public void testUnalignedAccess(long align) {
- ValueLayout layout = ValueLayout.JAVA_INT
- .withOrder(ByteOrder.BIG_ENDIAN);
- assertEquals(layout.byteAlignment(), 4);
- ValueLayout aligned = layout.withByteAlignment(align);
- try (Arena arena = Arena.ofConfined()) {
- MemoryLayout alignedGroup = MemoryLayout.structLayout(MemoryLayout.paddingLayout(1), aligned);
- assertEquals(alignedGroup.byteAlignment(), align);
- VarHandle vh = aligned.varHandle();
- MemorySegment segment = arena.allocate(alignedGroup);;
- vh.set(segment.asSlice(1L), 0L, -42);
- assertEquals(align, 8); //this is the only case where access is aligned
- } catch (IllegalArgumentException ex) {
- assertNotEquals(align, 8); //if align != 8, access is always unaligned
- }
- }
-
@Test(dataProvider = "alignments")
public void testUnalignedPath(long align) {
MemoryLayout layout = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN);
diff --git a/test/jdk/java/foreign/TestSpliterator.java b/test/jdk/java/foreign/TestSpliterator.java
index f15e038879ea..285e8ab27ea0 100644
--- a/test/jdk/java/foreign/TestSpliterator.java
+++ b/test/jdk/java/foreign/TestSpliterator.java
@@ -29,9 +29,7 @@
import java.lang.foreign.*;
import java.lang.invoke.VarHandle;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Spliterator;
+import java.util.*;
import java.util.concurrent.CountedCompleter;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.atomic.AtomicLong;
@@ -147,13 +145,19 @@ public void testBadStreamElementSizeZero() {
.elements(MemoryLayout.sequenceLayout(0, ValueLayout.JAVA_INT));
}
- @Test(expectedExceptions = IllegalArgumentException.class)
+ @Test
public void testHyperAligned() {
Arena scope = Arena.ofAuto();
MemorySegment segment = scope.allocate(8, 1);
// compute an alignment constraint (in bytes) which exceed that of the native segment
long bigByteAlign = Long.lowestOneBit(segment.address()) << 1;
- segment.elements(MemoryLayout.sequenceLayout(2, ValueLayout.JAVA_INT.withByteAlignment(bigByteAlign)));
+ MemoryLayout elementLayout = MemoryLayout.structLayout(
+ Collections.nCopies(Math.toIntExact(bigByteAlign), ValueLayout.JAVA_BYTE).toArray(MemoryLayout[]::new))
+ .withByteAlignment(bigByteAlign);
+ SequenceLayout layout = MemoryLayout.sequenceLayout(2, elementLayout);
+ IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
+ () -> segment.elements(layout));
+ assertEquals(iae.getMessage(), "Incompatible alignment constraints");
}
static long sumSingle(long acc, MemorySegment segment) {
From 8f396579aaffc7a28c5acb131d2142b3b819cebd Mon Sep 17 00:00:00 2001
From: Vladimir Ivanov
Date: Wed, 19 Aug 2026 00:04:52 +0000
Subject: [PATCH 63/88] 8368597: make should support selection by status for
JTREG tests
Reviewed-by: erikj, jpai
---
make/RunTests.gmk | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/make/RunTests.gmk b/make/RunTests.gmk
index 1ae49298785e..1433ab32e5ce 100644
--- a/make/RunTests.gmk
+++ b/make/RunTests.gmk
@@ -1054,9 +1054,11 @@ define SetupRunJtregTestBody
$1_JTREG_BASIC_OPTIONS += -timeoutFactor:$$(JTREG_TIMEOUT_FACTOR)
clean-outputdirs-$1:
- $$(call LogWarn, Clean up dirs for $1)
- $$(RM) -r $$($1_TEST_SUPPORT_DIR)
- $$(RM) -r $$($1_TEST_RESULTS_DIR)
+ ifeq ($(JTREG_STATUS),)
+ $$(call LogWarn, Clean up dirs for $1)
+ $$(RM) -r $$($1_TEST_SUPPORT_DIR)
+ $$(RM) -r $$($1_TEST_RESULTS_DIR)
+ endif
$1_COMMAND_LINE := \
$$(JTREG_JAVA) $$($1_JTREG_LAUNCHER_OPTIONS) \
From a30608cb94583bb7394c7d41898fa731d84e9ee2 Mon Sep 17 00:00:00 2001
From: Xueming Shen
Date: Wed, 19 Aug 2026 00:14:46 +0000
Subject: [PATCH 64/88] 8389858: [VectorAPI] VectorMask.toVector API note
incorrectly describes the result for fp masks
Reviewed-by: psandoz
---
.../classes/jdk/incubator/vector/VectorMask.java | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java
index 607b194946b0..e7e28e7dc9eb 100644
--- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java
+++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorMask.java
@@ -545,13 +545,10 @@ public static VectorMask fromLong(VectorSpecies species, long bits) {
* {@code ETYPE} value and the {@code ETYPE} value representing
* {@code -1}, respectively.
*
- * @apiNote For the sake of static type checking, users may wish
- * to check the resulting vector against the expected integral
- * lane type or species. If the mask is for a float-point
- * species, then the resulting vector will have the same shape and
- * lane size, but an integral type. If the mask is for an
- * integral species, the resulting vector will be of exactly that
- * species.
+ * @apiNote The returned vector has the same species as this mask.
+ * For a floating-point species, a set mask lane is represented in
+ * the returned vector by the floating-point value {@code -1},
+ * rather than by setting all bits of the corresponding vector lane.
*
* @return a vector representation of this mask
* @see Vector#check(Class)
From d59bc6d6a6a4b67bdfebf8543f19bd1166184da2 Mon Sep 17 00:00:00 2001
From: Shiv Shah
Date: Wed, 19 Aug 2026 01:56:27 +0000
Subject: [PATCH 65/88] 8390330: Remove deprecated Log() constructor and
logTo() method from nsk.share.Log
Reviewed-by: dholmes, coleenp
---
.../isPackagePrivate/accipp001.java | 4 +--
.../Accessible/isPrivate/isPrivate001.java | 4 +--
.../isProtected/isProtected001.java | 4 +--
.../jdi/Accessible/isPublic/isPublic001.java | 4 +--
.../Accessible/modifiers/modifiers001.java | 2 +-
.../AttachingConnector/attach/attach002.java | 4 +--
.../reflectedType/reflectype001.java | 4 +--
.../reflectedType/reflectype002.java | 13 ++++-----
.../ReferenceType/allFields/allfields001.java | 13 ++++-----
.../ReferenceType/allFields/allfields002.java | 4 +--
.../ReferenceType/allFields/allfields003.java | 13 ++++-----
.../ReferenceType/allFields/allfields004.java | 13 ++++-----
.../allMethods/allmethods001.java | 13 ++++-----
.../allMethods/allmethods002.java | 4 +--
.../allMethods/allmethods003.java | 13 ++++-----
.../allMethods/allmethods004.java | 13 ++++-----
.../classObject/classobj001.java | 13 ++++-----
.../classObject/classobj002.java | 13 ++++-----
.../failedToInitialize001.java | 13 ++++-----
.../fieldByName/fieldbyname001.java | 13 ++++-----
.../fieldByName/fieldbyname002.java | 4 +--
.../fieldByName/fieldbyname003.java | 13 ++++-----
.../isAbstract/isAbstract001.java | 13 ++++-----
.../isVerified/isVerified001.java | 6 ++--
.../methodsByName_s/methbyname_s001.java | 13 ++++-----
.../methodsByName_s/methbyname_s002.java | 4 +--
.../methodsByName_s/methbyname_s003.java | 13 ++++-----
.../methodsByName_s/methbyname_s004.java | 13 ++++-----
.../methodsByName_ss/methbyname_ss001.java | 13 ++++-----
.../methodsByName_ss/methbyname_ss002.java | 4 +--
.../visibleFields/visibfield001.java | 13 ++++-----
.../visibleFields/visibfield002.java | 4 +--
.../visibleFields/visibfield003.java | 13 ++++-----
.../visibleFields/visibfield004.java | 13 ++++-----
.../visibleMethods/visibmethod001.java | 13 ++++-----
.../visibleMethods/visibmethod002.java | 4 +--
.../visibleMethods/visibmethod003.java | 13 ++++-----
.../visibleMethods/visibmethod004.java | 13 ++++-----
.../visibleMethods/visibmethod005.java | 13 ++++-----
.../jtreg/vmTestbase/nsk/share/Log.java | 28 ++-----------------
40 files changed, 176 insertions(+), 224 deletions(-)
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPackagePrivate/accipp001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPackagePrivate/accipp001.java
index 201995bde3f0..51b5c3918ad8 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPackagePrivate/accipp001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPackagePrivate/accipp001.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,7 @@
* of the JDI interface Accessible works fine with
* the ArrayType sub-interface.
*/
-public class accipp001 extends Log {
+public class accipp001 {
final static boolean MODE_VERBOSE = false;
/** The main class names of the debugger & debugee applications. */
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPrivate/isPrivate001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPrivate/isPrivate001.java
index c85fa1c0a2a0..d945672439fd 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPrivate/isPrivate001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPrivate/isPrivate001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -37,7 +37,7 @@
* for ArrayType, ClassType, InterfaceType
*/
-public class isPrivate001 extends Log {
+public class isPrivate001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false;
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isProtected/isProtected001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isProtected/isProtected001.java
index b1d5cfdf199e..49780d19b271 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isProtected/isProtected001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isProtected/isProtected001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -37,7 +37,7 @@
* for ArrayType, ClassType, InterfaceType
*/
-public class isProtected001 extends Log {
+public class isProtected001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false;
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPublic/isPublic001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPublic/isPublic001.java
index d4883f7573c5..0238dc5e6a06 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPublic/isPublic001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/isPublic/isPublic001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -37,7 +37,7 @@
* for ArrayType, ClassType, InterfaceType
*/
-public class isPublic001 extends Log {
+public class isPublic001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false;
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java
index a749568ad4c9..a8d22483ad87 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/Accessible/modifiers/modifiers001.java
@@ -37,7 +37,7 @@
* for ClassType, InterfaceType
*/
-public class modifiers001 extends Log {
+public class modifiers001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false;
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/AttachingConnector/attach/attach002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/AttachingConnector/attach/attach002.java
index 9132b986a079..7bc941afc71f 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/AttachingConnector/attach/attach002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/AttachingConnector/attach/attach002.java
@@ -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.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -44,7 +44,7 @@
* a target VM via com.sun.jdi.SharedMemoryAttach connector.
* The test also analyzes exit code of debugee's process.
*/
-public class attach002 extends Log {
+public class attach002 {
static final int PASSED = 0;
static final int FAILED = 2;
static final int JCK_STATUS_BASE = 95;
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype001.java
index bc093a807ba7..501f38358d86 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,7 +36,7 @@
* of the JDI interface ClassObjectReference of com.sun.jdi package
*/
-public class reflectype001 extends Log {
+public class reflectype001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002.java
index 227d67ab4ea7..ceb8015b5b37 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ClassObjectReference/reflectedType/reflectype002.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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,10 +35,11 @@
* of the JDI interface ClassObjectReference of com.sun.jdi package
*/
-public class reflectype002 extends Log {
+public class reflectype002 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -79,7 +80,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
private static void print_log_without_verbose(String message) {
@@ -105,11 +106,9 @@ private int runThis (String argv[], PrintStream out) {
argv = argHandler.getArguments();
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = debugee.createIOPipe();
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields001.java
index fb1b2f683ed0..b627f40f8178 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class allfields001 extends Log {
+public class allfields001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -148,7 +149,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -167,11 +168,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields002.java
index ae8e7ca3d6c1..ee94fd88bc42 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields002.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,7 +36,7 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class allfields002 extends Log {
+public class allfields002 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003.java
index c97278309c33..0ee77b3aa62e 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields003.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class allfields003 extends Log {
+public class allfields003 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -80,7 +81,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
private static void print_log_without_verbose(String message) {
@@ -105,11 +106,9 @@ private int runThis (String argv[], PrintStream out) {
verbose_mode = argHandler.verbose();
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = debugee.createIOPipe();
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields004.java
index 96cb9d2924d5..d47c016a7251 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields004.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allFields/allfields004.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class allfields004 extends Log {
+public class allfields004 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -78,7 +79,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -97,11 +98,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package for class without any declarated fields\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods001.java
index 0a15a520f3e5..adba1920a95b 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class allmethods001 extends Log {
+public class allmethods001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -203,7 +204,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -222,11 +223,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods002.java
index 7e0757957b42..856366d67e0d 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods002.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,7 +36,7 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class allmethods002 extends Log {
+public class allmethods002 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003.java
index 38039d69767c..5bf40e7c57b1 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods003.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class allmethods003 extends Log {
+public class allmethods003 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -80,7 +81,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
private static void print_log_without_verbose(String message) {
@@ -106,11 +107,9 @@ private int runThis (String argv[], PrintStream out) {
argv = argHandler.getArguments();
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = debugee.createIOPipe();
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods004.java
index 681bf7af8bb9..1a06a9a55bed 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods004.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/allMethods/allmethods004.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class allmethods004 extends Log {
+public class allmethods004 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -78,7 +79,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -97,11 +98,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package for class without any methods\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj001.java
index 1948b32cdc49..eb8b3e097da0 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class classobj001 extends Log {
+public class classobj001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -104,7 +105,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -123,11 +124,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package for ArraType, ClassType, InterfaceType\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002.java
index 82322f250e1f..7fcc0796f247 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/classObject/classobj002.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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,10 +35,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class classobj002 extends Log {
+public class classobj002 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -79,7 +80,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
private static void print_log_without_verbose(String message) {
@@ -105,11 +106,9 @@ private int runThis (String argv[], PrintStream out) {
argv = argHandler.getArguments();
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = debugee.createIOPipe();
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedToInitialize001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedToInitialize001.java
index 096d1c24ca5a..78dc82ad6ee5 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedToInitialize001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/failedToInitialize/failedToInitialize001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -37,9 +37,10 @@
* for ClassType, InterfaceType
*/
-public class failedToInitialize001 extends Log {
+public class failedToInitialize001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false;
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -93,7 +94,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -112,11 +113,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package for ClassType, InterfaceType\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname001.java
index 1bfb90ff309b..7319373724a1 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class fieldbyname001 extends Log {
+public class fieldbyname001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -141,7 +142,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -160,11 +161,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname002.java
index 32eea48a50f0..7721afec2a2a 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname002.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,7 +36,7 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class fieldbyname002 extends Log {
+public class fieldbyname002 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003.java
index a8c7f6f35fc9..572668ca5e5f 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/fieldByName/fieldbyname003.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class fieldbyname003 extends Log {
+public class fieldbyname003 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -81,7 +82,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
private static void print_log_without_verbose(String message) {
@@ -107,11 +108,9 @@ private int runThis (String argv[], PrintStream out) {
argv = argHandler.getArguments();
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = debugee.createIOPipe();
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isAbstract001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isAbstract001.java
index d7638379138a..d496dfcf5292 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isAbstract001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isAbstract/isAbstract001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -37,9 +37,10 @@
* for ClassType, InterfaceType
*/
-public class isAbstract001 extends Log {
+public class isAbstract001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false;
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -91,7 +92,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -110,11 +111,9 @@ private int runThis (String argv[], PrintStream out) {
verbose_mode = argHandler.verbose();
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isVerified001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isVerified001.java
index f0bb211b7da3..94b2bba78b2c 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isVerified001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/isVerified/isVerified001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -37,7 +37,7 @@
* for ClassType, InterfaceType
*/
-public class isVerified001 extends Log {
+public class isVerified001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false;
@@ -97,7 +97,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ logHandler.display(message);
}
/**
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s001.java
index b059903782d1..8d85373b68e7 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class methbyname_s001 extends Log {
+public class methbyname_s001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -183,7 +184,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -202,11 +203,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s002.java
index e09a6e384ada..57663947dd15 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s002.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,7 +36,7 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class methbyname_s002 extends Log {
+public class methbyname_s002 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003.java
index ef3885f6e5f7..d9a7aa3becf9 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s003.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class methbyname_s003 extends Log {
+public class methbyname_s003 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -80,7 +81,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
private static void print_log_without_verbose(String message) {
@@ -106,11 +107,9 @@ private int runThis (String argv[], PrintStream out) {
argv = argHandler.getArguments();
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = debugee.createIOPipe();
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s004.java
index 47bb9b350f3c..4d7892f4c903 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s004.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_s/methbyname_s004.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class methbyname_s004 extends Log {
+public class methbyname_s004 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -109,7 +110,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -128,11 +129,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package for overloaded methods\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss001.java
index 08da95dd7fd8..dae3e3c06cf9 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class methbyname_ss001 extends Log {
+public class methbyname_ss001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -196,7 +197,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -215,11 +216,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" ReferenceType interface of the com.sun.jdi package\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss002.java
index e3090542cdd3..4fdd23afb729 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/methodsByName_ss/methbyname_ss002.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,7 +36,7 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class methbyname_ss002 extends Log {
+public class methbyname_ss002 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield001.java
index cf6f4ae420f8..b42249bf6980 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class visibfield001 extends Log {
+public class visibfield001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -135,7 +136,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -154,11 +155,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield002.java
index a9cb1fb4f412..1e11aac9eba6 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield002.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,7 +36,7 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class visibfield002 extends Log {
+public class visibfield002 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003.java
index 14520271c172..e730099bb7bc 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield003.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class visibfield003 extends Log {
+public class visibfield003 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -80,7 +81,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
private static void print_log_without_verbose(String message) {
@@ -106,11 +107,9 @@ private int runThis (String argv[], PrintStream out) {
argv = argHandler.getArguments();
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = debugee.createIOPipe();
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield004.java
index 5eee5228479f..78e0a925369c 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield004.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleFields/visibfield004.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class visibfield004 extends Log {
+public class visibfield004 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -78,7 +79,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -97,11 +98,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package for class without visible fields\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod001.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod001.java
index d3bcc84c7e29..6c5cc8de830d 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod001.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod001.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class visibmethod001 extends Log {
+public class visibmethod001 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -181,7 +182,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -200,11 +201,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod002.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod002.java
index 5f3f9e1d6e11..b8c2d032c315 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod002.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod002.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,7 +36,7 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class visibmethod002 extends Log {
+public class visibmethod002 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003.java
index 94f8616e6274..ee6bde142b55 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod003.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class visibmethod003 extends Log {
+public class visibmethod003 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to true
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -80,7 +81,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
private static void print_log_without_verbose(String message) {
@@ -106,11 +107,9 @@ private int runThis (String argv[], PrintStream out) {
argv = argHandler.getArguments();
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = debugee.createIOPipe();
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod004.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod004.java
index ba1449d960ed..8711a559c32a 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod004.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod004.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class visibmethod004 extends Log {
+public class visibmethod004 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -78,7 +79,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -97,11 +98,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package for class without visible methods\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod005.java b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod005.java
index 0c752e720c3f..28bec81d290d 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod005.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jdi/ReferenceType/visibleMethods/visibmethod005.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -36,10 +36,11 @@
* of the JDI interface ReferenceType of com.sun.jdi package
*/
-public class visibmethod005 extends Log {
+public class visibmethod005 {
static java.io.PrintStream out_stream;
static boolean verbose_mode = false; // test argument -vbs or -verbose switches to static
// - for more easy failure evaluation
+ static Log log;
/** The main class names of the debugger & debugee applications. */
private final static String
@@ -90,7 +91,7 @@ public static int run (String argv[], PrintStream out) {
}
private void print_log_on_verbose(String message) {
- display(message);
+ log.display(message);
}
/**
@@ -109,11 +110,9 @@ private int runThis (String argv[], PrintStream out) {
out_stream.println(" of the com.sun.jdi package for multiple inherited abstract methods\n");
String debugee_launch_command = debugeeName;
- if (verbose_mode) {
- logTo(out_stream);
- }
+ log = new Log(out_stream, argHandler);
- Binder binder = new Binder(argHandler,this);
+ Binder binder = new Binder(argHandler, log);
Debugee debugee = binder.bindToDebugee(debugee_launch_command);
IOPipe pipe = new IOPipe(debugee);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java b/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java
index 99467fc03341..24711527606f 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/share/Log.java
@@ -144,25 +144,14 @@ public static String getLevelsString() {
/////////////////////////////////////////////////////////////////
- /**
- * Create new Log's only with Log(out) or with
- * Log(out,argsHandler) constructors.
- *
- * @deprecated Extending test class with Log is obsolete.
- */
- @Deprecated
- protected Log() {
- // Don't log exceptions from this method. It would just add unnecessary logs.
- loggedExceptions.add("nsk.share.jdi.SerialExecutionDebugger.executeTests");
- }
-
/**
* Incarnate new Log for the given stream and
* for non-verbose mode.
*/
public Log(PrintStream stream) {
- this();
+ // Don't log exceptions from this method. It would just add unnecessary logs.
+ loggedExceptions.add("nsk.share.jdi.SerialExecutionDebugger.executeTests");
out = stream;
}
@@ -337,19 +326,6 @@ private void logExceptionForFailureAnalysis(String msg) {
/////////////////////////////////////////////////////////////////
- /**
- * Redirect log to the given stream.
- *
- * @deprecated This method is obsolete.
- */
- @Deprecated
- protected synchronized void logTo(PrintStream stream) {
- if (out != null) {
- out.flush();
- }
- out = stream;
- }
-
/////////////////////////////////////////////////////////////////
/**
From 7d95b50c7f921bb4538b7fa5428f49078ea79b51 Mon Sep 17 00:00:00 2001
From: Gui Cao
Date: Wed, 19 Aug 2026 02:21:27 +0000
Subject: [PATCH 66/88] 8389378: RISC-V: Eliminate redundant sext.w by fusing
ConvI2L with int producers
Reviewed-by: dzhang, fyang
---
src/hotspot/cpu/riscv/riscv.ad | 151 +++++++++++++++++++++++++++++++++
1 file changed, 151 insertions(+)
diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad
index 6f9508865115..17cd4592fd29 100644
--- a/src/hotspot/cpu/riscv/riscv.ad
+++ b/src/hotspot/cpu/riscv/riscv.ad
@@ -8575,6 +8575,157 @@ instruct convI2L_reg_reg(iRegLNoSp dst, iRegIorL2I src)
ins_pipe(ialu_reg);
%}
+// Fused int-producer + ConvI2L rules.
+instruct convI2L_addI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
+ match(Set dst (ConvI2L (AddI src1 src2)));
+
+ ins_cost(ALU_COST);
+ format %{ "addw $dst, $src1, $src2\t#@convI2L_addI_reg_reg" %}
+
+ ins_encode %{
+ __ addw(as_Register($dst$$reg),
+ as_Register($src1$$reg),
+ as_Register($src2$$reg));
+ %}
+
+ ins_pipe(ialu_reg_reg);
+%}
+
+instruct convI2L_addI_reg_imm(iRegLNoSp dst, iRegIorL2I src1, immIAdd src2) %{
+ match(Set dst (ConvI2L (AddI src1 src2)));
+
+ ins_cost(ALU_COST);
+ format %{ "addiw $dst, $src1, $src2\t#@convI2L_addI_reg_imm" %}
+
+ ins_encode %{
+ __ addiw(as_Register($dst$$reg),
+ as_Register($src1$$reg),
+ $src2$$constant);
+ %}
+
+ ins_pipe(ialu_reg_imm);
+%}
+
+instruct convI2L_subI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
+ match(Set dst (ConvI2L (SubI src1 src2)));
+
+ ins_cost(ALU_COST);
+ format %{ "subw $dst, $src1, $src2\t#@convI2L_subI_reg_reg" %}
+
+ ins_encode %{
+ __ subw(as_Register($dst$$reg),
+ as_Register($src1$$reg),
+ as_Register($src2$$reg));
+ %}
+
+ ins_pipe(ialu_reg_reg);
+%}
+
+instruct convI2L_mulI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
+ match(Set dst (ConvI2L (MulI src1 src2)));
+
+ ins_cost(IMUL_COST);
+ format %{ "mulw $dst, $src1, $src2\t#@convI2L_mulI_reg_reg" %}
+
+ ins_encode %{
+ __ mulw(as_Register($dst$$reg),
+ as_Register($src1$$reg),
+ as_Register($src2$$reg));
+ %}
+
+ ins_pipe(imul_reg_reg);
+%}
+
+instruct convI2L_lShiftI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
+ match(Set dst (ConvI2L (LShiftI src1 src2)));
+
+ ins_cost(ALU_COST);
+ format %{ "sllw $dst, $src1, $src2\t#@convI2L_lShiftI_reg_reg" %}
+
+ ins_encode %{
+ __ sllw(as_Register($dst$$reg),
+ as_Register($src1$$reg),
+ as_Register($src2$$reg));
+ %}
+
+ ins_pipe(ialu_reg_reg_vshift);
+%}
+
+instruct convI2L_lShiftI_reg_imm(iRegLNoSp dst, iRegIorL2I src1, immI src2) %{
+ match(Set dst (ConvI2L (LShiftI src1 src2)));
+
+ ins_cost(ALU_COST);
+ format %{ "slliw $dst, $src1, ($src2 & 0x1f)\t#@convI2L_lShiftI_reg_imm" %}
+
+ ins_encode %{
+ __ slliw(as_Register($dst$$reg),
+ as_Register($src1$$reg),
+ (unsigned) $src2$$constant & 0x1f);
+ %}
+
+ ins_pipe(ialu_reg_shift);
+%}
+
+instruct convI2L_urShiftI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
+ match(Set dst (ConvI2L (URShiftI src1 src2)));
+
+ ins_cost(ALU_COST);
+ format %{ "srlw $dst, $src1, $src2\t#@convI2L_urShiftI_reg_reg" %}
+
+ ins_encode %{
+ __ srlw(as_Register($dst$$reg),
+ as_Register($src1$$reg),
+ as_Register($src2$$reg));
+ %}
+
+ ins_pipe(ialu_reg_reg_vshift);
+%}
+
+instruct convI2L_urShiftI_reg_imm(iRegLNoSp dst, iRegIorL2I src1, immI src2) %{
+ match(Set dst (ConvI2L (URShiftI src1 src2)));
+
+ ins_cost(ALU_COST);
+ format %{ "srliw $dst, $src1, ($src2 & 0x1f)\t#@convI2L_urShiftI_reg_imm" %}
+
+ ins_encode %{
+ __ srliw(as_Register($dst$$reg),
+ as_Register($src1$$reg),
+ (unsigned) $src2$$constant & 0x1f);
+ %}
+
+ ins_pipe(ialu_reg_shift);
+%}
+
+instruct convI2L_rShiftI_reg_reg(iRegLNoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
+ match(Set dst (ConvI2L (RShiftI src1 src2)));
+
+ ins_cost(ALU_COST);
+ format %{ "sraw $dst, $src1, $src2\t#@convI2L_rShiftI_reg_reg" %}
+
+ ins_encode %{
+ __ sraw(as_Register($dst$$reg),
+ as_Register($src1$$reg),
+ as_Register($src2$$reg));
+ %}
+
+ ins_pipe(ialu_reg_reg_vshift);
+%}
+
+instruct convI2L_rShiftI_reg_imm(iRegLNoSp dst, iRegIorL2I src1, immI src2) %{
+ match(Set dst (ConvI2L (RShiftI src1 src2)));
+
+ ins_cost(ALU_COST);
+ format %{ "sraiw $dst, $src1, ($src2 & 0x1f)\t#@convI2L_rShiftI_reg_imm" %}
+
+ ins_encode %{
+ __ sraiw(as_Register($dst$$reg),
+ as_Register($src1$$reg),
+ (unsigned) $src2$$constant & 0x1f);
+ %}
+
+ ins_pipe(ialu_reg_shift);
+%}
+
instruct convL2I_reg(iRegINoSp dst, iRegL src) %{
match(Set dst (ConvL2I src));
From 4b77534a55aa00ea2346a9740660b0c85a1f4374 Mon Sep 17 00:00:00 2001
From: Vladimir Kozlov
Date: Wed, 19 Aug 2026 05:15:36 +0000
Subject: [PATCH 67/88] 8390590: [BACKOUT] C2: Fix the memory around some
intrinsics nodes
Reviewed-by: dlong, vlivanov, thartmann
---
src/hotspot/share/opto/graphKit.cpp | 91 +++-----
src/hotspot/share/opto/graphKit.hpp | 3 +-
src/hotspot/share/opto/intrinsicnode.cpp | 2 +
src/hotspot/share/opto/intrinsicnode.hpp | 200 +++++++-----------
src/hotspot/share/opto/library_call.cpp | 14 +-
.../intrinsics/string/TestAntiDependency.java | 128 -----------
6 files changed, 111 insertions(+), 327 deletions(-)
delete mode 100644 test/hotspot/jtreg/compiler/intrinsics/string/TestAntiDependency.java
diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp
index e41beaa26e0d..a2da4efa2a1b 100644
--- a/src/hotspot/share/opto/graphKit.cpp
+++ b/src/hotspot/share/opto/graphKit.cpp
@@ -46,17 +46,14 @@
#include "opto/intrinsicnode.hpp"
#include "opto/locknode.hpp"
#include "opto/machnode.hpp"
-#include "opto/memnode.hpp"
#include "opto/multnode.hpp"
#include "opto/narrowptrnode.hpp"
#include "opto/opaquenode.hpp"
-#include "opto/opcodes.hpp"
#include "opto/parse.hpp"
#include "opto/reachability.hpp"
#include "opto/rootnode.hpp"
#include "opto/runtime.hpp"
#include "opto/subtypenode.hpp"
-#include "opto/type.hpp"
#include "runtime/arguments.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/sharedRuntime.hpp"
@@ -4862,81 +4859,51 @@ void GraphKit::store_String_coder(Node* str, Node* value) {
value, TypeInt::BYTE, T_BYTE, IN_HEAP | MO_UNORDERED);
}
-// If input and output memory types differ, capture the whole memory to preserve
-// the dependency between preceding and subsequent loads/stores.
-// For example, the following program:
-// StoreB
-// compress_string
-// LoadB
-// has this memory graph (use->def):
-// LoadB -> compress_string -> CharMem
-// ... -> StoreB -> ByteMem
-// The intrinsic hides the dependency between LoadB and StoreB, causing
-// the load to read from memory not containing the result of the StoreB.
-// The correct memory graph should look like this:
-// LoadB -> compress_string -> MergeMem -> StoreB
-Node* GraphKit::capture_memory(const TypePtr*& combined_type, const TypePtr* src_type, const TypePtr* dst_type) {
+// Capture src and dst memory state with a MergeMemNode
+Node* GraphKit::capture_memory(const TypePtr* src_type, const TypePtr* dst_type) {
if (src_type == dst_type) {
// Types are equal, we don't need a MergeMemNode
- combined_type = src_type;
return memory(src_type);
}
- Node* mem = reset_memory();
- set_all_memory(mem);
- combined_type = TypePtr::BOTTOM;
- return mem;
-}
-
-// If dst_type and src_type are different, str may have an anti-dependency with another node
-// consuming src_type.
-// For example:
-// compress_string
-// StoreC
-// has this memory graph (use->def):
-// compress_string -> MergeMem -> CharMem
-// StoreC
-// The scheduler needs to ensure that compress_string is not executed after StoreC, or it will read
-// the wrong memory. For normal loads, the scheduler computes its anti-dependencies to ensure the
-// memory it reads from is not killed. Since we do not compute anti-dependencies for
-// StrCompressedCopyNode, manually insert a MemBar so the anti-dependency becomes use-def
-// dependency:
-// StoreC -> MemBar -> MergeMem -> compress_string -> MergeMem -> CharMem
-// -------------------------------->
-void GraphKit::memory_effect(Node* res_mem, const TypePtr* src_type, const TypePtr* dst_type) {
- set_memory(res_mem, dst_type);
- if (src_type != dst_type) {
- Node* all_mem = reset_memory();
- set_all_memory(all_mem);
- Node* membar = new MemBarCPUOrderNode(C, C->get_alias_index(src_type), nullptr);
- membar->init_req(TypeFunc::Control, control());
- membar->init_req(TypeFunc::Memory, all_mem);
- membar = _gvn.transform(membar);
- set_control(_gvn.transform(new ProjNode(membar, TypeFunc::Control)));
- set_memory(_gvn.transform(new ProjNode(membar, TypeFunc::Memory)), src_type);
- }
+ MergeMemNode* merge = MergeMemNode::make(map()->memory());
+ record_for_igvn(merge); // fold it up later, if possible
+ int src_idx = C->get_alias_index(src_type);
+ int dst_idx = C->get_alias_index(dst_type);
+ merge->set_memory_at(src_idx, memory(src_idx));
+ merge->set_memory_at(dst_idx, memory(dst_idx));
+ return merge;
}
Node* GraphKit::compress_string(Node* src, const TypeAryPtr* src_type, Node* dst, Node* count) {
assert(Matcher::match_rule_supported(Op_StrCompressedCopy), "Intrinsic not supported");
assert(src_type == TypeAryPtr::BYTES || src_type == TypeAryPtr::CHARS, "invalid source type");
- const TypePtr* dst_type = TypeAryPtr::BYTES;
- const TypePtr* adr_type;
- Node* mem = capture_memory(adr_type, src_type, dst_type);
- StrCompressedCopyNode* str = new StrCompressedCopyNode(control(), mem, adr_type, src, dst, count);
+ // If input and output memory types differ, capture both states to preserve
+ // the dependency between preceding and subsequent loads/stores.
+ // For example, the following program:
+ // StoreB
+ // compress_string
+ // LoadB
+ // has this memory graph (use->def):
+ // LoadB -> compress_string -> CharMem
+ // ... -> StoreB -> ByteMem
+ // The intrinsic hides the dependency between LoadB and StoreB, causing
+ // the load to read from memory not containing the result of the StoreB.
+ // The correct memory graph should look like this:
+ // LoadB -> compress_string -> MergeMem(CharMem, StoreB(ByteMem))
+ Node* mem = capture_memory(src_type, TypeAryPtr::BYTES);
+ StrCompressedCopyNode* str = new StrCompressedCopyNode(control(), mem, src, dst, count);
Node* res_mem = _gvn.transform(new SCMemProjNode(_gvn.transform(str)));
- memory_effect(res_mem, src_type, dst_type);
+ set_memory(res_mem, TypeAryPtr::BYTES);
return str;
}
void GraphKit::inflate_string(Node* src, Node* dst, const TypeAryPtr* dst_type, Node* count) {
assert(Matcher::match_rule_supported(Op_StrInflatedCopy), "Intrinsic not supported");
assert(dst_type == TypeAryPtr::BYTES || dst_type == TypeAryPtr::CHARS, "invalid dest type");
- const TypePtr* src_type = TypeAryPtr::BYTES;
- const TypePtr* adr_type;
- Node* mem = capture_memory(adr_type, src_type, dst_type);
- StrInflatedCopyNode* str = new StrInflatedCopyNode(control(), mem, adr_type, src, dst, count);
- Node* res_mem = _gvn.transform(str);
- memory_effect(res_mem, src_type, dst_type);
+ // Capture src and dst memory (see comment in 'compress_string').
+ Node* mem = capture_memory(TypeAryPtr::BYTES, dst_type);
+ StrInflatedCopyNode* str = new StrInflatedCopyNode(control(), mem, src, dst, count);
+ set_memory(_gvn.transform(str), dst_type);
}
void GraphKit::inflate_string_slow(Node* src, Node* dst, Node* start, Node* count) {
diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp
index 1c109cb56757..59a95baa5e76 100644
--- a/src/hotspot/share/opto/graphKit.hpp
+++ b/src/hotspot/share/opto/graphKit.hpp
@@ -886,8 +886,7 @@ class GraphKit : public Phase {
Node* load_String_coder(Node* str, bool set_ctrl);
void store_String_value(Node* str, Node* value);
void store_String_coder(Node* str, Node* value);
- Node* capture_memory(const TypePtr*& combined_type, const TypePtr* src_type, const TypePtr* dst_type);
- void memory_effect(Node* res_mem, const TypePtr* src_type, const TypePtr* dst_type);
+ Node* capture_memory(const TypePtr* src_type, const TypePtr* dst_type);
Node* compress_string(Node* src, const TypeAryPtr* src_type, Node* dst, Node* count);
void inflate_string(Node* src, Node* dst, const TypeAryPtr* dst_type, Node* count);
void inflate_string_slow(Node* src, Node* dst, Node* start, Node* count);
diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp
index 887681233f16..d3e62dacfe80 100644
--- a/src/hotspot/share/opto/intrinsicnode.cpp
+++ b/src/hotspot/share/opto/intrinsicnode.cpp
@@ -63,6 +63,8 @@ const Type* StrIntrinsicNode::Value(PhaseGVN* phase) const {
return bottom_type();
}
+uint StrIntrinsicNode::size_of() const { return sizeof(*this); }
+
//=============================================================================
//------------------------------Ideal------------------------------------------
// Return a node which is more "ideal" than the current node. Strip out
diff --git a/src/hotspot/share/opto/intrinsicnode.hpp b/src/hotspot/share/opto/intrinsicnode.hpp
index 1fe61cfb1785..d81e7bed7e96 100644
--- a/src/hotspot/share/opto/intrinsicnode.hpp
+++ b/src/hotspot/share/opto/intrinsicnode.hpp
@@ -48,7 +48,7 @@ class PartialSubtypeCheckNode : public Node {
//------------------------------StrIntrinsic-------------------------------
// Base class for Ideal nodes used in String intrinsic code.
-class StrIntrinsicNode : public Node {
+class StrIntrinsicNode: public Node {
public:
// Possible encodings of the parameters passed to the string intrinsic.
// 'L' stands for Latin1 and 'U' stands for UTF16. For example, 'LU' means that
@@ -59,11 +59,7 @@ class StrIntrinsicNode : public Node {
protected:
// Encoding of strings. Used to select the right version of the intrinsic.
const ArgEncoding _encoding;
- virtual uint size_of() const override { return sizeof(StrIntrinsicNode); }
- virtual uint hash() const override { return Node::hash() + _encoding; }
- virtual bool cmp(const Node& n) const override {
- return Node::cmp(n) && _encoding == static_cast(n)._encoding;
- }
+ virtual uint size_of() const;
public:
StrIntrinsicNode(Node* control, Node* char_array_mem,
@@ -81,189 +77,141 @@ class StrIntrinsicNode : public Node {
Node(control, char_array_mem, s1, s2), _encoding(encoding) {
}
- virtual const TypePtr* adr_type() const override = 0;
- virtual uint match_edge(uint idx) const override;
- virtual uint ideal_reg() const override { return Op_RegI; }
- virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override;
- virtual const Type* Value(PhaseGVN* phase) const override;
+ virtual const TypePtr* adr_type() const { return TypeAryPtr::BYTES; }
+ virtual uint match_edge(uint idx) const;
+ virtual uint ideal_reg() const { return Op_RegI; }
+ virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
+ virtual const Type* Value(PhaseGVN* phase) const;
ArgEncoding encoding() const { return _encoding; }
private:
- virtual bool depends_only_on_test_impl() const override { return false; }
+ virtual bool depends_only_on_test_impl() const { return false; }
};
//------------------------------StrComp-------------------------------------
-class StrCompNode final : public StrIntrinsicNode {
+class StrCompNode: public StrIntrinsicNode {
public:
StrCompNode(Node* control, Node* char_array_mem,
Node* s1, Node* c1, Node* s2, Node* c2, ArgEncoding encoding):
StrIntrinsicNode(control, char_array_mem, s1, c1, s2, c2, encoding) {};
- virtual int Opcode() const override;
- virtual const Type* bottom_type() const override { return TypeInt::INT; }
- virtual const TypePtr* adr_type() const override { return TypeAryPtr::BYTES; }
+ virtual int Opcode() const;
+ virtual const Type* bottom_type() const { return TypeInt::INT; }
};
//------------------------------StrEquals-------------------------------------
-class StrEqualsNode final : public StrIntrinsicNode {
+class StrEqualsNode: public StrIntrinsicNode {
public:
StrEqualsNode(Node* control, Node* char_array_mem,
Node* s1, Node* s2, Node* c, ArgEncoding encoding):
StrIntrinsicNode(control, char_array_mem, s1, s2, c, encoding) {};
- virtual int Opcode() const override;
- virtual const Type* bottom_type() const override { return TypeInt::BOOL; }
- virtual const TypePtr* adr_type() const override { return TypeAryPtr::BYTES; }
+ virtual int Opcode() const;
+ virtual const Type* bottom_type() const { return TypeInt::BOOL; }
};
//------------------------------StrIndexOf-------------------------------------
-class StrIndexOfNode final : public StrIntrinsicNode {
+class StrIndexOfNode: public StrIntrinsicNode {
public:
StrIndexOfNode(Node* control, Node* char_array_mem,
Node* s1, Node* c1, Node* s2, Node* c2, ArgEncoding encoding):
StrIntrinsicNode(control, char_array_mem, s1, c1, s2, c2, encoding) {};
- virtual int Opcode() const override;
- virtual const Type* bottom_type() const override { return TypeInt::INT; }
- virtual const TypePtr* adr_type() const override { return TypeAryPtr::BYTES; }
+ virtual int Opcode() const;
+ virtual const Type* bottom_type() const { return TypeInt::INT; }
};
//------------------------------StrIndexOfChar-------------------------------------
-class StrIndexOfCharNode final : public StrIntrinsicNode {
+class StrIndexOfCharNode: public StrIntrinsicNode {
public:
StrIndexOfCharNode(Node* control, Node* char_array_mem,
Node* s1, Node* c1, Node* c, ArgEncoding encoding):
StrIntrinsicNode(control, char_array_mem, s1, c1, c, encoding) {};
- virtual int Opcode() const override;
- virtual const Type* bottom_type() const override { return TypeInt::INT; }
- virtual const TypePtr* adr_type() const override { return TypeAryPtr::BYTES; }
+ virtual int Opcode() const;
+ virtual const Type* bottom_type() const { return TypeInt::INT; }
};
//--------------------------StrCompressedCopy-------------------------------
-class StrCompressedCopyNode final : public StrIntrinsicNode {
-private:
- const TypePtr* const _adr_type;
-
-public:
- StrCompressedCopyNode(Node* control, Node* arymem, const TypePtr* adr_type,
+class StrCompressedCopyNode: public StrIntrinsicNode {
+ public:
+ StrCompressedCopyNode(Node* control, Node* arymem,
Node* s1, Node* s2, Node* c):
- StrIntrinsicNode(control, arymem, s1, s2, c, none), _adr_type(adr_type) {};
- virtual int Opcode() const override;
- virtual const Type* bottom_type() const override { return TypeInt::INT; }
- virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override;
-
-private:
- virtual uint size_of() const override { return sizeof(StrCompressedCopyNode); }
- virtual uint hash() const override { return StrIntrinsicNode::hash() + (uint)(uintptr_t) _adr_type; }
- virtual bool cmp(const Node& n) const override {
- return StrIntrinsicNode::cmp(n) && _adr_type == static_cast(n)._adr_type;
- }
- virtual const TypePtr* adr_type() const override { return _adr_type; }
+ StrIntrinsicNode(control, arymem, s1, s2, c, none) {};
+ virtual int Opcode() const;
+ virtual const Type* bottom_type() const { return TypeInt::INT; }
+ virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; }
+ virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
};
//--------------------------StrInflatedCopy---------------------------------
-class StrInflatedCopyNode final : public StrIntrinsicNode {
-private:
- const TypePtr* const _adr_type;
-
-public:
- StrInflatedCopyNode(Node* control, Node* arymem, const TypePtr* adr_type,
+class StrInflatedCopyNode: public StrIntrinsicNode {
+ public:
+ StrInflatedCopyNode(Node* control, Node* arymem,
Node* s1, Node* s2, Node* c):
- StrIntrinsicNode(control, arymem, s1, s2, c, none), _adr_type(adr_type) {};
- virtual int Opcode() const override;
- virtual const Type* bottom_type() const override { return Type::MEMORY; }
- virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override;
-
-private:
- virtual uint size_of() const override { return sizeof(StrInflatedCopyNode); }
- virtual uint hash() const override { return StrIntrinsicNode::hash() + (uint)(uintptr_t) _adr_type; }
- virtual bool cmp(const Node& n) const override {
- return StrIntrinsicNode::cmp(n) && _adr_type == static_cast(n)._adr_type;
- }
- virtual const TypePtr* adr_type() const override { return _adr_type; }
+ StrIntrinsicNode(control, arymem, s1, s2, c, none) {};
+ virtual int Opcode() const;
+ virtual const Type* bottom_type() const { return Type::MEMORY; }
+ virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; }
+ virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
};
//------------------------------AryEq---------------------------------------
-class AryEqNode final : public StrIntrinsicNode {
-private:
- const TypeAryPtr* const _in_adr_type;
-
-public:
- AryEqNode(Node* control, Node* char_array_mem, const TypeAryPtr* in_adr_type,
+class AryEqNode: public StrIntrinsicNode {
+ public:
+ AryEqNode(Node* control, Node* char_array_mem,
Node* s1, Node* s2, ArgEncoding encoding):
- StrIntrinsicNode(control, char_array_mem, s1, s2, encoding), _in_adr_type(in_adr_type) {};
- virtual int Opcode() const override;
- virtual const Type* bottom_type() const override { return TypeInt::BOOL; }
-
-private:
- virtual uint size_of() const override { return sizeof(AryEqNode); }
- virtual uint hash() const override { return StrIntrinsicNode::hash() + (uint)(uintptr_t) _in_adr_type; }
- virtual bool cmp(const Node& n) const override {
- return StrIntrinsicNode::cmp(n) && _in_adr_type == static_cast(n)._in_adr_type;
- }
- virtual const TypePtr* adr_type() const override { return _in_adr_type; }
+ StrIntrinsicNode(control, char_array_mem, s1, s2, encoding) {};
+ virtual int Opcode() const;
+ virtual const Type* bottom_type() const { return TypeInt::BOOL; }
};
//------------------------------CountPositives------------------------------
-class CountPositivesNode final : public StrIntrinsicNode {
+class CountPositivesNode: public StrIntrinsicNode {
public:
CountPositivesNode(Node* control, Node* char_array_mem, Node* s1, Node* c1):
StrIntrinsicNode(control, char_array_mem, s1, c1, none) {};
- virtual int Opcode() const override;
- virtual const Type* bottom_type() const override { return TypeInt::POS; }
- virtual const TypePtr* adr_type() const override { return TypeAryPtr::BYTES; }
+ virtual int Opcode() const;
+ virtual const Type* bottom_type() const { return TypeInt::POS; }
};
//------------------------------VectorizedHashCodeNode----------------------
-class VectorizedHashCodeNode final : public Node {
-private:
- const TypeAryPtr* const _in_adr_type;
-
-public:
- VectorizedHashCodeNode(Node* control, Node* ary_mem, const TypeAryPtr* in_adr_type, Node* arg1, Node* cnt1, Node* result, Node* basic_type)
- : Node(control, ary_mem, arg1, cnt1, result, basic_type), _in_adr_type(in_adr_type) {};
- virtual int Opcode() const override;
- virtual const Type* bottom_type() const override { return TypeInt::INT; }
- virtual uint match_edge(uint idx) const override;
- virtual uint ideal_reg() const override { return Op_RegI; }
- virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override;
- virtual const Type* Value(PhaseGVN* phase) const override;
+class VectorizedHashCodeNode: public Node {
+ public:
+ VectorizedHashCodeNode(Node* control, Node* ary_mem, Node* arg1, Node* cnt1, Node* result, Node* basic_type)
+ : Node(control, ary_mem, arg1, cnt1, result, basic_type) {};
+ virtual int Opcode() const;
+ virtual const Type* bottom_type() const { return TypeInt::INT; }
+ virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; }
+ virtual uint match_edge(uint idx) const;
+ virtual uint ideal_reg() const { return Op_RegI; }
+ virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
+ virtual const Type* Value(PhaseGVN* phase) const;
private:
- virtual uint size_of() const override { return sizeof(VectorizedHashCodeNode); }
- virtual uint hash() const override { return Node::hash() + (uint)(uintptr_t) _in_adr_type; }
- virtual bool cmp(const Node& n) const override {
- return Node::cmp(n) && _in_adr_type == static_cast(n)._in_adr_type;
- }
- virtual const TypePtr* adr_type() const override { return _in_adr_type; }
- virtual bool depends_only_on_test_impl() const override { return false; }
+ virtual bool depends_only_on_test_impl() const { return false; }
};
//------------------------------EncodeISOArray--------------------------------
// encode char[] to byte[] in ISO_8859_1 or ASCII
-class EncodeISOArrayNode final : public Node {
-private:
- const TypePtr* const _adr_type;
+class EncodeISOArrayNode: public Node {
bool _ascii;
-
-public:
- EncodeISOArrayNode(Node* control, Node* arymem, const TypePtr* adr_type, Node* s1, Node* s2, Node* c, bool ascii)
- : Node(control, arymem, s1, s2, c), _adr_type(adr_type), _ascii(ascii) {}
+ public:
+ EncodeISOArrayNode(Node* control, Node* arymem, Node* s1, Node* s2, Node* c, bool ascii)
+ : Node(control, arymem, s1, s2, c), _ascii(ascii) {}
bool is_ascii() { return _ascii; }
- virtual int Opcode() const override;
- virtual const Type* bottom_type() const override { return TypeInt::INT; }
- virtual uint match_edge(uint idx) const override;
- virtual uint ideal_reg() const override { return Op_RegI; }
- virtual Node* Ideal(PhaseGVN* phase, bool can_reshape) override;
- virtual const Type* Value(PhaseGVN* phase) const override;
+ virtual int Opcode() const;
+ virtual const Type* bottom_type() const { return TypeInt::INT; }
+ virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; }
+ virtual uint match_edge(uint idx) const;
+ virtual uint ideal_reg() const { return Op_RegI; }
+ virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
+ virtual const Type* Value(PhaseGVN* phase) const;
+ virtual uint size_of() const { return sizeof(EncodeISOArrayNode); }
+ virtual uint hash() const { return Node::hash() + _ascii; }
+ virtual bool cmp(const Node& n) const {
+ return Node::cmp(n) && _ascii == ((EncodeISOArrayNode&)n).is_ascii();
+ }
private:
- virtual uint size_of() const override { return sizeof(EncodeISOArrayNode); }
- virtual uint hash() const override { return Node::hash() + (uint)(uintptr_t) _adr_type + _ascii; }
- virtual bool cmp(const Node& n) const override {
- const EncodeISOArrayNode& e = static_cast(n);
- return Node::cmp(n) && _ascii == e._ascii && _adr_type == e._adr_type;
- }
- virtual const TypePtr* adr_type() const override { return _adr_type; }
- virtual bool depends_only_on_test_impl() const override { return false; }
+ virtual bool depends_only_on_test_impl() const { return false; }
};
//-------------------------------DigitNode----------------------------------------
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index ddd59936f4da..36d69f18041b 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -1162,7 +1162,7 @@ bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) {
Node* arg2 = argument(1);
const TypeAryPtr* mtype = (ae == StrIntrinsicNode::UU) ? TypeAryPtr::CHARS : TypeAryPtr::BYTES;
- set_result(_gvn.transform(new AryEqNode(control(), memory(mtype), mtype, arg1, arg2, ae)));
+ set_result(_gvn.transform(new AryEqNode(control(), memory(mtype), arg1, arg2, ae)));
clear_upper_avx();
return true;
@@ -7247,14 +7247,11 @@ bool LibraryCallKit::inline_encodeISOArray(bool ascii) {
// 'src_start' points to src array + scaled offset
// 'dst_start' points to dst array + scaled offset
- // See GraphKit::compress_string
- const TypePtr* adr_type;
- Node* mem = capture_memory(adr_type, src_type, dst_type);
- Node* enc = new EncodeISOArrayNode(control(), mem, adr_type, src_start, dst_start, length, ascii);
+ const TypeAryPtr* mtype = TypeAryPtr::BYTES;
+ Node* enc = new EncodeISOArrayNode(control(), memory(mtype), src_start, dst_start, length, ascii);
enc = _gvn.transform(enc);
Node* res_mem = _gvn.transform(new SCMemProjNode(enc));
- memory_effect(res_mem, src_type, dst_type);
-
+ set_memory(res_mem, mtype);
set_result(enc);
clear_upper_avx();
@@ -7733,8 +7730,7 @@ bool LibraryCallKit::inline_vectorizedHashCode() {
// Resolve address of first element
Node* array_start = array_element_address(array, offset, bt);
- const TypeAryPtr* in_adr_type = TypeAryPtr::get_array_body_type(bt);
- set_result(_gvn.transform(new VectorizedHashCodeNode(control(), memory(in_adr_type), in_adr_type,
+ set_result(_gvn.transform(new VectorizedHashCodeNode(control(), memory(TypeAryPtr::get_array_body_type(bt)),
array_start, length, initialValue, basic_type)));
clear_upper_avx();
diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestAntiDependency.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestAntiDependency.java
deleted file mode 100644
index c48b24f7c567..000000000000
--- a/test/hotspot/jtreg/compiler/intrinsics/string/TestAntiDependency.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.intrinsics.string;
-
-import compiler.lib.ir_framework.DontInline;
-import compiler.lib.ir_framework.Run;
-import compiler.lib.ir_framework.Test;
-import compiler.lib.ir_framework.TestFramework;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-
-import jdk.test.lib.Asserts;
-
-/*
- * @test
- * @bug 8373591
- * @summary Verify that StringLatin1::inflate, StringUTF16::compress, and
- * StringCoding::implEncodeAsciiArray are scheduled properly
- * @library /test/lib /
- * @modules java.base/java.lang:+open
- * @run driver ${test.main.class}
- */
-public class TestAntiDependency {
- static final MethodHandle COMPRESS_HANDLE;
- static final MethodHandle INFLATE_HANDLE;
- static final MethodHandle ENCODE_ISO_HANDLE;
- static {
- try {
- var currentLookup = MethodHandles.lookup();
- var stringLookup = MethodHandles.privateLookupIn(String.class, currentLookup);
- Class> stringUtf16Class = stringLookup.findClass("java.lang.StringUTF16");
- var stringUtf16Lookup = MethodHandles.privateLookupIn(stringUtf16Class, currentLookup);
- COMPRESS_HANDLE = stringUtf16Lookup.findStatic(stringUtf16Class, "compress0",
- MethodType.methodType(int.class, char[].class, int.class, byte[].class, int.class, int.class));
- Class> stringLatin1Class = stringLookup.findClass("java.lang.StringLatin1");
- var stringLatin1Lookup = MethodHandles.privateLookupIn(stringLatin1Class, currentLookup);
- INFLATE_HANDLE = stringLatin1Lookup.findStatic(stringLatin1Class, "inflate0",
- MethodType.methodType(void.class, byte[].class, int.class, char[].class, int.class, int.class));
- Class> stringCodingClass = stringLookup.findClass("java.lang.StringCoding");
- ENCODE_ISO_HANDLE = stringLookup.findStatic(stringCodingClass, "encodeAsciiArray0",
- MethodType.methodType(int.class, char[].class, int.class, byte[].class, int.class, int.class));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public static void main(String[] args) {
- var testFramework = new TestFramework();
- testFramework.setDefaultWarmup(1);
- testFramework.addFlags("--add-opens=java.base/java.lang=ALL-UNNAMED");
- testFramework.start();
- }
-
- @DontInline
- static void consume(Object o1, Object o2) {}
-
- @Test
- static int testStringCompress() throws Throwable {
- byte[] dst = new byte[4];
- char[] src = new char[4];
- consume(dst, src);
-
- // The compiler must not schedule this after the store to src, either by having
- // StringCompressedCopyNode kill the whole memory, or by taking into consideration the
- // anti-dependency between 2 nodes
- int _ = (int) COMPRESS_HANDLE.invokeExact(src, 0, dst, 0, 4);
- src[0] = 1;
- return dst[0];
- }
-
- @Test
- static int testStringInflate() throws Throwable {
- char[] dst = new char[4];
- byte[] src = new byte[4];
- consume(dst, src);
-
- // The compiler must not schedule this after the store to src, either by having
- // StringInflatedCopyNode kill the whole memory, or by taking into consideration the
- // anti-dependency between 2 nodes
- INFLATE_HANDLE.invokeExact(src, 0, dst, 0, 4);
- src[0] = 1;
- return dst[0];
- }
-
- @Test
- static int testEncodeISO() throws Throwable {
- byte[] dst = new byte[4];
- char[] src = new char[4];
- consume(dst, src);
-
- // The compiler must not schedule this after the store to src, either by having
- // EncodeISOArrayNode kill the whole memory, or by taking into consideration the
- // anti-dependency between 2 nodes
- int _ = (int) ENCODE_ISO_HANDLE.invokeExact(src, 0, dst, 0, 4);
- src[0] = 1;
- return dst[0];
- }
-
- @Run(test = {"testStringCompress", "testStringInflate", "testEncodeISO"})
- public void run() throws Throwable {
- Asserts.assertEQ(0, testStringCompress());
- Asserts.assertEQ(0, testStringInflate());
- Asserts.assertEQ(0, testEncodeISO());
- }
-}
From aa7e2c6ba9c5c97207a90636dea126521ac5c0d3 Mon Sep 17 00:00:00 2001
From: Vladimir Petko
Date: Wed, 19 Aug 2026 06:55:07 +0000
Subject: [PATCH 68/88] 8390432: Zero i386 build broken after JDK-8389219
Reviewed-by: shade
---
src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
index e14c9201c389..351f412ce752 100644
--- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp
@@ -198,6 +198,7 @@ void ShenandoahArguments::initialize() {
ShenandoahAllocRateSampleWindow));
}
+#ifdef _LP64
if (Arguments::is_valhalla_enabled()) {
// Flat atomic payloads may contain embedded oops. Current Valhalla code does not handle
// it well, missing the GC barriers. As the temporary kludge, disable compressed oops:
@@ -206,6 +207,7 @@ void ShenandoahArguments::initialize() {
log_warning(gc)("Shenandoah disables compressed oops to avoid breaking with Valhalla");
FLAG_SET_ERGO(UseCompressedOops, false);
}
+#endif
FullGCForwarding::initialize_flags(MaxHeapSize);
}
From efb9d9f92f8ab103d62cf3a12b8cd47bcfc39e2e Mon Sep 17 00:00:00 2001
From: Lijuan Li
Date: Wed, 19 Aug 2026 07:16:33 +0000
Subject: [PATCH 69/88] 8390044: RISC-V: Support vector integer division
Reviewed-by: fyang, dzhang
---
src/hotspot/cpu/riscv/riscv_v.ad | 36 +++++++++++++++++++
.../compiler/c2/cr7200264/TestIntVect.java | 10 +++---
.../compiler/vectorapi/VectorDivTest.java | 27 +++++++-------
3 files changed, 56 insertions(+), 17 deletions(-)
diff --git a/src/hotspot/cpu/riscv/riscv_v.ad b/src/hotspot/cpu/riscv/riscv_v.ad
index ca77e6ba3413..ef0ce89133ed 100644
--- a/src/hotspot/cpu/riscv/riscv_v.ad
+++ b/src/hotspot/cpu/riscv/riscv_v.ad
@@ -1580,6 +1580,42 @@ instruct vnotL_masked(vReg dst_src, immI_M1 m1, vRegMask_V0 v0) %{
ins_pipe(pipe_slow);
%}
+// vector integer div
+
+instruct vdiv(vReg dst, vReg src1, vReg src2) %{
+ match(Set dst (DivVB src1 src2));
+ match(Set dst (DivVS src1 src2));
+ match(Set dst (DivVI src1 src2));
+ match(Set dst (DivVL src1 src2));
+ format %{ "vdiv $dst, $src1, $src2" %}
+ ins_encode %{
+ BasicType bt = Matcher::vector_element_basic_type(this);
+ __ vsetvli_helper(bt, Matcher::vector_length(this));
+ __ vdiv_vv(as_VectorRegister($dst$$reg),
+ as_VectorRegister($src1$$reg),
+ as_VectorRegister($src2$$reg));
+ %}
+ ins_pipe(pipe_slow);
+%}
+
+// vector integer div - predicated
+
+instruct vdiv_masked(vReg dst_src1, vReg src2, vRegMask_V0 v0) %{
+ match(Set dst_src1 (DivVB (Binary dst_src1 src2) v0));
+ match(Set dst_src1 (DivVS (Binary dst_src1 src2) v0));
+ match(Set dst_src1 (DivVI (Binary dst_src1 src2) v0));
+ match(Set dst_src1 (DivVL (Binary dst_src1 src2) v0));
+ format %{ "vdiv_masked $dst_src1, $dst_src1, $src2, $v0" %}
+ ins_encode %{
+ BasicType bt = Matcher::vector_element_basic_type(this);
+ __ vsetvli_helper(bt, Matcher::vector_length(this));
+ __ vdiv_vv(as_VectorRegister($dst_src1$$reg),
+ as_VectorRegister($dst_src1$$reg),
+ as_VectorRegister($src2$$reg), Assembler::v0_t);
+ %}
+ ins_pipe(pipe_slow);
+%}
+
// vector float div
instruct vdiv_hfp(vReg dst, vReg src1, vReg src2) %{
diff --git a/test/hotspot/jtreg/compiler/c2/cr7200264/TestIntVect.java b/test/hotspot/jtreg/compiler/c2/cr7200264/TestIntVect.java
index 8bf8c9846ec7..53f6b28906b3 100644
--- a/test/hotspot/jtreg/compiler/c2/cr7200264/TestIntVect.java
+++ b/test/hotspot/jtreg/compiler/c2/cr7200264/TestIntVect.java
@@ -23,7 +23,7 @@
/**
* @test
- * @bug 7200264
+ * @bug 7200264 8390044
* @summary 7192963 changes disabled shift vectors
* @library /test/lib /
* @run driver compiler.c2.cr7200264.TestIntVect
@@ -555,16 +555,16 @@ void test_divc_n(int[] a0, int[] a1) {
}
}
- // Not vectorized: On AArch64 SVE, vectorization for this example results in
- // DivVI nodes.
+ // Vectorization for this example results in DivVI nodes on AArch64 SVE and
+ // RISC-V RVV.
@Test
@IR(counts = { IRNode.LOAD_VECTOR_I, "> 0",
IRNode.STORE_VECTOR, "> 0",
IRNode.DIV_VI, "> 0" },
- applyIfCPUFeature = {"sve", "true"})
+ applyIfCPUFeatureOr = {"sve", "true", "rvv", "true"})
@IR(counts = { IRNode.LOAD_VECTOR_I, "= 0",
IRNode.STORE_VECTOR, "= 0" },
- applyIfCPUFeature = {"sve", "false"})
+ applyIfCPUFeatureAnd = {"sve", "false", "rvv", "false"})
void test_divv(int[] a0, int[] a1, int b) {
for (int i = 0; i < a0.length; i+=1) {
a0[i] = (int)(a1[i]/b);
diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorDivTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorDivTest.java
index cfa51b9434c1..94c995f3f687 100644
--- a/test/hotspot/jtreg/compiler/vectorapi/VectorDivTest.java
+++ b/test/hotspot/jtreg/compiler/vectorapi/VectorDivTest.java
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8387594
+ * @bug 8387594 8390044
* @key randomness
* @library /test/lib /
* @summary IR tests for Vector API lanewise DIV
@@ -111,7 +111,7 @@ public class VectorDivTest {
@Test
@IR(counts = { IRNode.DIV_VB, ">= 1" },
- applyIfCPUFeature = { "sve", "true" })
+ applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" })
public static void testDivByte() {
ByteVector va = ByteVector.fromArray(B_SPECIES, ba, 0);
ByteVector vb = ByteVector.fromArray(B_SPECIES, bb, 0);
@@ -120,7 +120,7 @@ public static void testDivByte() {
@Test
@IR(counts = { IRNode.DIV_VS, ">= 1" },
- applyIfCPUFeature = { "sve", "true" })
+ applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" })
public static void testDivShort() {
ShortVector va = ShortVector.fromArray(S_SPECIES, sa, 0);
ShortVector vb = ShortVector.fromArray(S_SPECIES, sb, 0);
@@ -129,7 +129,7 @@ public static void testDivShort() {
@Test
@IR(counts = { IRNode.DIV_VI, ">= 1" },
- applyIfCPUFeature = { "sve", "true" })
+ applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" })
public static void testDivInt() {
IntVector va = IntVector.fromArray(I_SPECIES, ia, 0);
IntVector vb = IntVector.fromArray(I_SPECIES, ib, 0);
@@ -138,7 +138,7 @@ public static void testDivInt() {
@Test
@IR(counts = { IRNode.DIV_VL, ">= 1" },
- applyIfCPUFeature = { "sve", "true" })
+ applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" })
public static void testDivLong() {
LongVector va = LongVector.fromArray(L_SPECIES, la, 0);
LongVector vb = LongVector.fromArray(L_SPECIES, lb, 0);
@@ -165,11 +165,13 @@ public static void testDivDouble() {
// Masked lanewise DIV. On AArch64, BYTE/SHORT have no native predicated
// divide, so they are lowered to an unpredicated divide combined with a
- // VectorBlend.
+ // VectorBlend. RVV has native predicated integer division for all element
+ // sizes.
@Test
- @IR(counts = { IRNode.DIV_VB, ">= 1",
- IRNode.VECTOR_BLEND_B, ">= 1" },
+ @IR(counts = { IRNode.DIV_VB, ">= 1" },
+ applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" })
+ @IR(counts = { IRNode.VECTOR_BLEND_B, ">= 1" },
applyIfCPUFeature = { "sve", "true" })
public static void testMaskedDivByte() {
VectorMask mask = VectorMask.fromArray(B_SPECIES, mask_arr, 0);
@@ -179,8 +181,9 @@ public static void testMaskedDivByte() {
}
@Test
- @IR(counts = { IRNode.DIV_VS, ">= 1",
- IRNode.VECTOR_BLEND_S, ">= 1" },
+ @IR(counts = { IRNode.DIV_VS, ">= 1" },
+ applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" })
+ @IR(counts = { IRNode.VECTOR_BLEND_S, ">= 1" },
applyIfCPUFeature = { "sve", "true" })
public static void testMaskedDivShort() {
VectorMask mask = VectorMask.fromArray(S_SPECIES, mask_arr, 0);
@@ -191,7 +194,7 @@ public static void testMaskedDivShort() {
@Test
@IR(counts = { IRNode.DIV_VI, ">= 1" },
- applyIfCPUFeature = { "sve", "true" })
+ applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" })
public static void testMaskedDivInt() {
VectorMask mask = VectorMask.fromArray(I_SPECIES, mask_arr, 0);
IntVector va = IntVector.fromArray(I_SPECIES, ia, 0);
@@ -201,7 +204,7 @@ public static void testMaskedDivInt() {
@Test
@IR(counts = { IRNode.DIV_VL, ">= 1" },
- applyIfCPUFeature = { "sve", "true" })
+ applyIfCPUFeatureOr = { "sve", "true", "rvv", "true" })
public static void testMaskedDivLong() {
VectorMask mask = VectorMask.fromArray(L_SPECIES, mask_arr, 0);
LongVector va = LongVector.fromArray(L_SPECIES, la, 0);
From 90f142d65372d2d0f0460b790404dda297336972 Mon Sep 17 00:00:00 2001
From: Daniel Skantz
Date: Wed, 19 Aug 2026 07:51:31 +0000
Subject: [PATCH 70/88] 8387014: C2: stringopts produces
NegativeArraySizeException instead of OOM for UTF-16 overflow
Reviewed-by: thartmann, qamai
---
src/hotspot/share/opto/stringopts.cpp | 6 +-
.../TestStackedConcatsManyUTF16Overflow.java | 112 ++++++++++++++++++
2 files changed, 115 insertions(+), 3 deletions(-)
create mode 100644 test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java
diff --git a/src/hotspot/share/opto/stringopts.cpp b/src/hotspot/share/opto/stringopts.cpp
index 5437f4e9a689..e79fa522051a 100644
--- a/src/hotspot/share/opto/stringopts.cpp
+++ b/src/hotspot/share/opto/stringopts.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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
@@ -2060,9 +2060,9 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
ShouldNotReachHere();
}
if (argi > 0) {
- // Check that the sum hasn't overflowed
+ // Check that the sum won't overflow the destination byte array.
IfNode* iff = kit.create_and_map_if(kit.control(),
- __ Bool(__ CmpI(length, __ intcon(0)), BoolTest::lt),
+ __ Bool(_gvn->transform(new CmpUNode(length, __ RShiftI(__ intcon(max_jint), coder))), BoolTest::gt),
PROB_MIN, COUNT_UNKNOWN);
kit.set_control(__ IfFalse(iff));
overflow->set_req(argi, __ IfTrue(iff));
diff --git a/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java
new file mode 100644
index 000000000000..1c9bdf153e8f
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/stringopts/TestStackedConcatsManyUTF16Overflow.java
@@ -0,0 +1,112 @@
+/*
+ * 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 8387014
+ * @summary Test that UTF-16 string concat overflow does not produce a negative size backing array
+ * @requires vm.compiler2.enabled & os.maxMemory > 4G
+ * @library /test/lib /
+ * @run main/othervm -Xmx4g -XX:-OptoScheduling ${test.main.class}
+ * @run main/othervm -Xmx4g -Xint ${test.main.class}
+ * @run main/othervm -Xmx4g -XX:-TieredCompilation -Xcomp -XX:-OptoScheduling
+ * -XX:CompileOnly=${test.main.class}::f
+ * ${test.main.class}
+ */
+
+// The test uses -XX:-OptoScheduling to avoid the assert "too many D-U pinch points" on aarch64 (JDK-8328078).
+
+package compiler.stringopts;
+
+import jdk.test.lib.Asserts;
+
+public class TestStackedConcatsManyUTF16Overflow {
+
+ public static void main (String... args) {
+ new StringBuilder(); // Trigger loading of the StringBuilder class.
+ try {
+ String s = f();
+ String z = "🙂";
+ for (int i = 0; i < 29; i++) {
+ z = z + z;
+ }
+ Asserts.assertEQ(s, z);
+ } catch (OutOfMemoryError e) {
+ Asserts.assertTrue(e.getMessage().equals("Required array length 1073741824 + 1073741824 is too large"));
+ // Specifically, we should not get "`main' threw exception: java.lang.NegativeArraySizeException: -2147483648"
+ return;
+ }
+ throw new RuntimeException("Unreachable.");
+ }
+
+ static String f() {
+
+ String s = "🙂"; // length() 2 UTF-16 string
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+ s = new StringBuilder().append(s).append(s).toString();
+
+ s = new StringBuilder().append(s).append(s).toString();
+
+ return s;
+ }
+}
From 45f3ef3ce2f9aa1bc54359c92bfd267a4f5b26a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Johan=20Sj=C3=B6len?=
Date: Wed, 19 Aug 2026 09:47:04 +0000
Subject: [PATCH 71/88] 8389576: Test
resourcehogs/runtime/ValueTearingTest.java fails due to
ArrayIndexOutOfBoundsException on many-core machines
Reviewed-by: cnorrbin, haosun
---
test/hotspot/jtreg/resourcehogs/runtime/ValueTearingTest.java | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/test/hotspot/jtreg/resourcehogs/runtime/ValueTearingTest.java b/test/hotspot/jtreg/resourcehogs/runtime/ValueTearingTest.java
index 95952b2cf997..774d46fd6721 100644
--- a/test/hotspot/jtreg/resourcehogs/runtime/ValueTearingTest.java
+++ b/test/hotspot/jtreg/resourcehogs/runtime/ValueTearingTest.java
@@ -122,8 +122,7 @@ static class Scenario {
static final int BATCH_SIZE = 10_000;
static int incrementIndex(int idx, int n) {
- idx += INCREMENTS[n];
- if (idx >= N_PRECOMPUTED) idx -= N_PRECOMPUTED;
+ idx = (idx + INCREMENTS[n]) % N_PRECOMPUTED;
return idx;
}
From a158131e0f2b5d9e320bf991171e0c3da1001b1f Mon Sep 17 00:00:00 2001
From: Thomas Schatzl
Date: Wed, 19 Aug 2026 12:07:32 +0000
Subject: [PATCH 72/88] 8389096: Multiple hangs trying to execute GC with
GCALotAtAllSafepoints and ScavengeALot
Reviewed-by: iwalulya, ayang, aboldtch
---
.../share/runtime/interfaceSupport.cpp | 11 +-
src/hotspot/share/runtime/javaThread.cpp | 4 +-
src/hotspot/share/runtime/javaThread.hpp | 4 +-
src/hotspot/share/runtime/mutex.cpp | 12 +-
src/hotspot/share/runtime/mutex.hpp | 2 +-
.../jtreg/gc/TestGCALotAtAllSafepoints.java | 110 ++++++++++++++++++
6 files changed, 132 insertions(+), 11 deletions(-)
create mode 100644 test/hotspot/jtreg/gc/TestGCALotAtAllSafepoints.java
diff --git a/src/hotspot/share/runtime/interfaceSupport.cpp b/src/hotspot/share/runtime/interfaceSupport.cpp
index 6ccf63b4c5e5..2b8d87cbf573 100644
--- a/src/hotspot/share/runtime/interfaceSupport.cpp
+++ b/src/hotspot/share/runtime/interfaceSupport.cpp
@@ -85,10 +85,17 @@ unsigned int InterfaceSupport::_fullgc_alot_counter = 1;
intx InterfaceSupport::_fullgc_alot_invocation = 0;
void InterfaceSupport::gc_alot() {
- Thread *thread = Thread::current();
+ Thread* thread = Thread::current();
if (!thread->is_Java_thread()) return; // Avoid concurrent calls
+ JavaThread* current_thread = JavaThread::cast(thread);
+ // Do not request a GC in a critical section: garbage collectors in this state
+ // cannot complete a GC until all threads have left the JNI critical sections,
+ // and threads cannot leave while they are waiting for GC, thus deadlocking.
+ if (current_thread->in_critical()) return;
+ // A GC would try to acquire Heap_lock in the prologue. Do not try to acquire the
+ // lock recursively as this would cause a hang.
+ if (Heap_lock->owned_by_self()) return;
// Check for new, not quite initialized thread. A thread in new mode cannot initiate a GC.
- JavaThread *current_thread = JavaThread::cast(thread);
if (current_thread->active_handles() == nullptr) return;
// Short-circuit any possible re-entrant gc-a-lot attempt
diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp
index c0877c470367..8e5fde069c68 100644
--- a/src/hotspot/share/runtime/javaThread.cpp
+++ b/src/hotspot/share/runtime/javaThread.cpp
@@ -281,7 +281,7 @@ void JavaThread::check_possible_safepoint() {
#endif // CHECK_UNHANDLED_OOPS
}
-void JavaThread::check_for_valid_safepoint_state() {
+void JavaThread::check_for_valid_safepoint_state(bool allow_gcalot) {
// Don't complain if running a debugging command.
if (DebuggingContext::is_enabled()) return;
@@ -294,7 +294,7 @@ void JavaThread::check_for_valid_safepoint_state() {
fatal("LEAF method calling lock?");
}
- if (GCALotAtAllSafepoints) {
+ if (GCALotAtAllSafepoints && allow_gcalot) {
// We could enter a safepoint here and thus have a gc
InterfaceSupport::check_gc_alot();
}
diff --git a/src/hotspot/share/runtime/javaThread.hpp b/src/hotspot/share/runtime/javaThread.hpp
index 698f64dd97ed..b08a4e6da007 100644
--- a/src/hotspot/share/runtime/javaThread.hpp
+++ b/src/hotspot/share/runtime/javaThread.hpp
@@ -284,8 +284,8 @@ class JavaThread: public Thread {
public:
// These functions check conditions before possibly going to a safepoint.
// including NoSafepointVerifier.
- void check_for_valid_safepoint_state() NOT_DEBUG_RETURN;
- void check_possible_safepoint() NOT_DEBUG_RETURN;
+ void check_for_valid_safepoint_state(bool allow_gcalot = true) NOT_DEBUG_RETURN;
+ void check_possible_safepoint() NOT_DEBUG_RETURN;
#ifdef ASSERT
private:
diff --git a/src/hotspot/share/runtime/mutex.cpp b/src/hotspot/share/runtime/mutex.cpp
index 8e0c1d10e5c6..9f3be83b3e0b 100644
--- a/src/hotspot/share/runtime/mutex.cpp
+++ b/src/hotspot/share/runtime/mutex.cpp
@@ -61,7 +61,7 @@ void Mutex::check_block_state(Thread* thread) {
"locking not allowed when crash protection is set");
}
-void Mutex::check_safepoint_state(Thread* thread) {
+void Mutex::check_safepoint_state(Thread* thread, bool allow_gcalot) {
check_block_state(thread);
// If the lock acquisition checks for safepoint, verify that the lock was created with rank that
@@ -72,7 +72,7 @@ void Mutex::check_safepoint_state(Thread* thread) {
if (thread->is_active_Java_thread()) {
// Also check NoSafepointVerifier, and thread state is _thread_in_vm
- JavaThread::cast(thread)->check_for_valid_safepoint_state();
+ JavaThread::cast(thread)->check_for_valid_safepoint_state(allow_gcalot);
}
}
@@ -116,7 +116,7 @@ void Mutex::lock_contended(Thread* self) {
void Mutex::lock(Thread* self) {
assert(owner() != self, "invariant");
- check_safepoint_state(self);
+ check_safepoint_state(self, true /* allow_gcalot */);
check_rank(self);
OrderAccess::fence();
@@ -245,7 +245,11 @@ bool Monitor::wait(uint64_t timeout) {
set_owner(nullptr);
// Check safepoint state after resetting owner and possible NSV.
- check_safepoint_state(self);
+ // Although the (HotSpot) monitor is logically released, the underlying
+ // OS monitor is still held. If this is the Heap_lock we would
+ // deadlock in the GC prologue trying to acquire the lock recursively.
+ // Suppress GC-a-lot in that case.
+ check_safepoint_state(self, this != Heap_lock);
int wait_status;
InFlightMutexRelease ifmr(this);
diff --git a/src/hotspot/share/runtime/mutex.hpp b/src/hotspot/share/runtime/mutex.hpp
index 4d30a320cbf8..e497fbb34585 100644
--- a/src/hotspot/share/runtime/mutex.hpp
+++ b/src/hotspot/share/runtime/mutex.hpp
@@ -141,7 +141,7 @@ class Mutex : public CHeapObj {
protected:
void set_owner_implementation(Thread* owner) NOT_DEBUG({ raw_set_owner(owner);});
void check_block_state (Thread* thread) NOT_DEBUG_RETURN;
- void check_safepoint_state (Thread* thread) NOT_DEBUG_RETURN;
+ void check_safepoint_state (Thread* thread, bool allow_gcalot) NOT_DEBUG_RETURN;
void check_no_safepoint_state(Thread* thread) NOT_DEBUG_RETURN;
void check_rank (Thread* thread) NOT_DEBUG_RETURN;
void assert_owner (Thread* expected) NOT_DEBUG_RETURN;
diff --git a/test/hotspot/jtreg/gc/TestGCALotAtAllSafepoints.java b/test/hotspot/jtreg/gc/TestGCALotAtAllSafepoints.java
new file mode 100644
index 000000000000..76a0482322a9
--- /dev/null
+++ b/test/hotspot/jtreg/gc/TestGCALotAtAllSafepoints.java
@@ -0,0 +1,110 @@
+/*
+ * 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 gc;
+
+/**
+ * @test id=Serial
+ * @bug 8389096
+ * @summary Verify that -XX:GCALotAtAllSafepoints and -XX:+ScavengeALot do not hang the VM.
+ * @comment GCALotAtAllSafepoints and ScavengeALot cause garbage collections at many places in the VM. These
+ * garbage collection should not cause hangs.
+ * @requires vm.flagless
+ * @requires vm.debug
+ * @requires vm.gc.Serial
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib /
+ * @run driver/timeout=60 gc.TestGCALotAtAllSafepoints -XX:+UseSerialGC
+ */
+
+/**
+ * @test id=Parallel
+ * @bug 8389096
+ * @summary Verify that -XX:GCALotAtAllSafepoints and -XX:+ScavengeALot do not hang the VM.
+ * @comment GCALotAtAllSafepoints and ScavengeALot cause garbage collections at many places in the VM. These
+ * garbage collection should not cause hangs.
+ * @requires vm.flagless
+ * @requires vm.debug
+ * @requires vm.gc.Parallel
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib /
+ * @run driver/timeout=60 gc.TestGCALotAtAllSafepoints -XX:+UseParallelGC
+ */
+
+/**
+ * @test id=G1
+ * @bug 8389096
+ * @summary Verify that -XX:GCALotAtAllSafepoints and -XX:+ScavengeALot do not hang the VM.
+ * @comment GCALotAtAllSafepoints and ScavengeALot cause garbage collections at many places in the VM. These
+ * garbage collection should not cause hangs.
+ * @requires vm.flagless
+ * @requires vm.debug
+ * @requires vm.gc.G1
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib /
+ * @run driver/timeout=60 gc.TestGCALotAtAllSafepoints -XX:+UseG1GC
+ */
+
+/**
+ * @test id=Z
+ * @bug 8389096
+ * @summary Verify that -XX:GCALotAtAllSafepoints and -XX:+ScavengeALot do not hang the VM.
+ * @comment GCALotAtAllSafepoints and ScavengeALot cause garbage collections at many places in the VM. These
+ * garbage collection should not cause hangs.
+ * @requires vm.flagless
+ * @requires vm.debug
+ * @requires vm.gc.Z
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib /
+ * @run driver/timeout=60 gc.TestGCALotAtAllSafepoints -XX:+UseZGC
+ */
+
+/**
+ * @test id=Shenandoah
+ * @bug 8389096
+ * @summary Verify that -XX:GCALotAtAllSafepoints and -XX:+ScavengeALot do not hang the VM.
+ * @comment GCALotAtAllSafepoints and ScavengeALot cause garbage collections at many places in the VM. These
+ * garbage collection should not cause hangs.
+ * @requires vm.flagless
+ * @requires vm.debug
+ * @requires vm.gc.Shenandoah
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib /
+ * @run driver/timeout=60 gc.TestGCALotAtAllSafepoints -XX:+UseShenandoahGC
+ */
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class TestGCALotAtAllSafepoints {
+ public static void main(String[] args) throws Exception {
+ ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(args[0],
+ "-Xmx16m",
+ "-XX:+GCALotAtAllSafepoints",
+ "-XX:+ScavengeALot",
+ "NoSuchClass");
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+ output.shouldMatch("Error: Could not find or load main class NoSuchClass");
+ output.shouldHaveExitValue(1);
+ }
+}
From 03f94c677d1310dbf38f4e54c9a017a35c310d8d Mon Sep 17 00:00:00 2001
From: Casper Norrbin
Date: Wed, 19 Aug 2026 12:37:26 +0000
Subject: [PATCH 73/88] 8383567: VM.class_print_layout should use external
class names
Reviewed-by: kevinw, fparain
---
src/hotspot/share/memory/heapInspection.cpp | 16 ++++++++++++----
.../inlinetypes/ClassPrintLayoutDcmd.java | 8 ++++----
2 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/src/hotspot/share/memory/heapInspection.cpp b/src/hotspot/share/memory/heapInspection.cpp
index 8fc06c253557..82a4a369a5f7 100644
--- a/src/hotspot/share/memory/heapInspection.cpp
+++ b/src/hotspot/share/memory/heapInspection.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -597,7 +597,15 @@ void ClassPrintLayout::class_print_layout(outputStream* st, char* class_name) {
return;
}
- Symbol* classname = SymbolTable::probe(class_name, (int)strlen(class_name));
+ ResourceMark rm;
+ char* normalized_name = ResourceArea::strdup(class_name);
+ for (char* p = normalized_name; *p != '\0'; p++) {
+ if (*p == JVM_SIGNATURE_DOT) {
+ *p = JVM_SIGNATURE_SLASH;
+ }
+ }
+
+ Symbol* classname = SymbolTable::probe(normalized_name, (int)strlen(normalized_name));
GrowableArray* klasses = new (mtServiceability) GrowableArray(100, mtServiceability);
@@ -608,9 +616,9 @@ void ClassPrintLayout::class_print_layout(outputStream* st, char* class_name) {
Klass* klass = klasses->at(i);
if (!klass->is_instance_klass()) continue; // Skip
InstanceKlass* ik = InstanceKlass::cast(klass);
- st->print_cr("Class %s [@%s]:", klass->name()->as_C_string(),
- klass->class_loader_data()->loader_name());
ResourceMark rm;
+ st->print_cr("Class %s [@%s]:", klass->external_name(),
+ klass->class_loader_data()->loader_name());
GrowableArray* fields = new (mtServiceability) GrowableArray(100, mtServiceability);
for (AllFieldStream fd(ik); !fd.done(); fd.next()) {
if (!fd.access_flags().is_static()) {
diff --git a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java
index afbe6e2182fc..e792fea3422f 100644
--- a/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java
+++ b/test/hotspot/jtreg/runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd.java
@@ -81,10 +81,10 @@ static void testCmd(String arg, int expectExitCode, String... expectStrings) thr
}
public static void main(String args[]) throws Exception {
- testCmd("foo/bar", 0, "");
+ testCmd("foo.bar", 0, "");
testCmd("", 1, "IllegalArgumentException", "mandatory");
- testCmd("java/lang/Object", 0, "java/lang/Object", "@bootstrap");
- testCmd("java/lang/Class", 0, "java/lang/Class", "@bootstrap");
- testCmd("runtime/valhalla/inlinetypes/ClassPrintLayoutDcmd$Line", 0, "@app", "p1", "p2");
+ testCmd("java.lang.Object", 0, "java.lang.Object", "@bootstrap");
+ testCmd("java.lang.Class", 0, "java.lang.Class", "@bootstrap");
+ testCmd("runtime.valhalla.inlinetypes.ClassPrintLayoutDcmd$Line", 0, "@app", "p1", "p2");
}
}
From 6994a51e9c3730c68da9f6d46e092b66c37b3186 Mon Sep 17 00:00:00 2001
From: Vladimir Kozlov
Date: Wed, 19 Aug 2026 13:33:07 +0000
Subject: [PATCH 74/88] 8390591: Add regression test for JDK-8390590
Co-authored-by: Lorenzo Dematte <[lorenzo.dematte@protonmail.com](mailto:lorenzo.dematte@protonmail.com)>
Co-authored-by: Tobias Hartmann
Reviewed-by: dlong, vlivanov, thartmann
---
.../intrinsics/string/TestEncodeISOArray.java | 156 ++++++++++++++++++
1 file changed, 156 insertions(+)
create mode 100644 test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeISOArray.java
diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeISOArray.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeISOArray.java
new file mode 100644
index 000000000000..cb2b37b75bd0
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestEncodeISOArray.java
@@ -0,0 +1,156 @@
+/*
+ * 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 8390546
+ * @summary Verify that the memory effect of the encodeISOArray intrinsic is correctly wired in.
+ * @library /test/lib
+ * @requires vm.compiler2.enabled
+ * @modules java.base/java.lang:+open java.base/sun.nio.cs:+open
+ * @run main compiler.intrinsics.string.TestEncodeISOArray
+ * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:CompileThreshold=100
+ * -XX:+IgnoreUnrecognizedVMOptions -XX:UseAVX=0 -XX:-UseSSE42Intrinsics
+ * compiler.intrinsics.string.TestEncodeISOArray
+ */
+
+package compiler.intrinsics.string;
+
+import jdk.test.lib.Asserts;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+public class TestEncodeISOArray {
+ private static final int ITERATIONS = 20_000;
+ private static final int FIRST_BYTE = 'C';
+ private static final char[] SOURCE = "C2 go brrr".toCharArray();
+ private static final byte[] UTF16_SOURCE = toUTF16Bytes(SOURCE);
+ private static final byte[] EXPECTED = "C2 go brrr".getBytes(StandardCharsets.UTF_8);
+ private static final MethodType ENCODE_TYPE = MethodType.methodType(int.class, char[].class, int.class, byte[].class, int.class, int.class);
+ private static final MethodType ENCODE_BYTE_TYPE = MethodType.methodType(int.class, byte[].class, int.class, byte[].class, int.class, int.class);
+ private static final MethodHandle ENCODE_ASCII_ARRAY = findEncoder("java.lang.StringCoding", "encodeAsciiArray0", ENCODE_TYPE);
+ private static final MethodHandle ENCODE_ISO_ARRAY = findEncoder("sun.nio.cs.ISO_8859_1$Encoder", "encodeISOArray0", ENCODE_TYPE);
+ private static final MethodHandle ENCODE_BYTE_ISO_ARRAY = findEncoder("java.lang.StringCoding", "encodeISOArray0", ENCODE_BYTE_TYPE);
+
+ private static byte[] toUTF16Bytes(char[] chars) {
+ ByteBuffer buffer = ByteBuffer.allocate(chars.length * Character.BYTES).order(ByteOrder.nativeOrder());
+ for (char c : chars) {
+ buffer.putChar(c);
+ }
+ return buffer.array();
+ }
+
+ private static MethodHandle findEncoder(String className, String methodName, MethodType type) {
+ try {
+ Class> holder = Class.forName(className);
+ MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(holder, MethodHandles.lookup());
+ return lookup.findStatic(holder, methodName, type);
+ } catch (ReflectiveOperationException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ // Original reproducer from JDK-8390546
+ private static byte[] toUtf8Bytes(char[] chars) {
+ ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(chars));
+ return Arrays.copyOfRange(byteBuffer.array(), 0, byteBuffer.limit());
+ }
+
+ // Targeted check that does not depend on the UTF-8 encoder and arraycopy both being inlined.
+ private static int encodeASCIIAndLoadFirstByte() throws Throwable {
+ byte[] destination = new byte[4];
+ int encoded = (int) ENCODE_ASCII_ARRAY.invokeExact(SOURCE, 0, destination, 0, 4);
+ if (encoded != 4) {
+ return -1;
+ }
+ return destination[0];
+ }
+
+ private static int encodeISOAndLoadFirstByte() throws Throwable {
+ byte[] destination = new byte[4];
+ int encoded = (int) ENCODE_ISO_ARRAY.invokeExact(SOURCE, 0, destination, 0, 4);
+ if (encoded != 4) {
+ return -1;
+ }
+ return destination[0];
+ }
+
+ private static int encodeByteISOAndLoadFirstByte() throws Throwable {
+ byte[] destination = new byte[4];
+ int encoded = (int) ENCODE_BYTE_ISO_ARRAY.invokeExact(UTF16_SOURCE, 0, destination, 0, 4);
+ if (encoded != 4) {
+ return -1;
+ }
+ return destination[0];
+ }
+
+ private static boolean runOriginalReproducer() {
+ for (int i = 0; i < ITERATIONS; i++) {
+ if (!Arrays.equals(toUtf8Bytes(SOURCE), EXPECTED)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean runASCIITest() throws Throwable {
+ for (int i = 0; i < ITERATIONS; i++) {
+ if (encodeASCIIAndLoadFirstByte() != FIRST_BYTE) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean runISOTest() throws Throwable {
+ for (int i = 0; i < ITERATIONS; i++) {
+ if (encodeISOAndLoadFirstByte() != FIRST_BYTE) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean runByteISOTest() throws Throwable {
+ for (int i = 0; i < ITERATIONS; i++) {
+ if (encodeByteISOAndLoadFirstByte() != FIRST_BYTE) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void main(String[] args) throws Throwable {
+ Asserts.assertTrue(runOriginalReproducer(), "Original reproducer failed");
+ Asserts.assertTrue(runASCIITest(), "ASCII encoding failed");
+ Asserts.assertTrue(runISOTest(), "ISO-8859-1 encoding failed");
+ Asserts.assertTrue(runByteISOTest(), "Byte ISO-8859-1 encoding failed");
+ }
+}
From d36beadd0ee07421f3bf7c63c693bdf8233d95f7 Mon Sep 17 00:00:00 2001
From: Patricio Chilano Mateo
Date: Wed, 19 Aug 2026 14:10:11 +0000
Subject: [PATCH 75/88] 8390480: Remove unnecessary check in ThawBase::patch
Reviewed-by: jsjolen, dholmes
---
src/hotspot/share/runtime/continuationFreezeThaw.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
index bfc5fa2b71e5..8f847614f5cc 100644
--- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp
+++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
@@ -2633,14 +2633,14 @@ inline void ThawBase::patch(frame& f, const frame& caller, bool bottom) {
if (bottom) {
ContinuationHelper::Frame::patch_pc(caller, _cont.is_empty() ? caller.pc()
: StubRoutines::cont_returnBarrier());
- } else if (_should_patch_caller_pc || caller.is_compiled_frame()) {
+ } else if (_should_patch_caller_pc) {
// Caller was deoptimized during thaw but we've overwritten the return address when copying f from the heap.
// Also, on some platforms, if the caller is interpreted but the callee not we also need to patch.
#if defined(PPC64) || defined(S390)
- assert(!_should_patch_caller_pc || caller.is_deoptimized_frame() || caller.is_interpreted_frame(), "");
+ assert(caller.is_deoptimized_frame() || caller.is_interpreted_frame(), "");
#else
- assert(!_should_patch_caller_pc || caller.is_deoptimized_frame(), "");
+ assert(caller.is_deoptimized_frame(), "");
#endif
ContinuationHelper::Frame::patch_pc(caller, caller.raw_pc());
From 5da4e41da29be9d91a85b05ea43dad2311a16188 Mon Sep 17 00:00:00 2001
From: Patricio Chilano Mateo
Date: Wed, 19 Aug 2026 14:54:59 +0000
Subject: [PATCH 76/88] 8389310: Virtual thread can miss deoptimization when
propagating exception
Reviewed-by: dlong, coleenp
---
src/hotspot/share/runtime/sharedRuntime.cpp | 9 +-
.../virtual/DeoptimizedMethodOnException.java | 155 ++++++++++++++++++
2 files changed, 163 insertions(+), 1 deletion(-)
create mode 100644 test/jdk/java/lang/Thread/virtual/DeoptimizedMethodOnException.java
diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
index bb773dc523bc..aaf9c956ca07 100644
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -592,7 +592,14 @@ address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* curr
// native nmethods don't have exception handlers
assert(!nm->is_native_method() || nm->method()->is_continuation_enter_intrinsic(), "no exception handler");
assert(nm->header_begin() != nm->exception_begin(), "no exception handler");
- if (nm->is_deopt_pc(return_address)) {
+ // For platform threads, checking the return pc already covers the case
+ // where only this compiled frame was deoptimized, as well as the case
+ // where the nmethod was marked for deoptimization. For virtual threads,
+ // we also need to check if the nmethod is marked for deoptimization because
+ // the return pc may not have been patched if the nmethod was deoptimized
+ // while the frame was frozen. Since this check is benign for platform
+ // threads, we do it unconditionally.
+ if (nm->is_deopt_pc(return_address) || nm->is_marked_for_deoptimization()) {
// If we come here because of a stack overflow, the stack may be
// unguarded. Reguard the stack otherwise if we return to the
// deopt blob and the stack bang causes a stack overflow we
diff --git a/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodOnException.java b/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodOnException.java
new file mode 100644
index 000000000000..3021ad105e73
--- /dev/null
+++ b/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodOnException.java
@@ -0,0 +1,155 @@
+/*
+ * 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 id=default
+ * @bug 8389310
+ * @summary Test exception propagation when caller nmethod is marked for deoptimization
+ * @requires vm.continuations
+ * @library /test/lib /test/hotspot/jtreg
+ * @run main/othervm -XX:CompileCommand=dontinline,*::bar DeoptimizedMethodOnException
+ */
+
+/*
+ * @test id=Xcomp
+ * @bug 8389310
+ * @summary Test exception propagation when caller nmethod is marked for deoptimization
+ * @requires vm.continuations
+ * @library /test/lib /test/hotspot/jtreg
+ * @run main/othervm -XX:CompileCommand=dontinline,*::bar -Xcomp DeoptimizedMethodOnException
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.test.lib.Asserts;
+
+public class DeoptimizedMethodOnException {
+ private static CountDownLatch sync = new CountDownLatch(0);
+ private static A receiver = new A();
+ private static int resultInt;
+ private static String resultStr;
+
+ public static void main(String args[]) throws Exception {
+ warmUp();
+ Asserts.assertTrue(resultInt == 1, "resultInt=" + resultInt);
+ Asserts.assertTrue(resultStr.equals("MyExceptionWithExtraFields"), "resultStr=" + resultStr);
+
+ var started = new CountDownLatch(1);
+ Thread vthread = Thread.ofVirtual().unstarted(() -> {
+ started.countDown();
+ foo();
+ });
+
+ sync = new CountDownLatch(1);
+ vthread.start();
+ started.await();
+ // wait until vthread blocks in sync
+ await(vthread, Thread.State.WAITING);
+ receiver = new B();
+ sync.countDown();
+
+ vthread.join();
+ Asserts.assertTrue(resultInt == 3, "resultInt=" + resultInt);
+ Asserts.assertTrue(resultStr.equals("MyExceptionWithoutExtraFields"), "resultStr=" + resultStr);
+ }
+
+ public static void foo() {
+ try {
+ bar();
+ } catch (MyException e) {
+ resultInt = receiver.m();
+ String tmp = e.getExceptionName();
+ Asserts.assertTrue(tmp.length() > 0, "length=" + tmp.length());
+ resultStr = tmp;
+ return;
+ }
+ throw new RuntimeException("Should not reach here");
+ }
+
+ public static void bar() {
+ try {
+ sync.await();
+ } catch (InterruptedException ie) {}
+ receiver.throwException();
+ }
+
+ private static void warmUp() {
+ for (int i = 0; i < 30_000; i++) {
+ foo();
+ }
+ }
+
+ /**
+ * Waits for the given thread to reach a given state.
+ */
+ private static void await(Thread thread, Thread.State expectedState) throws InterruptedException {
+ Thread.State state = thread.getState();
+ while (state != expectedState) {
+ Asserts.assertTrue(state != Thread.State.TERMINATED, "Thread has terminated");
+ Thread.sleep(10);
+ state = thread.getState();
+ }
+ }
+}
+
+abstract class MyException extends RuntimeException {
+ abstract String getExceptionName();
+}
+
+class MyExceptionWithExtraFields extends MyException {
+ Integer l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15;
+ String exceptionName;
+ MyExceptionWithExtraFields() {
+ l1=1;l2=2;l3=3;l4=4;l5=5;l6=6;l7=7;l8=8;l9=9;l10=10;l11=11;l12=12;l13=13;l14=14;l15=15;
+ exceptionName = new String("MyExceptionWithExtraFields");
+ }
+ @Override
+ String getExceptionName() { return exceptionName; }
+}
+
+class MyExceptionWithoutExtraFields extends MyException {
+ String exceptionName;
+ MyExceptionWithoutExtraFields() {
+ exceptionName = new String("MyExceptionWithoutExtraFields");
+ }
+ @Override
+ String getExceptionName() { return exceptionName; }
+}
+
+class A {
+ int m() {
+ return 1;
+ }
+ void throwException() {
+ throw new MyExceptionWithExtraFields();
+ }
+}
+
+class B extends A {
+ int m() {
+ return 3;
+ }
+ void throwException() {
+ throw new MyExceptionWithoutExtraFields();
+ }
+}
From c1090a0f48db3dc16bfdd5473b9dc8e1b228109f Mon Sep 17 00:00:00 2001
From: Coleen Phillimore
Date: Wed, 19 Aug 2026 15:51:36 +0000
Subject: [PATCH 77/88] 8384600: Improve
java_lang_Throwable::fill_in_stack_trace
Reviewed-by: matsaave, dholmes
---
src/hotspot/share/classfile/javaClasses.cpp | 1 -
.../share/classfile/javaStackTraceClasses.cpp | 251 +++++++-----------
.../share/classfile/javaStackTraceClasses.hpp | 16 --
src/hotspot/share/oops/instanceKlass.cpp | 11 +
src/hotspot/share/oops/instanceKlass.hpp | 1 +
src/hotspot/share/oops/typeArrayOop.hpp | 5 +-
.../share/oops/typeArrayOop.inline.hpp | 23 +-
.../ThrowableIntrospectionSegfault.java | 4 +-
.../RedefineRunningMethodsWithBacktrace.java | 42 ++-
test/jdk/com/sun/jdi/BacktraceFieldTest.java | 8 +-
10 files changed, 149 insertions(+), 213 deletions(-)
diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp
index e3e9b0ee830b..0c5b84e8bff4 100644
--- a/src/hotspot/share/classfile/javaClasses.cpp
+++ b/src/hotspot/share/classfile/javaClasses.cpp
@@ -61,7 +61,6 @@
#include "oops/symbol.hpp"
#include "oops/typeArrayOop.inline.hpp"
#include "prims/jvmtiExport.hpp"
-#include "prims/methodHandles.hpp"
#include "prims/resolvedMethodTable.hpp"
#include "runtime/continuationJavaClasses.inline.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
diff --git a/src/hotspot/share/classfile/javaStackTraceClasses.cpp b/src/hotspot/share/classfile/javaStackTraceClasses.cpp
index 00afb19ac3bf..14c24c8f038d 100644
--- a/src/hotspot/share/classfile/javaStackTraceClasses.cpp
+++ b/src/hotspot/share/classfile/javaStackTraceClasses.cpp
@@ -26,6 +26,7 @@
#include "classfile/javaStackTraceClasses.hpp"
#include "classfile/moduleEntry.hpp"
#include "classfile/stringTable.hpp"
+#include "classfile/symbolTable.hpp"
#include "classfile/vmSymbols.hpp"
#include "code/debugInfo.hpp"
#include "code/pcDesc.hpp"
@@ -40,8 +41,6 @@
#include "oops/refArrayOop.inline.hpp"
#include "oops/symbol.hpp"
#include "oops/typeArrayOop.inline.hpp"
-#include "runtime/continuationEntry.inline.hpp"
-#include "runtime/continuationJavaClasses.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/javaCalls.hpp"
@@ -49,55 +48,27 @@
#include "runtime/safepointVerifiers.hpp"
#include "runtime/vframe.inline.hpp"
#include "utilities/globalDefinitions.hpp"
-#include "utilities/ostream.hpp"
#include "utilities/preserveException.hpp"
-// Internal methods to compose bits of the backtrace in java_lang_Throwable
-class Backtrace: AllStatic {
- public:
- // Helper backtrace functions to store bci|version together.
- static int merge_bci_and_version(int bci, int version);
- static int merge_mid_and_cpref(int mid, int cpref);
- static int bci_at(unsigned int merged);
- static int version_at(unsigned int merged);
- static int mid_at(unsigned int merged);
- static int cpref_at(unsigned int merged);
- static int get_line_number(Method* method, int bci);
- static Symbol* get_source_file_name(InstanceKlass* holder, int version);
-};
-
-
-inline int Backtrace::merge_bci_and_version(int bci, int version) {
- // only store u2 for version, checking for overflow.
- if (version > USHRT_MAX || version < 0) version = USHRT_MAX;
- assert((u2)bci == bci, "bci should be short");
- return build_int_from_shorts((u2)version, (u2)bci);
-}
+// Inline helper functions
-inline int Backtrace::merge_mid_and_cpref(int mid, int cpref) {
- // only store u2 for mid and cpref, checking for overflow.
- assert((u2)mid == mid, "mid should be short");
- assert((u2)cpref == cpref, "cpref should be short");
- return build_int_from_shorts((u2)cpref, (u2)mid);
+static inline int64_t merge_method_id_bci_and_version(u2 method_id, u2 bci, int version) {
+ return (int64_t)(((uint64_t)version << 32) | (uint32_t)build_int_from_shorts(method_id, bci));
}
-inline int Backtrace::bci_at(unsigned int merged) {
- return extract_high_short_from_int(merged);
+static inline int version_at(int64_t merged) {
+ return (int)((uint64_t)merged >> 32);
}
-inline int Backtrace::version_at(unsigned int merged) {
- return extract_low_short_from_int(merged);
+static inline int method_id_at(int64_t merged) {
+ return extract_low_short_from_int((uint32_t)merged);
}
-inline int Backtrace::mid_at(unsigned int merged) {
- return extract_high_short_from_int(merged);
+static inline int bci_at(int64_t merged) {
+ return extract_high_short_from_int((uint32_t)merged);
}
-inline int Backtrace::cpref_at(unsigned int merged) {
- return extract_low_short_from_int(merged);
-}
-
-inline int Backtrace::get_line_number(Method* method, int bci) {
+static inline int get_line_number(Method* method, int bci) {
int line_number = 0;
if (method->is_native()) {
// Negative value different from -1 below, enabling Java code in
@@ -111,19 +82,6 @@ inline int Backtrace::get_line_number(Method* method, int bci) {
return line_number;
}
-inline Symbol* Backtrace::get_source_file_name(InstanceKlass* holder, int version) {
- // RedefineClasses() currently permits redefine operations to
- // happen in parallel using a "last one wins" philosophy. That
- // spec laxness allows the constant pool entry associated with
- // the source_file_name_index for any older constant pool version
- // to be unstable so we shouldn't try to use it.
- if (holder->constants()->version() != version) {
- return nullptr;
- } else {
- return holder->source_file_name();
- }
-}
-
// java_lang_Throwable
int java_lang_Throwable::_backtrace_offset;
@@ -216,9 +174,7 @@ void java_lang_Throwable::print(oop throwable, outputStream* st) {
}
}
-// After this many redefines, the stack trace is unreliable.
static inline bool version_matches(Method* method, int version) {
- assert(version < USHRT_MAX, "version is too big");
return method != nullptr && (method->constants()->version() == version);
}
@@ -232,79 +188,29 @@ class BacktraceBuilder: public StackObj {
private:
refArrayHandle _backtrace;
refArrayOop _head;
- typeArrayOop _methods;
- typeArrayOop _bcis;
+ typeArrayOop _methods_and_bcis;
refArrayOop _mirrors;
- typeArrayOop _names; // Needed to insulate method name against redefinition.
// True if the top frame of the backtrace is omitted because it shall be hidden.
bool _has_hidden_top_frame;
int _index;
NoSafepointVerifier _nsv;
- enum {
- trace_methods_offset = java_lang_Throwable::trace_methods_offset,
- trace_bcis_offset = java_lang_Throwable::trace_bcis_offset,
- trace_mirrors_offset = java_lang_Throwable::trace_mirrors_offset,
- trace_names_offset = java_lang_Throwable::trace_names_offset,
- trace_conts_offset = java_lang_Throwable::trace_conts_offset,
- trace_next_offset = java_lang_Throwable::trace_next_offset,
- trace_hidden_offset = java_lang_Throwable::trace_hidden_offset,
- trace_size = java_lang_Throwable::trace_size,
- trace_chunk_size = java_lang_Throwable::trace_chunk_size
- };
-
// get info out of chunks
- static typeArrayOop get_methods(refArrayHandle chunk) {
+ static typeArrayOop get_methods_and_bcis(refArrayHandle chunk) {
typeArrayOop methods = typeArrayOop(chunk->obj_at(trace_methods_offset));
assert(methods != nullptr, "method array should be initialized in backtrace");
return methods;
}
- static typeArrayOop get_bcis(refArrayHandle chunk) {
- typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset));
- assert(bcis != nullptr, "bci array should be initialized in backtrace");
- return bcis;
- }
static refArrayOop get_mirrors(refArrayHandle chunk) {
refArrayOop mirrors = refArrayOop(chunk->obj_at(trace_mirrors_offset));
assert(mirrors != nullptr, "mirror array should be initialized in backtrace");
return mirrors;
}
- static typeArrayOop get_names(refArrayHandle chunk) {
- typeArrayOop names = typeArrayOop(chunk->obj_at(trace_names_offset));
- assert(names != nullptr, "names array should be initialized in backtrace");
- return names;
- }
static bool has_hidden_top_frame(refArrayHandle chunk) {
oop hidden = chunk->obj_at(trace_hidden_offset);
return hidden != nullptr;
}
- public:
-
- // constructor for new backtrace
- BacktraceBuilder(TRAPS): _head(nullptr), _methods(nullptr), _bcis(nullptr), _mirrors(nullptr), _names(nullptr), _has_hidden_top_frame(false) {
- expand(CHECK);
- _backtrace = refArrayHandle(THREAD, _head);
- _index = 0;
- }
-
- BacktraceBuilder(Thread* thread, refArrayHandle backtrace) {
- _methods = get_methods(backtrace);
- _bcis = get_bcis(backtrace);
- _mirrors = get_mirrors(backtrace);
- _names = get_names(backtrace);
- _has_hidden_top_frame = has_hidden_top_frame(backtrace);
- assert(_methods->length() == _bcis->length() &&
- _methods->length() == _mirrors->length() &&
- _mirrors->length() == _names->length(),
- "method and source information arrays should match");
-
- // head is the preallocated backtrace
- _head = backtrace();
- _backtrace = refArrayHandle(thread, _head);
- _index = 0;
- }
-
void expand(TRAPS) {
refArrayHandle old_head(THREAD, _head);
PauseNoSafepointVerifier pnsv(&_nsv);
@@ -312,35 +218,58 @@ class BacktraceBuilder: public StackObj {
refArrayOop head = oopFactory::new_objectArray(trace_size, CHECK);
refArrayHandle new_head(THREAD, head);
- typeArrayOop methods = oopFactory::new_shortArray(trace_chunk_size, CHECK);
+ typeArrayOop methods = oopFactory::new_longArray(trace_chunk_size, CHECK);
typeArrayHandle new_methods(THREAD, methods);
- typeArrayOop bcis = oopFactory::new_intArray(trace_chunk_size, CHECK);
- typeArrayHandle new_bcis(THREAD, bcis);
-
refArrayOop mirrors = oopFactory::new_objectArray(trace_chunk_size, CHECK);
refArrayHandle new_mirrors(THREAD, mirrors);
- typeArrayOop names = oopFactory::new_symbolArray(trace_chunk_size, CHECK);
- typeArrayHandle new_names(THREAD, names);
-
if (!old_head.is_null()) {
old_head->obj_at_put(trace_next_offset, new_head());
}
new_head->obj_at_put(trace_methods_offset, new_methods());
- new_head->obj_at_put(trace_bcis_offset, new_bcis());
new_head->obj_at_put(trace_mirrors_offset, new_mirrors());
- new_head->obj_at_put(trace_names_offset, new_names());
new_head->obj_at_put(trace_hidden_offset, nullptr);
- _head = new_head();
- _methods = new_methods();
- _bcis = new_bcis();
+ _head = new_head();
+ _methods_and_bcis = new_methods();
_mirrors = new_mirrors();
- _names = new_names();
_index = 0;
}
+ public:
+
+ // Offsets into oop for backtrace() and constants.
+ enum {
+ trace_methods_offset = 0,
+ trace_mirrors_offset = 1,
+ trace_next_offset = 2,
+ trace_hidden_offset = 3,
+ trace_size = 4,
+ trace_chunk_size = 32
+ };
+
+ // constructor for new backtrace
+ BacktraceBuilder(TRAPS): _head(nullptr), _methods_and_bcis(nullptr), _mirrors(nullptr), _has_hidden_top_frame(false) {
+ expand(CHECK);
+ _backtrace = refArrayHandle(THREAD, _head);
+ _index = 0;
+ }
+
+ BacktraceBuilder(Thread* thread, refArrayHandle backtrace) {
+ _methods_and_bcis = get_methods_and_bcis(backtrace);
+ _mirrors = get_mirrors(backtrace);
+ _has_hidden_top_frame = has_hidden_top_frame(backtrace);
+ assert(_methods_and_bcis->length() == _mirrors->length(),
+ "method and source information arrays should match");
+
+ // head is the preallocated backtrace
+ _head = backtrace();
+ _backtrace = refArrayHandle(thread, _head);
+ _index = 0;
+ }
+
+ public:
refArrayOop backtrace() {
return _backtrace();
}
@@ -357,13 +286,9 @@ class BacktraceBuilder: public StackObj {
method = mhandle();
}
- _methods->ushort_at_put(_index, method->orig_method_idnum());
- _bcis->int_at_put(_index, Backtrace::merge_bci_and_version(bci, method->constants()->version()));
-
- // Note:this doesn't leak symbols because the mirror in the backtrace keeps the
- // klass owning the symbols alive so their refcounts aren't decremented.
- Symbol* name = method->name();
- _names->symbol_at_put(_index, name);
+ _methods_and_bcis->long_at_put(_index,
+ merge_method_id_bci_and_version(
+ method->orig_method_idnum(), bci, method->constants()->version()));
// We need to save the mirrors in the backtrace to keep the class
// from being unloaded while we still have this stack trace.
@@ -379,10 +304,10 @@ class BacktraceBuilder: public StackObj {
// to indicate that this backtrace has a hidden top frame.
// But this code is used before TRUE is allocated.
// Therefore let's just use an arbitrary legal oop
- // available right here. _methods is a short[].
- assert(_methods != nullptr, "we need a legal oop");
+ // available right here. _methods_and_bcis is a long[].
+ assert(_methods_and_bcis != nullptr, "we need a legal oop");
_has_hidden_top_frame = true;
- _head->obj_at_put(trace_hidden_offset, _methods);
+ _head->obj_at_put(trace_hidden_offset, _methods_and_bcis);
}
}
};
@@ -391,10 +316,9 @@ struct BacktraceElement : public StackObj {
int _method_id;
int _bci;
int _version;
- Symbol* _name;
Handle _mirror;
- BacktraceElement(Handle mirror, int mid, int version, int bci, Symbol* name) :
- _method_id(mid), _bci(bci), _version(version), _name(name), _mirror(mirror) {}
+ BacktraceElement(Handle mirror, int mid, int version, int bci) :
+ _method_id(mid), _bci(bci), _version(version), _mirror(mirror) {}
};
class BacktraceIterator : public StackObj {
@@ -402,36 +326,32 @@ class BacktraceIterator : public StackObj {
refArrayHandle _result;
refArrayHandle _mirrors;
typeArrayHandle _methods;
- typeArrayHandle _bcis;
- typeArrayHandle _names;
void init(refArrayHandle result, Thread* thread) {
// Get method id, bci, version and mirror from chunk
_result = result;
if (_result.not_null()) {
- _methods = typeArrayHandle(thread, BacktraceBuilder::get_methods(_result));
- _bcis = typeArrayHandle(thread, BacktraceBuilder::get_bcis(_result));
+ _methods = typeArrayHandle(thread, BacktraceBuilder::get_methods_and_bcis(_result));
_mirrors = refArrayHandle(thread, BacktraceBuilder::get_mirrors(_result));
- _names = typeArrayHandle(thread, BacktraceBuilder::get_names(_result));
_index = 0;
}
}
public:
BacktraceIterator(refArrayHandle result, Thread* thread) {
init(result, thread);
- assert(_methods.is_null() || _methods->length() == java_lang_Throwable::trace_chunk_size, "lengths don't match");
+ assert(_methods.is_null() || _methods->length() == BacktraceBuilder::trace_chunk_size, "lengths don't match");
}
BacktraceElement next(Thread* thread) {
+ int64_t merged_method_data = _methods->long_at(_index);
BacktraceElement e (Handle(thread, _mirrors->obj_at(_index)),
- _methods->ushort_at(_index),
- Backtrace::version_at(_bcis->int_at(_index)),
- Backtrace::bci_at(_bcis->int_at(_index)),
- _names->symbol_at(_index));
+ method_id_at(merged_method_data),
+ version_at(merged_method_data),
+ bci_at(merged_method_data));
_index++;
- if (_index >= java_lang_Throwable::trace_chunk_size) {
- int next_offset = java_lang_Throwable::trace_next_offset;
+ if (_index >= BacktraceBuilder::trace_chunk_size) {
+ int next_offset = BacktraceBuilder::trace_next_offset;
// Get next chunk
refArrayHandle result (thread, refArrayOop(_result->obj_at(next_offset)));
init(result, thread);
@@ -444,17 +364,28 @@ class BacktraceIterator : public StackObj {
}
};
+static inline const char* method_id_to_name(InstanceKlass* holder, int method_id) {
+ // If no method was found with this original idnum, it was deleted. This is rare
+ // and has been deprecated.
+ Method* method = holder->method_with_orig_idnum(method_id);
+ return method == nullptr ? "" : method->name()->as_C_string();
+}
+
+static inline Symbol* method_id_to_name_symbol(InstanceKlass* holder, int method_id) {
+ Method* method = holder->method_with_orig_idnum(method_id);
+ return (method == nullptr) ? SymbolTable::new_symbol("unknown_deleted_by_redefinition") : method->name();
+}
// Print stack trace element to the specified output stream.
// The output is formatted into a stringStream and written to the outputStream in one step.
static void print_stack_element_to_stream(outputStream* st, Handle mirror, int method_id,
- int version, int bci, Symbol* name) {
+ int version, int bci) {
ResourceMark rm;
stringStream ss;
InstanceKlass* holder = java_lang_Class::as_InstanceKlass(mirror());
const char* klass_name = holder->external_name();
- char* method_name = name->as_C_string();
+ const char* method_name = method_id_to_name(holder, method_id);
ss.print("\tat %s.%s(", klass_name, method_name);
// Print module information
@@ -470,17 +401,17 @@ static void print_stack_element_to_stream(outputStream* st, Handle mirror, int m
}
char* source_file_name = nullptr;
- Symbol* source = Backtrace::get_source_file_name(holder, version);
+ Symbol* source = holder->source_file_name(version);
if (source != nullptr) {
source_file_name = source->as_C_string();
}
- // The method can be null if the requested class version is gone
+ // Now get the exact method from the current or previous version of the InstanceKlass, if it exists.
Method* method = holder->method_with_orig_idnum(method_id, version);
if (!version_matches(method, version)) {
- ss.print("Redefined)");
+ ss.print("(Redefined)");
} else {
- int line_number = Backtrace::get_line_number(method, bci);
+ int line_number = get_line_number(method, bci);
if (line_number == -2) {
ss.print("Native Method)");
} else {
@@ -509,7 +440,7 @@ void java_lang_Throwable::print_stack_element(outputStream *st, Method* method,
Handle mirror (Thread::current(), method->method_holder()->java_mirror());
int method_id = method->orig_method_idnum();
int version = method->constants()->version();
- print_stack_element_to_stream(st, mirror, method_id, version, bci, method->name());
+ print_stack_element_to_stream(st, mirror, method_id, version, bci);
}
/**
@@ -533,7 +464,7 @@ void java_lang_Throwable::print_stack_trace(Handle throwable, outputStream* st)
while (iter.repeat()) {
BacktraceElement bte = iter.next(THREAD);
- print_stack_element_to_stream(st, bte._mirror, bte._method_id, bte._version, bte._bci, bte._name);
+ print_stack_element_to_stream(st, bte._mirror, bte._method_id, bte._version, bte._bci);
}
if (THREAD->can_call_java()) {
// Call getCause() which doesn't necessarily return the _cause field.
@@ -766,7 +697,6 @@ void java_lang_Throwable::allocate_backtrace(Handle throwable, TRAPS) {
set_backtrace(throwable(), bt.backtrace());
}
-
void java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(Handle throwable) {
// Fill in stack trace into preallocated backtrace (no GC)
@@ -795,7 +725,7 @@ void java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(Handle t
chunk_count++;
// Bail-out for deep stacks
- if (chunk_count >= trace_chunk_size) break;
+ if (chunk_count >= BacktraceBuilder::trace_chunk_size) break;
}
set_depth(throwable(), chunk_count);
log_info(stacktrace)("%s, %d", throwable->klass()->external_name(), chunk_count);
@@ -832,13 +762,16 @@ void java_lang_Throwable::get_stack_trace_elements(int depth, Handle backtrace,
}
InstanceKlass* holder = java_lang_Class::as_InstanceKlass(bte._mirror());
+ // Get the exact method if it has been redefined and still exists.
methodHandle method (THREAD, holder->method_with_orig_idnum(bte._method_id, bte._version));
+ // Get the method name from the method_id.
+ Symbol* method_name = method_id_to_name_symbol(holder, bte._method_id);
java_lang_StackTraceElement::fill_in(stack_trace_element, holder,
method,
bte._version,
bte._bci,
- bte._name,
+ method_name,
CHECK);
}
}
@@ -909,7 +842,7 @@ bool java_lang_Throwable::get_top_method_and_bci(oop throwable, Method** method,
// If the exception happened in a frame that has been hidden, i.e.,
// omitted from the back trace, we can not compute the message.
- oop hidden = backtrace(throwable)->obj_at(trace_hidden_offset);
+ oop hidden = backtrace(throwable)->obj_at(BacktraceBuilder::trace_hidden_offset);
if (hidden != nullptr) {
return false;
}
@@ -1009,7 +942,7 @@ void java_lang_StackTraceElement::decode_file_and_line(Handle java_class,
oop& source_file,
int& line_number, TRAPS) {
// Fill in source file name and line number.
- source = Backtrace::get_source_file_name(holder, version);
+ source = holder->source_file_name(version);
source_file = java_lang_Class::source_file(java_class());
if (source != nullptr) {
// Class was not redefined. We can trust its cache if set,
@@ -1025,7 +958,7 @@ void java_lang_StackTraceElement::decode_file_and_line(Handle java_class,
java_lang_Class::set_source_file(java_class(), source_file);
}
}
- line_number = Backtrace::get_line_number(method(), bci);
+ line_number = get_line_number(method(), bci);
}
// java_lang_ClassFrameInfo
diff --git a/src/hotspot/share/classfile/javaStackTraceClasses.hpp b/src/hotspot/share/classfile/javaStackTraceClasses.hpp
index e4466b625d57..e95757c7b0ba 100644
--- a/src/hotspot/share/classfile/javaStackTraceClasses.hpp
+++ b/src/hotspot/share/classfile/javaStackTraceClasses.hpp
@@ -44,19 +44,6 @@ class java_lang_Throwable: AllStatic {
friend class BacktraceIterator;
private:
- // Trace constants
- enum {
- trace_methods_offset = 0,
- trace_bcis_offset = 1,
- trace_mirrors_offset = 2,
- trace_names_offset = 3,
- trace_conts_offset = 4,
- trace_next_offset = 5,
- trace_hidden_offset = 6,
- trace_size = 7,
- trace_chunk_size = 32
- };
-
static int _backtrace_offset;
static int _detailMessage_offset;
static int _stackTrace_offset;
@@ -109,9 +96,6 @@ class java_lang_Throwable: AllStatic {
static void java_printStackTrace(Handle throwable, TRAPS);
// Gets the method and bci of the top frame (TOS). Returns false if this failed.
static bool get_top_method_and_bci(oop throwable, Method** method, int* bci);
-
- // Debugging
- friend class JavaClasses;
};
// Interface to java.lang.StackTraceElement objects
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index 0b47b182e520..010510c2f3fd 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -3406,6 +3406,17 @@ Symbol* InstanceKlass::source_file_name() const { return _constant
u2 InstanceKlass::source_file_name_index() const { return _constants->source_file_name_index(); }
void InstanceKlass::set_source_file_name_index(u2 sourcefile_index) { _constants->set_source_file_name_index(sourcefile_index); }
+Symbol* InstanceKlass::source_file_name(int version) const {
+ // Return the source file name for this version of the classfile, if redefined with RedefineClasses
+ const InstanceKlass* holder = get_klass_version(version);
+ if (holder == nullptr) {
+ // Redefined previous class has been cleaned up.
+ return nullptr;
+ } else {
+ return holder->source_file_name();
+ }
+}
+
// minor and major version numbers of class file
u2 InstanceKlass::minor_version() const { return _constants->minor_version(); }
void InstanceKlass::set_minor_version(u2 minor_version) { _constants->set_minor_version(minor_version); }
diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp
index aba81900985a..768da36dc4be 100644
--- a/src/hotspot/share/oops/instanceKlass.hpp
+++ b/src/hotspot/share/oops/instanceKlass.hpp
@@ -771,6 +771,7 @@ class InstanceKlass: public Klass {
Symbol* source_file_name() const;
u2 source_file_name_index() const;
void set_source_file_name_index(u2 sourcefile_index);
+ Symbol* source_file_name(int version) const;
// minor and major version numbers of class file
u2 minor_version() const;
diff --git a/src/hotspot/share/oops/typeArrayOop.hpp b/src/hotspot/share/oops/typeArrayOop.hpp
index e5750052541b..de020920db3b 100644
--- a/src/hotspot/share/oops/typeArrayOop.hpp
+++ b/src/hotspot/share/oops/typeArrayOop.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
@@ -108,9 +108,6 @@ class typeArrayOopDesc : public arrayOopDesc {
jbyte byte_at_acquire(int which) const;
void release_byte_at_put(int which, jbyte contents);
- Symbol* symbol_at(int which) const;
- void symbol_at_put(int which, Symbol* contents);
-
// Sizing
// Returns the number of words necessary to hold an array of "len"
diff --git a/src/hotspot/share/oops/typeArrayOop.inline.hpp b/src/hotspot/share/oops/typeArrayOop.inline.hpp
index c431e3db16d6..61ffd33706d7 100644
--- a/src/hotspot/share/oops/typeArrayOop.inline.hpp
+++ b/src/hotspot/share/oops/typeArrayOop.inline.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
@@ -162,25 +162,4 @@ inline void typeArrayOopDesc::release_byte_at_put(int which, jbyte contents) {
AtomicAccess::release_store(byte_at_addr(which), contents);
}
-// Java thinks Symbol arrays are just arrays of either long or int, since
-// there doesn't seem to be T_ADDRESS, so this is a bit of unfortunate
-// casting
-#ifdef _LP64
-inline Symbol* typeArrayOopDesc::symbol_at(int which) const {
- return *reinterpret_cast(long_at_addr(which));
-}
-
-inline void typeArrayOopDesc::symbol_at_put(int which, Symbol* contents) {
- *reinterpret_cast(long_at_addr(which)) = contents;
-}
-#else
-inline Symbol* typeArrayOopDesc::symbol_at(int which) const {
- return *reinterpret_cast(int_at_addr(which));
-}
-inline void typeArrayOopDesc::symbol_at_put(int which, Symbol* contents) {
- *reinterpret_cast(int_at_addr(which)) = contents;
-}
-#endif // _LP64
-
-
#endif // SHARE_OOPS_TYPEARRAYOOP_INLINE_HPP
diff --git a/test/hotspot/jtreg/runtime/Throwable/ThrowableIntrospectionSegfault.java b/test/hotspot/jtreg/runtime/Throwable/ThrowableIntrospectionSegfault.java
index a242d3afcd72..75c0fe0c8ffa 100644
--- a/test/hotspot/jtreg/runtime/Throwable/ThrowableIntrospectionSegfault.java
+++ b/test/hotspot/jtreg/runtime/Throwable/ThrowableIntrospectionSegfault.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 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
@@ -63,7 +63,7 @@ public static void main(java.lang.String[] unused) {
try {
// Retrieve the class of throwable.backtrace[0][0].
- Class class2 = ((Object[]) ((Object[]) backtrace)[2])[0].getClass();
+ Class class2 = ((Object[]) ((Object[]) backtrace)[1])[0].getClass();
// Segfault occurs while executing this line, to retrieve the name of
// this class.
diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRunningMethodsWithBacktrace.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRunningMethodsWithBacktrace.java
index bc69bd971de3..f5ddb642559e 100644
--- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRunningMethodsWithBacktrace.java
+++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/RedefineRunningMethodsWithBacktrace.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2023, 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
@@ -118,11 +118,29 @@ static void localSleep() {
}
public static void infinite() {}
public static void throwable() {
- throw new RuntimeException("throwable called");
+ throw new RuntimeException("throwable called"); /* line 13 */
}
}
""";
+ private static boolean matchSource(String source) {
+ // The first version is in this file, the second version was cleaned up.
+ return source == null ||
+ source.equals("RedefineRunningMethodsWithBacktrace.java") ||
+ source.equals("RedefineRunningMethodsWithBacktrace_B.java");
+ }
+
+ static final int firstLineNumber = 82;
+ static final int cleanedupLineNumber = -1;
+ static final int rerunLineNumber = 13;
+
+ private static boolean matchLineNumber(int lineNumber) {
+ // Line number of the throw in the each version of the redefined class.
+ // The first version of this method is running so we have the line number,
+ // the second is cleaned up, so we don't, the third version is current so we do.
+ return lineNumber == firstLineNumber || lineNumber == cleanedupLineNumber || lineNumber == rerunLineNumber;
+ }
+
private static void touchRedefinedMethodInBacktrace(Throwable throwable) {
System.out.println("touchRedefinedMethodInBacktrace: ");
throwable.printStackTrace(); // this actually crashes with the bug in
@@ -131,8 +149,24 @@ private static void touchRedefinedMethodInBacktrace(Throwable throwable) {
// Make sure that we can convert the backtrace, which is referring to
// the redefined method, to a StrackTraceElement[] without crashing.
StackTraceElement[] stackTrace = throwable.getStackTrace();
- for (int i = 0; i < stackTrace.length; i++) {
- StackTraceElement frame = stackTrace[i];
+ StackTraceElement frame = stackTrace[0];
+ assertEquals(frame.getClassName(), "RedefineRunningMethodsWithBacktrace_B",
+ "\nTest failed: trace[0].getClassName() returned " + frame.getClassName());
+ assertEquals(frame.getMethodName(), "throwable",
+ "\nTest failed: trace[0].getMethodName() returned " + frame.getMethodName());
+ assertTrue(matchSource(frame.getFileName()),
+ "\nTest failed: trace[0].getFileName() returned " + frame.getFileName());
+ assertTrue(matchLineNumber(frame.getLineNumber()),
+ "\nTest failed: trace[0].getLineNumber() returned " + frame.getLineNumber());
+
+ frame = stackTrace[1];
+ assertEquals(frame.getClassName(), "RedefineRunningMethodsWithBacktrace",
+ "\nTest failed: trace[1].getClassName() returned " + frame.getClassName());
+ assertEquals(frame.getMethodName(), "getThrowableInB",
+ "\nTest failed: trace[1].getMethodName() returned " + frame.getMethodName());
+
+ for (int i = 2; i < stackTrace.length; i++) {
+ frame = stackTrace[i];
assertNotNull(frame.getClassName(),
"\nTest failed: trace[" + i + "].getClassName() returned null");
assertNotNull(frame.getMethodName(),
diff --git a/test/jdk/com/sun/jdi/BacktraceFieldTest.java b/test/jdk/com/sun/jdi/BacktraceFieldTest.java
index 294b858e2f71..d3d2b137c1a1 100644
--- a/test/jdk/com/sun/jdi/BacktraceFieldTest.java
+++ b/test/jdk/com/sun/jdi/BacktraceFieldTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 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
@@ -158,12 +158,10 @@ protected void runTests() throws Exception {
println("BT: backTraceVal = " + backTraceVal);
printval(backTraceVal, 0);
- printval(backTraceVal, 1);
- printval(backTraceVal, 2);
- printval(backTraceVal, 3); // backtrace has 4 elements
+ printval(backTraceVal, 1); // backtrace has 2 elements
try {
- printval(backTraceVal, 4);
+ printval(backTraceVal, 2);
} catch (Exception e) {
println("Exception " + e);
}
From bbd0ca0d5c4ce5e9fa0c6ded8048e44acdb7e277 Mon Sep 17 00:00:00 2001
From: Vicente Romero
Date: Wed, 19 Aug 2026 15:57:12 +0000
Subject: [PATCH 78/88] 8388979: javac crashes with NullPointerException in
Types.erasure when evaluating bounds of mutually dependent array type
variables inside an intersection type definition
Reviewed-by: mcimadamore
---
.../com/sun/tools/javac/code/Types.java | 2 +-
.../typevars/NPEArrayInIntersectionTest.java | 9 +++
.../typevars/NPEArrayInIntersectionTest.out | 2 +
.../typevars/TypeVarArrayInBound.jcod | 74 +++++++++++++++++++
.../typevars/TypeVarArrayInBoundTest.java | 12 +++
5 files changed, 98 insertions(+), 1 deletion(-)
create mode 100644 test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.java
create mode 100644 test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.out
create mode 100644 test/langtools/tools/javac/generics/typevars/TypeVarArrayInBound.jcod
create mode 100644 test/langtools/tools/javac/generics/typevars/TypeVarArrayInBoundTest.java
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
index 5173f37bcf9c..a6d8d88e972a 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
@@ -2552,7 +2552,7 @@ public IntersectionClassType makeIntersectionType(List bounds, boolean all
syms.noSymbol);
IntersectionClassType intersectionType = new IntersectionClassType(bounds, bc, allInterfaces);
bc.type = intersectionType;
- bc.erasure_field = (bounds.head.hasTag(TYPEVAR)) ?
+ bc.erasure_field = (bounds.head.hasTag(TYPEVAR)) || bounds.head.hasTag(ARRAY) ?
syms.objectType : // error condition, recover
erasure(firstExplicitBound);
bc.members_field = WriteableScope.create(bc);
diff --git a/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.java b/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.java
new file mode 100644
index 000000000000..4b8485f89297
--- /dev/null
+++ b/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.java
@@ -0,0 +1,9 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8388979
+ * @summary javac crashes with NullPointerException in Types.erasure when evaluating bounds
+ * of mutually dependent array type variables inside an intersection type definition
+ * @compile/fail/ref=NPEArrayInIntersectionTest.out -XDrawDiagnostics NPEArrayInIntersectionTest.java
+ */
+
+class NPEArrayInIntersectionTest {}
diff --git a/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.out b/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.out
new file mode 100644
index 000000000000..3888a8743705
--- /dev/null
+++ b/test/langtools/tools/javac/generics/typevars/NPEArrayInIntersectionTest.out
@@ -0,0 +1,2 @@
+NPEArrayInIntersectionTest.java:9:45: compiler.err.type.found.req: B[], (compiler.misc.type.req.class)
+1 error
diff --git a/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBound.jcod b/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBound.jcod
new file mode 100644
index 000000000000..fb558cb42305
--- /dev/null
+++ b/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBound.jcod
@@ -0,0 +1,74 @@
+/* A hand-crafted classfile carrying a generic ClassSignature attribute of
+ * the form "Ljava/lang/Object;"
+ * i.e. the classfile-level equivalent of:
+ *
+ * class TypeVarArrayInBound {}
+ */
+class TypeVarArrayInBound {
+ 0xCAFEBABE;
+ 0; // minor version
+ 72; // version
+ [] { // Constant Pool
+ ; // first element is empty
+ Method #2 #3; // #1
+ class #4; // #2
+ NameAndType #5 #6; // #3
+ Utf8 "java/lang/Object"; // #4
+ Utf8 ""; // #5
+ Utf8 "()V"; // #6
+ class #8; // #7
+ Utf8 "TypeVarArrayInBound"; // #8
+ Utf8 "Code"; // #9
+ Utf8 "LineNumberTable"; // #10
+ Utf8 "Signature"; // #11
+ Utf8 "Ljava/lang/Object;"; // #12
+ Utf8 "SourceFile"; // #13
+ Utf8 "TypeVarArrayInBound.java"; // #14
+ } // Constant Pool
+
+ 0x0020; // access
+ #7;// this_cpx
+ #2;// super_cpx
+
+ [] { // Interfaces
+ } // Interfaces
+
+ [] { // Fields
+ } // Fields
+
+ [] { // Methods
+ { // method
+ 0x0000; // access
+ #5; // name_index
+ #6; // descriptor_index
+ [] { // Attributes
+ Attr(#9) { // Code
+ 1; // max_stack
+ 1; // max_locals
+ Bytes[]{
+ 0x2AB70001B1;
+ }
+ [] { // Traps
+ } // end Traps
+ [] { // Attributes
+ Attr(#10) { // LineNumberTable
+ [] { // line_number_table
+ 0 1;
+ }
+ } // end LineNumberTable
+ } // Attributes
+ } // end Code
+ } // Attributes
+ }
+ } // Methods
+
+ [] { // Attributes
+ Attr(#11) { // Signature
+ #12;
+ } // end Signature
+ ;
+ Attr(#13) { // SourceFile
+ #14;
+ } // end SourceFile
+ } // Attributes
+} // end class TypeVarArrayInBound
diff --git a/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBoundTest.java b/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBoundTest.java
new file mode 100644
index 000000000000..5a5134a72713
--- /dev/null
+++ b/test/langtools/tools/javac/generics/typevars/TypeVarArrayInBoundTest.java
@@ -0,0 +1,12 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8388979
+ * @summary javac crashes with NullPointerException in Types.erasure when
+ * evaluating bounds of mutually dependent array type variables
+ * inside an intersection type definition
+ * @build TypeVarArrayInBound
+ * @compile TypeVarArrayInBoundTest.java
+ */
+class TypeVarArrayInBoundTest {
+ TypeVarArrayInBound, ?> field;
+}
From bc038026aad96285cf30b75b4fdf36829a292263 Mon Sep 17 00:00:00 2001
From: Leonid Mesnik
Date: Wed, 19 Aug 2026 16:32:48 +0000
Subject: [PATCH 79/88] 8378071: Class ThreadSnapshot$ThreadLock is not
initialized by VM
Reviewed-by: cjplummer, dholmes
---
src/hotspot/share/services/threadService.cpp | 11 +++--
.../ThreadSnapshot/ThreadLockClassInit.java | 45 +++++++++++++++++++
2 files changed, 52 insertions(+), 4 deletions(-)
create mode 100644 test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadLockClassInit.java
diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp
index 6ee592bd7740..5c9e6ad166a8 100644
--- a/src/hotspot/share/services/threadService.cpp
+++ b/src/hotspot/share/services/threadService.cpp
@@ -1499,12 +1499,15 @@ oop ThreadSnapshotFactory::get_thread_snapshot(jobject jthread, TRAPS) {
}
// Locks
- Symbol* lock_sym = vmSymbols::jdk_internal_vm_ThreadLock();
- Klass* lock_k = SystemDictionary::resolve_or_fail(lock_sym, true, CHECK_NULL);
- InstanceKlass* lock_klass = InstanceKlass::cast(lock_k);
-
refArrayHandle locks;
if (cl._locks != nullptr && cl._locks->length() > 0) {
+ Symbol* lock_sym = vmSymbols::jdk_internal_vm_ThreadLock();
+ Klass* lock_k = SystemDictionary::resolve_or_fail(lock_sym, true, CHECK_NULL);
+ if (lock_k->should_be_initialized()) {
+ lock_k->initialize(CHECK_NULL);
+ }
+
+ InstanceKlass* lock_klass = InstanceKlass::cast(lock_k);
locks = oopFactory::new_refArray_handle(lock_klass, cl._locks->length(), CHECK_NULL);
for (int n = 0; n < cl._locks->length(); n++) {
GetThreadSnapshotHandshakeClosure::OwnedLock* lock_info = cl._locks->adr_at(n);
diff --git a/test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadLockClassInit.java b/test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadLockClassInit.java
new file mode 100644
index 000000000000..d1a2b65728a1
--- /dev/null
+++ b/test/jdk/jdk/internal/vm/ThreadSnapshot/ThreadLockClassInit.java
@@ -0,0 +1,45 @@
+/*
+ * 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 8378071
+ * @summary Test jdk.internal.vm.ThreadSnapshot.of(Thread) correctly initialize ThreadLock class
+ *
+ * @modules java.base/jdk.internal.vm
+ * @run main ThreadLockClassInit
+ * @run main/othervm -Xcomp -XX:-Inline -XX:CompileCommand=compileonly,*ThreadSnapshot*::* ThreadLockClassInit
+ */
+
+import jdk.internal.vm.ThreadSnapshot;
+
+public class ThreadLockClassInit {
+ public static final Object LOCK = new Object();
+
+ public static void main(String[] args) throws Exception {
+ synchronized (LOCK) {
+ // The ThreadSnapshot doesn't have any public methods so nothing to check.
+ ThreadSnapshot.of(Thread.currentThread());
+ }
+ }
+}
From 212d3a220cad1a50e0f89f9b7c4f6b6594f7ebbe Mon Sep 17 00:00:00 2001
From: Thomas Schatzl
Date: Wed, 19 Aug 2026 16:50:43 +0000
Subject: [PATCH 80/88] 8390683: Problemlist gc/TestGCALotAtSafepoints tests
Reviewed-by: iwalulya
---
test/hotspot/jtreg/ProblemList.txt | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
index d41a77a1b850..3e73792dd7df 100644
--- a/test/hotspot/jtreg/ProblemList.txt
+++ b/test/hotspot/jtreg/ProblemList.txt
@@ -87,6 +87,11 @@ gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java#generational 83869
gc/stress/jfr/TestStressAllocationGCEventsWithShenandoah.java#default 8386964 generic-all
gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#generational 8386964 generic-all
gc/stress/jfr/TestStressBigAllocationGCEventsWithShenandoah.java#default 8386964 generic-all
+gc/TestGCALotAtAllSafepoints.java#Parallel 8390661 generic-all
+gc/TestGCALotAtAllSafepoints.java#Serial 8390661 generic-all
+gc/TestGCALotAtAllSafepoints.java#G1 8390661 generic-all
+gc/TestGCALotAtAllSafepoints.java#Z 8390661 generic-all
+gc/TestGCALotAtAllSafepoints.java#Shenandoah 8390661 generic-all
#############################################################################
From d4bfdf70a2752f048c26a27eafc6fd5496703b22 Mon Sep 17 00:00:00 2001
From: Xueming Shen
Date: Wed, 19 Aug 2026 17:18:44 +0000
Subject: [PATCH 81/88] 8387798: VectorAPI: VectorOperators.DIV javadoc
incorrectly says "Floating only"
Reviewed-by: erfang, liach, psandoz
---
.../share/classes/jdk/incubator/vector/VectorOperators.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java
index 4d4eea0c3c4f..1fa75b4b69a1 100644
--- a/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java
+++ b/src/jdk.incubator.vector/share/classes/jdk/incubator/vector/VectorOperators.java
@@ -533,7 +533,7 @@ static boolean opKind(Operator op, int bit) {
public static final Binary SUB = binary("SUB", "-", VectorSupport.VECTOR_OP_SUB, VO_ALL);
/** Produce {@code a*b}. */
public static final Associative MUL = assoc("MUL", "*", VectorSupport.VECTOR_OP_MUL, VO_ALL+VO_ASSOC);
- /** Produce {@code a/b}. Floating only. */
+ /** Produce {@code a/b}. */
public static final Binary DIV = binary("DIV", "/", VectorSupport.VECTOR_OP_DIV, VO_ALL| VO_SPECIAL);
/** Produce {@code min(a,b)}. */
public static final Associative MIN = assoc("MIN", "min", VectorSupport.VECTOR_OP_MIN, VO_ALL+VO_ASSOC);
From 9a601b46b2f2bc68f4de42fe2c00b77da55246b0 Mon Sep 17 00:00:00 2001
From: Ioi Lam
Date: Wed, 19 Aug 2026 21:07:33 +0000
Subject: [PATCH 82/88] 8390335: Refactor AOTClassLocationConfig::validate()
Reviewed-by: kvn, asmehra
---
src/hotspot/share/cds/aotClassLocation.cpp | 98 +++++++++++++---------
src/hotspot/share/cds/aotClassLocation.hpp | 3 +
src/hotspot/share/cds/filemap.cpp | 12 ---
3 files changed, 61 insertions(+), 52 deletions(-)
diff --git a/src/hotspot/share/cds/aotClassLocation.cpp b/src/hotspot/share/cds/aotClassLocation.cpp
index 464bacd1ca07..48b91960ce77 100644
--- a/src/hotspot/share/cds/aotClassLocation.cpp
+++ b/src/hotspot/share/cds/aotClassLocation.cpp
@@ -994,61 +994,64 @@ bool AOTClassLocationConfig::need_lcp_match_helper(int start, int end, ClassLoca
return true;
}
-bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_linked_classes, bool has_full_module_graph) const {
+bool AOTClassLocationConfig::validate_helper(const char* cache_filename, bool has_aot_linked_classes, bool has_full_module_graph) const {
ResourceMark rm;
AllClassLocationStreams all_css;
log_locations(cache_filename, /*is_write=*/false);
- // (1) Check JRT modules image
- const char* jrt = ClassLoader::get_jrt_entry()->name();
- log_info(class, path)("Checking [0] (modules image)");
- bool success = class_location_at(0)->check(jrt, has_aot_linked_classes);
- log_info(class, path)("Modules image %s validation: %s", jrt, success ? "passed" : "failed");
- if (!success) {
+ if (!check_jrt(has_aot_linked_classes)) {
return false;
}
- {
- // (2) Check boot/app classpath
- bool use_lcp_match = need_lcp_match(all_css);
- const char* runtime_lcp;
- size_t runtime_lcp_len;
+ if (!check_classpaths(has_aot_linked_classes, all_css)) {
+ return false;
+ }
- log_info(class, path)("Longest common prefix substitution in boot/app classpath matching: %s",
- use_lcp_match ? "yes" : "no");
- if (use_lcp_match) {
- runtime_lcp = find_lcp(all_css.boot_and_app_cp(), runtime_lcp_len);
- log_info(class, path)("Longest common prefix: %s (%zu chars)", runtime_lcp, runtime_lcp_len);
- } else {
- runtime_lcp = nullptr;
- runtime_lcp_len = 0;
- }
+ bool status = check_module_paths(has_aot_linked_classes, has_full_module_graph, all_css.module_path());
+ log_info(class, path)("Archived module path validation: %s", status ? "passed" : "failed");
+ return status;
+}
- success = check_classpaths(true, has_aot_linked_classes, boot_cp_start_index(), boot_cp_end_index(), all_css.boot_cp(),
- use_lcp_match, runtime_lcp, runtime_lcp_len);
- log_info(class, path)("Archived boot classpath validation: %s", success ? "passed" : "failed");
+bool AOTClassLocationConfig::check_jrt(bool has_aot_linked_classes) const {
+ const char* jrt = ClassLoader::get_jrt_entry()->name();
+ log_info(class, path)("Checking [0] (modules image)");
+ bool status = class_location_at(0)->check(jrt, has_aot_linked_classes);
+ log_info(class, path)("Modules image %s validation: %s", jrt, status ? "passed" : "failed");
+ return status;
+}
+
+bool AOTClassLocationConfig::check_classpaths(bool has_aot_linked_classes, AllClassLocationStreams& all_css) const {
+ const char* runtime_lcp = nullptr;
+ size_t runtime_lcp_len = 0;
+
+ bool use_lcp_match = need_lcp_match(all_css);
+ log_info(class, path)("Longest common prefix substitution in boot/app classpath matching: %s",
+ use_lcp_match ? "yes" : "no");
+ if (use_lcp_match) {
+ runtime_lcp = find_lcp(all_css.boot_and_app_cp(), runtime_lcp_len);
+ log_info(class, path)("Longest common prefix: %s (%zu chars)", runtime_lcp, runtime_lcp_len);
+ }
- if (success && need_to_check_app_classpath()) {
- success = check_classpaths(false, has_aot_linked_classes, app_cp_start_index(), app_cp_end_index(), all_css.app_cp(),
+ bool status = check_classpaths(true, has_aot_linked_classes, boot_cp_start_index(), boot_cp_end_index(), all_css.boot_cp(),
use_lcp_match, runtime_lcp, runtime_lcp_len);
- log_info(class, path)("Archived app classpath validation: %s", success ? "passed" : "failed");
- }
+ log_info(class, path)("Archived boot classpath validation: %s", status ? "passed" : "failed");
- // (3) Check module paths
- if (success) {
- success = check_module_paths(has_aot_linked_classes, has_full_module_graph, all_css.module_path());
- log_info(class, path)("Archived module path validation: %s", success ? "passed" : "failed");
- }
+ if (status && need_to_check_app_classpath()) {
+ status = check_classpaths(false, has_aot_linked_classes, app_cp_start_index(), app_cp_end_index(), all_css.app_cp(),
+ use_lcp_match, runtime_lcp, runtime_lcp_len);
+ log_info(class, path)("Archived app classpath validation: %s", status ? "passed" : "failed");
+ }
- if (runtime_lcp_len > 0) {
- os::free((void*)runtime_lcp);
- }
+ if (runtime_lcp_len > 0) {
+ os::free((void*)runtime_lcp);
}
- if (success) {
- _runtime_instance = this;
- } else {
+ return status;
+}
+
+bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_linked_classes, bool has_full_module_graph) const {
+ if (!validate_helper(cache_filename, has_aot_linked_classes, has_full_module_graph)) {
const char* mismatch_msg = "shared class paths mismatch";
const char* hint_msg = log_is_enabled(Info, class, path) ?
"" : " (hint: enable -Xlog:class+path=info to diagnose the failure)";
@@ -1064,8 +1067,23 @@ bool AOTClassLocationConfig::validate(const char* cache_filename, bool has_aot_l
} else {
AOTMetaspace::report_loading_error("%s%s", mismatch_msg, hint_msg);
}
+ return false;
}
- return success;
+
+ if (CDSConfig::is_dumping_dynamic_archive()) {
+ // Only support dynamic dumping with the usage of the default CDS archive
+ // or a simple base archive.
+ // If the base layer archive contains additional path component besides
+ // the runtime image and the -cp, dynamic dumping is disabled.
+ if (num_boot_classpaths() > 0) {
+ CDSConfig::disable_dumping_dynamic_archive();
+ aot_log_warning(aot)(
+ "Dynamic archiving is disabled because base layer archive has appended boot classpath");
+ }
+ }
+
+ _runtime_instance = this;
+ return true;
}
void AOTClassLocationConfig::log_locations(const char* cache_filename, bool is_write) const {
diff --git a/src/hotspot/share/cds/aotClassLocation.hpp b/src/hotspot/share/cds/aotClassLocation.hpp
index bdf50535c9ea..771f4951671c 100644
--- a/src/hotspot/share/cds/aotClassLocation.hpp
+++ b/src/hotspot/share/cds/aotClassLocation.hpp
@@ -162,6 +162,9 @@ class AOTClassLocationConfig : public CHeapObj {
Group group, bool parse_manifest, bool from_cpattr);
void dumptime_init_helper(TRAPS);
+ bool validate_helper(const char* cache_filename, bool has_aot_linked_classes, bool has_full_module_graph) const;
+ bool check_jrt(bool has_aot_linked_classes) const;
+ bool check_classpaths(bool has_aot_linked_classes, AllClassLocationStreams& all_css) const;
bool check_classpaths(bool is_boot_classpath, bool has_aot_linked_classes,
int index_start, int index_end, ClassLocationStream& runtime_css,
bool use_lcp_match, const char* runtime_lcp, size_t runtime_lcp_len) const;
diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp
index a7320dc23cfe..76f133d2f419 100644
--- a/src/hotspot/share/cds/filemap.cpp
+++ b/src/hotspot/share/cds/filemap.cpp
@@ -415,18 +415,6 @@ bool FileMapInfo::validate_class_location() {
}
}
- if (CDSConfig::is_dumping_dynamic_archive()) {
- // Only support dynamic dumping with the usage of the default CDS archive
- // or a simple base archive.
- // If the base layer archive contains additional path component besides
- // the runtime image and the -cp, dynamic dumping is disabled.
- if (config->num_boot_classpaths() > 0) {
- CDSConfig::disable_dumping_dynamic_archive();
- aot_log_warning(aot)(
- "Dynamic archiving is disabled because base layer archive has appended boot classpath");
- }
- }
-
#if INCLUDE_JVMTI
if (_classpath_entries_for_jvmti != nullptr) {
os::free(_classpath_entries_for_jvmti);
From 84f01bbb4bc564a58d9d95495e8e81ee06c14d42 Mon Sep 17 00:00:00 2001
From: Patricio Chilano Mateo
Date: Wed, 19 Aug 2026 22:32:04 +0000
Subject: [PATCH 83/88] 8390240: Full chunk thawing path misses deoptimization
check in monitorenter case
Reviewed-by: fparain, coleenp
---
.../share/runtime/continuationFreezeThaw.cpp | 21 ++++
.../DeoptimizedMethodAtMonitorEnter.java | 119 ++++++++++++++++++
2 files changed, 140 insertions(+)
create mode 100644 test/jdk/java/lang/Thread/virtual/DeoptimizedMethodAtMonitorEnter.java
diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
index 8f847614f5cc..4a8c1f79a64e 100644
--- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp
+++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp
@@ -2091,6 +2091,7 @@ class ThawBase : public StackObj {
template
int remove_top_compiled_frame_from_chunk(stackChunkOop chunk, int &argsize);
int remove_scalarized_frames(StackChunkFrameStream& scfs, int &argsize);
+ void check_top_for_deoptimization(stackChunkOop chunk);
void copy_from_chunk(intptr_t* from, intptr_t* to, int size);
void thaw_lockstack(stackChunkOop chunk);
@@ -2203,6 +2204,23 @@ inline void ThawBase::clear_chunk(stackChunkOop chunk) {
chunk->set_max_thawing_size(0);
}
+void ThawBase::check_top_for_deoptimization(stackChunkOop chunk) {
+ StackChunkFrameStream f(chunk);
+ if (f.is_stub()) {
+ f.next(SmallRegisterMap::instance_no_args(), true /* stop */);
+ assert(!f.is_done(), "");
+
+ f.get_cb();
+ assert(f.is_compiled(), "");
+ if (f.cb()->as_nmethod()->is_marked_for_deoptimization()) {
+ // The caller of the runtime stub when the continuation is preempted is not at a
+ // Java call instruction, and so cannot rely on nmethod patching for deopt.
+ log_develop_trace(continuations)("Deoptimizing runtime stub caller");
+ f.to_frame().deoptimize(nullptr); // the null thread simply avoids the assertion in deoptimize which we're not set up for
+ }
+ }
+}
+
int ThawBase::remove_scalarized_frames(StackChunkFrameStream& f, int &argsize) {
intptr_t* top = f.sp();
@@ -2340,6 +2358,9 @@ NOINLINE intptr_t* Thaw::thaw_fast(stackChunkOop chunk) {
if (LIKELY(!ForceSingleFrameThaw && (full_chunk_size < threshold))) {
prefetch_chunk_pd(chunk->start_address(), full_chunk_size); // prefetch anticipating memcpy starting at highest address
+ if (check_stub) {
+ check_top_for_deoptimization(chunk);
+ }
partial = false;
argsize = chunk->argsize(); // must be called *before* clearing the chunk
clear_chunk(chunk);
diff --git a/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodAtMonitorEnter.java b/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodAtMonitorEnter.java
new file mode 100644
index 000000000000..4eab3f65ab1f
--- /dev/null
+++ b/test/jdk/java/lang/Thread/virtual/DeoptimizedMethodAtMonitorEnter.java
@@ -0,0 +1,119 @@
+/*
+ * 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 id=thaw_fast_full
+ * @bug 8390240
+ * @summary Test the full thaw path when the runtime stub's compiled
+ * caller is marked for deoptimization
+ * @requires vm.continuations
+ * @library /test/lib /test/hotspot/jtreg
+ * @run main/othervm -Xcomp DeoptimizedMethodAtMonitorEnter
+ */
+
+/*
+ * @test id=thaw_fast_partial
+ * @bug 8390240
+ * @summary Test the partial thaw path when the runtime stub's compiled
+ * caller is marked for deoptimization
+ * @requires vm.debug == true & vm.continuations
+ * @library /test/lib /test/hotspot/jtreg
+ * @run main/othervm -Xcomp -XX:+ForceSingleFrameThaw DeoptimizedMethodAtMonitorEnter
+ */
+
+/*
+ * @test id=thaw_slow
+ * @bug 8390240
+ * @summary Test the slow thaw path when the runtime stub's compiled
+ * caller is marked for deoptimization
+ * @requires vm.continuations
+ * @library /test/lib /test/hotspot/jtreg
+ * @run main/othervm DeoptimizedMethodAtMonitorEnter
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+import jdk.test.lib.Asserts;
+
+public class DeoptimizedMethodAtMonitorEnter {
+ private static final Object lock = new Object();
+ private static A receiver = new A();
+ private static int result;
+
+ public static void main(String[] args) throws Exception {
+ warmUp();
+ Asserts.assertTrue(receiver.m() == 1, "unexpected value=" + receiver.m());
+
+ var started = new CountDownLatch(1);
+ Thread vthread = Thread.ofVirtual().unstarted(() -> {
+ started.countDown();
+ foo();
+ });
+
+ synchronized (lock) {
+ vthread.start();
+ started.await();
+ await(vthread, Thread.State.BLOCKED);
+ receiver = new B();
+ }
+
+ vthread.join();
+ Asserts.assertTrue(result == 3, "unexpected result=" + result);
+ }
+
+ public static void foo() {
+ synchronized (lock) {
+ result = receiver.m();
+ }
+ }
+
+ private static void warmUp() {
+ for (int i = 0; i < 30_000; i++) {
+ foo();
+ }
+ }
+
+ /**
+ * Waits for the given thread to reach a given state.
+ */
+ private static void await(Thread thread, Thread.State expectedState) throws InterruptedException {
+ Thread.State state = thread.getState();
+ while (state != expectedState) {
+ Asserts.assertTrue(state != Thread.State.TERMINATED, "Thread has terminated");
+ Thread.sleep(10);
+ state = thread.getState();
+ }
+ }
+
+ static class A {
+ int m() {
+ return 1;
+ }
+ }
+
+ static class B extends A {
+ int m() {
+ return 3;
+ }
+ }
+}
From bd929ac41e6c76884441b419845766d5dd082323 Mon Sep 17 00:00:00 2001
From: Gui Cao
Date: Thu, 20 Aug 2026 01:08:55 +0000
Subject: [PATCH 84/88] 8390522: RISC-V: Avoid clobbering ra in the nmethod
entry barrier
Reviewed-by: fyang, dzhang
---
.../cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
index 809221d66373..2139ffd52345 100644
--- a/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
+++ b/src/hotspot/cpu/riscv/gc/shared/barrierSetAssembler_riscv.cpp
@@ -300,8 +300,10 @@ void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm, Label* slo
// Because processors will not start the second load until the first comes back.
// This means you can't overlap the two loads,
// which is stronger than needed for ordering (stronger than TSO).
- __ srli(ra, t0, 32);
- __ orr(t1, t1, ra);
+ // XOR the guard into the epoch address twice. This preserves the
+ // address while making it dependent on the guard load.
+ __ xorr(t1, t1, t0);
+ __ xorr(t1, t1, t0);
}
// Read the global epoch value.
__ lwu(t1, t1);
From de2b1b805e32982aa6aa476915e08f71da3921cc Mon Sep 17 00:00:00 2001
From: Shiv Shah
Date: Thu, 20 Aug 2026 01:31:26 +0000
Subject: [PATCH 85/88] 8298991: vmTestbase/nsk/sysdict tests fail with OOME:
Java heap space: failed reallocation of scalar replaced objects
Reviewed-by: lmesnik, epavlova
---
.../nsk/share/runner/ThreadsRunner.java | 29 ++++++++++++++-----
.../nsk/sysdict/share/SysDictTest.java | 6 ++--
2 files changed, 24 insertions(+), 11 deletions(-)
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java b/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java
index 98481fdf87ed..e7ff1dbba1c9 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/share/runner/ThreadsRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 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
@@ -119,15 +119,18 @@ public void run() {
test.run();
LockSupport.parkNanos(1);
}
- } catch (OutOfMemoryError oom) {
- if (test instanceof OOMStress) {
- // Test stressing OOM, not a failure.
- log.info("Caught OutOfMemoryError in OOM stress test, omitting exception.");
+ } catch (Throwable t) {
+ if (test instanceof OOMStress && isCausedByOOM(t)) {
+ // Test stressing OOM, not a failure. The OOME may arrive
+ // wrapped in another exception.
+ try {
+ log.info("Caught " + t + " in OOM stress test, omitting exception.");
+ } catch (OutOfMemoryError oom) {
+ // no memory left to log, still not a failure
+ }
} else {
- failWithException(oom);
+ failWithException(t);
}
- } catch (Throwable t) {
- failWithException(t);
} finally {
waitForOtherThreads();
stresser.finish();
@@ -149,6 +152,16 @@ private void waitForOtherThreads() {
}
}
+ private static boolean isCausedByOOM(Throwable t) {
+ while (t != null) {
+ if (t instanceof OutOfMemoryError) {
+ return true;
+ }
+ t = t.getCause();
+ }
+ return false;
+ }
+
private void failWithException(Throwable t) {
log.debug("Exception in ");
log.debug(test);
diff --git a/test/hotspot/jtreg/vmTestbase/nsk/sysdict/share/SysDictTest.java b/test/hotspot/jtreg/vmTestbase/nsk/sysdict/share/SysDictTest.java
index 2b217a368c25..5b8763592742 100644
--- a/test/hotspot/jtreg/vmTestbase/nsk/sysdict/share/SysDictTest.java
+++ b/test/hotspot/jtreg/vmTestbase/nsk/sysdict/share/SysDictTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -30,10 +30,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import nsk.share.ClassUnloader;
import nsk.share.TestFailure;
import nsk.share.gc.ThreadedGCTest;
import nsk.share.gc.gp.GarbageUtils;
+import nsk.share.gc.OOMStress;
import nsk.share.test.ExecutionController;
import nsk.share.test.LocalRandom;
@@ -99,7 +99,7 @@ ClassLoader[] createClassLoadersInternal() {
}
volatile ClassLoader[] currentClassLoaders;
- class Worker implements Runnable {
+ class Worker implements Runnable, OOMStress {
private ClassLoader loader;
private String[] names;
From 87952df444ec7bc0db3f25bbeebd10204180e609 Mon Sep 17 00:00:00 2001
From: Dingli Zhang
Date: Thu, 20 Aug 2026 02:17:12 +0000
Subject: [PATCH 86/88] 8390521: RISC-V: Clean up vector register dispatch in
MachSpillCopyNode::implementation
Reviewed-by: fyang, gcao
---
src/hotspot/cpu/riscv/riscv.ad | 36 ++++++++++++++++++----------------
1 file changed, 19 insertions(+), 17 deletions(-)
diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad
index 17cd4592fd29..033e4a4222e0 100644
--- a/src/hotspot/cpu/riscv/riscv.ad
+++ b/src/hotspot/cpu/riscv/riscv.ad
@@ -1557,12 +1557,16 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
// address also needs t0 to materialize an offset outside the 12-bit range.
if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) {
int last_dst_offset = dst_offset;
- if (bottom_type()->isa_pvectmask()) {
- int vmask_size_in_bytes = Matcher::scalable_predicate_reg_slots() * 32 / 8;
- last_dst_offset += vmask_size_in_bytes - 4;
- } else if (ideal_reg() == Op_VecA) {
- int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE);
- last_dst_offset += vector_reg_size_in_bytes - 8;
+ if (bottom_type()->isa_vect() != nullptr) {
+ if (!bottom_type()->isa_pvectmask()) {
+ assert(ideal_reg() == Op_VecA, "Must be Op_VecA");
+ int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE);
+ last_dst_offset += vector_reg_size_in_bytes - 8;
+ } else {
+ assert(ideal_reg() == Op_RegVectMask, "Must be Op_RegVectMask");
+ int vmask_size_in_bytes = Matcher::scalable_predicate_reg_slots() * 32 / 8;
+ last_dst_offset += vmask_size_in_bytes - 4;
+ }
}
if (masm != nullptr && !Assembler::is_simm12(last_dst_offset)) {
@@ -1574,9 +1578,9 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
}
}
- if (bottom_type()->isa_vect() != nullptr) {
- uint ireg = ideal_reg();
- if (ireg == Op_VecA && masm) {
+ if (bottom_type()->isa_vect() != nullptr && masm != nullptr) {
+ if (!bottom_type()->isa_pvectmask()) {
+ assert(ideal_reg() == Op_VecA, "Must be Op_VecA");
int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE);
if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) {
// stack to stack
@@ -1595,7 +1599,8 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
} else {
ShouldNotReachHere();
}
- } else if (bottom_type()->isa_pvectmask() && masm) {
+ } else {
+ assert(ideal_reg() == Op_RegVectMask, "Must be Op_RegVectMask");
int vmask_size_in_bytes = Matcher::scalable_predicate_reg_slots() * 32 / 8;
if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) {
// stack to stack
@@ -1698,14 +1703,11 @@ uint MachSpillCopyNode::implementation(C2_MacroAssembler *masm, PhaseRegAlloc *r
st->print("%s", Matcher::regName[dst_lo]);
}
if (bottom_type()->isa_vect() && !bottom_type()->isa_pvectmask()) {
- int vsize = 0;
- if (ideal_reg() == Op_VecA) {
- vsize = Matcher::scalable_vector_reg_size(T_BYTE) * 8;
- } else {
- ShouldNotReachHere();
- }
+ assert(ideal_reg() == Op_VecA, "Must be Op_VecA");
+ int vsize = Matcher::scalable_vector_reg_size(T_BYTE) * 8;
st->print("\t# vector spill size = %d", vsize);
- } else if (ideal_reg() == Op_RegVectMask) {
+ } else if (bottom_type()->isa_pvectmask()) {
+ assert(ideal_reg() == Op_RegVectMask, "Must be Op_RegVectMask");
assert(Matcher::supports_scalable_vector(), "bad register type for spill");
int vsize = Matcher::scalable_predicate_reg_slots() * 32;
st->print("\t# vmask spill size = %d", vsize);
From ab2ddc44b8dc7514351f38ab5ccd33d7801dce46 Mon Sep 17 00:00:00 2001
From: Kuai Wei
Date: Thu, 20 Aug 2026 05:59:10 +0000
Subject: [PATCH 87/88] 8389939: RISC-V: OrderAccess can be simplified by
UseZtso
Reviewed-by: fyang, gcao, dzhang
---
.../linux_riscv/orderAccess_linux_riscv.hpp | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp b/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp
index 26530cca5ba1..260b6ce32a8c 100644
--- a/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp
+++ b/src/hotspot/os_cpu/linux_riscv/orderAccess_linux_riscv.hpp
@@ -41,12 +41,25 @@ inline void OrderAccess::storeload() { fence(); }
#define READ_MEM_BARRIER __atomic_thread_fence(__ATOMIC_ACQUIRE);
#define WRITE_MEM_BARRIER __atomic_thread_fence(__ATOMIC_RELEASE);
+// A compiler barrier, forcing the C++ compiler to invalidate all memory assumptions
+static inline void compiler_barrier() {
+ __asm__ volatile ("" : : : "memory");
+}
+
inline void OrderAccess::acquire() {
- READ_MEM_BARRIER;
+ if (UseZtso) {
+ compiler_barrier();
+ } else {
+ READ_MEM_BARRIER;
+ }
}
inline void OrderAccess::release() {
- WRITE_MEM_BARRIER;
+ if (UseZtso) {
+ compiler_barrier();
+ } else {
+ WRITE_MEM_BARRIER;
+ }
}
inline void OrderAccess::fence() {
From 6175a0a8b7a4a102afb9c5608623a4513ef6828f Mon Sep 17 00:00:00 2001
From: Tobias Hartmann
Date: Thu, 20 Aug 2026 06:39:51 +0000
Subject: [PATCH 88/88] 8390625: C2_MacroAssembler::vector_iota_entry_index
hits assert(regs[i] != regs[j])
Reviewed-by: mchevalier, qamai, kvn
---
.../cpu/aarch64/c2_MacroAssembler_aarch64.cpp | 3 +-
.../vectorapi/TestSelectFromSameOperand.java | 58 +++++++++++++++++++
2 files changed, 60 insertions(+), 1 deletion(-)
create mode 100644 test/hotspot/jtreg/compiler/vectorapi/TestSelectFromSameOperand.java
diff --git a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
index 55cfd0756f6f..fba316bf293e 100644
--- a/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c2_MacroAssembler_aarch64.cpp
@@ -2436,7 +2436,8 @@ void C2_MacroAssembler::neon_reverse_bytes(FloatRegister dst, FloatRegister src,
void C2_MacroAssembler::neon_rearrange_hsd(FloatRegister dst, FloatRegister src,
FloatRegister shuffle, FloatRegister tmp,
BasicType bt, bool isQ) {
- assert_different_registers(dst, src, shuffle, tmp);
+ assert_different_registers(dst, src, tmp);
+ assert_different_registers(shuffle, tmp);
SIMD_Arrangement size1 = isQ ? T16B : T8B;
SIMD_Arrangement size2 = esize2arrangement((uint)type2aelembytes(bt), isQ);
diff --git a/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromSameOperand.java b/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromSameOperand.java
new file mode 100644
index 000000000000..789c0c71a4bd
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/vectorapi/TestSelectFromSameOperand.java
@@ -0,0 +1,58 @@
+/*
+ * 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 8390625
+ * @summary Test VectorRearrange with the same source and shuffle operand
+ * @requires vm.compiler2.enabled
+ * @modules jdk.incubator.vector
+ * @library /test/lib
+ * @run main/othervm -Xbatch ${test.main.class}
+ */
+
+package compiler.vectorapi;
+
+import jdk.incubator.vector.ShortVector;
+import jdk.test.lib.Asserts;
+
+public class TestSelectFromSameOperand {
+ private static final short[] INPUT = {7, 6, 5, 4, 3, 2, 1, 0};
+ private static final short[] OUTPUT = new short[INPUT.length];
+
+ public static void test() {
+ // The already masked 'vector' becomes both source and shuffle of the VectorRearrange node
+ ShortVector vector = ShortVector.fromArray(ShortVector.SPECIES_128, INPUT, 0).and((short) 7);
+ vector.selectFrom(vector).intoArray(OUTPUT, 0);
+ }
+
+ public static void main(String[] args) {
+ for (int i = 0; i < 100_000; i++) {
+ test();
+ }
+ for (int i = 0; i < OUTPUT.length; i++) {
+ Asserts.assertEQ(OUTPUT[i], (short) i);
+ }
+ }
+}
+