From 7da91533aaf2033cedee6e2a56fb693f26909df5 Mon Sep 17 00:00:00 2001 From: Sergey Chernyshev Date: Mon, 8 Dec 2025 09:06:21 +0000 Subject: [PATCH 01/10] 8369950: TLS connection to IPv6 address fails with BCJSSE due to IllegalArgumentException Co-authored-by: Mikhail Yankelevich Reviewed-by: djelinski, vyazici, dfuchs, myankelevich --- .../net/www/protocol/https/HttpsClient.java | 11 +- .../HttpsURLConnection/SubjectAltNameIP.java | 379 ++++++++++++++++++ 2 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 test/jdk/javax/net/ssl/HttpsURLConnection/SubjectAltNameIP.java diff --git a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java index 2f011f5805b..9f1d7b07021 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java +++ b/src/java.base/share/classes/sun/net/www/protocol/https/HttpsClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -44,6 +44,7 @@ import java.util.Objects; import java.util.StringTokenizer; import javax.net.ssl.*; +import sun.net.util.IPAddressUtil; import sun.net.www.http.HttpClient; import sun.net.www.protocol.http.AuthCacheImpl; import sun.net.www.protocol.http.HttpURLConnection; @@ -471,7 +472,13 @@ final class HttpsClient extends HttpClient SSLParameters parameters = s.getSSLParameters(); parameters.setEndpointIdentificationAlgorithm("HTTPS"); // host has been set previously for SSLSocketImpl - if (!(s instanceof SSLSocketImpl)) { + if (!(s instanceof SSLSocketImpl) && + !IPAddressUtil.isIPv4LiteralAddress(host) && + !(host.charAt(0) == '[' && host.charAt(host.length() - 1) == ']' && + IPAddressUtil.isIPv6LiteralAddress(host.substring(1, host.length() - 1)) + )) { + // Fully qualified DNS hostname of the server, as per section 3, RFC 6066 + // Literal IPv4 and IPv6 addresses are not permitted in "HostName". parameters.setServerNames(List.of(new SNIHostName(host))); } s.setSSLParameters(parameters); diff --git a/test/jdk/javax/net/ssl/HttpsURLConnection/SubjectAltNameIP.java b/test/jdk/javax/net/ssl/HttpsURLConnection/SubjectAltNameIP.java new file mode 100644 index 00000000000..2def2f69d6e --- /dev/null +++ b/test/jdk/javax/net/ssl/HttpsURLConnection/SubjectAltNameIP.java @@ -0,0 +1,379 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8369950 + * @summary Test that the HttpsURLConnection does not set IP address literals for + * SNI hostname during TLS handshake + * @library /test/lib + * @modules java.base/sun.net.util + * @comment Insert -Djavax.net.debug=all into the following lines to enable SSL debugging + * @run main/othervm SubjectAltNameIP 127.0.0.1 + * @run main/othervm SubjectAltNameIP [::1] + */ + +import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.URI; +import java.util.concurrent.CountDownLatch; +import java.util.function.Consumer; + +import jdk.test.lib.Asserts; +import jdk.test.lib.net.IPSupport; +import jdk.test.lib.net.SimpleSSLContext; +import jtreg.SkippedException; +import sun.net.util.IPAddressUtil; + +public class SubjectAltNameIP { + + // Is the server ready to serve? + private final CountDownLatch serverReady = new CountDownLatch(1); + + // Use any free port by default. + volatile int serverPort = 0; + + // Stores an exception thrown by server in a separate thread. + volatile Exception serverException = null; + + // SSLSocket object created by HttpsClient internally. + SSLSocket clientSSLSocket = null; + + // The hostname the server socket is bound to. + String hostName; + + static final byte[] requestEnd = new byte[] {'\r', '\n', '\r', '\n' }; + + // Read until the end of the request. + void readOneRequest(InputStream is) throws IOException { + int requestEndCount = 0, r; + while ((r = is.read()) != -1) { + if (r == requestEnd[requestEndCount]) { + requestEndCount++; + if (requestEndCount == 4) { + break; + } + } else { + requestEndCount = 0; + } + } + } + + /* + * Define the server side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doServerSide() throws Exception { + SSLServerSocketFactory sslssf = + new SimpleSSLContext().get().getServerSocketFactory(); + SSLServerSocket sslServerSocket = + (SSLServerSocket) sslssf.createServerSocket( + serverPort, 0, + InetAddress.getByName(hostName)); + sslServerSocket.setEnabledProtocols(new String[]{"TLSv1.3"}); + serverPort = sslServerSocket.getLocalPort(); + + /* + * Signal the client, the server is ready to accept connection. + */ + serverReady.countDown(); + + SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); + OutputStream sslOS = sslSocket.getOutputStream(); + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sslOS)); + bw.write("HTTP/1.1 200 OK\r\n\r\n"); + bw.flush(); + readOneRequest(sslSocket.getInputStream()); + sslSocket.close(); + } + + /* + * Define the client side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doClientSide() throws Exception { + + /* + * Wait for server to get started. + */ + serverReady.await(); + if (serverException != null) { + throw new RuntimeException("Server failed to start.", serverException); + } + + SSLSocketFactory sf = new SimpleSSLContext().get().getSocketFactory(); + URI uri = new URI("https://" + hostName + ":" + serverPort + "/index.html"); + HttpsURLConnection conn = (HttpsURLConnection)uri.toURL().openConnection(); + + /* + * Simulate an external JSSE implementation and store the client SSLSocket + * used internally. + */ + conn.setSSLSocketFactory(wrapSocketFactory(sf, + sslSocket -> { + Asserts.assertEquals(null, clientSSLSocket, "clientSSLSocket is"); + clientSSLSocket = sslSocket; + })); + conn.getInputStream(); + + var sniSN = clientSSLSocket.getSSLParameters().getServerNames(); + if (sniSN != null && !sniSN.isEmpty()) { + throw new RuntimeException("SNI server name '" + + sniSN.getFirst() + "' must not be set."); + } + + if (conn.getResponseCode() == -1) { + throw new RuntimeException("getResponseCode() returns -1"); + } + } + + public static void main(String[] args) throws Exception { + + if (IPAddressUtil.isIPv6LiteralAddress(args[0]) && !IPSupport.hasIPv6()) { + throw new SkippedException("Skipping test - IPv6 is not supported"); + } + /* + * Start the tests. + */ + new SubjectAltNameIP(args[0]); + } + + Thread serverThread = null; + + /* + * Primary constructor, used to drive remainder of the test. + * + * Fork off the other side, then do your work. + */ + SubjectAltNameIP(String host) throws Exception { + hostName = host; + startServer(); + doClientSide(); + + /* + * Wait for other side to close down. + */ + serverThread.join(); + + if (serverException != null) + throw serverException; + } + + void startServer() { + serverThread = new Thread(() -> { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Store the exception and release the client. + */ + serverException = e; + serverReady.countDown(); + } + }); + serverThread.start(); + } + + /* + * Wraps SSLSocketImpl to simulate a different JSSE implementation + */ + private static SSLSocketFactory wrapSocketFactory(final SSLSocketFactory wrap, final Consumer store) { + return new SSLSocketFactory() { + @Override + public String[] getDefaultCipherSuites() { + return wrap.getDefaultCipherSuites(); + } + @Override + public String[] getSupportedCipherSuites() { + return wrap.getSupportedCipherSuites(); + } + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException { + final SSLSocket so = + (SSLSocket) wrap.createSocket(s, host, port, autoClose); + + // store the underlying SSLSocket for later use + store.accept(so); + + return new SSLSocket() { + @Override + public void connect(SocketAddress endpoint, + int timeout) throws IOException { + so.connect(endpoint, timeout); + } + @Override + public String[] getSupportedCipherSuites() { + return so.getSupportedCipherSuites(); + } + @Override + public String[] getEnabledCipherSuites() { + return so.getEnabledCipherSuites(); + } + @Override + public void setEnabledCipherSuites(String[] suites) { + so.setEnabledCipherSuites(suites); + } + @Override + public String[] getSupportedProtocols() { + return so.getSupportedProtocols(); + } + @Override + public String[] getEnabledProtocols() { + return so.getEnabledProtocols(); + } + @Override + public void setEnabledProtocols(String[] protocols) { + so.setEnabledProtocols(protocols); + } + @Override + public SSLSession getSession() { + return so.getSession(); + } + @Override + public SSLSession getHandshakeSession() { + return so.getHandshakeSession(); + } + @Override + public void addHandshakeCompletedListener(HandshakeCompletedListener listener) { + so.addHandshakeCompletedListener(listener); + } + @Override + public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) { + so.removeHandshakeCompletedListener(listener); + } + @Override + public void startHandshake() throws IOException { + so.startHandshake(); + } + @Override + public void setUseClientMode(boolean mode) { + so.setUseClientMode(mode); + } + @Override + public boolean getUseClientMode() { + return so.getUseClientMode(); + } + @Override + public void setNeedClientAuth(boolean need) { + } + @Override + public boolean getNeedClientAuth() { + return false; + } + @Override + public void setWantClientAuth(boolean want) { + } + @Override + public boolean getWantClientAuth() { + return false; + } + @Override + public void setEnableSessionCreation(boolean flag) { + so.setEnableSessionCreation(flag); + } + @Override + public boolean getEnableSessionCreation() { + return so.getEnableSessionCreation(); + } + @Override + public void close() throws IOException { + so.close(); + } + @Override + public boolean isClosed() { + return so.isClosed(); + } + @Override + public void shutdownInput() throws IOException { + so.shutdownInput(); + } + @Override + public boolean isInputShutdown() { + return so.isInputShutdown(); + } + @Override + public void shutdownOutput() throws IOException { + so.shutdownOutput(); + } + @Override + public boolean isOutputShutdown() { + return so.isOutputShutdown(); + } + @Override + public InputStream getInputStream() throws IOException { + return so.getInputStream(); + } + @Override + public OutputStream getOutputStream() throws IOException { + return so.getOutputStream(); + } + @Override + public SSLParameters getSSLParameters() { + return so.getSSLParameters(); + } + @Override + public void setSSLParameters(SSLParameters params) { + so.setSSLParameters(params); + } + }; + } + @Override + public Socket createSocket(String h, int p) { + return null; + } + @Override + public Socket createSocket(String h, int p, InetAddress ipa, int lp) { + return null; + } + @Override + public Socket createSocket(InetAddress h, int p) { + return null; + } + @Override + public Socket createSocket(InetAddress a, int p, InetAddress l, int lp) { + return null; + } + }; + } +} From 350015088281eb9e6e9e3a9811f38adac5f7a975 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Mon, 8 Dec 2025 10:04:44 +0000 Subject: [PATCH 02/10] 8373094: javac may fail because of unattributed break in a loop Reviewed-by: vromero --- .../com/sun/tools/javac/comp/Attr.java | 16 +- .../tools/javac/recovery/AttrRecovery.java | 158 +++++++++++++++++- 2 files changed, 167 insertions(+), 7 deletions(-) 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 3f72ada94e8..cc21113882f 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 @@ -5634,12 +5634,16 @@ public class Attr extends JCTree.Visitor { chk.validateRepeatable(c, repeatable, cbPos); } } else { - // Check that all extended classes and interfaces - // are compatible (i.e. no two define methods with same arguments - // yet different return types). (JLS 8.4.8.3) - chk.checkCompatibleSupertypes(tree.pos(), c.type); - chk.checkDefaultMethodClashes(tree.pos(), c.type); - chk.checkPotentiallyAmbiguousOverloads(tree, c.type); + try { + // Check that all extended classes and interfaces + // are compatible (i.e. no two define methods with same arguments + // yet different return types). (JLS 8.4.8.3) + chk.checkCompatibleSupertypes(tree.pos(), c.type); + chk.checkDefaultMethodClashes(tree.pos(), c.type); + chk.checkPotentiallyAmbiguousOverloads(tree, c.type); + } catch (CompletionFailure cf) { + chk.completionError(tree.pos(), cf); + } } // Check that class does not import the same parameterized interface diff --git a/test/langtools/tools/javac/recovery/AttrRecovery.java b/test/langtools/tools/javac/recovery/AttrRecovery.java index 9a37ce60654..ac1455e71d7 100644 --- a/test/langtools/tools/javac/recovery/AttrRecovery.java +++ b/test/langtools/tools/javac/recovery/AttrRecovery.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8301580 8322159 8333107 8332230 8338678 8351260 8366196 8372336 + * @bug 8301580 8322159 8333107 8332230 8338678 8351260 8366196 8372336 8373094 * @summary Verify error recovery w.r.t. Attr * @library /tools/lib * @modules jdk.compiler/com.sun.tools.javac.api @@ -32,8 +32,11 @@ * @run main AttrRecovery */ +import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberReferenceTree; +import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.MethodTree; import com.sun.source.tree.VariableTree; import com.sun.source.util.TaskEvent; import com.sun.source.util.TaskListener; @@ -41,11 +44,13 @@ import com.sun.source.util.TreePathScanner; import com.sun.source.util.Trees; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.lang.model.element.Element; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; @@ -495,4 +500,155 @@ public class AttrRecovery extends TestRunner { } } } + + @Test //JDK-8373094 + public void testSensibleAttribution() throws Exception { + Path curPath = Path.of("."); + Path lib = curPath.resolve("lib"); + Path classes = lib.resolve("classes"); + Files.createDirectories(classes); + new JavacTask(tb) + .outdir(classes) + .sources(""" + package test; + public class Intermediate extends Base {} + """, + """ + package test; + public class Base { + public void t(Missing m) {} + } + """, + """ + package test; + public class Missing { + } + """) + .run() + .writeAll(); + + Files.delete(classes.resolve("test").resolve("Missing.class")); + + record TestCase(String code, List options, String... expectedErrors) {} + TestCase[] testCases = new TestCase[] { + new TestCase(""" + package test; + public class Test extends Intermediate { + private void test() { + int i = 0; + System.err.println(i); + while (true) { + break; + } + } + } + """, + List.of(), + "Test.java:2:8: compiler.err.cant.access: test.Missing, (compiler.misc.class.file.not.found: test.Missing)", + "1 error"), + new TestCase(""" + package test; + public class Test extends Intermediate { + private void test() { + int i = 0; + System.err.println(i); + while (true) { + break; + } + } + } + """, + List.of("-XDshould-stop.at=FLOW"), + "Test.java:2:8: compiler.err.cant.access: test.Missing, (compiler.misc.class.file.not.found: test.Missing)", + "1 error"), + }; + + for (TestCase tc : testCases) { + List attributes = new ArrayList<>(); + List actual = new JavacTask(tb) + .options(Stream.concat(List.of("-XDrawDiagnostics", "-XDdev").stream(), + tc.options.stream()).toList()) + .classpath(classes) + .sources(tc.code()) + .outdir(curPath) + .callback(task -> { + task.addTaskListener(new TaskListener() { + @Override + public void finished(TaskEvent e) { + if (e.getKind() != TaskEvent.Kind.ANALYZE) { + return ; + } + Trees trees = Trees.instance(task); + new TreePathScanner() { + boolean check; + + @Override + public Void visitMethod(MethodTree node, Void p) { + if (node.getName().contentEquals("test")) { + check = true; + try { + return super.visitMethod(node, p); + } finally { + check = false; + } + } + + return super.visitMethod(node, p); + } + + @Override + public Void visitMethodInvocation(MethodInvocationTree node, Void p) { + if (!node.toString().contains("super")) { + verifyElement(); + } + return super.visitMethodInvocation(node, p); + } + + @Override + public Void visitIdentifier(IdentifierTree node, Void p) { + verifyElement(); + return super.visitIdentifier(node, p); + } + + @Override + public Void visitMemberSelect(MemberSelectTree node, Void p) { + verifyElement(); + return super.visitMemberSelect(node, p); + } + + private void verifyElement() { + if (!check) { + return ; + } + + Element el = trees.getElement(getCurrentPath()); + if (el == null) { + error("Unattributed tree: " + getCurrentPath().getLeaf()); + } else { + attributes.add(el.toString()); + } + } + }.scan(e.getCompilationUnit(), null); + } + }); + }) + .run(Expect.FAIL) + .writeAll() + .getOutputLines(OutputKind.DIRECT); + + List expectedErrors = List.of(tc.expectedErrors); + + if (!Objects.equals(actual, expectedErrors)) { + error("Expected: " + expectedErrors + ", but got: " + actual); + } + + List expectedAttributes = + List.of("println(int)", "println(int)", "err", "java.lang.System", "i"); + + if (!Objects.equals(attributes, expectedAttributes)) { + error("Expected: " + expectedAttributes + ", but got: " + attributes); + } + } + } + } From a6594794839807d56434d6f28fe3d581fb1e36c0 Mon Sep 17 00:00:00 2001 From: Francesco Andreuzzi Date: Mon, 8 Dec 2025 11:45:53 +0000 Subject: [PATCH 03/10] 8367541: Parallel: Make young and old generation fields nonstatic in ParallelScavengeHeap Reviewed-by: ayang --- src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp | 2 -- src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp | 8 ++++---- src/hotspot/share/gc/parallel/psScavenge.hpp | 2 +- src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp | 4 ++-- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 9df3deedf89..b85b16f58b5 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -58,8 +58,6 @@ #include "utilities/macros.hpp" #include "utilities/vmError.hpp" -PSYoungGen* ParallelScavengeHeap::_young_gen = nullptr; -PSOldGen* ParallelScavengeHeap::_old_gen = nullptr; PSAdaptiveSizePolicy* ParallelScavengeHeap::_size_policy = nullptr; GCPolicyCounters* ParallelScavengeHeap::_gc_policy_counters = nullptr; size_t ParallelScavengeHeap::_desired_page_size = 0; diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp index 5d8ddbcaaed..588ddfa3f0c 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp @@ -69,8 +69,8 @@ class ReservedSpace; class ParallelScavengeHeap : public CollectedHeap { friend class VMStructs; private: - static PSYoungGen* _young_gen; - static PSOldGen* _old_gen; + PSYoungGen* _young_gen; + PSOldGen* _old_gen; // Sizing policy for entire heap static PSAdaptiveSizePolicy* _size_policy; @@ -160,8 +160,8 @@ public: GrowableArray memory_managers() override; GrowableArray memory_pools() override; - static PSYoungGen* young_gen() { return _young_gen; } - static PSOldGen* old_gen() { return _old_gen; } + PSYoungGen* young_gen() const { return _young_gen; } + PSOldGen* old_gen() const { return _old_gen; } PSAdaptiveSizePolicy* size_policy() { return _size_policy; } diff --git a/src/hotspot/share/gc/parallel/psScavenge.hpp b/src/hotspot/share/gc/parallel/psScavenge.hpp index af9b91f74bc..df97a1c1ede 100644 --- a/src/hotspot/share/gc/parallel/psScavenge.hpp +++ b/src/hotspot/share/gc/parallel/psScavenge.hpp @@ -115,7 +115,7 @@ class PSScavenge: AllStatic { } static bool is_obj_in_to_space(oop o) { - return ParallelScavengeHeap::young_gen()->to_space()->contains(o); + return ParallelScavengeHeap::heap()->young_gen()->to_space()->contains(o); } }; diff --git a/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp b/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp index f5e7375fca1..e45bd45400c 100644 --- a/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp +++ b/src/hotspot/share/gc/parallel/vmStructs_parallelgc.hpp @@ -64,8 +64,8 @@ nonstatic_field(PSOldGen, _max_gen_size, const size_t) \ \ \ - static_field(ParallelScavengeHeap, _young_gen, PSYoungGen*) \ - static_field(ParallelScavengeHeap, _old_gen, PSOldGen*) \ + nonstatic_field(ParallelScavengeHeap, _young_gen, PSYoungGen*) \ + nonstatic_field(ParallelScavengeHeap, _old_gen, PSOldGen*) \ \ #define VM_TYPES_PARALLELGC(declare_type, \ From b83bf0717eb8926efcf85a32be08f33a41bb48dd Mon Sep 17 00:00:00 2001 From: Qizheng Xing Date: Mon, 8 Dec 2025 13:16:39 +0000 Subject: [PATCH 04/10] 8360192: C2: Make the type of count leading/trailing zero nodes more precise Reviewed-by: qamai, epeter, jbhateja --- src/hotspot/share/opto/countbitsnode.cpp | 149 +++-- .../compiler/c2/gvn/TestCountBitsRange.java | 570 ++++++++++++++++++ .../compiler/lib/ir_framework/IRNode.java | 20 + .../bench/vm/compiler/CountLeadingZeros.java | 74 +++ 4 files changed, 747 insertions(+), 66 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java create mode 100644 test/micro/org/openjdk/bench/vm/compiler/CountLeadingZeros.java diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index aac874e94b1..1601b33ea2b 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -26,97 +26,114 @@ #include "opto/opcodes.hpp" #include "opto/phaseX.hpp" #include "opto/type.hpp" +#include "utilities/count_leading_zeros.hpp" +#include "utilities/count_trailing_zeros.hpp" #include "utilities/population_count.hpp" +static int count_leading_zeros_int(jint i) { + return i == 0 ? BitsPerInt : count_leading_zeros(i); +} + +static int count_leading_zeros_long(jlong l) { + return l == 0 ? BitsPerLong : count_leading_zeros(l); +} + +static int count_trailing_zeros_int(jint i) { + return i == 0 ? BitsPerInt : count_trailing_zeros(i); +} + +static int count_trailing_zeros_long(jlong l) { + return l == 0 ? BitsPerLong : count_trailing_zeros(l); +} + //------------------------------Value------------------------------------------ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); - if (t == Type::TOP) return Type::TOP; - const TypeInt* ti = t->isa_int(); - if (ti && ti->is_con()) { - jint i = ti->get_con(); - // HD, Figure 5-6 - if (i == 0) - return TypeInt::make(BitsPerInt); - int n = 1; - unsigned int x = i; - if (x >> 16 == 0) { n += 16; x <<= 16; } - if (x >> 24 == 0) { n += 8; x <<= 8; } - if (x >> 28 == 0) { n += 4; x <<= 4; } - if (x >> 30 == 0) { n += 2; x <<= 2; } - n -= x >> 31; - return TypeInt::make(n); + if (t == Type::TOP) { + return Type::TOP; } - return TypeInt::INT; + + // To minimize `count_leading_zeros(x)`, we should make the highest 1 bit in x + // as far to the left as possible. A bit in x can be 1 iff this bit is not + // forced to be 0, i.e. the corresponding bit in `x._bits._zeros` is 0. Thus: + // min(clz(x)) = number of bits to the left of the highest 0 bit in x._bits._zeros + // = count_leading_ones(x._bits._zeros) = clz(~x._bits._zeros) + // + // To maximize `count_leading_zeros(x)`, we should make the leading zeros as + // many as possible. A bit in x can be 0 iff this bit is not forced to be 1, + // i.e. the corresponding bit in `x._bits._ones` is 0. Thus: + // max(clz(x)) = clz(x._bits._ones) + // + // Therefore, the range of `count_leading_zeros(x)` is: + // [clz(~x._bits._zeros), clz(x._bits._ones)] + // + // A more detailed proof using Z3 can be found at: + // https://github.com/openjdk/jdk/pull/25928#discussion_r2256750507 + const TypeInt* ti = t->is_int(); + return TypeInt::make(count_leading_zeros_int(~ti->_bits._zeros), + count_leading_zeros_int(ti->_bits._ones), + ti->_widen); } //------------------------------Value------------------------------------------ const Type* CountLeadingZerosLNode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); - if (t == Type::TOP) return Type::TOP; - const TypeLong* tl = t->isa_long(); - if (tl && tl->is_con()) { - jlong l = tl->get_con(); - // HD, Figure 5-6 - if (l == 0) - return TypeInt::make(BitsPerLong); - int n = 1; - unsigned int x = (((julong) l) >> 32); - if (x == 0) { n += 32; x = (int) l; } - if (x >> 16 == 0) { n += 16; x <<= 16; } - if (x >> 24 == 0) { n += 8; x <<= 8; } - if (x >> 28 == 0) { n += 4; x <<= 4; } - if (x >> 30 == 0) { n += 2; x <<= 2; } - n -= x >> 31; - return TypeInt::make(n); + if (t == Type::TOP) { + return Type::TOP; } - return TypeInt::INT; + + // The proof of correctness is same as the above comments + // in `CountLeadingZerosINode::Value`. + const TypeLong* tl = t->is_long(); + return TypeInt::make(count_leading_zeros_long(~tl->_bits._zeros), + count_leading_zeros_long(tl->_bits._ones), + tl->_widen); } //------------------------------Value------------------------------------------ const Type* CountTrailingZerosINode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); - if (t == Type::TOP) return Type::TOP; - const TypeInt* ti = t->isa_int(); - if (ti && ti->is_con()) { - jint i = ti->get_con(); - // HD, Figure 5-14 - int y; - if (i == 0) - return TypeInt::make(BitsPerInt); - int n = 31; - y = i << 16; if (y != 0) { n = n - 16; i = y; } - y = i << 8; if (y != 0) { n = n - 8; i = y; } - y = i << 4; if (y != 0) { n = n - 4; i = y; } - y = i << 2; if (y != 0) { n = n - 2; i = y; } - y = i << 1; if (y != 0) { n = n - 1; } - return TypeInt::make(n); + if (t == Type::TOP) { + return Type::TOP; } - return TypeInt::INT; + + // To minimize `count_trailing_zeros(x)`, we should make the lowest 1 bit in x + // as far to the right as possible. A bit in x can be 1 iff this bit is not + // forced to be 0, i.e. the corresponding bit in `x._bits._zeros` is 0. Thus: + // min(ctz(x)) = number of bits to the right of the lowest 0 bit in x._bits._zeros + // = count_trailing_ones(x._bits._zeros) = ctz(~x._bits._zeros) + // + // To maximize `count_trailing_zeros(x)`, we should make the trailing zeros as + // many as possible. A bit in x can be 0 iff this bit is not forced to be 1, + // i.e. the corresponding bit in `x._bits._ones` is 0. Thus: + // max(ctz(x)) = ctz(x._bits._ones) + // + // Therefore, the range of `count_trailing_zeros(x)` is: + // [ctz(~x._bits._zeros), ctz(x._bits._ones)] + // + // A more detailed proof using Z3 can be found at: + // https://github.com/openjdk/jdk/pull/25928#discussion_r2256750507 + const TypeInt* ti = t->is_int(); + return TypeInt::make(count_trailing_zeros_int(~ti->_bits._zeros), + count_trailing_zeros_int(ti->_bits._ones), + ti->_widen); } //------------------------------Value------------------------------------------ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); - if (t == Type::TOP) return Type::TOP; - const TypeLong* tl = t->isa_long(); - if (tl && tl->is_con()) { - jlong l = tl->get_con(); - // HD, Figure 5-14 - int x, y; - if (l == 0) - return TypeInt::make(BitsPerLong); - int n = 63; - y = (int) l; if (y != 0) { n = n - 32; x = y; } else x = (((julong) l) >> 32); - y = x << 16; if (y != 0) { n = n - 16; x = y; } - y = x << 8; if (y != 0) { n = n - 8; x = y; } - y = x << 4; if (y != 0) { n = n - 4; x = y; } - y = x << 2; if (y != 0) { n = n - 2; x = y; } - y = x << 1; if (y != 0) { n = n - 1; } - return TypeInt::make(n); + if (t == Type::TOP) { + return Type::TOP; } - return TypeInt::INT; + + // The proof of correctness is same as the above comments + // in `CountTrailingZerosINode::Value`. + const TypeLong* tl = t->is_long(); + return TypeInt::make(count_trailing_zeros_long(~tl->_bits._zeros), + count_trailing_zeros_long(tl->_bits._ones), + tl->_widen); } + // We use the KnownBits information from the integer types to derive how many one bits // we have at least and at most. // From the definition of KnownBits, we know: diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java new file mode 100644 index 00000000000..00aa466e822 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2025 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.gvn; + +import compiler.lib.generators.Generator; +import compiler.lib.generators.Generators; +import compiler.lib.generators.RestrictableGenerator; +import compiler.lib.ir_framework.*; +import java.util.function.Function; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8360192 + * @summary Tests that count bits nodes are handled correctly. + * @library /test/lib / + * @run driver compiler.c2.gvn.TestCountBitsRange + */ +public class TestCountBitsRange { + private static final Generator INTS = Generators.G.ints(); + private static final Generator LONGS = Generators.G.longs(); + + private static final RestrictableGenerator INTS_32 = Generators.G.ints().restricted(0, 32); + private static final RestrictableGenerator INTS_64 = Generators.G.ints().restricted(0, 64); + + private static final int LIMITS_32_0 = INTS_32.next(); + private static final int LIMITS_32_1 = INTS_32.next(); + private static final int LIMITS_32_2 = INTS_32.next(); + private static final int LIMITS_32_3 = INTS_32.next(); + private static final int LIMITS_32_4 = INTS_32.next(); + private static final int LIMITS_32_5 = INTS_32.next(); + private static final int LIMITS_32_6 = INTS_32.next(); + private static final int LIMITS_32_7 = INTS_32.next(); + + private static final int LIMITS_64_0 = INTS_64.next(); + private static final int LIMITS_64_1 = INTS_64.next(); + private static final int LIMITS_64_2 = INTS_64.next(); + private static final int LIMITS_64_3 = INTS_64.next(); + private static final int LIMITS_64_4 = INTS_64.next(); + private static final int LIMITS_64_5 = INTS_64.next(); + private static final int LIMITS_64_6 = INTS_64.next(); + private static final int LIMITS_64_7 = INTS_64.next(); + + private static final IntRange RANGE_INT = IntRange.generate(INTS); + private static final LongRange RANGE_LONG = LongRange.generate(LONGS); + + public static void main(String[] args) { + TestFramework.run(); + } + + @Run(test = { + "clzConstInts", "clzCompareInt", "clzDiv8Int", "clzRandLimitInt", + "clzConstLongs", "clzCompareLong", "clzDiv8Long", "clzRandLimitLong", + "ctzConstInts", "ctzCompareInt", "ctzDiv8Int", "ctzRandLimitInt", + "ctzConstLongs", "ctzCompareLong", "ctzDiv8Long", "ctzRandLimitLong", + }) + public void runTest() { + int randInt = INTS.next(); + long randLong = LONGS.next(); + assertResult(randInt, randLong); + } + + @DontCompile + public void assertResult(int randInt, long randLong) { + checkConstResults(clzConstInts(), x -> Integer.numberOfLeadingZeros(x.intValue())); + Asserts.assertEQ(Integer.numberOfLeadingZeros(randInt) < 0 + || Integer.numberOfLeadingZeros(randInt) > 32, + clzCompareInt(randInt)); + Asserts.assertEQ(Integer.numberOfLeadingZeros(randInt) / 8, + clzDiv8Int(randInt)); + Asserts.assertEQ(clzRandLimitInterpretedInt(randInt), clzRandLimitInt(randInt)); + + checkConstResults(clzConstLongs(), x -> Long.numberOfLeadingZeros(x.longValue())); + Asserts.assertEQ(Long.numberOfLeadingZeros(randLong) < 0 + || Long.numberOfLeadingZeros(randLong) > 64, + clzCompareLong(randLong)); + Asserts.assertEQ(Long.numberOfLeadingZeros(randLong) / 8, + clzDiv8Long(randLong)); + Asserts.assertEQ(clzRandLimitInterpretedLong(randLong), clzRandLimitLong(randLong)); + + checkConstResults(ctzConstInts(), x -> Integer.numberOfTrailingZeros(x.intValue())); + Asserts.assertEQ(Integer.numberOfTrailingZeros(randInt) < 0 + || Integer.numberOfTrailingZeros(randInt) > 32, + ctzCompareInt(randInt)); + Asserts.assertEQ(Integer.numberOfTrailingZeros(randInt) / 8, + ctzDiv8Int(randInt)); + Asserts.assertEQ(ctzRandLimitInterpretedInt(randInt), ctzRandLimitInt(randInt)); + + checkConstResults(ctzConstLongs(), x -> Long.numberOfTrailingZeros(x.longValue())); + Asserts.assertEQ(Long.numberOfTrailingZeros(randLong) < 0 + || Long.numberOfTrailingZeros(randLong) > 64, + ctzCompareLong(randLong)); + Asserts.assertEQ(Long.numberOfTrailingZeros(randLong) / 8, + ctzDiv8Long(randLong)); + Asserts.assertEQ(ctzRandLimitInterpretedLong(randLong), ctzRandLimitLong(randLong)); + } + + @DontCompile + public void checkConstResults(int[] results, Function op) { + Asserts.assertEQ(op.apply(Long.valueOf(0)), results[0]); + for (int i = 0; i < results.length - 1; ++i) { + Asserts.assertEQ(op.apply(Long.valueOf(1l << i)), results[i + 1]); + } + } + + // Test CLZ with constant integer inputs. + // All CLZs in this test are expected to be optimized away. + @Test + @IR(failOn = IRNode.COUNT_LEADING_ZEROS_I) + public int[] clzConstInts() { + return new int[] { + Integer.numberOfLeadingZeros(0), + Integer.numberOfLeadingZeros(1 << 0), + Integer.numberOfLeadingZeros(1 << 1), + Integer.numberOfLeadingZeros(1 << 2), + Integer.numberOfLeadingZeros(1 << 3), + Integer.numberOfLeadingZeros(1 << 4), + Integer.numberOfLeadingZeros(1 << 5), + Integer.numberOfLeadingZeros(1 << 6), + Integer.numberOfLeadingZeros(1 << 7), + Integer.numberOfLeadingZeros(1 << 8), + Integer.numberOfLeadingZeros(1 << 9), + Integer.numberOfLeadingZeros(1 << 10), + Integer.numberOfLeadingZeros(1 << 11), + Integer.numberOfLeadingZeros(1 << 12), + Integer.numberOfLeadingZeros(1 << 13), + Integer.numberOfLeadingZeros(1 << 14), + Integer.numberOfLeadingZeros(1 << 15), + Integer.numberOfLeadingZeros(1 << 16), + Integer.numberOfLeadingZeros(1 << 17), + Integer.numberOfLeadingZeros(1 << 18), + Integer.numberOfLeadingZeros(1 << 19), + Integer.numberOfLeadingZeros(1 << 20), + Integer.numberOfLeadingZeros(1 << 21), + Integer.numberOfLeadingZeros(1 << 22), + Integer.numberOfLeadingZeros(1 << 23), + Integer.numberOfLeadingZeros(1 << 24), + Integer.numberOfLeadingZeros(1 << 25), + Integer.numberOfLeadingZeros(1 << 26), + Integer.numberOfLeadingZeros(1 << 27), + Integer.numberOfLeadingZeros(1 << 28), + Integer.numberOfLeadingZeros(1 << 29), + Integer.numberOfLeadingZeros(1 << 30), + Integer.numberOfLeadingZeros(1 << 31), + }; + } + + // Test the range of CLZ with random integer input. + // The result of CLZ should be in range [0, 32], so CLZs in this test are + // expected to be optimized away, and the test should always return false. + @Test + @IR(failOn = IRNode.COUNT_LEADING_ZEROS_I) + public boolean clzCompareInt(int randInt) { + return Integer.numberOfLeadingZeros(randInt) < 0 + || Integer.numberOfLeadingZeros(randInt) > 32; + } + + // Test the combination of CLZ and division by 8. + // The result of CLZ should be positive, so the division by 8 should be + // optimized to a simple right shift without rounding. + @Test + @IR(counts = {IRNode.COUNT_LEADING_ZEROS_I, "1", + IRNode.RSHIFT_I, "1", + IRNode.URSHIFT_I, "0", + IRNode.ADD_I, "0"}) + public int clzDiv8Int(int randInt) { + return Integer.numberOfLeadingZeros(randInt) / 8; + } + + // Test the output range of CLZ with random input range. + @Test + public int clzRandLimitInt(int randInt) { + randInt = RANGE_INT.clamp(randInt); + int result = Integer.numberOfLeadingZeros(randInt); + return getResultChecksum32(result); + } + + @DontCompile + public int clzRandLimitInterpretedInt(int randInt) { + randInt = RANGE_INT.clamp(randInt); + int result = Integer.numberOfLeadingZeros(randInt); + return getResultChecksum32(result); + } + + // Test CLZ with constant long inputs. + // All CLZs in this test are expected to be optimized away. + @Test + @IR(failOn = IRNode.COUNT_LEADING_ZEROS_L) + public int[] clzConstLongs() { + return new int[] { + Long.numberOfLeadingZeros(0), + Long.numberOfLeadingZeros(1l << 0), + Long.numberOfLeadingZeros(1l << 1), + Long.numberOfLeadingZeros(1l << 2), + Long.numberOfLeadingZeros(1l << 3), + Long.numberOfLeadingZeros(1l << 4), + Long.numberOfLeadingZeros(1l << 5), + Long.numberOfLeadingZeros(1l << 6), + Long.numberOfLeadingZeros(1l << 7), + Long.numberOfLeadingZeros(1l << 8), + Long.numberOfLeadingZeros(1l << 9), + Long.numberOfLeadingZeros(1l << 10), + Long.numberOfLeadingZeros(1l << 11), + Long.numberOfLeadingZeros(1l << 12), + Long.numberOfLeadingZeros(1l << 13), + Long.numberOfLeadingZeros(1l << 14), + Long.numberOfLeadingZeros(1l << 15), + Long.numberOfLeadingZeros(1l << 16), + Long.numberOfLeadingZeros(1l << 17), + Long.numberOfLeadingZeros(1l << 18), + Long.numberOfLeadingZeros(1l << 19), + Long.numberOfLeadingZeros(1l << 20), + Long.numberOfLeadingZeros(1l << 21), + Long.numberOfLeadingZeros(1l << 22), + Long.numberOfLeadingZeros(1l << 23), + Long.numberOfLeadingZeros(1l << 24), + Long.numberOfLeadingZeros(1l << 25), + Long.numberOfLeadingZeros(1l << 26), + Long.numberOfLeadingZeros(1l << 27), + Long.numberOfLeadingZeros(1l << 28), + Long.numberOfLeadingZeros(1l << 29), + Long.numberOfLeadingZeros(1l << 30), + Long.numberOfLeadingZeros(1l << 31), + Long.numberOfLeadingZeros(1l << 32), + Long.numberOfLeadingZeros(1l << 33), + Long.numberOfLeadingZeros(1l << 34), + Long.numberOfLeadingZeros(1l << 35), + Long.numberOfLeadingZeros(1l << 36), + Long.numberOfLeadingZeros(1l << 37), + Long.numberOfLeadingZeros(1l << 38), + Long.numberOfLeadingZeros(1l << 39), + Long.numberOfLeadingZeros(1l << 40), + Long.numberOfLeadingZeros(1l << 41), + Long.numberOfLeadingZeros(1l << 42), + Long.numberOfLeadingZeros(1l << 43), + Long.numberOfLeadingZeros(1l << 44), + Long.numberOfLeadingZeros(1l << 45), + Long.numberOfLeadingZeros(1l << 46), + Long.numberOfLeadingZeros(1l << 47), + Long.numberOfLeadingZeros(1l << 48), + Long.numberOfLeadingZeros(1l << 49), + Long.numberOfLeadingZeros(1l << 50), + Long.numberOfLeadingZeros(1l << 51), + Long.numberOfLeadingZeros(1l << 52), + Long.numberOfLeadingZeros(1l << 53), + Long.numberOfLeadingZeros(1l << 54), + Long.numberOfLeadingZeros(1l << 55), + Long.numberOfLeadingZeros(1l << 56), + Long.numberOfLeadingZeros(1l << 57), + Long.numberOfLeadingZeros(1l << 58), + Long.numberOfLeadingZeros(1l << 59), + Long.numberOfLeadingZeros(1l << 60), + Long.numberOfLeadingZeros(1l << 61), + Long.numberOfLeadingZeros(1l << 62), + Long.numberOfLeadingZeros(1l << 63), + }; + } + + // Test the range of CLZ with random long input. + // The result of CLZ should be in range [0, 64], so CLZs in this test are + // expected to be optimized away, and the test should always return false. + @Test + @IR(failOn = IRNode.COUNT_LEADING_ZEROS_L) + public boolean clzCompareLong(long randLong) { + return Long.numberOfLeadingZeros(randLong) < 0 + || Long.numberOfLeadingZeros(randLong) > 64; + } + + // Test the combination of CLZ and division by 8. + // The result of CLZ should be positive, so the division by 8 should be + // optimized to a simple right shift without rounding. + @Test + @IR(counts = {IRNode.COUNT_LEADING_ZEROS_L, "1", + IRNode.RSHIFT_I, "1", + IRNode.URSHIFT_I, "0", + IRNode.ADD_I, "0"}) + public int clzDiv8Long(long randLong) { + return Long.numberOfLeadingZeros(randLong) / 8; + } + + // Test the output range of CLZ with random input range. + @Test + public int clzRandLimitLong(long randLong) { + randLong = RANGE_LONG.clamp(randLong); + int result = Long.numberOfLeadingZeros(randLong); + return getResultChecksum64(result); + } + + @DontCompile + public int clzRandLimitInterpretedLong(long randLong) { + randLong = RANGE_LONG.clamp(randLong); + int result = Long.numberOfLeadingZeros(randLong); + return getResultChecksum64(result); + } + + // Test CTZ with constant integer inputs. + // All CTZs in this test are expected to be optimized away. + @Test + @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_I) + public int[] ctzConstInts() { + return new int[] { + Integer.numberOfTrailingZeros(0), + Integer.numberOfTrailingZeros(1 << 0), + Integer.numberOfTrailingZeros(1 << 1), + Integer.numberOfTrailingZeros(1 << 2), + Integer.numberOfTrailingZeros(1 << 3), + Integer.numberOfTrailingZeros(1 << 4), + Integer.numberOfTrailingZeros(1 << 5), + Integer.numberOfTrailingZeros(1 << 6), + Integer.numberOfTrailingZeros(1 << 7), + Integer.numberOfTrailingZeros(1 << 8), + Integer.numberOfTrailingZeros(1 << 9), + Integer.numberOfTrailingZeros(1 << 10), + Integer.numberOfTrailingZeros(1 << 11), + Integer.numberOfTrailingZeros(1 << 12), + Integer.numberOfTrailingZeros(1 << 13), + Integer.numberOfTrailingZeros(1 << 14), + Integer.numberOfTrailingZeros(1 << 15), + Integer.numberOfTrailingZeros(1 << 16), + Integer.numberOfTrailingZeros(1 << 17), + Integer.numberOfTrailingZeros(1 << 18), + Integer.numberOfTrailingZeros(1 << 19), + Integer.numberOfTrailingZeros(1 << 20), + Integer.numberOfTrailingZeros(1 << 21), + Integer.numberOfTrailingZeros(1 << 22), + Integer.numberOfTrailingZeros(1 << 23), + Integer.numberOfTrailingZeros(1 << 24), + Integer.numberOfTrailingZeros(1 << 25), + Integer.numberOfTrailingZeros(1 << 26), + Integer.numberOfTrailingZeros(1 << 27), + Integer.numberOfTrailingZeros(1 << 28), + Integer.numberOfTrailingZeros(1 << 29), + Integer.numberOfTrailingZeros(1 << 30), + Integer.numberOfTrailingZeros(1 << 31), + }; + } + + // Test the range of CTZ with random integer input. + // The result of CTZ should be in range [0, 32], so CTZs in this test are + // expected to be optimized away, and the test should always return false. + @Test + @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_I) + public boolean ctzCompareInt(int randInt) { + return Integer.numberOfTrailingZeros(randInt) < 0 + || Integer.numberOfTrailingZeros(randInt) > 32; + } + + // Test the combination of CTZ and division by 8. + // The result of CTZ should be positive, so the division by 8 should be + // optimized to a simple right shift without rounding. + @Test + @IR(counts = {IRNode.COUNT_TRAILING_ZEROS_I, "1", + IRNode.RSHIFT_I, "1", + IRNode.URSHIFT_I, "0", + IRNode.ADD_I, "0"}) + public int ctzDiv8Int(int randInt) { + return Integer.numberOfTrailingZeros(randInt) / 8; + } + + // Test the output range of CTZ with random input range. + @Test + public int ctzRandLimitInt(int randInt) { + randInt = RANGE_INT.clamp(randInt); + int result = Integer.numberOfTrailingZeros(randInt); + return getResultChecksum32(result); + } + + @DontCompile + public int ctzRandLimitInterpretedInt(int randInt) { + randInt = RANGE_INT.clamp(randInt); + int result = Integer.numberOfTrailingZeros(randInt); + return getResultChecksum32(result); + } + + // Test CTZ with constant long inputs. + // All CTZs in this test are expected to be optimized away. + @Test + @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_L) + public int[] ctzConstLongs() { + return new int[] { + Long.numberOfTrailingZeros(0), + Long.numberOfTrailingZeros(1l << 0), + Long.numberOfTrailingZeros(1l << 1), + Long.numberOfTrailingZeros(1l << 2), + Long.numberOfTrailingZeros(1l << 3), + Long.numberOfTrailingZeros(1l << 4), + Long.numberOfTrailingZeros(1l << 5), + Long.numberOfTrailingZeros(1l << 6), + Long.numberOfTrailingZeros(1l << 7), + Long.numberOfTrailingZeros(1l << 8), + Long.numberOfTrailingZeros(1l << 9), + Long.numberOfTrailingZeros(1l << 10), + Long.numberOfTrailingZeros(1l << 11), + Long.numberOfTrailingZeros(1l << 12), + Long.numberOfTrailingZeros(1l << 13), + Long.numberOfTrailingZeros(1l << 14), + Long.numberOfTrailingZeros(1l << 15), + Long.numberOfTrailingZeros(1l << 16), + Long.numberOfTrailingZeros(1l << 17), + Long.numberOfTrailingZeros(1l << 18), + Long.numberOfTrailingZeros(1l << 19), + Long.numberOfTrailingZeros(1l << 20), + Long.numberOfTrailingZeros(1l << 21), + Long.numberOfTrailingZeros(1l << 22), + Long.numberOfTrailingZeros(1l << 23), + Long.numberOfTrailingZeros(1l << 24), + Long.numberOfTrailingZeros(1l << 25), + Long.numberOfTrailingZeros(1l << 26), + Long.numberOfTrailingZeros(1l << 27), + Long.numberOfTrailingZeros(1l << 28), + Long.numberOfTrailingZeros(1l << 29), + Long.numberOfTrailingZeros(1l << 30), + Long.numberOfTrailingZeros(1l << 31), + Long.numberOfTrailingZeros(1l << 32), + Long.numberOfTrailingZeros(1l << 33), + Long.numberOfTrailingZeros(1l << 34), + Long.numberOfTrailingZeros(1l << 35), + Long.numberOfTrailingZeros(1l << 36), + Long.numberOfTrailingZeros(1l << 37), + Long.numberOfTrailingZeros(1l << 38), + Long.numberOfTrailingZeros(1l << 39), + Long.numberOfTrailingZeros(1l << 40), + Long.numberOfTrailingZeros(1l << 41), + Long.numberOfTrailingZeros(1l << 42), + Long.numberOfTrailingZeros(1l << 43), + Long.numberOfTrailingZeros(1l << 44), + Long.numberOfTrailingZeros(1l << 45), + Long.numberOfTrailingZeros(1l << 46), + Long.numberOfTrailingZeros(1l << 47), + Long.numberOfTrailingZeros(1l << 48), + Long.numberOfTrailingZeros(1l << 49), + Long.numberOfTrailingZeros(1l << 50), + Long.numberOfTrailingZeros(1l << 51), + Long.numberOfTrailingZeros(1l << 52), + Long.numberOfTrailingZeros(1l << 53), + Long.numberOfTrailingZeros(1l << 54), + Long.numberOfTrailingZeros(1l << 55), + Long.numberOfTrailingZeros(1l << 56), + Long.numberOfTrailingZeros(1l << 57), + Long.numberOfTrailingZeros(1l << 58), + Long.numberOfTrailingZeros(1l << 59), + Long.numberOfTrailingZeros(1l << 60), + Long.numberOfTrailingZeros(1l << 61), + Long.numberOfTrailingZeros(1l << 62), + Long.numberOfTrailingZeros(1l << 63), + }; + } + + // Test the range of CTZ with random long input. + // The result of CTZ should be in range [0, 64], so CTZs in this test are + // expected to be optimized away, and the test should always return false. + @Test + @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_L) + public boolean ctzCompareLong(long randLong) { + return Long.numberOfTrailingZeros(randLong) < 0 + || Long.numberOfTrailingZeros(randLong) > 64; + } + + // Test the combination of CTZ and division by 8. + // The result of CTZ should be positive, so the division by 8 should be + // optimized to a simple right shift without rounding. + @Test + @IR(counts = {IRNode.COUNT_TRAILING_ZEROS_L, "1", + IRNode.RSHIFT_I, "1", + IRNode.URSHIFT_I, "0", + IRNode.ADD_I, "0"}) + public int ctzDiv8Long(long randLong) { + return Long.numberOfTrailingZeros(randLong) / 8; + } + + // Test the output range of CTZ with random input range. + @Test + public int ctzRandLimitLong(long randLong) { + randLong = RANGE_LONG.clamp(randLong); + int result = Long.numberOfLeadingZeros(randLong); + return getResultChecksum64(result); + } + + @DontCompile + public int ctzRandLimitInterpretedLong(long randLong) { + randLong = RANGE_LONG.clamp(randLong); + int result = Long.numberOfLeadingZeros(randLong); + return getResultChecksum64(result); + } + + record IntRange(int lo, int hi) { + IntRange { + if (lo > hi) { + throw new IllegalArgumentException("lo > hi"); + } + } + + @ForceInline + int clamp(int v) { + return v < lo ? lo : v > hi ? hi : v; + } + + static IntRange generate(Generator g) { + int a = g.next(), b = g.next(); + return a < b ? new IntRange(a, b) : new IntRange(b, a); + } + } + + record LongRange(long lo, long hi) { + LongRange { + if (lo > hi) { + throw new IllegalArgumentException("lo > hi"); + } + } + + @ForceInline + long clamp(long v) { + return v < lo ? lo : v > hi ? hi : v; + } + + static LongRange generate(Generator g) { + long a = g.next(), b = g.next(); + return a < b ? new LongRange(a, b) : new LongRange(b, a); + } + } + + @ForceInline + int getResultChecksum32(int result) { + int sum = 0; + if (result < LIMITS_32_0) sum += 1; + if (result < LIMITS_32_1) sum += 2; + if (result < LIMITS_32_2) sum += 4; + if (result < LIMITS_32_3) sum += 8; + if (result > LIMITS_32_4) sum += 16; + if (result > LIMITS_32_5) sum += 32; + if (result > LIMITS_32_6) sum += 64; + if (result > LIMITS_32_7) sum += 128; + return sum; + } + + @ForceInline + int getResultChecksum64(int result) { + int sum = 0; + if (result < LIMITS_64_0) sum += 1; + if (result < LIMITS_64_1) sum += 2; + if (result < LIMITS_64_2) sum += 4; + if (result < LIMITS_64_3) sum += 8; + if (result > LIMITS_64_4) sum += 16; + if (result > LIMITS_64_5) sum += 32; + if (result > LIMITS_64_6) sum += 64; + if (result > LIMITS_64_7) sum += 128; + return sum; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 85595b9b632..1648135434a 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -1661,6 +1661,16 @@ public class IRNode { vectorNode(POPCOUNT_VL, "PopCountVL", TYPE_LONG); } + public static final String COUNT_TRAILING_ZEROS_I = PREFIX + "COUNT_TRAILING_ZEROS_I" + POSTFIX; + static { + beforeMatchingNameRegex(COUNT_TRAILING_ZEROS_I, "CountTrailingZerosI"); + } + + public static final String COUNT_TRAILING_ZEROS_L = PREFIX + "COUNT_TRAILING_ZEROS_L" + POSTFIX; + static { + beforeMatchingNameRegex(COUNT_TRAILING_ZEROS_L, "CountTrailingZerosL"); + } + public static final String COUNT_TRAILING_ZEROS_VL = VECTOR_PREFIX + "COUNT_TRAILING_ZEROS_VL" + POSTFIX; static { vectorNode(COUNT_TRAILING_ZEROS_VL, "CountTrailingZerosV", TYPE_LONG); @@ -1671,6 +1681,16 @@ public class IRNode { vectorNode(COUNT_TRAILING_ZEROS_VI, "CountTrailingZerosV", TYPE_INT); } + public static final String COUNT_LEADING_ZEROS_I = PREFIX + "COUNT_LEADING_ZEROS_I" + POSTFIX; + static { + beforeMatchingNameRegex(COUNT_LEADING_ZEROS_I, "CountLeadingZerosI"); + } + + public static final String COUNT_LEADING_ZEROS_L = PREFIX + "COUNT_LEADING_ZEROS_L" + POSTFIX; + static { + beforeMatchingNameRegex(COUNT_LEADING_ZEROS_L, "CountLeadingZerosL"); + } + public static final String COUNT_LEADING_ZEROS_VL = VECTOR_PREFIX + "COUNT_LEADING_ZEROS_VL" + POSTFIX; static { vectorNode(COUNT_LEADING_ZEROS_VL, "CountLeadingZerosV", TYPE_LONG); diff --git a/test/micro/org/openjdk/bench/vm/compiler/CountLeadingZeros.java b/test/micro/org/openjdk/bench/vm/compiler/CountLeadingZeros.java new file mode 100644 index 00000000000..dc06e3ae764 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/CountLeadingZeros.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.ThreadLocalRandom; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 5, time = 1) +@State(Scope.Thread) +public class CountLeadingZeros { + private long[] longArray = new long[1000]; + + @Setup + public void setup() { + for (int i = 0; i < longArray.length; i++) { + longArray[i] = ThreadLocalRandom.current().nextLong(); + } + } + + @Benchmark + public int benchNumberOfNibbles() { + int sum = 0; + for (long l : longArray) { + sum += numberOfNibbles((int) l); + } + return sum; + } + + public static int numberOfNibbles(int i) { + int mag = Integer.SIZE - Integer.numberOfLeadingZeros(i); + return Math.max((mag + 3) / 4, 1); + } + + @Benchmark + public int benchClzLongConstrained() { + int sum = 0; + for (long l : longArray) { + sum += clzLongConstrained(l); + } + return sum; + } + + public static int clzLongConstrained(long param) { + long constrainedParam = Math.min(175, Math.max(param, 160)); + return Long.numberOfLeadingZeros(constrainedParam); + } +} From 6700baa5052046f53eb1b04ed3205bbd8e9e9070 Mon Sep 17 00:00:00 2001 From: Hamlin Li Date: Mon, 8 Dec 2025 13:38:22 +0000 Subject: [PATCH 05/10] 8357551: RISC-V: support CMoveF/D vectorization Reviewed-by: fyang, luhenry --- .../cpu/riscv/c2_MacroAssembler_riscv.cpp | 77 ++ .../cpu/riscv/c2_MacroAssembler_riscv.hpp | 7 + .../cpu/riscv/macroAssembler_riscv.cpp | 256 +++- .../cpu/riscv/macroAssembler_riscv.hpp | 18 + src/hotspot/cpu/riscv/riscv.ad | 282 +++- ...onalMove.java => TestConditionalMove.java} | 731 ++++++++++- .../c2/irTests/TestFPComparison2.java | 1140 ++++++++++++++++- .../TestScalarConditionalMoveCmpObj.java | 357 ++++++ .../compiler/lib/ir_framework/IRNode.java | 30 + .../bench/java/lang/ClassComparison.java | 45 +- .../openjdk/bench/java/lang/FPComparison.java | 176 ++- .../bench/java/lang/IntegerComparison.java | 292 ++++- .../bench/java/lang/LongComparison.java | 291 ++++- .../bench/java/lang/PointerComparison.java | 45 +- 14 files changed, 3604 insertions(+), 143 deletions(-) rename test/hotspot/jtreg/compiler/c2/irTests/{TestVectorConditionalMove.java => TestConditionalMove.java} (76%) create mode 100644 test/hotspot/jtreg/compiler/c2/irTests/TestScalarConditionalMoveCmpObj.java diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp index abbd7eedbba..dcf20752a21 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.cpp @@ -2067,6 +2067,83 @@ void C2_MacroAssembler::enc_cmove_cmp_fp(int cmpFlag, FloatRegister op1, FloatRe } } +void C2_MacroAssembler::enc_cmove_fp_cmp(int cmpFlag, Register op1, Register op2, + FloatRegister dst, FloatRegister src, bool is_single) { + bool is_unsigned = (cmpFlag & unsigned_branch_mask) == unsigned_branch_mask; + int op_select = cmpFlag & (~unsigned_branch_mask); + + switch (op_select) { + case BoolTest::eq: + cmov_fp_eq(op1, op2, dst, src, is_single); + break; + case BoolTest::ne: + cmov_fp_ne(op1, op2, dst, src, is_single); + break; + case BoolTest::le: + if (is_unsigned) { + cmov_fp_leu(op1, op2, dst, src, is_single); + } else { + cmov_fp_le(op1, op2, dst, src, is_single); + } + break; + case BoolTest::ge: + if (is_unsigned) { + cmov_fp_geu(op1, op2, dst, src, is_single); + } else { + cmov_fp_ge(op1, op2, dst, src, is_single); + } + break; + case BoolTest::lt: + if (is_unsigned) { + cmov_fp_ltu(op1, op2, dst, src, is_single); + } else { + cmov_fp_lt(op1, op2, dst, src, is_single); + } + break; + case BoolTest::gt: + if (is_unsigned) { + cmov_fp_gtu(op1, op2, dst, src, is_single); + } else { + cmov_fp_gt(op1, op2, dst, src, is_single); + } + break; + default: + assert(false, "unsupported compare condition"); + ShouldNotReachHere(); + } +} + +void C2_MacroAssembler::enc_cmove_fp_cmp_fp(int cmpFlag, + FloatRegister op1, FloatRegister op2, + FloatRegister dst, FloatRegister src, + bool cmp_single, bool cmov_single) { + int op_select = cmpFlag & (~unsigned_branch_mask); + + switch (op_select) { + case BoolTest::eq: + cmov_fp_cmp_fp_eq(op1, op2, dst, src, cmp_single, cmov_single); + break; + case BoolTest::ne: + cmov_fp_cmp_fp_ne(op1, op2, dst, src, cmp_single, cmov_single); + break; + case BoolTest::le: + cmov_fp_cmp_fp_le(op1, op2, dst, src, cmp_single, cmov_single); + break; + case BoolTest::ge: + cmov_fp_cmp_fp_ge(op1, op2, dst, src, cmp_single, cmov_single); + break; + case BoolTest::lt: + cmov_fp_cmp_fp_lt(op1, op2, dst, src, cmp_single, cmov_single); + break; + case BoolTest::gt: + cmov_fp_cmp_fp_gt(op1, op2, dst, src, cmp_single, cmov_single); + break; + default: + assert(false, "unsupported compare condition"); + ShouldNotReachHere(); + } +} + // Set dst to NaN if any NaN input. void C2_MacroAssembler::minmax_fp(FloatRegister dst, FloatRegister src1, FloatRegister src2, FLOAT_TYPE ft, bool is_min) { diff --git a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp index f08e5e27c87..fa87ceba295 100644 --- a/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/c2_MacroAssembler_riscv.hpp @@ -132,6 +132,13 @@ FloatRegister op1, FloatRegister op2, Register dst, Register src, bool is_single); + void enc_cmove_fp_cmp(int cmpFlag, Register op1, Register op2, + FloatRegister dst, FloatRegister src, bool is_single); + + void enc_cmove_fp_cmp_fp(int cmpFlag, FloatRegister op1, FloatRegister op2, + FloatRegister dst, FloatRegister src, + bool cmp_single, bool cmov_single); + void spill(Register r, bool is64, int offset) { is64 ? sd(r, Address(sp, offset)) : sw(r, Address(sp, offset)); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index 7a8496ae42b..a14a051fd3b 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -1233,7 +1233,119 @@ void MacroAssembler::cmov_gtu(Register cmp1, Register cmp2, Register dst, Regist bind(no_set); } -// ----------- cmove, compare float ----------- +// ----------- cmove float/double ----------- + +void MacroAssembler::cmov_fp_eq(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single) { + Label no_set; + bne(cmp1, cmp2, no_set); + if (is_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_ne(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single) { + Label no_set; + beq(cmp1, cmp2, no_set); + if (is_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_le(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single) { + Label no_set; + bgt(cmp1, cmp2, no_set); + if (is_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_leu(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single) { + Label no_set; + bgtu(cmp1, cmp2, no_set); + if (is_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_ge(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single) { + Label no_set; + blt(cmp1, cmp2, no_set); + if (is_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_geu(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single) { + Label no_set; + bltu(cmp1, cmp2, no_set); + if (is_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_lt(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single) { + Label no_set; + bge(cmp1, cmp2, no_set); + if (is_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_ltu(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single) { + Label no_set; + bgeu(cmp1, cmp2, no_set); + if (is_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_gt(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single) { + Label no_set; + ble(cmp1, cmp2, no_set); + if (is_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_gtu(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single) { + Label no_set; + bleu(cmp1, cmp2, no_set); + if (is_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +// ----------- cmove, compare float/double ----------- // // For CmpF/D + CMoveI/L, ordered ones are quite straight and simple, // so, just list behaviour of unordered ones as follow. @@ -1391,6 +1503,148 @@ void MacroAssembler::cmov_cmp_fp_gt(FloatRegister cmp1, FloatRegister cmp2, Regi bind(no_set); } +// ----------- cmove float/double, compare float/double ----------- + +// Move src to dst only if cmp1 == cmp2, +// otherwise leave dst unchanged, including the case where one of them is NaN. +// Clarification: +// java code : cmp1 != cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 eq cmp2), dst, src +void MacroAssembler::cmov_fp_cmp_fp_eq(FloatRegister cmp1, FloatRegister cmp2, + FloatRegister dst, FloatRegister src, + bool cmp_single, bool cmov_single) { + Label no_set; + if (cmp_single) { + // jump if cmp1 != cmp2, including the case of NaN + // not jump (i.e. move src to dst) if cmp1 == cmp2 + float_bne(cmp1, cmp2, no_set); + } else { + double_bne(cmp1, cmp2, no_set); + } + if (cmov_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +// Keep dst unchanged only if cmp1 == cmp2, +// otherwise move src to dst, including the case where one of them is NaN. +// Clarification: +// java code : cmp1 == cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 ne cmp2), dst, src +void MacroAssembler::cmov_fp_cmp_fp_ne(FloatRegister cmp1, FloatRegister cmp2, + FloatRegister dst, FloatRegister src, + bool cmp_single, bool cmov_single) { + Label no_set; + if (cmp_single) { + // jump if cmp1 == cmp2 + // not jump (i.e. move src to dst) if cmp1 != cmp2, including the case of NaN + float_beq(cmp1, cmp2, no_set); + } else { + double_beq(cmp1, cmp2, no_set); + } + if (cmov_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +// When cmp1 <= cmp2 or any of them is NaN then dst = src, otherwise, dst = dst +// Clarification +// scenario 1: +// java code : cmp2 < cmp1 ? dst : src +// transformed to : CMove dst, (cmp1 le cmp2), dst, src +// scenario 2: +// java code : cmp1 > cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 le cmp2), dst, src +void MacroAssembler::cmov_fp_cmp_fp_le(FloatRegister cmp1, FloatRegister cmp2, + FloatRegister dst, FloatRegister src, + bool cmp_single, bool cmov_single) { + Label no_set; + if (cmp_single) { + // jump if cmp1 > cmp2 + // not jump (i.e. move src to dst) if cmp1 <= cmp2 or either is NaN + float_bgt(cmp1, cmp2, no_set); + } else { + double_bgt(cmp1, cmp2, no_set); + } + if (cmov_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_cmp_fp_ge(FloatRegister cmp1, FloatRegister cmp2, + FloatRegister dst, FloatRegister src, + bool cmp_single, bool cmov_single) { + Label no_set; + if (cmp_single) { + // jump if cmp1 < cmp2 or either is NaN + // not jump (i.e. move src to dst) if cmp1 >= cmp2 + float_blt(cmp1, cmp2, no_set, false, true); + } else { + double_blt(cmp1, cmp2, no_set, false, true); + } + if (cmov_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +// When cmp1 < cmp2 or any of them is NaN then dst = src, otherwise, dst = dst +// Clarification +// scenario 1: +// java code : cmp2 <= cmp1 ? dst : src +// transformed to : CMove dst, (cmp1 lt cmp2), dst, src +// scenario 2: +// java code : cmp1 >= cmp2 ? dst : src +// transformed to : CMove dst, (cmp1 lt cmp2), dst, src +void MacroAssembler::cmov_fp_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, + FloatRegister dst, FloatRegister src, + bool cmp_single, bool cmov_single) { + Label no_set; + if (cmp_single) { + // jump if cmp1 >= cmp2 + // not jump (i.e. move src to dst) if cmp1 < cmp2 or either is NaN + float_bge(cmp1, cmp2, no_set); + } else { + double_bge(cmp1, cmp2, no_set); + } + if (cmov_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + +void MacroAssembler::cmov_fp_cmp_fp_gt(FloatRegister cmp1, FloatRegister cmp2, + FloatRegister dst, FloatRegister src, + bool cmp_single, bool cmov_single) { + Label no_set; + if (cmp_single) { + // jump if cmp1 <= cmp2 or either is NaN + // not jump (i.e. move src to dst) if cmp1 > cmp2 + float_ble(cmp1, cmp2, no_set, false, true); + } else { + double_ble(cmp1, cmp2, no_set, false, true); + } + if (cmov_single) { + fmv_s(dst, src); + } else { + fmv_d(dst, src); + } + bind(no_set); +} + // Float compare branch instructions #define INSN(NAME, FLOATCMP, BRANCH) \ diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp index 1908b9a9605..3b021388fa5 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.hpp @@ -665,6 +665,24 @@ class MacroAssembler: public Assembler { void cmov_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single); void cmov_cmp_fp_gt(FloatRegister cmp1, FloatRegister cmp2, Register dst, Register src, bool is_single); + void cmov_fp_eq(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single); + void cmov_fp_ne(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single); + void cmov_fp_le(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single); + void cmov_fp_leu(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single); + void cmov_fp_ge(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single); + void cmov_fp_geu(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single); + void cmov_fp_lt(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single); + void cmov_fp_ltu(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single); + void cmov_fp_gt(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single); + void cmov_fp_gtu(Register cmp1, Register cmp2, FloatRegister dst, FloatRegister src, bool is_single); + + void cmov_fp_cmp_fp_eq(FloatRegister cmp1, FloatRegister cmp2, FloatRegister dst, FloatRegister src, bool cmp_single, bool cmov_single); + void cmov_fp_cmp_fp_ne(FloatRegister cmp1, FloatRegister cmp2, FloatRegister dst, FloatRegister src, bool cmp_single, bool cmov_single); + void cmov_fp_cmp_fp_le(FloatRegister cmp1, FloatRegister cmp2, FloatRegister dst, FloatRegister src, bool cmp_single, bool cmov_single); + void cmov_fp_cmp_fp_ge(FloatRegister cmp1, FloatRegister cmp2, FloatRegister dst, FloatRegister src, bool cmp_single, bool cmov_single); + void cmov_fp_cmp_fp_lt(FloatRegister cmp1, FloatRegister cmp2, FloatRegister dst, FloatRegister src, bool cmp_single, bool cmov_single); + void cmov_fp_cmp_fp_gt(FloatRegister cmp1, FloatRegister cmp2, FloatRegister dst, FloatRegister src, bool cmp_single, bool cmov_single); + public: // We try to follow risc-v asm menomics. // But as we don't layout a reachable GOT, diff --git a/src/hotspot/cpu/riscv/riscv.ad b/src/hotspot/cpu/riscv/riscv.ad index 3f5dd4ad0ee..96984ba9a42 100644 --- a/src/hotspot/cpu/riscv/riscv.ad +++ b/src/hotspot/cpu/riscv/riscv.ad @@ -1924,8 +1924,6 @@ bool Matcher::match_rule_supported(int opcode) { case Op_SubHF: return UseZfh; - case Op_CMoveF: - case Op_CMoveD: case Op_CMoveP: case Op_CMoveN: return false; @@ -10466,6 +10464,286 @@ instruct cmovL_cmpP(iRegLNoSp dst, iRegL src, iRegP op1, iRegP op2, cmpOpU cop) ins_pipe(pipe_class_compare); %} +// --------- CMoveF --------- + +instruct cmovF_cmpI(fRegF dst, fRegF src, iRegI op1, iRegI op2, cmpOp cop) %{ + match(Set dst (CMoveF (Binary cop (CmpI op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpI\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovF_cmpU(fRegF dst, fRegF src, iRegI op1, iRegI op2, cmpOpU cop) %{ + match(Set dst (CMoveF (Binary cop (CmpU op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpU\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovF_cmpL(fRegF dst, fRegF src, iRegL op1, iRegL op2, cmpOp cop) %{ + match(Set dst (CMoveF (Binary cop (CmpL op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpL\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovF_cmpUL(fRegF dst, fRegF src, iRegL op1, iRegL op2, cmpOpU cop) %{ + match(Set dst (CMoveF (Binary cop (CmpUL op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpUL\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovF_cmpF(fRegF dst, fRegF src, fRegF op1, fRegF op2, cmpOp cop) %{ + match(Set dst (CMoveF (Binary cop (CmpF op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpF\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp_fp($cop$$cmpcode, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), + true /* cmp_single */, true /* cmov_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovF_cmpD(fRegF dst, fRegF src, fRegD op1, fRegD op2, cmpOp cop) %{ + match(Set dst (CMoveF (Binary cop (CmpD op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpD\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), + false /* cmp_single */, true /* cmov_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovF_cmpN(fRegF dst, fRegF src, iRegN op1, iRegN op2, cmpOp cop) %{ + match(Set dst (CMoveF (Binary cop (CmpN op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpN\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovF_cmpP(fRegF dst, fRegF src, iRegP op1, iRegP op2, cmpOp cop) %{ + match(Set dst (CMoveF (Binary cop (CmpP op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveF $dst, ($op1 $cop $op2), $dst, $src\t#@cmovF_cmpP\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), true /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +// --------- CMoveD --------- + +instruct cmovD_cmpI(fRegD dst, fRegD src, iRegI op1, iRegI op2, cmpOp cop) %{ + match(Set dst (CMoveD (Binary cop (CmpI op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpI\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovD_cmpU(fRegD dst, fRegD src, iRegI op1, iRegI op2, cmpOpU cop) %{ + match(Set dst (CMoveD (Binary cop (CmpU op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpU\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovD_cmpL(fRegD dst, fRegD src, iRegL op1, iRegL op2, cmpOp cop) %{ + match(Set dst (CMoveD (Binary cop (CmpL op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpL\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovD_cmpUL(fRegD dst, fRegD src, iRegL op1, iRegL op2, cmpOpU cop) %{ + match(Set dst (CMoveD (Binary cop (CmpUL op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpUL\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovD_cmpF(fRegD dst, fRegD src, fRegF op1, fRegF op2, cmpOp cop) %{ + match(Set dst (CMoveD (Binary cop (CmpF op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpF\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp_fp($cop$$cmpcode, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), + true /* cmp_single */, false /* cmov_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovD_cmpD(fRegD dst, fRegD src, fRegD op1, fRegD op2, cmpOp cop) %{ + match(Set dst (CMoveD (Binary cop (CmpD op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpD\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp_fp($cop$$cmpcode | C2_MacroAssembler::double_branch_mask, + as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), + false /* cmp_single */, false /* cmov_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovD_cmpN(fRegD dst, fRegD src, iRegN op1, iRegN op2, cmpOp cop) %{ + match(Set dst (CMoveD (Binary cop (CmpN op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpN\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + +instruct cmovD_cmpP(fRegD dst, fRegD src, iRegP op1, iRegP op2, cmpOp cop) %{ + match(Set dst (CMoveD (Binary cop (CmpP op1 op2)) (Binary dst src))); + ins_cost(ALU_COST + BRANCH_COST); + + format %{ + "CMoveD $dst, ($op1 $cop $op2), $dst, $src\t#@cmovD_cmpP\n\t" + %} + + ins_encode %{ + __ enc_cmove_fp_cmp($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, + as_Register($op1$$reg), as_Register($op2$$reg), + as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg), false /* is_single */); + %} + + ins_pipe(pipe_class_compare); +%} + // ============================================================================ // Procedure Call/Return Instructions diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorConditionalMove.java b/test/hotspot/jtreg/compiler/c2/irTests/TestConditionalMove.java similarity index 76% rename from test/hotspot/jtreg/compiler/c2/irTests/TestVectorConditionalMove.java rename to test/hotspot/jtreg/compiler/c2/irTests/TestConditionalMove.java index 4759add94e9..c531f73b71d 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestVectorConditionalMove.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestConditionalMove.java @@ -1,6 +1,7 @@ /* - * Copyright (c) 2022, Arm Limited. All rights reserved. - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Arm Limited. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Rivos Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,14 +36,15 @@ import jdk.test.lib.Utils; * @key randomness * @summary Auto-vectorization enhancement to support vector conditional move. * @library /test/lib / - * @run driver compiler.c2.irTests.TestVectorConditionalMove + * @run driver compiler.c2.irTests.TestConditionalMove */ -public class TestVectorConditionalMove { +public class TestConditionalMove { final private static int SIZE = 1024; private static final Random RANDOM = Utils.getRandomInstance(); public static void main(String[] args) { + // Vectorizaion: +UseCMoveUnconditionally, +UseVectorCmov // Cross-product: +-AlignVector and +-UseCompactObjectHeaders TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:+UseVectorCmov", "-XX:-UseCompactObjectHeaders", "-XX:-AlignVector"); @@ -52,6 +54,12 @@ public class TestVectorConditionalMove { "-XX:+UseCompactObjectHeaders", "-XX:-AlignVector"); TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:+UseVectorCmov", "-XX:+UseCompactObjectHeaders", "-XX:+AlignVector"); + + // Scalar: +UseCMoveUnconditionally, -UseVectorCmov + TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov", + "-XX:+UnlockExperimentalVMOptions", "-XX:-UseCompactObjectHeaders"); + TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov", + "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCompactObjectHeaders"); } // Compare 2 values, and pick one of them @@ -564,6 +572,7 @@ public class TestVectorConditionalMove { return (a > b) ? c : d; } + // Double comparison private int cmoveDGTforI(double a, double b, int c, int d) { return (a > b) ? c : d; } @@ -586,7 +595,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveVFGT(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] > b[i]) ? a[i] : b[i]; @@ -598,7 +613,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveVFGTSwap(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (b[i] > a[i]) ? a[i] : b[i]; @@ -610,7 +631,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveVFLT(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] < b[i]) ? a[i] : b[i]; @@ -622,7 +649,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveVFLTSwap(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (b[i] < a[i]) ? a[i] : b[i]; @@ -634,7 +667,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveVFEQ(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] == b[i]) ? a[i] : b[i]; @@ -646,7 +685,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveVDLE(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] <= b[i]) ? a[i] : b[i]; @@ -658,7 +703,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveVDLESwap(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (b[i] <= a[i]) ? a[i] : b[i]; @@ -670,7 +721,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveVDGE(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] >= b[i]) ? a[i] : b[i]; @@ -682,7 +739,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveVDGESwap(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (b[i] >= a[i]) ? a[i] : b[i]; @@ -694,7 +757,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveVDNE(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] != b[i]) ? a[i] : b[i]; @@ -707,7 +776,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFGTforFConst(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] > b[i]) ? 0.1f : -0.1f; @@ -719,7 +794,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFGEforFConst(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] >= b[i]) ? 0.1f : -0.1f; @@ -731,7 +812,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFLTforFConst(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] < b[i]) ? 0.1f : -0.1f; @@ -743,7 +830,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFLEforFConst(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] <= b[i]) ? 0.1f : -0.1f; @@ -755,7 +848,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFEQforFConst(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] == b[i]) ? 0.1f : -0.1f; @@ -767,7 +866,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFNEQforFConst(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] != b[i]) ? 0.1f : -0.1f; @@ -779,8 +884,19 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfOr = {"UseCompactObjectHeaders", "false", "AlignVector", "false"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfAnd = {"UseCompactObjectHeaders", "false", "UseVectorCmov", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(counts = {IRNode.LOAD_VECTOR_F, ">0", + IRNode.VECTOR_MASK_CMP_F, ">0", + IRNode.VECTOR_BLEND_F, ">0", + IRNode.STORE_VECTOR, ">0"}, + applyIfAnd = {"AlignVector", "false", "UseVectorCmov", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFLTforFConstH2(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i+=2) { c[i+0] = (a[i+0] < b[i+0]) ? 0.1f : -0.1f; @@ -797,8 +913,19 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfOr = {"UseCompactObjectHeaders", "false", "AlignVector", "false"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfAnd = {"UseCompactObjectHeaders", "false", "UseVectorCmov", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(counts = {IRNode.LOAD_VECTOR_F, ">0", + IRNode.VECTOR_MASK_CMP_F, ">0", + IRNode.VECTOR_BLEND_F, ">0", + IRNode.STORE_VECTOR, ">0"}, + applyIfAnd = {"AlignVector", "false", "UseVectorCmov", "true"}, + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFLEforFConstH2(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i+=2) { c[i+0] = (a[i+0] <= b[i+0]) ? 0.1f : -0.1f; @@ -815,7 +942,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, "=0", IRNode.VECTOR_BLEND_F, "=0", IRNode.STORE_VECTOR, "=0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFYYforFConstH2(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i+=2) { c[i+0] = (a[i+0] <= b[i+0]) ? 0.1f : -0.1f; @@ -828,7 +961,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, "=0", IRNode.VECTOR_BLEND_F, "=0", IRNode.STORE_VECTOR, "=0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFXXforFConstH2(float[] a, float[] b, float[] c) { for (int i = 0; i < a.length; i+=2) { c[i+0] = (a[i+0] < b[i+0]) ? 0.1f : -0.1f; @@ -841,7 +980,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDGTforDConst(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] > b[i]) ? 0.1 : -0.1; @@ -853,7 +998,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDGEforDConst(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] >= b[i]) ? 0.1 : -0.1; @@ -865,7 +1016,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDLTforDConst(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] < b[i]) ? 0.1 : -0.1; @@ -877,7 +1034,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDLEforDConst(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] <= b[i]) ? 0.1 : -0.1; @@ -889,7 +1052,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDEQforDConst(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] == b[i]) ? 0.1 : -0.1; @@ -901,7 +1070,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDNEQforDConst(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i++) { c[i] = (a[i] != b[i]) ? 0.1 : -0.1; @@ -913,7 +1088,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDLTforDConstH2(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i+=2) { c[i+0] = (a[i+0] < b[i+0]) ? 0.1 : -0.1; @@ -926,7 +1107,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDLEforDConstH2(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i+=2) { c[i+0] = (a[i+0] <= b[i+0]) ? 0.1 : -0.1; @@ -939,7 +1126,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, "=0", IRNode.VECTOR_BLEND_D, "=0", IRNode.STORE_VECTOR, "=0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDYYforDConstH2(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i+=2) { c[i+0] = (a[i+0] <= b[i+0]) ? 0.1 : -0.1; @@ -952,7 +1145,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, "=0", IRNode.VECTOR_BLEND_D, "=0", IRNode.STORE_VECTOR, "=0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDXXforDConstH2(double[] a, double[] b, double[] c) { for (int i = 0; i < a.length; i+=2) { c[i+0] = (a[i+0] < b[i+0]) ? 0.1 : -0.1; @@ -966,10 +1165,15 @@ public class TestVectorConditionalMove { // do not float down into the branches, I compute a value, and store it to r2 (same as r, except that the // compilation does not know that). // So far, vectorization only works for CMoveF/D, with same data-width comparison (F/I for F, D/L for D). + // TODO: enable CMOVE_I/L verification when it's guaranteed to generate CMOVE_I/L, JDK-8371984. + // // Signed comparison: I/L // I fo I @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIEQforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -981,6 +1185,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveINEforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -992,6 +1199,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIGTforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1003,6 +1213,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIGEforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1014,6 +1227,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveILTforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1025,6 +1241,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveILEforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1037,6 +1256,9 @@ public class TestVectorConditionalMove { // I fo L @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIEQforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1048,6 +1270,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveINEforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1059,6 +1284,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIGTforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1070,6 +1298,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIGEforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1081,6 +1312,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveILTforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1092,6 +1326,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_I, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveILEforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1108,7 +1345,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIEQforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1124,7 +1367,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveINEforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1140,7 +1389,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIGTforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1156,7 +1411,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIGEforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1172,7 +1433,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveILTforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1188,7 +1455,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveILEforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1201,6 +1474,9 @@ public class TestVectorConditionalMove { // I fo D @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIEQforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1212,6 +1488,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveINEforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1223,6 +1502,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIGTforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1234,6 +1516,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveIGEforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1245,6 +1530,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveILTforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1256,6 +1544,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_I, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveILEforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1268,6 +1559,9 @@ public class TestVectorConditionalMove { // L fo I @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLEQforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1279,6 +1573,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLNEforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1290,6 +1587,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLGTforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1301,6 +1601,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLGEforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1312,6 +1615,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLLTforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1323,6 +1629,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLLEforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1335,6 +1644,9 @@ public class TestVectorConditionalMove { // L fo L @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLEQforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1346,6 +1658,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLNEforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1357,6 +1672,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLGTforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1368,6 +1686,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLGEforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1379,6 +1700,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLLTforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1390,6 +1714,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_L, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLLEforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1402,6 +1729,9 @@ public class TestVectorConditionalMove { // L fo F @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLEQforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1413,6 +1743,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLNEforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1424,6 +1757,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLGTforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1435,6 +1771,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLGEforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1446,6 +1785,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLLTforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1457,6 +1799,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveLLEforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1473,7 +1818,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveLEQforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -1490,7 +1841,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveLNEforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -1507,7 +1864,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveLGTforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -1524,7 +1887,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveLGEforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -1541,7 +1910,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveLLTforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -1558,7 +1933,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_L, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveLLEforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -1573,6 +1954,9 @@ public class TestVectorConditionalMove { // I fo I @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIEQforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1584,6 +1968,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUINEforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1595,6 +1982,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIGTforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1606,6 +1996,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIGEforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1617,6 +2010,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUILTforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1628,6 +2024,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUILEforI(int[] a, int[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1640,6 +2039,9 @@ public class TestVectorConditionalMove { // I fo L @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIEQforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1651,6 +2053,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUINEforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1662,6 +2067,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIGTforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1673,6 +2081,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIGEforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1684,6 +2095,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUILTforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1695,6 +2109,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_U, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUILEforL(int[] a, int[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1711,7 +2128,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIEQforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1727,7 +2150,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUINEforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1743,7 +2172,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIGTforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1759,7 +2194,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIGEforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1775,7 +2216,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUILTforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1791,7 +2238,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_I, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.VECTOR_BLEND_F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUILEforF(int[] a, int[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -1804,6 +2257,9 @@ public class TestVectorConditionalMove { // I fo D @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIEQforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1815,6 +2271,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUINEforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1826,6 +2285,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIGTforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1837,6 +2299,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUIGEforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1848,6 +2313,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUILTforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1859,6 +2327,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_U, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveUILEforD(int[] a, int[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -1871,6 +2342,9 @@ public class TestVectorConditionalMove { // L fo I @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULEQforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1882,6 +2356,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULNEforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1893,6 +2370,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULGTforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1904,6 +2384,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULGEforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1915,6 +2398,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULLTforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1926,6 +2412,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULLEforI(long[] a, long[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -1938,6 +2427,9 @@ public class TestVectorConditionalMove { // L fo L @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULEQforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1949,6 +2441,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULNEforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1960,6 +2455,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULGTforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1971,6 +2469,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULGEforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1982,6 +2483,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULLTforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -1993,6 +2497,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_UL, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULLEforL(long[] a, long[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -2005,6 +2512,9 @@ public class TestVectorConditionalMove { // L fo F @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULEQforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -2016,6 +2526,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULNEforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -2027,6 +2540,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULGTforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -2038,6 +2554,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULGEforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -2049,6 +2568,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULLTforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -2060,6 +2582,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveULLEforF(long[] a, long[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -2076,7 +2601,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveULEQforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -2093,7 +2624,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveULNEforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -2110,7 +2647,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveULGTforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -2127,7 +2670,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveULGEforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -2144,7 +2693,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveULLTforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -2161,7 +2716,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_L, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.VECTOR_BLEND_D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx2", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_UL, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) // Requires avx2, else L is restricted to 16 byte, and D has 32. That leads to a vector elements mismatch of 2 to 4. private static void testCMoveULLEforD(long[] a, long[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { @@ -2174,6 +2735,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_F, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFGTforI(float[] a, float[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -2185,6 +2749,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_F, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFGTforL(float[] a, float[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -2199,7 +2766,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFGTforF(float[] a, float[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -2211,6 +2784,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFGTforD(float[] a, float[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -2222,6 +2798,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_D, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDGTforI(double[] a, double[] b, int[] c, int[] d, int[] r, int[] r2) { for (int i = 0; i < a.length; i++) { int cc = c[i]; @@ -2233,6 +2812,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_D, ">0"}, + // applyIf = {"UseVectorCmov", "false"}, + // applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDGTforL(double[] a, double[] b, long[] c, long[] d, long[] r, long[] r2) { for (int i = 0; i < a.length; i++) { long cc = c[i]; @@ -2244,6 +2826,9 @@ public class TestVectorConditionalMove { @Test @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDGTforF(double[] a, double[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; @@ -2258,7 +2843,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_D, ">0", IRNode.VECTOR_BLEND_D, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_D, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveDGTforD(double[] a, double[] b, double[] c, double[] d, double[] r, double[] r2) { for (int i = 0; i < a.length; i++) { double cc = c[i]; @@ -2274,7 +2865,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFGTforFCmpCon1(float a, float[] b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < b.length; i++) { float cc = c[i]; @@ -2289,7 +2886,13 @@ public class TestVectorConditionalMove { IRNode.VECTOR_MASK_CMP_F, ">0", IRNode.VECTOR_BLEND_F, ">0", IRNode.STORE_VECTOR, ">0"}, - applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) + applyIfCPUFeatureOr = {"avx", "true", "asimd", "true", "rvv", "true"}, + applyIf = {"UseVectorCmov", "true"}) + @IR(failOn = {IRNode.STORE_VECTOR}, + applyIf = {"UseVectorCmov", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_F, ">0"}, + applyIf = {"UseVectorCmov", "false"}, + applyIfPlatform = {"riscv64", "true"}) private static void testCMoveFGTforFCmpCon2(float[] a, float b, float[] c, float[] d, float[] r, float[] r2) { for (int i = 0; i < a.length; i++) { float cc = c[i]; diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestFPComparison2.java b/test/hotspot/jtreg/compiler/c2/irTests/TestFPComparison2.java index 59c70b6873f..8cf3f728666 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestFPComparison2.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestFPComparison2.java @@ -28,7 +28,7 @@ import java.util.List; /* * @test - * @bug 8358892 + * @bug 8358892 8357551 * @summary The test is to trigger code path of BoolTest::ge/gt in C2_MacroAssembler::enc_cmove_cmp_fp * @requires os.arch == "riscv64" * @requires vm.debug @@ -77,18 +77,33 @@ public class TestFPComparison2 { public static void main(String[] args) { List options = List.of("-XX:-TieredCompilation", "-Xlog:jit+compilation=trace"); // Booltest::ge - TestFramework framework = new TestFramework(Test_ge_1.class); + TestFramework + framework = new TestFramework(Test_ge_1.class); + framework.addFlags(options.toArray(new String[0])).start(); + framework = new TestFramework(Test_ge_cmove_fp_1.class); framework.addFlags(options.toArray(new String[0])).start(); framework = new TestFramework(Test_ge_2.class); framework.addFlags(options.toArray(new String[0])).start(); + framework = new TestFramework(Test_ge_cmove_fp_2.class); + framework.addFlags(options.toArray(new String[0])).start(); // Booltest::gt framework = new TestFramework(Test_gt_1.class); framework.addFlags(options.toArray(new String[0])).start(); + framework = new TestFramework(Test_gt_cmove_fp_1.class); + framework.addFlags(options.toArray(new String[0])).start(); framework = new TestFramework(Test_gt_2.class); framework.addFlags(options.toArray(new String[0])).start(); + framework = new TestFramework(Test_gt_cmove_fp_2.class); + framework.addFlags(options.toArray(new String[0])).start(); + + // BoolTest::ge/gt in C2_MacroAssembler::enc_cmove_fp_cmp_fp + framework = new TestFramework(Test_cmov_fp_cmp_fp_ge_3.class); + framework.addFlags(options.toArray(new String[0])).start(); + framework = new TestFramework(Test_cmov_fp_cmp_fp_ge_4.class); + framework.addFlags(options.toArray(new String[0])).start(); } } @@ -320,6 +335,235 @@ class Test_ge_1 { } } + +class Test_ge_cmove_fp_1 { + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_fixed_1_0(float x, float y) { + // return 1 + // when either x or y is NaN + // when neither is NaN, and x > y + // return 0 + // when neither is NaN, and x <= y + return !(x <= y) ? 1.0f : 0.0f; + } + @DontCompile + public static float golden_float_BoolTest_ge_fixed_1_0(float x, float y) { + return !(x <= y) ? 1.0f : 0.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_ge_fixed_1_0(double x, double y) { + // return 1 + // when either x or y is NaN + // when neither is NaN, and x > y + // return 0 + // when neither is NaN, and x <= y + return !(x <= y) ? 1.0f : 0.0f; + } + @DontCompile + public static float golden_double_BoolTest_ge_fixed_1_0(double x, double y) { + return !(x <= y) ? 1.0f : 0.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_fixed_0_1(float x, float y) { + return !(x <= y) ? 0.0f : 1.0f; + } + @DontCompile + public static float golden_float_BoolTest_ge_fixed_0_1(float x, float y) { + return !(x <= y) ? 0.0f : 1.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_ge_fixed_0_1(double x, double y) { + return !(x <= y) ? 0.0f : 1.0f; + } + @DontCompile + public static float golden_double_BoolTest_ge_fixed_0_1(double x, double y) { + return !(x <= y) ? 0.0f : 1.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_fixed_10_20(float x, float y) { + return !(x <= y) ? 10.0f : 20.0f; + } + @DontCompile + public static float golden_float_BoolTest_ge_fixed_10_20(float x, float y) { + return !(x <= y) ? 10.0f : 20.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_ge_fixed_10_20(double x, double y) { + return !(x <= y) ? 10.0f : 20.0f; + } + @DontCompile + public static float golden_double_BoolTest_ge_fixed_10_20(double x, double y) { + return !(x <= y) ? 10.0f : 20.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_variable_results(float x, float y, float a, float b) { + return !(x <= y) ? a : b; + } + @DontCompile + public static float golden_float_BoolTest_ge_variable_results(float x, float y, float a, float b) { + return !(x <= y) ? a : b; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_ge_variable_results(double x, double y, float a, float b) { + return !(x <= y) ? a : b; + } + @DontCompile + public static float golden_double_BoolTest_ge_variable_results(double x, double y, float a, float b) { + return !(x <= y) ? a : b; + } + + @Run(test = {"test_float_BoolTest_ge_fixed_1_0", "test_double_BoolTest_ge_fixed_1_0", + "test_float_BoolTest_ge_fixed_0_1", "test_double_BoolTest_ge_fixed_0_1", + "test_float_BoolTest_ge_fixed_10_20", "test_double_BoolTest_ge_fixed_10_20", + "test_float_BoolTest_ge_variable_results", "test_double_BoolTest_ge_variable_results"}) + public void runTests() { + int err = 0; + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_ge_fixed_1_0(x, y); + float expected = golden_float_BoolTest_ge_fixed_1_0(x, y); + if (actual != expected) { + System.out.println("Float failed (ge, 1, 0), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_ge_fixed_1_0(x, y); + float expected = golden_double_BoolTest_ge_fixed_1_0(x, y); + if (actual != expected) { + System.out.println("Double failed (ge, 1, 0), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_ge_fixed_0_1(x, y); + float expected = golden_float_BoolTest_ge_fixed_0_1(x, y); + if (actual != expected) { + System.out.println("Float failed (ge, 0, 1), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_ge_fixed_0_1(x, y); + float expected = golden_double_BoolTest_ge_fixed_0_1(x, y); + if (actual != expected) { + System.out.println("Double failed (ge, 0, 1), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_ge_fixed_10_20(x, y); + float expected = golden_float_BoolTest_ge_fixed_10_20(x, y); + if (actual != expected) { + System.out.println("Float failed (ge, 10, 20), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_ge_fixed_10_20(x, y); + float expected = golden_double_BoolTest_ge_fixed_10_20(x, y); + if (actual != expected) { + System.out.println("Double failed (ge, 10, 20), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + for (int m = 0; m < TestFPComparison2.FLOATS.length; m++) { + for (int n = 0; n < TestFPComparison2.FLOATS.length; n++) { + float a = TestFPComparison2.FLOATS[m]; + float b = TestFPComparison2.FLOATS[n]; + float actual = test_float_BoolTest_ge_variable_results(x, y, a, b); + float expected = golden_float_BoolTest_ge_variable_results(x, y, a, b); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (ge), x: " + x + ", y: " + y + ", a: " + a + ", b: " + b + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + for (int m = 0; m < TestFPComparison2.FLOATS.length; m++) { + for (int n = 0; n < TestFPComparison2.FLOATS.length; n++) { + float a = TestFPComparison2.FLOATS[m]; + float b = TestFPComparison2.FLOATS[n]; + float actual = test_double_BoolTest_ge_variable_results(x, y, a, b); + float expected = golden_double_BoolTest_ge_variable_results(x, y, a, b); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Double failed (ge), x: " + x + ", y: " + y + ", a: " + a + ", b: " + b + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + } + } + + if (err != 0) { + throw new RuntimeException("Some tests failed"); + } + } +} + class Test_ge_2 { @Test @IR(counts = {IRNode.CMOVE_I, "1"}) @@ -548,6 +792,234 @@ class Test_ge_2 { } } +class Test_ge_cmove_fp_2 { + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_fixed_1_0(float x, float y) { + // return 1 + // when either x or y is NaN + // when neither is NaN, and x < y + // return 0 + // when neither is NaN, and x >= y + return !(x >= y) ? 1.0f : 0.0f; + } + @DontCompile + public static float golden_float_BoolTest_ge_fixed_1_0(float x, float y) { + return !(x >= y) ? 1.0f : 0.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_ge_fixed_1_0(double x, double y) { + // return 1 + // when either x or y is NaN + // when neither is NaN, and x < y + // return 0 + // when neither is NaN, and x >= y + return !(x >= y) ? 1.0f : 0.0f; + } + @DontCompile + public static float golden_double_BoolTest_ge_fixed_1_0(double x, double y) { + return !(x >= y) ? 1.0f : 0.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_fixed_0_1(float x, float y) { + return !(x >= y) ? 0.0f : 1.0f; + } + @DontCompile + public static float golden_float_BoolTest_ge_fixed_0_1(float x, float y) { + return !(x >= y) ? 0.0f : 1.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_ge_fixed_0_1(double x, double y) { + return !(x >= y) ? 0.0f : 1.0f; + } + @DontCompile + public static float golden_double_BoolTest_ge_fixed_0_1(double x, double y) { + return !(x >= y) ? 0.0f : 1.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_fixed_10_20(float x, float y) { + return !(x >= y) ? 10.0f : 20.0f; + } + @DontCompile + public static float golden_float_BoolTest_ge_fixed_10_20(float x, float y) { + return !(x >= y) ? 10.0f : 20.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_ge_fixed_10_20(double x, double y) { + return !(x >= y) ? 10.0f : 20.0f; + } + @DontCompile + public static float golden_double_BoolTest_ge_fixed_10_20(double x, double y) { + return !(x >= y) ? 10.0f : 20.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_variable_results(float x, float y, float a, float b) { + return !(x >= y) ? a : b; + } + @DontCompile + public static float golden_float_BoolTest_ge_variable_results(float x, float y, float a, float b) { + return !(x >= y) ? a : b; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_ge_variable_results(double x, double y, float a, float b) { + return !(x >= y) ? a : b; + } + @DontCompile + public static float golden_double_BoolTest_ge_variable_results(double x, double y, float a, float b) { + return !(x >= y) ? a : b; + } + + @Run(test = {"test_float_BoolTest_ge_fixed_1_0", "test_double_BoolTest_ge_fixed_1_0", + "test_float_BoolTest_ge_fixed_0_1", "test_double_BoolTest_ge_fixed_0_1", + "test_float_BoolTest_ge_fixed_10_20", "test_double_BoolTest_ge_fixed_10_20", + "test_float_BoolTest_ge_variable_results", "test_double_BoolTest_ge_variable_results"}) + public void runTests() { + int err = 0; + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_ge_fixed_1_0(x, y); + float expected = golden_float_BoolTest_ge_fixed_1_0(x, y); + if (actual != expected) { + System.out.println("Float failed (ge), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_ge_fixed_1_0(x, y); + float expected = golden_double_BoolTest_ge_fixed_1_0(x, y); + if (actual != expected) { + System.out.println("Double failed (ge), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_ge_fixed_0_1(x, y); + float expected = golden_float_BoolTest_ge_fixed_0_1(x, y); + if (actual != expected) { + System.out.println("Float failed (ge, 0, 1), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_ge_fixed_0_1(x, y); + float expected = golden_double_BoolTest_ge_fixed_0_1(x, y); + if (actual != expected) { + System.out.println("Double failed (ge, 0, 1), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_ge_fixed_10_20(x, y); + float expected = golden_float_BoolTest_ge_fixed_10_20(x, y); + if (actual != expected) { + System.out.println("Float failed (ge, 10, 20), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_ge_fixed_10_20(x, y); + float expected = golden_double_BoolTest_ge_fixed_10_20(x, y); + if (actual != expected) { + System.out.println("Double failed (ge, 10, 20), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + for (int m = 0; m < TestFPComparison2.FLOATS.length; m++) { + for (int n = 0; n < TestFPComparison2.FLOATS.length; n++) { + float a = TestFPComparison2.FLOATS[m]; + float b = TestFPComparison2.FLOATS[n]; + float actual = test_float_BoolTest_ge_variable_results(x, y, a, b); + float expected = golden_float_BoolTest_ge_variable_results(x, y, a, b); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (ge), x: " + x + ", y: " + y + ", a: " + a + ", b: " + b + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + for (int m = 0; m < TestFPComparison2.FLOATS.length; m++) { + for (int n = 0; n < TestFPComparison2.FLOATS.length; n++) { + float a = TestFPComparison2.FLOATS[m]; + float b = TestFPComparison2.FLOATS[n]; + float actual = test_double_BoolTest_ge_variable_results(x, y, a, b); + float expected = golden_double_BoolTest_ge_variable_results(x, y, a, b); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Double failed (ge), x: " + x + ", y: " + y + ", a: " + a + ", b: " + b + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + } + } + + if (err != 0) { + throw new RuntimeException("Some tests failed"); + } + } +} + class Test_gt_1 { @Test @IR(counts = {IRNode.CMOVE_I, "1"}) @@ -776,6 +1248,234 @@ class Test_gt_1 { } } +class Test_gt_cmove_fp_1 { + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_fixed_1_0(float x, float y) { + // return 1 + // when either x or y is NaN + // when neither is NaN, and x >= y + // return 0 + // when neither is NaN, and x < y + return !(x < y) ? 1.0f : 0.0f; + } + @DontCompile + public static float golden_float_BoolTest_gt_fixed_1_0(float x, float y) { + return !(x < y) ? 1.0f : 0.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_gt_fixed_1_0(double x, double y) { + // return 1 + // when either x or y is NaN + // when neither is NaN, and x >= y + // return 0 + // when neither is NaN, and x < y + return !(x < y) ? 1.0f : 0.0f; + } + @DontCompile + public static float golden_double_BoolTest_gt_fixed_1_0(double x, double y) { + return !(x < y) ? 1.0f : 0.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_fixed_0_1(float x, float y) { + return !(x < y) ? 0.0f : 1.0f; + } + @DontCompile + public static float golden_float_BoolTest_gt_fixed_0_1(float x, float y) { + return !(x < y) ? 0.0f : 1.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_gt_fixed_0_1(double x, double y) { + return !(x < y) ? 0.0f : 1.0f; + } + @DontCompile + public static float golden_double_BoolTest_gt_fixed_0_1(double x, double y) { + return !(x < y) ? 0.0f : 1.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_fixed_10_20(float x, float y) { + return !(x < y) ? 10.0f : 20.0f; + } + @DontCompile + public static float golden_float_BoolTest_gt_fixed_10_20(float x, float y) { + return !(x < y) ? 10.0f : 20.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_gt_fixed_10_20(double x, double y) { + return !(x < y) ? 10.0f : 20.0f; + } + @DontCompile + public static float golden_double_BoolTest_gt_fixed_10_20(double x, double y) { + return !(x < y) ? 10.0f : 20.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_variable_results(float x, float y, float a, float b) { + return !(x < y) ? a : b; + } + @DontCompile + public static float golden_float_BoolTest_gt_variable_results(float x, float y, float a, float b) { + return !(x < y) ? a : b; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_gt_variable_results(double x, double y, float a, float b) { + return !(x < y) ? a : b; + } + @DontCompile + public static float golden_double_BoolTest_gt_variable_results(double x, double y, float a, float b) { + return !(x < y) ? a : b; + } + + @Run(test = {"test_float_BoolTest_gt_fixed_1_0", "test_double_BoolTest_gt_fixed_1_0", + "test_float_BoolTest_gt_fixed_0_1", "test_double_BoolTest_gt_fixed_0_1", + "test_float_BoolTest_gt_fixed_10_20", "test_double_BoolTest_gt_fixed_10_20", + "test_float_BoolTest_gt_variable_results", "test_double_BoolTest_gt_variable_results"}) + public void runTests() { + int err = 0; + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_gt_fixed_1_0(x, y); + float expected = golden_float_BoolTest_gt_fixed_1_0(x, y); + if (actual != expected) { + System.out.println("Float failed (gt), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_gt_fixed_1_0(x, y); + float expected = golden_double_BoolTest_gt_fixed_1_0(x, y); + if (actual != expected) { + System.out.println("Double failed (gt), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_gt_fixed_0_1(x, y); + float expected = golden_float_BoolTest_gt_fixed_0_1(x, y); + if (actual != expected) { + System.out.println("Float failed (gt, 0, 1), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_gt_fixed_0_1(x, y); + float expected = golden_double_BoolTest_gt_fixed_0_1(x, y); + if (actual != expected) { + System.out.println("Double failed (gt, 0, 1), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_gt_fixed_10_20(x, y); + float expected = golden_float_BoolTest_gt_fixed_10_20(x, y); + if (actual != expected) { + System.out.println("Float failed (gt, 10, 20), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_gt_fixed_10_20(x, y); + float expected = golden_double_BoolTest_gt_fixed_10_20(x, y); + if (actual != expected) { + System.out.println("Double failed (gt, 10, 20), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + for (int m = 0; m < TestFPComparison2.FLOATS.length; m++) { + for (int n = 0; n < TestFPComparison2.FLOATS.length; n++) { + float a = TestFPComparison2.FLOATS[m]; + float b = TestFPComparison2.FLOATS[n]; + float actual = test_float_BoolTest_gt_variable_results(x, y, a, b); + float expected = golden_float_BoolTest_gt_variable_results(x, y, a, b); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (gt), x: " + x + ", y: " + y + ", a: " + a + ", b: " + b + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + for (int m = 0; m < TestFPComparison2.FLOATS.length; m++) { + for (int n = 0; n < TestFPComparison2.FLOATS.length; n++) { + float a = TestFPComparison2.FLOATS[m]; + float b = TestFPComparison2.FLOATS[n]; + float actual = test_double_BoolTest_gt_variable_results(x, y, a, b); + float expected = golden_double_BoolTest_gt_variable_results(x, y, a, b); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Double failed (gt), x: " + x + ", y: " + y + ", a: " + a + ", b: " + b + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + } + } + + if (err != 0) { + throw new RuntimeException("Some tests failed"); + } + } +} + class Test_gt_2 { @Test @IR(counts = {IRNode.CMOVE_I, "1"}) @@ -1003,3 +1703,439 @@ class Test_gt_2 { } } } + +class Test_gt_cmove_fp_2 { + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_fixed_1_0(float x, float y) { + // return 1 + // when either x or y is NaN + // when neither is NaN, and x <= y + // return 0 + // when neither is NaN, and x > y + return !(x > y) ? 1.0f : 0.0f; + } + @DontCompile + public static float golden_float_BoolTest_gt_fixed_1_0(float x, float y) { + return !(x > y) ? 1.0f : 0.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_gt_fixed_1_0(double x, double y) { + // return 1 + // when either x or y is NaN + // when neither is NaN, and x <= y + // return 0 + // when neither is NaN, and x > y + return !(x > y) ? 1.0f : 0.0f; + } + @DontCompile + public static float golden_double_BoolTest_gt_fixed_1_0(double x, double y) { + return !(x > y) ? 1.0f : 0.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_fixed_0_1(float x, float y) { + return !(x > y) ? 0.0f : 1.0f; + } + @DontCompile + public static float golden_float_BoolTest_gt_fixed_0_1(float x, float y) { + return !(x > y) ? 0.0f : 1.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_gt_fixed_0_1(double x, double y) { + return !(x > y) ? 0.0f : 1.0f; + } + @DontCompile + public static float golden_double_BoolTest_gt_fixed_0_1(double x, double y) { + return !(x > y) ? 0.0f : 1.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_fixed_10_20(float x, float y) { + return !(x > y) ? 10.0f : 20.0f; + } + @DontCompile + public static float golden_float_BoolTest_gt_fixed_10_20(float x, float y) { + return !(x > y) ? 10.0f : 20.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_gt_fixed_10_20(double x, double y) { + return !(x > y) ? 10.0f : 20.0f; + } + @DontCompile + public static float golden_double_BoolTest_gt_fixed_10_20(double x, double y) { + return !(x > y) ? 10.0f : 20.0f; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_variable_results(float x, float y, float a, float b) { + return !(x > y) ? a : b; + } + @DontCompile + public static float golden_float_BoolTest_gt_variable_results(float x, float y, float a, float b) { + return !(x > y) ? a : b; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_double_BoolTest_gt_variable_results(double x, double y, float a, float b) { + return !(x > y) ? a : b; + } + @DontCompile + public static float golden_double_BoolTest_gt_variable_results(double x, double y, float a, float b) { + return !(x > y) ? a : b; + } + + @Run(test = {"test_float_BoolTest_gt_fixed_1_0", "test_double_BoolTest_gt_fixed_1_0", + "test_float_BoolTest_gt_fixed_0_1", "test_double_BoolTest_gt_fixed_0_1", + "test_float_BoolTest_gt_fixed_10_20", "test_double_BoolTest_gt_fixed_10_20", + "test_float_BoolTest_gt_variable_results", "test_double_BoolTest_gt_variable_results"}) + public void runTests() { + int err = 0; + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_gt_fixed_1_0(x, y); + float expected = golden_float_BoolTest_gt_fixed_1_0(x, y); + if (actual != expected) { + System.out.println("Float failed (gt), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_gt_fixed_1_0(x, y); + float expected = golden_double_BoolTest_gt_fixed_1_0(x, y); + if (actual != expected) { + System.out.println("Double failed (gt), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_gt_fixed_0_1(x, y); + float expected = golden_float_BoolTest_gt_fixed_0_1(x, y); + if (actual != expected) { + System.out.println("Float failed (gt, 0, 1), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_gt_fixed_0_1(x, y); + float expected = golden_double_BoolTest_gt_fixed_0_1(x, y); + if (actual != expected) { + System.out.println("Double failed (gt, 0, 1), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + float actual = test_float_BoolTest_gt_fixed_10_20(x, y); + float expected = golden_float_BoolTest_gt_fixed_10_20(x, y); + if (actual != expected) { + System.out.println("Float failed (gt, 10, 20), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + float actual = test_double_BoolTest_gt_fixed_10_20(x, y); + float expected = golden_double_BoolTest_gt_fixed_10_20(x, y); + if (actual != expected) { + System.out.println("Double failed (gt, 10, 20), x: " + x + ", y: " + y + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float y = TestFPComparison2.FLOATS[j]; + for (int m = 0; m < TestFPComparison2.FLOATS.length; m++) { + for (int n = 0; n < TestFPComparison2.FLOATS.length; n++) { + float a = TestFPComparison2.FLOATS[m]; + float b = TestFPComparison2.FLOATS[n]; + float actual = test_float_BoolTest_gt_variable_results(x, y, a, b); + float expected = golden_float_BoolTest_gt_variable_results(x, y, a, b); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (gt), x: " + x + ", y: " + y + ", a: " + a + ", b: " + b + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + } + } + + for (int i = 0; i < TestFPComparison2.DOUBLES.length; i++) { + for (int j = 0; j < TestFPComparison2.DOUBLES.length; j++) { + double x = TestFPComparison2.DOUBLES[i]; + double y = TestFPComparison2.DOUBLES[j]; + for (int m = 0; m < TestFPComparison2.FLOATS.length; m++) { + for (int n = 0; n < TestFPComparison2.FLOATS.length; n++) { + float a = TestFPComparison2.FLOATS[m]; + float b = TestFPComparison2.FLOATS[n]; + float actual = test_double_BoolTest_gt_variable_results(x, y, a, b); + float expected = golden_double_BoolTest_gt_variable_results(x, y, a, b); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Double failed (gt), x: " + x + ", y: " + y + ", a: " + a + ", b: " + b + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + } + } + + if (err != 0) { + throw new RuntimeException("Some tests failed"); + } + } +} + +class Test_cmov_fp_cmp_fp_ge_3 { + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_x_lt_0(float x) { + return x < 0 ? 0 : x; + } + @DontCompile + public static float golden_float_BoolTest_gt_x_lt_0(float x) { + return x < 0 ? 0 : x; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_x_gt_0(float x) { + return x > 0 ? 0 : x; + } + @DontCompile + public static float golden_float_BoolTest_gt_x_gt_0(float x) { + return x > 0 ? 0 : x; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_neg_x_lt_0(float x) { + return !(x < 0) ? 0 : x; + } + @DontCompile + public static float golden_float_BoolTest_gt_neg_x_lt_0(float x) { + return !(x < 0) ? 0 : x; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_gt_neg_x_gt_0(float x) { + return !(x > 0) ? 0 : x; + } + @DontCompile + public static float golden_float_BoolTest_gt_neg_x_gt_0(float x) { + return !(x > 0) ? 0 : x; + } + + @Run(test = {"test_float_BoolTest_gt_x_lt_0", "test_float_BoolTest_gt_x_gt_0", + "test_float_BoolTest_gt_neg_x_lt_0", "test_float_BoolTest_gt_neg_x_gt_0",}) + public void runTests() { + int err = 0; + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float actual = test_float_BoolTest_gt_x_lt_0(x); + float expected = golden_float_BoolTest_gt_x_lt_0(x); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (lt, x, 0), x: " + x + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float actual = test_float_BoolTest_gt_x_gt_0(x); + float expected = golden_float_BoolTest_gt_x_gt_0(x); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (gt, x, 0), x: " + x + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float actual = test_float_BoolTest_gt_neg_x_lt_0(x); + float expected = golden_float_BoolTest_gt_neg_x_lt_0(x); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (neg lt, x, 0), x: " + x + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float actual = test_float_BoolTest_gt_neg_x_gt_0(x); + float expected = golden_float_BoolTest_gt_neg_x_gt_0(x); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (neg gt, x, 0), x: " + x + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + if (err != 0) { + throw new RuntimeException("Some tests failed"); + } + } +} + +class Test_cmov_fp_cmp_fp_ge_4 { + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_x_le_0(float x) { + return x <= 0 ? 0 : x; + } + @DontCompile + public static float golden_float_BoolTest_ge_x_le_0(float x) { + return x <= 0 ? 0 : x; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_x_ge_0(float x) { + return x >= 0 ? 0 : x; + } + @DontCompile + public static float golden_float_BoolTest_ge_x_ge_0(float x) { + return x >= 0 ? 0 : x; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_neg_x_le_0(float x) { + return !(x <= 0) ? 0 : x; + } + @DontCompile + public static float golden_float_BoolTest_ge_neg_x_le_0(float x) { + return !(x <= 0) ? 0 : x; + } + + @Test + @IR(counts = {IRNode.CMOVE_F, "1"}) + public static float test_float_BoolTest_ge_neg_x_ge_0(float x) { + return !(x >= 0) ? 0 : x; + } + @DontCompile + public static float golden_float_BoolTest_ge_neg_x_ge_0(float x) { + return !(x >= 0) ? 0 : x; + } + + @Run(test = {"test_float_BoolTest_ge_x_le_0", "test_float_BoolTest_ge_x_ge_0", + "test_float_BoolTest_ge_neg_x_le_0", "test_float_BoolTest_ge_neg_x_ge_0",}) + public void runTests() { + int err = 0; + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float actual = test_float_BoolTest_ge_x_le_0(x); + float expected = golden_float_BoolTest_ge_x_le_0(x); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (le, x, 0), x: " + x + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float actual = test_float_BoolTest_ge_x_ge_0(x); + float expected = golden_float_BoolTest_ge_x_ge_0(x); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (ge, x, 0), x: " + x + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float actual = test_float_BoolTest_ge_neg_x_le_0(x); + float expected = golden_float_BoolTest_ge_neg_x_le_0(x); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (neg le, x, 0), x: " + x + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + for (int i = 0; i < TestFPComparison2.FLOATS.length; i++) { + for (int j = 0; j < TestFPComparison2.FLOATS.length; j++) { + float x = TestFPComparison2.FLOATS[i]; + float actual = test_float_BoolTest_ge_neg_x_ge_0(x); + float expected = golden_float_BoolTest_ge_neg_x_ge_0(x); + if (actual != expected && (!Float.isNaN(actual) || !Float.isNaN(expected))) { + System.out.println("Float failed (neg ge, x, 0), x: " + x + + ", actual: " + actual + ", expected: " + expected); + err++; + } + } + } + + if (err != 0) { + throw new RuntimeException("Some tests failed"); + } + } +} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestScalarConditionalMoveCmpObj.java b/test/hotspot/jtreg/compiler/c2/irTests/TestScalarConditionalMoveCmpObj.java new file mode 100644 index 00000000000..e332ac9e293 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestScalarConditionalMoveCmpObj.java @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2025, Rivos Inc. All rights reserved. + * 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.irTests; + +import compiler.lib.ir_framework.*; +import java.util.Random; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +/* + * @test + * @summary Test conditional move + compare object. + * @requires vm.simpleArch == "riscv64" + * @library /test/lib / + * @run driver compiler.c2.irTests.TestScalarConditionalMoveCmpObj + */ + +public class TestScalarConditionalMoveCmpObj { + final private static int SIZE = 1024; + private static final Random RANDOM = Utils.getRandomInstance(); + + public static void main(String[] args) { + TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov", + "-XX:+UnlockExperimentalVMOptions", "-XX:-UseCompactObjectHeaders", "-XX:+UseCompressedOops"); + TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov", + "-XX:+UnlockExperimentalVMOptions", "-XX:-UseCompactObjectHeaders", "-XX:-UseCompressedOops"); + TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov", + "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCompactObjectHeaders", "-XX:+UseCompressedOops"); + TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov", + "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCompactObjectHeaders", "-XX:-UseCompressedOops"); + } + + // Object comparison + // O for I + private int cmoveOEQforI(Object a, Object b, int c, int d) { + return (a == b) ? c : d; + } + + private int cmoveONEforI(Object a, Object b, int c, int d) { + return (a != b) ? c : d; + } + + // O for L + private long cmoveOEQforL(Object a, Object b, long c, long d) { + return (a == b) ? c : d; + } + + private long cmoveONEforL(Object a, Object b, long c, long d) { + return (a != b) ? c : d; + } + + // O for F + private float cmoveOEQforF(Object a, Object b, float c, float d) { + return (a == b) ? c : d; + } + + private float cmoveONEforF(Object a, Object b, float c, float d) { + return (a != b) ? c : d; + } + + // O for D + private double cmoveOEQforD(Object a, Object b, double c, double d) { + return (a == b) ? c : d; + } + + private double cmoveONEforD(Object a, Object b, double c, double d) { + return (a != b) ? c : d; + } + + // Tests shows CMoveI is generated, so let @IR verify CMOVE_I. + // + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_P, ">0"}, + applyIf = {"UseCompressedOops", "false"}) + @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_N, ">0"}, + applyIf = {"UseCompressedOops", "true"}) + private static void testCMoveOEQforI(Object[] a, Object[] b, int[] c, int[] d, int[] r, int[] r2) { + for (int i = 0; i < a.length; i++) { + int cc = c[i]; + int dd = d[i]; + r2[i] = cc + dd; + r[i] = (a[i] == b[i]) ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_P, ">0"}, + applyIf = {"UseCompressedOops", "false"}) + @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_N, ">0"}, + applyIf = {"UseCompressedOops", "true"}) + private static void testCMoveONEforI(Object[] a, Object[] b, int[] c, int[] d, int[] r, int[] r2) { + for (int i = 0; i < a.length; i++) { + int cc = c[i]; + int dd = d[i]; + r2[i] = cc + dd; + r[i] = (a[i] != b[i]) ? cc : dd; + } + } + + // So far, CMoveL is not guaranteed to be generated, so @IR not verify CMOVE_L. + // TODO: enable CMOVE_L verification when it's guaranteed to generate CMOVE_L. + // + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_P, ">0"}, + // applyIf = {"UseCompressedOops", "false"}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_N, ">0"}, + // applyIf = {"UseCompressedOops", "true"}) + private static void testCMoveOEQforL(Object[] a, Object[] b, long[] c, long[] d, long[] r, long[] r2) { + for (int i = 0; i < a.length; i++) { + long cc = c[i]; + long dd = d[i]; + r2[i] = cc + dd; + r[i] = (a[i] == b[i]) ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_P, ">0"}, + // applyIf = {"UseCompressedOops", "false"}) + // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_N, ">0"}, + // applyIf = {"UseCompressedOops", "true"}) + private static void testCMoveONEforL(Object[] a, Object[] b, long[] c, long[] d, long[] r, long[] r2) { + for (int i = 0; i < a.length; i++) { + long cc = c[i]; + long dd = d[i]; + r2[i] = cc + dd; + r[i] = (a[i] != b[i]) ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_P, ">0"}, + applyIf = {"UseCompressedOops", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_N, ">0"}, + applyIf = {"UseCompressedOops", "true"}) + private static void testCMoveOEQforF(Object[] a, Object[] b, float[] c, float[] d, float[] r, float[] r2) { + for (int i = 0; i < a.length; i++) { + float cc = c[i]; + float dd = d[i]; + r2[i] = cc + dd; + r[i] = (a[i] == b[i]) ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_P, ">0"}, + applyIf = {"UseCompressedOops", "false"}) + @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_N, ">0"}, + applyIf = {"UseCompressedOops", "true"}) + private static void testCMoveONEforF(Object[] a, Object[] b, float[] c, float[] d, float[] r, float[] r2) { + for (int i = 0; i < a.length; i++) { + float cc = c[i]; + float dd = d[i]; + r2[i] = cc + dd; + r[i] = (a[i] != b[i]) ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_P, ">0"}, + applyIf = {"UseCompressedOops", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_N, ">0"}, + applyIf = {"UseCompressedOops", "true"}) + private static void testCMoveOEQforD(Object[] a, Object[] b, double[] c, double[] d, double[] r, double[] r2) { + for (int i = 0; i < a.length; i++) { + double cc = c[i]; + double dd = d[i]; + r2[i] = cc + dd; + r[i] = (a[i] == b[i]) ? cc : dd; + } + } + + @Test + @IR(failOn = {IRNode.STORE_VECTOR}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_P, ">0"}, + applyIf = {"UseCompressedOops", "false"}) + @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_N, ">0"}, + applyIf = {"UseCompressedOops", "true"}) + private static void testCMoveONEforD(Object[] a, Object[] b, double[] c, double[] d, double[] r, double[] r2) { + for (int i = 0; i < a.length; i++) { + double cc = c[i]; + double dd = d[i]; + r2[i] = cc + dd; + r[i] = (a[i] != b[i]) ? cc : dd; + } + } + + @Warmup(0) + @Run(test = {// Object + "testCMoveOEQforI", + "testCMoveONEforI", + "testCMoveOEQforL", + "testCMoveONEforL", + "testCMoveOEQforF", + "testCMoveONEforF", + "testCMoveOEQforD", + "testCMoveONEforD", + }) + private void testCMove_runner_two() { + Object[] aO = new Object[SIZE]; + Object[] bO = new Object[SIZE]; + int[] cI = new int[SIZE]; + int[] dI = new int[SIZE]; + int[] rI = new int[SIZE]; + long[] cL = new long[SIZE]; + long[] dL = new long[SIZE]; + long[] rL = new long[SIZE]; + float[] cF = new float[SIZE]; + float[] dF = new float[SIZE]; + float[] rF = new float[SIZE]; + double[] cD = new double[SIZE]; + double[] dD = new double[SIZE]; + double[] rD = new double[SIZE]; + + init(aO); + shuffle(aO, bO); + init(cL); + init(dL); + init(cF); + init(dF); + init(cD); + init(dD); + + testCMoveOEQforI(aO, bO, cI, dI, rI, rI); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rI[i], cmoveOEQforI(aO[i], bO[i], cI[i], dI[i])); + } + + testCMoveONEforI(aO, bO, cI, dI, rI, rI); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rI[i], cmoveONEforI(aO[i], bO[i], cI[i], dI[i])); + } + + testCMoveOEQforL(aO, bO, cL, dL, rL, rL); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rL[i], cmoveOEQforL(aO[i], bO[i], cL[i], dL[i])); + } + + testCMoveONEforL(aO, bO, cL, dL, rL, rL); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rL[i], cmoveONEforL(aO[i], bO[i], cL[i], dL[i])); + } + + testCMoveOEQforF(aO, bO, cF, dF, rF, rF); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rF[i], cmoveOEQforF(aO[i], bO[i], cF[i], dF[i])); + } + + testCMoveONEforF(aO, bO, cF, dF, rF, rF); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rF[i], cmoveONEforF(aO[i], bO[i], cF[i], dF[i])); + } + + testCMoveOEQforD(aO, bO, cD, dD, rD, rD); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rD[i], cmoveOEQforD(aO[i], bO[i], cD[i], dD[i])); + } + + testCMoveONEforD(aO, bO, cD, dD, rD, rD); + for (int i = 0; i < SIZE; i++) { + Asserts.assertEquals(rD[i], cmoveONEforD(aO[i], bO[i], cD[i], dD[i])); + } + + } + + private static void init(Object[] a) { + for (int i = 0; i < SIZE; i++) { + a[i] = new Object(); + } + } + + private static void shuffle(Object[] a, Object[] b) { + for (int i = 0; i < a.length; i++) { + b[i] = a[i]; + } + Random rand = new Random(); + for (int i = 0; i < SIZE; i++) { + if (rand.nextInt(5) == 0) { + Object t = b[i]; + b[i] = b[SIZE-1-i]; + b[SIZE-1-i] = t; + } + } + } + + private static void init(int[] a) { + for (int i = 0; i < SIZE; i++) { + a[i] = RANDOM.nextInt(); + } + } + + private static void init(long[] a) { + for (int i = 0; i < SIZE; i++) { + a[i] = RANDOM.nextLong(); + } + } + + private static void init(float[] a) { + for (int i = 0; i < SIZE; i++) { + a[i] = switch(RANDOM.nextInt() % 20) { + case 0 -> Float.NaN; + case 1 -> 0; + case 2 -> 1; + case 3 -> Float.POSITIVE_INFINITY; + case 4 -> Float.NEGATIVE_INFINITY; + case 5 -> Float.MAX_VALUE; + case 6 -> Float.MIN_VALUE; + case 7, 8, 9 -> RANDOM.nextFloat(); + default -> Float.intBitsToFloat(RANDOM.nextInt()); + }; + } + } + + private static void init(double[] a) { + for (int i = 0; i < SIZE; i++) { + a[i] = switch(RANDOM.nextInt() % 20) { + case 0 -> Double.NaN; + case 1 -> 0; + case 2 -> 1; + case 3 -> Double.POSITIVE_INFINITY; + case 4 -> Double.NEGATIVE_INFINITY; + case 5 -> Double.MAX_VALUE; + case 6 -> Double.MIN_VALUE; + case 7, 8, 9 -> RANDOM.nextDouble(); + default -> Double.longBitsToDouble(RANDOM.nextLong()); + }; + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 1648135434a..41f185f3686 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -545,11 +545,36 @@ public class IRNode { trapNodes(CLASS_CHECK_TRAP, "class_check"); } + public static final String CMOVE_F = PREFIX + "CMOVE_F" + POSTFIX; + static { + beforeMatchingNameRegex(CMOVE_F, "CMoveF"); + } + + public static final String CMOVE_D = PREFIX + "CMOVE_D" + POSTFIX; + static { + beforeMatchingNameRegex(CMOVE_D, "CMoveD"); + } + public static final String CMOVE_I = PREFIX + "CMOVE_I" + POSTFIX; static { beforeMatchingNameRegex(CMOVE_I, "CMoveI"); } + public static final String CMOVE_L = PREFIX + "CMOVE_L" + POSTFIX; + static { + beforeMatchingNameRegex(CMOVE_L, "CMoveL"); + } + + public static final String CMP_F = PREFIX + "CMP_F" + POSTFIX; + static { + beforeMatchingNameRegex(CMP_F, "CmpF"); + } + + public static final String CMP_D = PREFIX + "CMP_D" + POSTFIX; + static { + beforeMatchingNameRegex(CMP_D, "CmpD"); + } + public static final String CMP_I = PREFIX + "CMP_I" + POSTFIX; static { beforeMatchingNameRegex(CMP_I, "CmpI"); @@ -585,6 +610,11 @@ public class IRNode { beforeMatchingNameRegex(CMP_P, "CmpP"); } + public static final String CMP_N = PREFIX + "CMP_N" + POSTFIX; + static { + beforeMatchingNameRegex(CMP_N, "CmpN"); + } + public static final String CMP_LT_MASK = PREFIX + "CMP_LT_MASK" + POSTFIX; static { beforeMatchingNameRegex(CMP_LT_MASK, "CmpLTMask"); diff --git a/test/micro/org/openjdk/bench/java/lang/ClassComparison.java b/test/micro/org/openjdk/bench/java/lang/ClassComparison.java index 2a768f243e2..15e631bb7fc 100644 --- a/test/micro/org/openjdk/bench/java/lang/ClassComparison.java +++ b/test/micro/org/openjdk/bench/java/lang/ClassComparison.java @@ -44,12 +44,8 @@ public class ClassComparison { Class[] c2; int[] res; long[] resLong; - Object[] resObject; - Object ro1; - Object ro2; - Object[] resClass; - Class rc1; - Class rc2; + float[] resFloat; + double[] resDouble; @Setup public void setup() { @@ -58,12 +54,8 @@ public class ClassComparison { c2 = new Class[INVOCATIONS]; res = new int[INVOCATIONS]; resLong = new long[INVOCATIONS]; - resObject = new Object[INVOCATIONS]; - ro1 = new Object(); - ro2 = new Object(); - resClass = new Class[INVOCATIONS]; - rc1 = Float.class; - rc2 = Double.class; + resFloat = new float[INVOCATIONS]; + resDouble = new double[INVOCATIONS]; for (int i = 0; i < INVOCATIONS; i++) { c1[i] = random.nextBoolean() ? Float.class : Double.class; } @@ -86,6 +78,7 @@ public class ClassComparison { } } + @Benchmark public void equalClassResLong() { for (int i = 0; i < INVOCATIONS; i++) { resLong[i] = (c1[i] == c2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; @@ -98,4 +91,32 @@ public class ClassComparison { resLong[i] = (c1[i] != c2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; } } + + @Benchmark + public void equalClassResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (c1[i] == c2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void notEqualClassResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (c1[i] != c2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void equalClassResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (c1[i] == c2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void notEqualClassResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (c1[i] != c2[i]) ? 0.1 : 0.2; + } + } } diff --git a/test/micro/org/openjdk/bench/java/lang/FPComparison.java b/test/micro/org/openjdk/bench/java/lang/FPComparison.java index 8074ada3257..18b2b79c7bd 100644 --- a/test/micro/org/openjdk/bench/java/lang/FPComparison.java +++ b/test/micro/org/openjdk/bench/java/lang/FPComparison.java @@ -37,34 +37,18 @@ import java.util.random.RandomGenerator; public class FPComparison { static final int INVOCATIONS = 1024; - float[] f1; - double[] d1; - float[] f2; - double[] d2; - int[] res; - long[] resLong; - Object[] resObject; - Object ro1; - Object ro2; - Class[] resClass; - Class rc1; - Class rc2; + static final float[] f1 = new float[INVOCATIONS]; + static final double[] d1 = new double[INVOCATIONS]; + static final float[] f2 = new float[INVOCATIONS]; + static final double[] d2 = new double[INVOCATIONS]; + static final int[] res = new int[INVOCATIONS];; + static final long[] resLong = new long[INVOCATIONS]; + static final float[] resFloat = new float[INVOCATIONS]; + static final double[] resDouble = new double[INVOCATIONS]; @Setup public void setup() { var random = RandomGenerator.getDefault(); - f1 = new float[INVOCATIONS]; - d1 = new double[INVOCATIONS]; - f2 = new float[INVOCATIONS]; - d2 = new double[INVOCATIONS]; - res = new int[INVOCATIONS]; - resLong = new long[INVOCATIONS]; - resObject = new Object[INVOCATIONS]; - ro1 = new Object(); - ro2 = new Object(); - resClass = new Class[INVOCATIONS]; - rc1 = Float.class; - rc2 = Double.class; for (int i = 0; i < INVOCATIONS; i++) { int type = random.nextInt(5); if (type == 1) { @@ -274,4 +258,148 @@ public class FPComparison { resLong[i] = (d1[i] >= d2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; } } + + // --------- result: float --------- + + @Benchmark + public void equalFloatResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (f1[i] == f2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void equalDoubleResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (d1[i] == d2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessFloatResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (f1[i] < f2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessDoubleResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (d1[i] < d2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessEqualFloatResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (f1[i] <= f2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessEqualDoubleResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (d1[i] <= d2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterFloatResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (f1[i] > f2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterDoubleResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (d1[i] > d2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterEqualFloatResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (f1[i] >= f2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterEqualDoubleResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (d1[i] >= d2[i]) ? 0.1f : 0.2f; + } + } + + // --------- result: double --------- + + @Benchmark + public void equalFloatResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (f1[i] == f2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void equalDoubleResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (d1[i] == d2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessFloatResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (f1[i] < f2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessDoubleResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (d1[i] < d2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessEqualFloatResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (f1[i] <= f2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessEqualDoubleResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (d1[i] <= d2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterFloatResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (f1[i] > f2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterDoubleResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (d1[i] > d2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterEqualFloatResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (f1[i] >= f2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterEqualDoubleResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (d1[i] >= d2[i]) ? 0.1 : 0.2; + } + } } diff --git a/test/micro/org/openjdk/bench/java/lang/IntegerComparison.java b/test/micro/org/openjdk/bench/java/lang/IntegerComparison.java index 1853be8497d..832d398fc3d 100644 --- a/test/micro/org/openjdk/bench/java/lang/IntegerComparison.java +++ b/test/micro/org/openjdk/bench/java/lang/IntegerComparison.java @@ -42,12 +42,8 @@ public class IntegerComparison { int[] i2; int[] res; long[] resLong; - Object[] resObject; - Object ro1; - Object ro2; - Object[] resClass; - Class rc1; - Class rc2; + float[] resFloat; + double[] resDouble; @Setup public void setup() { @@ -56,18 +52,17 @@ public class IntegerComparison { i2 = new int[INVOCATIONS]; res = new int[INVOCATIONS]; resLong = new long[INVOCATIONS]; - resObject = new Object[INVOCATIONS]; - ro1 = new Object(); - ro2 = new Object(); - resClass = new Class[INVOCATIONS]; - rc1 = Float.class; - rc2 = Double.class; + resFloat = new float[INVOCATIONS]; + resDouble = new double[INVOCATIONS]; for (int i = 0; i < INVOCATIONS; i++) { i1[i] = random.nextInt(INVOCATIONS); i2[i] = random.nextInt(INVOCATIONS); } } + // --------- result: int --------- + // Signed comparison + @Benchmark public void equalInteger() { for (int i = 0; i < INVOCATIONS; i++) { @@ -110,8 +105,55 @@ public class IntegerComparison { } } - // --------- result: long --------- + // Unsigned comparison + @Benchmark + public void equalIntegerUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Integer.compareUnsigned(i1[i], i2[i]) == 0 ? 1 : 2; + } + } + + @Benchmark + public void notEqualIntegerUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Integer.compareUnsigned(i1[i], i2[i]) != 0 ? 1 : 2; + } + } + + @Benchmark + public void lessIntegerUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Integer.compareUnsigned(i1[i], i2[i]) < 0 ? 1 : 2; + } + } + + @Benchmark + public void lessEqualIntegerUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Integer.compareUnsigned(i1[i], i2[i]) <= 0 ? 1 : 2; + } + } + + @Benchmark + public void greaterIntegerUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Integer.compareUnsigned(i1[i], i2[i]) > 0 ? 1 : 2; + } + } + + @Benchmark + public void greaterEqualIntegerUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Integer.compareUnsigned(i1[i], i2[i]) >= 0 ? 1 : 2; + } + } + + + // --------- result: long --------- + // Signed comparison + + @Benchmark public void equalIntegerResLong() { for (int i = 0; i < INVOCATIONS; i++) { resLong[i] = (i1[i] == i2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; @@ -125,6 +167,7 @@ public class IntegerComparison { } } + @Benchmark public void lessIntegerResLong() { for (int i = 0; i < INVOCATIONS; i++) { resLong[i] = (i1[i] < i2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; @@ -138,6 +181,7 @@ public class IntegerComparison { } } + @Benchmark public void greaterIntegerResLong() { for (int i = 0; i < INVOCATIONS; i++) { resLong[i] = (i1[i] > i2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; @@ -150,4 +194,226 @@ public class IntegerComparison { resLong[i] = (i1[i] >= i2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; } } + + // Unsigned comparison + + @Benchmark + public void equalIntegerUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Integer.compareUnsigned(i1[i], i2[i]) == 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void notEqualIntegerUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Integer.compareUnsigned(i1[i], i2[i]) != 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void lessIntegerUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Integer.compareUnsigned(i1[i], i2[i]) < 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void lessEqualIntegerUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Integer.compareUnsigned(i1[i], i2[i]) <= 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void greaterIntegerUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Integer.compareUnsigned(i1[i], i2[i]) > 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void greaterEqualIntegerUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Integer.compareUnsigned(i1[i], i2[i]) >= 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + // --------- result: float --------- + // Signed comparison + + @Benchmark + public void equalIntegerResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (i1[i] == i2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void notEqualIntegerResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (i1[i] != i2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessIntegerResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (i1[i] < i2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessEqualIntegerResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (i1[i] <= i2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterIntegerResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (i1[i] > i2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterEqualIntegerResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (i1[i] >= i2[i]) ? 0.1f : 0.2f; + } + } + + // Unsigned comparison + + @Benchmark + public void equalIntegerUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Integer.compareUnsigned(i1[i], i2[i]) == 0 ? 0.1f : 0.2f; + } + } + + @Benchmark + public void notEqualIntegerUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Integer.compareUnsigned(i1[i], i2[i]) != 0 ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessIntegerUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Integer.compareUnsigned(i1[i], i2[i]) < 0 ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessEqualIntegerUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Integer.compareUnsigned(i1[i], i2[i]) <= 0 ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterIntegerUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Integer.compareUnsigned(i1[i], i2[i]) > 0 ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterEqualIntegerUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Integer.compareUnsigned(i1[i], i2[i]) >= 0 ? 0.1f : 0.2f; + } + } + + // --------- result: double --------- + // Signed comparison + + @Benchmark + public void equalIntegerResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (i1[i] == i2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void notEqualIntegerResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (i1[i] != i2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessIntegerResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (i1[i] < i2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessEqualIntegerResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (i1[i] <= i2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterIntegerResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (i1[i] > i2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterEqualIntegerResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (i1[i] >= i2[i]) ? 0.1 : 0.2; + } + } + + // Unsigned comparison + + @Benchmark + public void equalIntegerUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Integer.compareUnsigned(i1[i], i2[i]) == 0 ? 0.1 : 0.2; + } + } + + @Benchmark + public void notEqualIntegerUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Integer.compareUnsigned(i1[i], i2[i]) != 0 ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessIntegerUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Integer.compareUnsigned(i1[i], i2[i]) < 0 ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessEqualIntegerUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Integer.compareUnsigned(i1[i], i2[i]) <= 0 ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterIntegerUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Integer.compareUnsigned(i1[i], i2[i]) > 0 ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterEqualIntegerUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Integer.compareUnsigned(i1[i], i2[i]) >= 0 ? 0.1 : 0.2; + } + } } diff --git a/test/micro/org/openjdk/bench/java/lang/LongComparison.java b/test/micro/org/openjdk/bench/java/lang/LongComparison.java index bed5ee245b2..a7317244c6f 100644 --- a/test/micro/org/openjdk/bench/java/lang/LongComparison.java +++ b/test/micro/org/openjdk/bench/java/lang/LongComparison.java @@ -41,12 +41,8 @@ public class LongComparison { long[] l2; int[] res; long[] resLong; - Object[] resObject; - Object ro1; - Object ro2; - Object[] resClass; - Class rc1; - Class rc2; + float[] resFloat; + double[] resDouble; @Setup public void setup() { @@ -55,18 +51,17 @@ public class LongComparison { l2 = new long[INVOCATIONS]; res = new int[INVOCATIONS]; resLong = new long[INVOCATIONS]; - resObject = new Object[INVOCATIONS]; - ro1 = new Object(); - ro2 = new Object(); - resClass = new Class[INVOCATIONS]; - rc1 = Float.class; - rc2 = Double.class; + resFloat = new float[INVOCATIONS]; + resDouble = new double[INVOCATIONS]; for (int i = 0; i < INVOCATIONS; i++) { l1[i] = random.nextLong(INVOCATIONS); l2[i] = random.nextLong(INVOCATIONS); } } + // --------- result: int --------- + // Signed comparison + @Benchmark public void equalLong() { for (int i = 0; i < INVOCATIONS; i++) { @@ -109,8 +104,54 @@ public class LongComparison { } } - // --------- result: long --------- + // Unsigned comparison + @Benchmark + public void equalLongUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Long.compareUnsigned(l1[i], l2[i]) == 0 ? 1 : 2; + } + } + + @Benchmark + public void notEqualLongUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Long.compareUnsigned(l1[i], l2[i]) != 0 ? 1 : 2; + } + } + + @Benchmark + public void lessLongUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Long.compareUnsigned(l1[i], l2[i]) < 0 ? 1 : 2; + } + } + + @Benchmark + public void lessEqualLongUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Long.compareUnsigned(l1[i], l2[i]) <= 0 ? 1 : 2; + } + } + + @Benchmark + public void greaterLongUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Long.compareUnsigned(l1[i], l2[i]) > 0 ? 1 : 2; + } + } + + @Benchmark + public void greaterEqualLongUnsigned() { + for (int i = 0; i < INVOCATIONS; i++) { + res[i] = Long.compareUnsigned(l1[i], l2[i]) >= 0 ? 1 : 2; + } + } + + // --------- result: long --------- + // Signed comparison + + @Benchmark public void equalLongResLong() { for (int i = 0; i < INVOCATIONS; i++) { resLong[i] = (l1[i] == l2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; @@ -124,6 +165,7 @@ public class LongComparison { } } + @Benchmark public void lessLongResLong() { for (int i = 0; i < INVOCATIONS; i++) { resLong[i] = (l1[i] < l2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; @@ -137,6 +179,7 @@ public class LongComparison { } } + @Benchmark public void greaterLongResLong() { for (int i = 0; i < INVOCATIONS; i++) { resLong[i] = (l1[i] > l2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; @@ -149,4 +192,226 @@ public class LongComparison { resLong[i] = (l1[i] >= l2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; } } + + // Unsigned comparison + + @Benchmark + public void equalLongUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Long.compareUnsigned(l1[i], l2[i]) == 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void notEqualLongUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Long.compareUnsigned(l1[i], l2[i]) != 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void lessLongUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Long.compareUnsigned(l1[i], l2[i]) < 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void lessEqualLongUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Long.compareUnsigned(l1[i], l2[i]) <= 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void greaterLongUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Long.compareUnsigned(l1[i], l2[i]) > 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + @Benchmark + public void greaterEqualLongUnsignedResLong() { + for (int i = 0; i < INVOCATIONS; i++) { + resLong[i] = Long.compareUnsigned(l1[i], l2[i]) >= 0 ? Long.MAX_VALUE : Long.MIN_VALUE; + } + } + + // --------- result: float --------- + // Signed comparison + + @Benchmark + public void equalLongResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (l1[i] == l2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void notEqualLongResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (l1[i] != l2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessLongResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (l1[i] < l2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessEqualLongResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (l1[i] <= l2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterLongResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (l1[i] > l2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterEqualLongResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (l1[i] >= l2[i]) ? 0.1f : 0.2f; + } + } + + // Unsigned comparison + + @Benchmark + public void equalLongUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Long.compareUnsigned(l1[i], l2[i]) == 0 ? 0.1f : 0.2f; + } + } + + @Benchmark + public void notEqualLongUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Long.compareUnsigned(l1[i], l2[i]) != 0 ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessLongUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Long.compareUnsigned(l1[i], l2[i]) < 0 ? 0.1f : 0.2f; + } + } + + @Benchmark + public void lessEqualLongUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Long.compareUnsigned(l1[i], l2[i]) <= 0 ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterLongUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Long.compareUnsigned(l1[i], l2[i]) > 0 ? 0.1f : 0.2f; + } + } + + @Benchmark + public void greaterEqualLongUnsignedResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = Long.compareUnsigned(l1[i], l2[i]) >= 0 ? 0.1f : 0.2f; + } + } + + // --------- result: double --------- + // Signed comparison + + @Benchmark + public void equalLongResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (l1[i] == l2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void notEqualLongResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (l1[i] != l2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessLongResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (l1[i] < l2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessEqualLongResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (l1[i] <= l2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterLongResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (l1[i] > l2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterEqualLongResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (l1[i] >= l2[i]) ? 0.1 : 0.2; + } + } + + // Unsigned comparison + + @Benchmark + public void equalLongUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Long.compareUnsigned(l1[i], l2[i]) == 0 ? 0.1 : 0.2; + } + } + + @Benchmark + public void notEqualLongUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Long.compareUnsigned(l1[i], l2[i]) != 0 ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessLongUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Long.compareUnsigned(l1[i], l2[i]) < 0 ? 0.1 : 0.2; + } + } + + @Benchmark + public void lessEqualLongUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Long.compareUnsigned(l1[i], l2[i]) <= 0 ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterLongUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Long.compareUnsigned(l1[i], l2[i]) > 0 ? 0.1 : 0.2; + } + } + + @Benchmark + public void greaterEqualLongUnsignedResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = Long.compareUnsigned(l1[i], l2[i]) >= 0 ? 0.1 : 0.2; + } + } } diff --git a/test/micro/org/openjdk/bench/java/lang/PointerComparison.java b/test/micro/org/openjdk/bench/java/lang/PointerComparison.java index b6bcf008619..fc2fd95b1c0 100644 --- a/test/micro/org/openjdk/bench/java/lang/PointerComparison.java +++ b/test/micro/org/openjdk/bench/java/lang/PointerComparison.java @@ -44,12 +44,8 @@ public class PointerComparison { Object[] o2; int[] res; long[] resLong; - Object[] resObject; - Object ro1; - Object ro2; - Object[] resClass; - Class rc1; - Class rc2; + float[] resFloat; + double[] resDouble; @Setup public void setup() { @@ -58,12 +54,8 @@ public class PointerComparison { o2 = new Object[INVOCATIONS]; res = new int[INVOCATIONS]; resLong = new long[INVOCATIONS]; - resObject = new Object[INVOCATIONS]; - ro1 = new Object(); - ro2 = new Object(); - resClass = new Class[INVOCATIONS]; - rc1 = Float.class; - rc2 = Double.class; + resFloat = new float[INVOCATIONS]; + resDouble = new double[INVOCATIONS]; for (int i = 0; i < INVOCATIONS; i++) { o1[i] = new Object(); } @@ -86,6 +78,7 @@ public class PointerComparison { } } + @Benchmark public void equalObjectResLong() { for (int i = 0; i < INVOCATIONS; i++) { resLong[i] = (o1[i] == o2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; @@ -98,4 +91,32 @@ public class PointerComparison { resLong[i] = (o1[i] != o2[i]) ? Long.MAX_VALUE : Long.MIN_VALUE; } } + + @Benchmark + public void equalObjecResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (o1[i] == o2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void notEqualObjecResFloat() { + for (int i = 0; i < INVOCATIONS; i++) { + resFloat[i] = (o1[i] != o2[i]) ? 0.1f : 0.2f; + } + } + + @Benchmark + public void equalObjecResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (o1[i] == o2[i]) ? 0.1 : 0.2; + } + } + + @Benchmark + public void notEqualObjecResDouble() { + for (int i = 0; i < INVOCATIONS; i++) { + resDouble[i] = (o1[i] != o2[i]) ? 0.1 : 0.2; + } + } } From ed5fc9ad2defb75ea5a68fe6427a591376ce6d6b Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 8 Dec 2025 14:21:40 +0000 Subject: [PATCH 06/10] 8373087: Parallel: Rename PSGenerationPool to PSOldGenerationPool Reviewed-by: tschatzl, jsikstro, iwalulya --- src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp | 6 +++--- src/hotspot/share/gc/parallel/psMemoryPool.cpp | 8 ++++---- src/hotspot/share/gc/parallel/psMemoryPool.hpp | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index b85b16f58b5..cff53e84059 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -141,9 +141,9 @@ void ParallelScavengeHeap::initialize_serviceability() { "PS Survivor Space", false /* support_usage_threshold */); - _old_pool = new PSGenerationPool(_old_gen, - "PS Old Gen", - true /* support_usage_threshold */); + _old_pool = new PSOldGenerationPool(_old_gen, + "PS Old Gen", + true /* support_usage_threshold */); _young_manager = new GCMemoryManager("PS Scavenge"); _old_manager = new GCMemoryManager("PS MarkSweep"); diff --git a/src/hotspot/share/gc/parallel/psMemoryPool.cpp b/src/hotspot/share/gc/parallel/psMemoryPool.cpp index c120664600b..81c7b17a1ef 100644 --- a/src/hotspot/share/gc/parallel/psMemoryPool.cpp +++ b/src/hotspot/share/gc/parallel/psMemoryPool.cpp @@ -24,14 +24,14 @@ #include "gc/parallel/psMemoryPool.hpp" -PSGenerationPool::PSGenerationPool(PSOldGen* old_gen, - const char* name, - bool support_usage_threshold) : +PSOldGenerationPool::PSOldGenerationPool(PSOldGen* old_gen, + const char* name, + bool support_usage_threshold) : CollectedMemoryPool(name, old_gen->capacity_in_bytes(), old_gen->reserved().byte_size(), support_usage_threshold), _old_gen(old_gen) { } -MemoryUsage PSGenerationPool::get_memory_usage() { +MemoryUsage PSOldGenerationPool::get_memory_usage() { size_t maxSize = (available_for_allocation() ? max_size() : 0); size_t used = used_in_bytes(); size_t committed = _old_gen->capacity_in_bytes(); diff --git a/src/hotspot/share/gc/parallel/psMemoryPool.hpp b/src/hotspot/share/gc/parallel/psMemoryPool.hpp index 58f39cdc79f..0da47e5a8ef 100644 --- a/src/hotspot/share/gc/parallel/psMemoryPool.hpp +++ b/src/hotspot/share/gc/parallel/psMemoryPool.hpp @@ -31,12 +31,12 @@ #include "services/memoryPool.hpp" #include "services/memoryUsage.hpp" -class PSGenerationPool : public CollectedMemoryPool { +class PSOldGenerationPool : public CollectedMemoryPool { private: PSOldGen* _old_gen; public: - PSGenerationPool(PSOldGen* pool, const char* name, bool support_usage_threshold); + PSOldGenerationPool(PSOldGen* pool, const char* name, bool support_usage_threshold); MemoryUsage get_memory_usage(); size_t used_in_bytes() { return _old_gen->used_in_bytes(); } From ac81ce51fa4ed04b6dbcc28cb2dd8eabcfe52ad7 Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Mon, 8 Dec 2025 15:38:35 +0000 Subject: [PATCH 07/10] 8372555: Test com/sun/jdi/ExceptionEvents.java failed: ObjectCollectedException Reviewed-by: amenkov, dholmes --- test/jdk/com/sun/jdi/ExceptionEvents.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/jdk/com/sun/jdi/ExceptionEvents.java b/test/jdk/com/sun/jdi/ExceptionEvents.java index de5f51c4aa2..ee9e91b4dd9 100644 --- a/test/jdk/com/sun/jdi/ExceptionEvents.java +++ b/test/jdk/com/sun/jdi/ExceptionEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -355,7 +355,16 @@ public class ExceptionEvents extends TestScaffold { if (event.request() == request) { try { System.out.print("ExceptionEvent: "); - System.out.print("" + event.exception().referenceType().name()); + try { + System.out.print("" + event.exception().referenceType().name()); + } catch (ObjectCollectedException e) { + if (event.request().suspendPolicy() == EventRequest.SUSPEND_NONE) { + // Since the thread was not suspended, the exception object can be collected. + System.out.print(""); + } else { + throw e; + } + } Location loc = event.location(); System.out.print(" @ " + loc.method().name()); System.out.print(":" + loc.lineNumber()); From 355755d35de5c3155d1ea8d1afdd0debe5296a13 Mon Sep 17 00:00:00 2001 From: Anton Artemov Date: Mon, 8 Dec 2025 16:07:01 +0000 Subject: [PATCH 08/10] 8366671: Refactor Thread::SpinAcquire and Thread::SpinRelease Co-authored-by: Axel Boldt-Christmas Reviewed-by: coleenp, kbarrett, dholmes, aboldtch --- .../recorder/service/jfrEventThrottler.cpp | 4 +- .../share/jfr/support/jfrAdaptiveSampler.cpp | 4 +- .../share/jfr/support/jfrThreadLocal.cpp | 1 - .../share/jfr/utilities/jfrSpinlockHelper.hpp | 44 ------------ src/hotspot/share/runtime/objectMonitor.cpp | 24 +++---- src/hotspot/share/runtime/park.cpp | 7 +- .../share/runtime/safepointVerifiers.cpp | 4 +- src/hotspot/share/runtime/thread.cpp | 46 ------------- src/hotspot/share/runtime/thread.hpp | 5 -- .../share/utilities/spinCriticalSection.cpp | 69 +++++++++++++++++++ .../share/utilities/spinCriticalSection.hpp | 63 +++++++++++++++++ .../gtest/jfr/test_adaptiveSampler.cpp | 3 +- 12 files changed, 155 insertions(+), 119 deletions(-) delete mode 100644 src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp create mode 100644 src/hotspot/share/utilities/spinCriticalSection.cpp create mode 100644 src/hotspot/share/utilities/spinCriticalSection.hpp diff --git a/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp b/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp index 787b2d7456b..e5420d29d94 100644 --- a/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp +++ b/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp @@ -24,11 +24,11 @@ */ #include "jfr/recorder/service/jfrEventThrottler.hpp" -#include "jfr/utilities/jfrSpinlockHelper.hpp" #include "jfrfiles/jfrEventIds.hpp" #include "logging/log.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/spinCriticalSection.hpp" constexpr static const JfrSamplerParams _disabled_params = { 0, // sample points per window @@ -128,7 +128,7 @@ JfrEventThrottler* JfrEventThrottler::create_throttler(JfrEventId id) { * - period_ms time period expressed in milliseconds */ void JfrEventThrottler::configure(int64_t sample_size, int64_t period_ms) { - JfrSpinlockHelper mutex(&_lock); + SpinCriticalSection scs(&_lock); _sample_size = sample_size; _period_ms = period_ms; _update = true; diff --git a/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp b/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp index 22399f42bbb..571b5656576 100644 --- a/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp +++ b/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp @@ -25,13 +25,13 @@ #include "jfr/support/jfrAdaptiveSampler.hpp" #include "jfr/utilities/jfrRandom.inline.hpp" -#include "jfr/utilities/jfrSpinlockHelper.hpp" #include "jfr/utilities/jfrTime.hpp" #include "jfr/utilities/jfrTimeConverter.hpp" #include "jfr/utilities/jfrTryLock.hpp" #include "logging/log.hpp" #include "runtime/atomicAccess.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/spinCriticalSection.hpp" #include @@ -342,7 +342,7 @@ JfrGTestFixedRateSampler::JfrGTestFixedRateSampler(size_t sample_points_per_wind bool JfrGTestFixedRateSampler::initialize() { const bool result = JfrAdaptiveSampler::initialize(); - JfrSpinlockHelper mutex(&_lock); + SpinCriticalSection scs(&_lock); reconfigure(); return result; } diff --git a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp index 5fe5546e0a9..39b0eb3656c 100644 --- a/src/hotspot/share/jfr/support/jfrThreadLocal.cpp +++ b/src/hotspot/share/jfr/support/jfrThreadLocal.cpp @@ -36,7 +36,6 @@ #include "jfr/recorder/storage/jfrStorage.hpp" #include "jfr/support/jfrThreadId.inline.hpp" #include "jfr/support/jfrThreadLocal.hpp" -#include "jfr/utilities/jfrSpinlockHelper.hpp" #include "jfr/writers/jfrJavaEventWriter.hpp" #include "logging/log.hpp" #include "memory/allocation.inline.hpp" diff --git a/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp b/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp deleted file mode 100644 index 4b5ca80470e..00000000000 --- a/src/hotspot/share/jfr/utilities/jfrSpinlockHelper.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2013, 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. - * - */ - -#ifndef SHARE_JFR_UTILITIES_JFRSPINLOCKHELPER_HPP -#define SHARE_JFR_UTILITIES_JFRSPINLOCKHELPER_HPP - -#include "runtime/javaThread.hpp" - -class JfrSpinlockHelper { - private: - volatile int* const _lock; - - public: - JfrSpinlockHelper(volatile int* lock) : _lock(lock) { - Thread::SpinAcquire(_lock); - } - - ~JfrSpinlockHelper() { - Thread::SpinRelease(_lock); - } -}; - -#endif // SHARE_JFR_UTILITIES_JFRSPINLOCKHELPER_HPP diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index ee7629ec6f5..785ee2af592 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -59,6 +59,7 @@ #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" #include "utilities/preserveException.hpp" +#include "utilities/spinCriticalSection.hpp" #if INCLUDE_JFR #include "jfr/support/jfrFlush.hpp" #endif @@ -1863,9 +1864,10 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // returns because of a timeout of interrupt. Contention is exceptionally rare // so we use a simple spin-lock instead of a heavier-weight blocking lock. - Thread::SpinAcquire(&_wait_set_lock); - add_waiter(&node); - Thread::SpinRelease(&_wait_set_lock); + { + SpinCriticalSection scs(&_wait_set_lock); + add_waiter(&node); + } intx save = _recursions; // record the old recursion count _waiters++; // increment the number of waiters @@ -1922,12 +1924,11 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { // That is, we fail toward safety. if (node.TState == ObjectWaiter::TS_WAIT) { - Thread::SpinAcquire(&_wait_set_lock); + SpinCriticalSection scs(&_wait_set_lock); if (node.TState == ObjectWaiter::TS_WAIT) { dequeue_specific_waiter(&node); // unlink from wait_set node.TState = ObjectWaiter::TS_RUN; } - Thread::SpinRelease(&_wait_set_lock); } // The thread is now either on off-list (TS_RUN), @@ -2036,7 +2037,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { bool ObjectMonitor::notify_internal(JavaThread* current) { bool did_notify = false; - Thread::SpinAcquire(&_wait_set_lock); + SpinCriticalSection scs(&_wait_set_lock); ObjectWaiter* iterator = dequeue_waiter(); if (iterator != nullptr) { guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant"); @@ -2095,7 +2096,6 @@ bool ObjectMonitor::notify_internal(JavaThread* current) { } } } - Thread::SpinRelease(&_wait_set_lock); return did_notify; } @@ -2198,9 +2198,10 @@ void ObjectMonitor::vthread_wait(JavaThread* current, jlong millis, bool interru // returns because of a timeout or interrupt. Contention is exceptionally rare // so we use a simple spin-lock instead of a heavier-weight blocking lock. - Thread::SpinAcquire(&_wait_set_lock); - add_waiter(node); - Thread::SpinRelease(&_wait_set_lock); + { + SpinCriticalSection scs(&_wait_set_lock); + add_waiter(node); + } node->_recursions = _recursions; // record the old recursion count _recursions = 0; // set the recursion level to be 0 @@ -2221,12 +2222,11 @@ bool ObjectMonitor::vthread_wait_reenter(JavaThread* current, ObjectWaiter* node // need to check if we were interrupted or the wait timed-out, and // in that case remove ourselves from the _wait_set queue. if (node->TState == ObjectWaiter::TS_WAIT) { - Thread::SpinAcquire(&_wait_set_lock); + SpinCriticalSection scs(&_wait_set_lock); if (node->TState == ObjectWaiter::TS_WAIT) { dequeue_specific_waiter(node); // unlink from wait_set node->TState = ObjectWaiter::TS_RUN; } - Thread::SpinRelease(&_wait_set_lock); } // If this was an interrupted case, set the _interrupted boolean so that diff --git a/src/hotspot/share/runtime/park.cpp b/src/hotspot/share/runtime/park.cpp index 37dfe6fcc3d..338a01bbfb9 100644 --- a/src/hotspot/share/runtime/park.cpp +++ b/src/hotspot/share/runtime/park.cpp @@ -25,6 +25,7 @@ #include "memory/allocation.inline.hpp" #include "nmt/memTracker.hpp" #include "runtime/javaThread.hpp" +#include "utilities/spinCriticalSection.hpp" // Lifecycle management for TSM ParkEvents. // ParkEvents are type-stable (TSM). @@ -60,14 +61,13 @@ ParkEvent * ParkEvent::Allocate (Thread * t) { // Using a spin lock since we are part of the mutex impl. // 8028280: using concurrent free list without memory management can leak // pretty badly it turns out. - Thread::SpinAcquire(&ListLock); { + SpinCriticalSection scs(&ListLock); ev = FreeList; if (ev != nullptr) { FreeList = ev->FreeNext; } } - Thread::SpinRelease(&ListLock); if (ev != nullptr) { guarantee (ev->AssociatedWith == nullptr, "invariant") ; @@ -88,12 +88,11 @@ void ParkEvent::Release (ParkEvent * ev) { ev->AssociatedWith = nullptr ; // Note that if we didn't have the TSM/immortal constraint, then // when reattaching we could trim the list. - Thread::SpinAcquire(&ListLock); { + SpinCriticalSection scs(&ListLock); ev->FreeNext = FreeList; FreeList = ev; } - Thread::SpinRelease(&ListLock); } // Override operator new and delete so we can ensure that the diff --git a/src/hotspot/share/runtime/safepointVerifiers.cpp b/src/hotspot/share/runtime/safepointVerifiers.cpp index 6eb61efe0ca..0c6f2e9add3 100644 --- a/src/hotspot/share/runtime/safepointVerifiers.cpp +++ b/src/hotspot/share/runtime/safepointVerifiers.cpp @@ -30,7 +30,9 @@ #ifdef ASSERT -NoSafepointVerifier::NoSafepointVerifier(bool active) : _thread(Thread::current()), _active(active) { +NoSafepointVerifier::NoSafepointVerifier(bool active) + : _thread(active ? Thread::current() : nullptr), + _active(active) { if (!_active) { return; } diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index d018c8a1a3a..f3c08ae62f7 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -566,49 +566,3 @@ bool Thread::set_as_starting_thread(JavaThread* jt) { DEBUG_ONLY(_starting_thread = jt;) return os::create_main_thread(jt); } - -// Ad-hoc mutual exclusion primitive: spin lock -// -// We employ a spin lock _only for low-contention, fixed-length -// short-duration critical sections where we're concerned -// about native mutex_t or HotSpot Mutex:: latency. - -void Thread::SpinAcquire(volatile int * adr) { - if (AtomicAccess::cmpxchg(adr, 0, 1) == 0) { - return; // normal fast-path return - } - - // Slow-path : We've encountered contention -- Spin/Yield/Block strategy. - int ctr = 0; - int Yields = 0; - for (;;) { - while (*adr != 0) { - ++ctr; - if ((ctr & 0xFFF) == 0 || !os::is_MP()) { - if (Yields > 5) { - os::naked_short_sleep(1); - } else { - os::naked_yield(); - ++Yields; - } - } else { - SpinPause(); - } - } - if (AtomicAccess::cmpxchg(adr, 0, 1) == 0) return; - } -} - -void Thread::SpinRelease(volatile int * adr) { - assert(*adr != 0, "invariant"); - // Roach-motel semantics. - // It's safe if subsequent LDs and STs float "up" into the critical section, - // but prior LDs and STs within the critical section can't be allowed - // to reorder or float past the ST that releases the lock. - // Loads and stores in the critical section - which appear in program - // order before the store that releases the lock - must also appear - // before the store that releases the lock in memory visibility order. - // So we need a #loadstore|#storestore "release" memory barrier before - // the ST of 0 into the lock-word which releases the lock. - AtomicAccess::release_store(adr, 0); -} diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 240821e90bd..181dfc46f87 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -602,11 +602,6 @@ protected: jint _hashStateY; jint _hashStateZ; - // Low-level leaf-lock primitives used to implement synchronization. - // Not for general synchronization use. - static void SpinAcquire(volatile int * Lock); - static void SpinRelease(volatile int * Lock); - #if defined(__APPLE__) && defined(AARCH64) private: DEBUG_ONLY(bool _wx_init); diff --git a/src/hotspot/share/utilities/spinCriticalSection.cpp b/src/hotspot/share/utilities/spinCriticalSection.cpp new file mode 100644 index 00000000000..0bbbc82c12b --- /dev/null +++ b/src/hotspot/share/utilities/spinCriticalSection.cpp @@ -0,0 +1,69 @@ +/* + * 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. + * + */ + +#include "runtime/atomicAccess.hpp" +#include "utilities/spinCriticalSection.hpp" + +void SpinCriticalSection::spin_acquire(volatile int* adr) { + if (AtomicAccess::cmpxchg(adr, 0, 1) == 0) { + return; // normal fast-path return + } + + // Slow-path : We've encountered contention -- Spin/Yield/Block strategy. + int ctr = 0; + int Yields = 0; + for (;;) { + while (*adr != 0) { + ++ctr; + if ((ctr & 0xFFF) == 0 || !os::is_MP()) { + if (Yields > 5) { + os::naked_short_sleep(1); + } + else { + os::naked_yield(); + ++Yields; + } + } + else { + SpinPause(); + } + } + if (AtomicAccess::cmpxchg(adr, 0, 1) == 0) return; + } +} + +void SpinCriticalSection::spin_release(volatile int* adr) { + assert(*adr != 0, "invariant"); + // Roach-motel semantics. + // It's safe if subsequent LDs and STs float "up" into the critical section, + // but prior LDs and STs within the critical section can't be allowed + // to reorder or float past the ST that releases the lock. + // Loads and stores in the critical section - which appear in program + // order before the store that releases the lock - must also appear + // before the store that releases the lock in memory visibility order. + // So we need a #loadstore|#storestore "release" memory barrier before + // the ST of 0 into the lock-word which releases the lock. + AtomicAccess::release_store(adr, 0); +} + diff --git a/src/hotspot/share/utilities/spinCriticalSection.hpp b/src/hotspot/share/utilities/spinCriticalSection.hpp new file mode 100644 index 00000000000..0ebdc5de63e --- /dev/null +++ b/src/hotspot/share/utilities/spinCriticalSection.hpp @@ -0,0 +1,63 @@ +/* + * 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. + * + */ + +#ifndef SHARE_UTILITIES_SPINCRITICALSECTION_HPP +#define SHARE_UTILITIES_SPINCRITICALSECTION_HPP + +#include "runtime/javaThread.hpp" +#include "runtime/safepointVerifiers.hpp" +#include "runtime/thread.hpp" +#include "utilities/macros.hpp" + +// Ad-hoc mutual exclusion primitive: spin critical section, +// which employs a spin lock. +// +// We use this critical section only for low-contention code, and +// when it is know that the duration is short. To be used where +// we're concerned about native mutex_t or HotSpot Mutex:: latency. +// This class uses low-level leaf-lock primitives to implement +// synchronization and is not for general synchronization use. +// Should not be used in signal-handling contexts. +class SpinCriticalSection { +private: + // We use int type as 32-bit atomic operation is the most performant + // compared to smaller/larger types. + volatile int* const _lock; + DEBUG_ONLY(NoSafepointVerifier _nsv;) + + static void spin_acquire(volatile int* Lock); + static void spin_release(volatile int* Lock); +public: + NONCOPYABLE(SpinCriticalSection); + SpinCriticalSection(volatile int* lock) + : _lock(lock) + DEBUG_ONLY(COMMA _nsv(Thread::current_or_null() != nullptr)) { + spin_acquire(_lock); + } + ~SpinCriticalSection() { + spin_release(_lock); + } +}; + +#endif // SHARE_UTILITIES_SPINCRITICALSECTION_HPP diff --git a/test/hotspot/gtest/jfr/test_adaptiveSampler.cpp b/test/hotspot/gtest/jfr/test_adaptiveSampler.cpp index 69548b06e51..8625f64099d 100644 --- a/test/hotspot/gtest/jfr/test_adaptiveSampler.cpp +++ b/test/hotspot/gtest/jfr/test_adaptiveSampler.cpp @@ -34,13 +34,12 @@ #include "jfr/utilities/jfrAllocation.hpp" #include "jfr/utilities/jfrRandom.inline.hpp" -#include "jfr/utilities/jfrSpinlockHelper.hpp" #include "jfr/utilities/jfrTime.hpp" #include "jfr/utilities/jfrTimeConverter.hpp" #include "jfr/utilities/jfrTryLock.hpp" #include "logging/log.hpp" -#include "runtime/atomicAccess.hpp" #include "utilities/globalDefinitions.hpp" +#include "utilities/spinCriticalSection.hpp" #include "unittest.hpp" #include From 811591c5c332e6427dc96819451e046841fe635b Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 8 Dec 2025 16:11:28 +0000 Subject: [PATCH 09/10] 8373262: Parallel: gc/metaspace/CompressedClassSpaceSizeInJmapHeap.java fails Reviewed-by: cjplummer --- .../sun/jvm/hotspot/gc/parallel/ParallelScavengeHeap.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/parallel/ParallelScavengeHeap.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/parallel/ParallelScavengeHeap.java index 76125e33e80..b0d158f409d 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/parallel/ParallelScavengeHeap.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/parallel/ParallelScavengeHeap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -60,11 +60,11 @@ public class ParallelScavengeHeap extends CollectedHeap { // Accessors public PSYoungGen youngGen() { - return VMObjectFactory.newObject(PSYoungGen.class, youngGenField.getValue()); + return VMObjectFactory.newObject(PSYoungGen.class, youngGenField.getValue(addr)); } public PSOldGen oldGen() { - return VMObjectFactory.newObject(PSOldGen.class, oldGenField.getValue()); + return VMObjectFactory.newObject(PSOldGen.class, oldGenField.getValue(addr)); } public long capacity() { From d34ef196c298aa91f8511714cfb04b15ae7fbf0a Mon Sep 17 00:00:00 2001 From: Albert Mingkun Yang Date: Mon, 8 Dec 2025 18:51:34 +0000 Subject: [PATCH 10/10] 8370198: Test gc/arguments/TestShrinkHeapInSteps.java crashed: assert(left >= right) failed: avoid underflow Reviewed-by: stefank, tschatzl --- .../cpu/aarch64/macroAssembler_aarch64.cpp | 1 - src/hotspot/cpu/riscv/macroAssembler_riscv.cpp | 1 - src/hotspot/share/gc/serial/serialHeap.cpp | 17 +++++++++++++++++ src/hotspot/share/gc/serial/serialHeap.hpp | 2 ++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp index ceedb4f1063..2ccc755be3c 100644 --- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp @@ -5379,7 +5379,6 @@ void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int index = oop_recorder()->find_index(k); - assert(! Universe::heap()->is_in(k), "should not be an oop"); InstructionMark im(this); RelocationHolder rspec = metadata_Relocation::spec(index); diff --git a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp index a14a051fd3b..43b17a13c20 100644 --- a/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/macroAssembler_riscv.cpp @@ -5187,7 +5187,6 @@ void MacroAssembler::set_narrow_klass(Register dst, Klass* k) { assert (UseCompressedClassPointers, "should only be used for compressed headers"); assert (oop_recorder() != nullptr, "this assembler needs an OopRecorder"); int index = oop_recorder()->find_index(k); - assert(!Universe::heap()->is_in(k), "should not be an oop"); narrowKlass nk = CompressedKlassPointers::encode(k); relocate(metadata_Relocation::spec(index), [&] { diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp index 932c06b8109..104924c1cad 100644 --- a/src/hotspot/share/gc/serial/serialHeap.cpp +++ b/src/hotspot/share/gc/serial/serialHeap.cpp @@ -630,6 +630,14 @@ bool SerialHeap::requires_barriers(stackChunkOop obj) const { // Returns "TRUE" iff "p" points into the committed areas of the heap. bool SerialHeap::is_in(const void* p) const { + // precondition + verify_not_in_native_if_java_thread(); + + if (!is_in_reserved(p)) { + // If it's not even in reserved. + return false; + } + return _young_gen->is_in(p) || _old_gen->is_in(p); } @@ -797,3 +805,12 @@ void SerialHeap::gc_epilogue(bool full) { MetaspaceCounters::update_performance_counters(); }; + +#ifdef ASSERT +void SerialHeap::verify_not_in_native_if_java_thread() { + if (Thread::current()->is_Java_thread()) { + JavaThread* thread = JavaThread::current(); + assert(thread->thread_state() != _thread_in_native, "precondition"); + } +} +#endif diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp index ee016173c2a..f5286179abf 100644 --- a/src/hotspot/share/gc/serial/serialHeap.hpp +++ b/src/hotspot/share/gc/serial/serialHeap.hpp @@ -111,6 +111,8 @@ private: void print_tracing_info() const override; void stop() override {}; + static void verify_not_in_native_if_java_thread() NOT_DEBUG_RETURN; + public: // Returns JNI_OK on success jint initialize() override;