From c8efd7f3a6ec66782917dbbaa51807bf18a92b2a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 21 Aug 2025 17:03:20 +0200 Subject: [PATCH 01/33] JDK-8359412 --- .../library/Expression.java | 58 +++++++++++++++++++ .../library/Operations.java | 46 +++++++++++++++ .../examples/TestExpressions.java | 42 ++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java create mode 100644 test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java new file mode 100644 index 0000000000000..6216d50f2c1ac --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -0,0 +1,58 @@ +/* + * 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. + */ + +package compiler.lib.template_framework.library; + +import java.util.List; + +/** + * TODO: desc + */ +public class Expression { + + private CodeGenerationDataNameType returnType; + private List argumentTypes; + private List strings; + + private Expression(CodeGenerationDataNameType returnType, + List argumentTypes, + List strings) { + if (argumentTypes.size() + 1 != strings.size()) { + throw new RuntimeException("Must have one more string than argument."); + } + this.returnType = returnType; + this.argumentTypes = argumentTypes; + this.strings = strings; + } + + + /** + * TODO: desc unary + */ + public static Expression make(CodeGenerationDataNameType returnType, + String s0, + CodeGenerationDataNameType t0, + String s1) { + return new Expression(returnType, List.of(t0), List.of(s0, s1)); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java new file mode 100644 index 0000000000000..d6779232b7da0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -0,0 +1,46 @@ +/* + * 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. + */ + +package compiler.lib.template_framework.library; + +import java.util.List; +import java.util.ArrayList; + +import static compiler.lib.template_framework.library.PrimitiveType.INTS; +import static compiler.lib.template_framework.library.PrimitiveType.LONGS; + +/** + * TODO: desc + */ +public final class Operations { + + public static final List PRIMITIVE_OPERATIONS = generatePrimitiveOperations(); + + private static List generatePrimitiveOperations() { + List ops = new ArrayList<>(); + + ops.add(Expression.make(INTS, "(", LONGS, ")")); + + return ops; + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java new file mode 100644 index 0000000000000..3042d888e8811 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java @@ -0,0 +1,42 @@ +/* + * 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 8359412 + * @summary Demonstrate the use of Expressions form the Template Library. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../../../compiler/lib/verify/Verify.java + * @run main template_framework.examples.TestExpressions + */ + +package template_framework.examples; + +import compiler.lib.template_framework.library.Operations; + +public class TestExpressions { + public static void main(String[] args) { + System.out.println(Operations.PRIMITIVE_OPERATIONS); + } +} From 1144adf08b2a24b17f6f380ca3fe0f3da774e17d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 26 Aug 2025 15:02:12 +0200 Subject: [PATCH 02/33] first use --- .../library/Expression.java | 33 +++++++- .../library/Operations.java | 3 +- .../examples/TestExpressions.java | 77 ++++++++++++++++++- 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 6216d50f2c1ac..f5376552d1382 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -23,15 +23,20 @@ package compiler.lib.template_framework.library; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.body; + import java.util.List; +import java.util.ArrayList; /** * TODO: desc */ public class Expression { - private CodeGenerationDataNameType returnType; - private List argumentTypes; + public CodeGenerationDataNameType returnType; + public List argumentTypes; private List strings; private Expression(CodeGenerationDataNameType returnType, @@ -55,4 +60,28 @@ public static Expression make(CodeGenerationDataNameType returnType, String s1) { return new Expression(returnType, List.of(t0), List.of(s0, s1)); } + + /** + * TODO: desc + */ + public TemplateToken asToken(List arguments) { + if (arguments.size() != argumentTypes.size()) { + throw new IllegalArgumentException("Wrong number of arguments:" + + " expected: " + argumentTypes.size() + + " but got: " + arguments.size()); + } + + // List of tokens: interleave strings and arguments. + List tokens = new ArrayList<>(); + for (int i = 0; i < argumentTypes.size(); i++) { + tokens.add(strings.get(i)); + tokens.add(arguments.get(i)); + } + tokens.add(strings.get(strings.size()-1)); + + var template = Template.make(() -> body( + tokens + )); + return template.asToken(); + } } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index d6779232b7da0..a71d11817d65e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -39,7 +39,8 @@ public final class Operations { private static List generatePrimitiveOperations() { List ops = new ArrayList<>(); - ops.add(Expression.make(INTS, "(", LONGS, ")")); + ops.add(Expression.make(INTS, "(int)(", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", INTS, ")")); return ops; } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java index 3042d888e8811..c0af3b259fa02 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java @@ -33,10 +33,85 @@ package template_framework.examples; +import java.util.List; +import java.util.Map; +import java.util.HashMap; + +import compiler.lib.compile_framework.*; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; +import compiler.lib.template_framework.library.Expression; import compiler.lib.template_framework.library.Operations; public class TestExpressions { public static void main(String[] args) { - System.out.println(Operations.PRIMITIVE_OPERATIONS); + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("p.xyz.InnerTest", generate()); + + // Compile the source file. + comp.compile(); + + // p.xyz.InnerTest.main(); + comp.invoke("p.xyz.InnerTest", "main", new Object[] {}); + } + + // Generate a Java source file as String + public static String generate() { + // Generate a list of test methods. + Map tests = new HashMap<>(); + + // Create a test method that executes the expression, with constant arguments. + var withConstantsTemplate = Template.make("name", "expression", (String name, Expression expression) -> body( + //let("CON1", type.con()), + //let("CON2", type.con()), + let("returnType", expression.returnType), + """ + public static #returnType #name() { + """, + "return ", expression.asToken(expression.argumentTypes.stream().map(t -> t.con()).toList()), ";\n", + """ + } + """ + )); + + int idx = 0; + for (Expression operation : Operations.PRIMITIVE_OPERATIONS) { + String name = "test" + (idx++); + tests.put(name, withConstantsTemplate.asToken(name, operation)); + } + + // Finally, put all the tests together in a class, and invoke all + // tests from the main method. + var template = Template.make(() -> body( + """ + package p.xyz; + + import compiler.lib.verify.*; + import java.lang.foreign.MemorySegment; + + public class InnerTest { + public static void main() { + """, + // Call all test methods from main. + tests.keySet().stream().map( + n -> List.of(n, "();\n") + ).toList(), + """ + } + """, + // Now add all the test methods. + tests.values().stream().toList(), + """ + } + """ + )); + + // Render the template to a String. + return template.render(); } } From d933e67cf58926f510ba2de84250905b5f45f328 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 26 Aug 2025 15:37:12 +0200 Subject: [PATCH 03/33] refactor --- .../examples/TestExpressions.java | 99 +++++++++---------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java index c0af3b259fa02..7dec3fc902ea7 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java @@ -34,8 +34,8 @@ package template_framework.examples; import java.util.List; -import java.util.Map; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.Set; import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; @@ -44,6 +44,7 @@ import static compiler.lib.template_framework.Template.let; import compiler.lib.template_framework.library.Expression; import compiler.lib.template_framework.library.Operations; +import compiler.lib.template_framework.library.TestFrameworkClass; public class TestExpressions { public static void main(String[] args) { @@ -51,67 +52,65 @@ public static void main(String[] args) { CompileFramework comp = new CompileFramework(); // Add a java source file. - comp.addJavaSourceCode("p.xyz.InnerTest", generate()); + comp.addJavaSourceCode("p.xyz.InnerTest", generate(comp)); // Compile the source file. comp.compile(); - // p.xyz.InnerTest.main(); - comp.invoke("p.xyz.InnerTest", "main", new Object[] {}); + // p.xyz.InnterTest.main(new String[] {}); + comp.invoke("p.xyz.InnerTest", "main", new Object[] {new String[] {}}); } // Generate a Java source file as String - public static String generate() { + public static String generate(CompileFramework comp) { // Generate a list of test methods. - Map tests = new HashMap<>(); + List tests = new ArrayList<>(); // Create a test method that executes the expression, with constant arguments. - var withConstantsTemplate = Template.make("name", "expression", (String name, Expression expression) -> body( - //let("CON1", type.con()), - //let("CON2", type.con()), - let("returnType", expression.returnType), - """ - public static #returnType #name() { - """, - "return ", expression.asToken(expression.argumentTypes.stream().map(t -> t.con()).toList()), ";\n", - """ - } - """ - )); - - int idx = 0; - for (Expression operation : Operations.PRIMITIVE_OPERATIONS) { - String name = "test" + (idx++); - tests.put(name, withConstantsTemplate.asToken(name, operation)); - } - - // Finally, put all the tests together in a class, and invoke all - // tests from the main method. - var template = Template.make(() -> body( - """ - package p.xyz; + var withConstantsTemplate = Template.make("expression", (Expression expression) -> { + // Create a token: fill the expression with a fixed set of constants. + // We then use the same token with the same constants, once compiled and once not compiled. + TemplateToken expressionToken = expression.asToken(expression.argumentTypes.stream().map(t -> t.con()).toList()); + return body( + let("returnType", expression.returnType), + """ + @Test + public static void $test() { + #returnType v0 = ${test}_compiled(); + #returnType v1 = ${test}_reference(); + Verify.checkEQ(v0, v1); + } - import compiler.lib.verify.*; - import java.lang.foreign.MemorySegment; + @DontInline + public static #returnType ${test}_compiled() { + """, + "return ", expressionToken, ";\n", + """ + } - public class InnerTest { - public static void main() { - """, - // Call all test methods from main. - tests.keySet().stream().map( - n -> List.of(n, "();\n") - ).toList(), - """ + @DontCompile + public static #returnType ${test}_reference() { + """, + "return ", expressionToken, ";\n", + """ } - """, - // Now add all the test methods. - tests.values().stream().toList(), - """ - } - """ - )); + """ + ); + }); + + for (Expression operation : Operations.PRIMITIVE_OPERATIONS) { + tests.add(withConstantsTemplate.asToken(operation)); + } - // Render the template to a String. - return template.render(); + // Create the test class, which runs all tests. + return TestFrameworkClass.render( + // package and class name. + "p.xyz", "InnerTest", + // Set of imports. + Set.of("compiler.lib.verify.*"), + // classpath, so the Test VM has access to the compiled class files. + comp.getEscapedClassPathOfCompiledClasses(), + // The list of tests. + tests); } } From 17d09c641331308da463c6c59638d0f1cfb996fc Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 26 Aug 2025 17:37:48 +0200 Subject: [PATCH 04/33] add a lot of operators --- .../library/Expression.java | 203 ++++++++++++++- .../library/Operations.java | 236 +++++++++++++++++- 2 files changed, 434 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index f5376552d1382..89a84dbbd2a7b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.ArrayList; +import java.util.Set; /** * TODO: desc @@ -38,27 +39,223 @@ public class Expression { public CodeGenerationDataNameType returnType; public List argumentTypes; private List strings; + public Info info; private Expression(CodeGenerationDataNameType returnType, List argumentTypes, - List strings) { + List strings, + Info info) { if (argumentTypes.size() + 1 != strings.size()) { throw new RuntimeException("Must have one more string than argument."); } this.returnType = returnType; this.argumentTypes = argumentTypes; this.strings = strings; + this.info = info; } /** - * TODO: desc unary + * TODO: desc: used for all sorts of optional info. + */ + public static class Info { + public Set exceptions = Set.of(); + public boolean isResultDeterministic = true; + + public Info() {} + + private Info(Info info) { + this.exceptions = Set.copyOf(info.exceptions); + this.isResultDeterministic = info.isResultDeterministic; + } + + public Info withExceptions(Set exceptions) { + Info info = new Info(this); + info.exceptions = Set.copyOf(exceptions); + return info; + } + + public Info withNondeterministicResult() { + Info info = new Info(this); + info.isResultDeterministic = false; + return info; + } + } + + /** + * Creates a new Espression with 1 arguments. + * + * @param returnType The return type of the expression. + * @param s0 The first string, to be placed before {@code t0}. + * @param t0 The type of the first argument. + * @param s1 The last string, finishing the expression. */ public static Expression make(CodeGenerationDataNameType returnType, String s0, CodeGenerationDataNameType t0, String s1) { - return new Expression(returnType, List.of(t0), List.of(s0, s1)); + return new Expression(returnType, List.of(t0), List.of(s0, s1), new Info()); + } + + /** + * Creates a new Espression with 1 argument. + * + * @param returnType The return type of the expression. + * @param s0 The first string, to be placed before {@code t0}. + * @param t0 The type of the first argument. + * @param s1 The last string, finishing the expression. + * @param info Additional information about the Expression. + */ + public static Expression make(CodeGenerationDataNameType returnType, + String s0, + CodeGenerationDataNameType t0, + String s1, + Info info) { + return new Expression(returnType, List.of(t0), List.of(s0, s1), info); + } + + /** + * Creates a new Espression with 2 arguments. + * + * @param returnType The return type of the expression. + * @param s0 The first string, to be placed before {@code t0}. + * @param t0 The type of the first argument. + * @param s1 The second string, to be placed before {@code t1}. + * @param t1 The type of the second argument. + * @param s2 The last string, finishing the expression. + */ + public static Expression make(CodeGenerationDataNameType returnType, + String s0, + CodeGenerationDataNameType t0, + String s1, + CodeGenerationDataNameType t1, + String s2) { + return new Expression(returnType, List.of(t0, t1), List.of(s0, s1, s2), new Info()); + } + + /** + * Creates a new Espression with 2 arguments. + * + * @param returnType The return type of the expression. + * @param s0 The first string, to be placed before {@code t0}. + * @param t0 The type of the first argument. + * @param s1 The second string, to be placed before {@code t1}. + * @param t1 The type of the second argument. + * @param s2 The last string, finishing the expression. + * @param info Additional information about the Expression. + */ + public static Expression make(CodeGenerationDataNameType returnType, + String s0, + CodeGenerationDataNameType t0, + String s1, + CodeGenerationDataNameType t1, + String s2, + Info info) { + return new Expression(returnType, List.of(t0, t1), List.of(s0, s1, s2), info); + } + + /** + * Creates a new Espression with 3 arguments. + * + * @param returnType The return type of the expression. + * @param s0 The first string, to be placed before {@code t0}. + * @param t0 The type of the first argument. + * @param s1 The second string, to be placed before {@code t1}. + * @param t1 The type of the second argument. + * @param s2 The third string, to be placed before {@code t2}. + * @param t2 The type of the third argument. + * @param s3 The last string, finishing the expression. + */ + public static Expression make(CodeGenerationDataNameType returnType, + String s0, + CodeGenerationDataNameType t0, + String s1, + CodeGenerationDataNameType t1, + String s2, + CodeGenerationDataNameType t2, + String s3) { + return new Expression(returnType, List.of(t0, t1, t2), List.of(s0, s1, s2, s3), new Info()); + } + + /** + * Creates a new Espression with 3 arguments. + * + * @param returnType The return type of the expression. + * @param s0 The first string, to be placed before {@code t0}. + * @param t0 The type of the first argument. + * @param s1 The second string, to be placed before {@code t1}. + * @param t1 The type of the second argument. + * @param s2 The third string, to be placed before {@code t2}. + * @param t2 The type of the third argument. + * @param s3 The last string, finishing the expression. + * @param info Additional information about the Expression. + */ + public static Expression make(CodeGenerationDataNameType returnType, + String s0, + CodeGenerationDataNameType t0, + String s1, + CodeGenerationDataNameType t1, + String s2, + CodeGenerationDataNameType t2, + String s3, + Info info) { + return new Expression(returnType, List.of(t0, t1, t2), List.of(s0, s1, s2, s3), info); + } + + /** + * Creates a new Espression with 4 arguments. + * + * @param returnType The return type of the expression. + * @param s0 The first string, to be placed before {@code t0}. + * @param t0 The type of the first argument. + * @param s1 The second string, to be placed before {@code t1}. + * @param t1 The type of the second argument. + * @param s2 The third string, to be placed before {@code t2}. + * @param t2 The type of the third argument. + * @param s3 The fourth string, to be placed before {@code t3}. + * @param t3 The type of the fourth argument. + * @param s4 The last string, finishing the expression. + */ + public static Expression make(CodeGenerationDataNameType returnType, + String s0, + CodeGenerationDataNameType t0, + String s1, + CodeGenerationDataNameType t1, + String s2, + CodeGenerationDataNameType t2, + String s3, + CodeGenerationDataNameType t3, + String s4) { + return new Expression(returnType, List.of(t0, t1, t2, t3), List.of(s0, s1, s2, s3, s4), new Info()); + } + + /** + * Creates a new Espression with 4 arguments. + * + * @param returnType The return type of the expression. + * @param s0 The first string, to be placed before {@code t0}. + * @param t0 The type of the first argument. + * @param s1 The second string, to be placed before {@code t1}. + * @param t1 The type of the second argument. + * @param s2 The third string, to be placed before {@code t2}. + * @param t2 The type of the third argument. + * @param s3 The fourth string, to be placed before {@code t3}. + * @param t3 The type of the fourth argument. + * @param s4 The last string, finishing the expression. + * @param info Additional information about the Expression. + */ + public static Expression make(CodeGenerationDataNameType returnType, + String s0, + CodeGenerationDataNameType t0, + String s1, + CodeGenerationDataNameType t1, + String s2, + CodeGenerationDataNameType t2, + String s3, + CodeGenerationDataNameType t3, + String s4, + Info info) { + return new Expression(returnType, List.of(t0, t1, t2, t3), List.of(s0, s1, s2, s3, s4), info); } /** diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index a71d11817d65e..7dc40857ba01a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -25,9 +25,16 @@ import java.util.List; import java.util.ArrayList; +import java.util.Set; +import static compiler.lib.template_framework.library.PrimitiveType.BYTES; +import static compiler.lib.template_framework.library.PrimitiveType.SHORTS; +import static compiler.lib.template_framework.library.PrimitiveType.CHARS; import static compiler.lib.template_framework.library.PrimitiveType.INTS; import static compiler.lib.template_framework.library.PrimitiveType.LONGS; +import static compiler.lib.template_framework.library.PrimitiveType.FLOATS; +import static compiler.lib.template_framework.library.PrimitiveType.DOUBLES; +import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; /** * TODO: desc @@ -39,8 +46,233 @@ public final class Operations { private static List generatePrimitiveOperations() { List ops = new ArrayList<>(); - ops.add(Expression.make(INTS, "(int)(", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", INTS, ")")); + Expression.Info withArithmeticException = new Expression.Info().withExceptions(Set.of("ArithmeticException")); + Expression.Info withNondeterministicResult = new Expression.Info().withNondeterministicResult(); + + // ------------ byte ------------- + ops.add(Expression.make(BYTES, "(byte)(", BYTES, ")")); + ops.add(Expression.make(BYTES, "(byte)(", SHORTS, ")")); + ops.add(Expression.make(BYTES, "(byte)(", CHARS, ")")); + ops.add(Expression.make(BYTES, "(byte)(", INTS, ")")); + ops.add(Expression.make(BYTES, "(byte)(", LONGS, ")")); + ops.add(Expression.make(BYTES, "(byte)(", FLOATS, ")")); + ops.add(Expression.make(BYTES, "(byte)(", DOUBLES, ")")); + // There is no cast from boolean. + + ops.add(Expression.make(BYTES, "(", BOOLEANS, "?", BYTES, ":", BYTES, ")")); + + // Arithmetic operations are not performned in byte, but rather promoted to int. + + // ------------ Byte ------------- + ops.add(Expression.make(INTS, "Byte.compare(", BYTES, ", ", BYTES, ")")); + ops.add(Expression.make(INTS, "Byte.compareUnsigned(", BYTES, ", ", BYTES, ")")); + ops.add(Expression.make(INTS, "Byte.toUnsignedInt(", BYTES, ")")); + ops.add(Expression.make(LONGS, "Byte.toUnsignedLong(", BYTES, ")")); + + // ------------ char ------------- + ops.add(Expression.make(CHARS, "(char)(", BYTES, ")")); + ops.add(Expression.make(CHARS, "(char)(", SHORTS, ")")); + ops.add(Expression.make(CHARS, "(char)(", CHARS, ")")); + ops.add(Expression.make(CHARS, "(char)(", INTS, ")")); + ops.add(Expression.make(CHARS, "(char)(", LONGS, ")")); + ops.add(Expression.make(CHARS, "(char)(", FLOATS, ")")); + ops.add(Expression.make(CHARS, "(char)(", DOUBLES, ")")); + // There is no cast from boolean. + + ops.add(Expression.make(CHARS, "(", BOOLEANS, "?", CHARS, ":", CHARS, ")")); + + // Arithmetic operations are not performned in char, but rather promoted to int. + + // ------------ Character ------------- + ops.add(Expression.make(INTS, "Character.compare(", CHARS, ", ", CHARS, ")")); + ops.add(Expression.make(CHARS, "Character.reverseBytes(", CHARS, ")")); + + // ------------ short ------------- + ops.add(Expression.make(SHORTS, "(short)(", BYTES, ")")); + ops.add(Expression.make(SHORTS, "(short)(", SHORTS, ")")); + ops.add(Expression.make(SHORTS, "(short)(", CHARS, ")")); + ops.add(Expression.make(SHORTS, "(short)(", INTS, ")")); + ops.add(Expression.make(SHORTS, "(short)(", LONGS, ")")); + ops.add(Expression.make(SHORTS, "(short)(", FLOATS, ")")); + ops.add(Expression.make(SHORTS, "(short)(", DOUBLES, ")")); + // There is no cast from boolean. + + ops.add(Expression.make(SHORTS, "(", BOOLEANS, "?", SHORTS, ":", SHORTS, ")")); + + // Arithmetic operations are not performned in short, but rather promoted to int. + + // ------------ Short ------------- + ops.add(Expression.make(INTS, "Short.compare(", SHORTS, ", ", SHORTS, ")")); + ops.add(Expression.make(INTS, "Short.compareUnsigned(", SHORTS, ", ", SHORTS, ")")); + ops.add(Expression.make(SHORTS, "Short.reverseBytes(", SHORTS, ")")); + ops.add(Expression.make(INTS, "Short.toUnsignedInt(", SHORTS, ")")); + ops.add(Expression.make(LONGS, "Short.toUnsignedLong(", SHORTS, ")")); + + // ------------ int ------------- + ops.add(Expression.make(INTS, "(int)(", BYTES, ")")); + ops.add(Expression.make(INTS, "(int)(", SHORTS, ")")); + ops.add(Expression.make(INTS, "(int)(", CHARS, ")")); + ops.add(Expression.make(INTS, "(int)(", INTS, ")")); + ops.add(Expression.make(INTS, "(int)(", LONGS, ")")); + ops.add(Expression.make(INTS, "(int)(", FLOATS, ")")); + ops.add(Expression.make(INTS, "(int)(", DOUBLES, ")")); + // There is no cast from boolean. + + ops.add(Expression.make(INTS, "(", BOOLEANS, "?", INTS, ":", INTS, ")")); + + // Arithmetic operators + ops.add(Expression.make(INTS, "(-(", INTS, "))")); + ops.add(Expression.make(INTS, "(~(", INTS, "))")); + ops.add(Expression.make(INTS, "(", INTS, " + ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " - ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " * ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " / ", INTS, ")", withArithmeticException)); + ops.add(Expression.make(INTS, "(", INTS, " % ", INTS, ")", withArithmeticException)); + ops.add(Expression.make(INTS, "(", INTS, " & ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " | ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " ^ ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " << ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " >> ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " >>> ", INTS, ")")); + + // ------------ Integer ------------- + ops.add(Expression.make(INTS, "Integer.bitCount(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.compare(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.compareUnsigned(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.compress(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.divideUnsigned(", INTS, ", ", INTS, ")", withArithmeticException)); + ops.add(Expression.make(INTS, "Integer.expand(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.highestOneBit(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.lowestOneBit(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.max(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.min(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.numberOfLeadingZeros(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.numberOfTrailingZeros(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.remainderUnsigned(", INTS, ", ", INTS, ")", withArithmeticException)); + ops.add(Expression.make(INTS, "Integer.reverse(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.reverseBytes(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.rotateLeft(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.rotateRight(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.signum(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.sum(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(LONGS, "Integer.toUnsignedLong(", INTS, ")")); + + // ------------ long ------------- + ops.add(Expression.make(LONGS, "(long)(", BYTES, ")")); + ops.add(Expression.make(LONGS, "(long)(", SHORTS, ")")); + ops.add(Expression.make(LONGS, "(long)(", CHARS, ")")); + ops.add(Expression.make(LONGS, "(long)(", INTS, ")")); + ops.add(Expression.make(LONGS, "(long)(", LONGS, ")")); + ops.add(Expression.make(LONGS, "(long)(", FLOATS, ")")); + ops.add(Expression.make(LONGS, "(long)(", DOUBLES, ")")); + // There is no cast from boolean. + + ops.add(Expression.make(LONGS, "(", BOOLEANS, "?", LONGS, ":", LONGS, ")")); + + // Arithmetic operators + ops.add(Expression.make(LONGS, "(-(", LONGS, "))")); + ops.add(Expression.make(LONGS, "(~(", LONGS, "))")); + ops.add(Expression.make(LONGS, "(", LONGS, " + ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " - ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " * ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " / ", LONGS, ")", withArithmeticException)); + ops.add(Expression.make(LONGS, "(", LONGS, " % ", LONGS, ")", withArithmeticException)); + ops.add(Expression.make(LONGS, "(", LONGS, " & ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " | ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " ^ ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " << ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " >> ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " >>> ", LONGS, ")")); + + // ------------ Long ------------- + ops.add(Expression.make(INTS, "Long.bitCount(", LONGS, ")")); + ops.add(Expression.make(INTS, "Long.compare(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(INTS, "Long.compareUnsigned(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(LONGS, "Long.compress(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(LONGS, "Long.divideUnsigned(", LONGS, ", ", LONGS, ")", withArithmeticException)); + ops.add(Expression.make(LONGS, "Long.expand(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(LONGS, "Long.highestOneBit(", LONGS, ")")); + ops.add(Expression.make(LONGS, "Long.lowestOneBit(", LONGS, ")")); + ops.add(Expression.make(LONGS, "Long.max(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(LONGS, "Long.min(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(INTS, "Long.numberOfLeadingZeros(", LONGS, ")")); + ops.add(Expression.make(INTS, "Long.numberOfTrailingZeros(", LONGS, ")")); + ops.add(Expression.make(LONGS, "Long.remainderUnsigned(", LONGS, ", ", LONGS, ")", withArithmeticException)); + ops.add(Expression.make(LONGS, "Long.reverse(", LONGS, ")")); + ops.add(Expression.make(LONGS, "Long.reverseBytes(", LONGS, ")")); + ops.add(Expression.make(LONGS, "Long.rotateLeft(", LONGS, ", ", INTS, ")")); + ops.add(Expression.make(LONGS, "Long.rotateRight(", LONGS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Long.signum(", LONGS, ")")); + ops.add(Expression.make(LONGS, "Long.sum(", LONGS, ", ", LONGS, ")")); + + // ------------ float ------------- + ops.add(Expression.make(FLOATS, "(float)(", BYTES, ")")); + ops.add(Expression.make(FLOATS, "(float)(", SHORTS, ")")); + ops.add(Expression.make(FLOATS, "(float)(", CHARS, ")")); + ops.add(Expression.make(FLOATS, "(float)(", INTS, ")")); + ops.add(Expression.make(FLOATS, "(float)(", LONGS, ")")); + ops.add(Expression.make(FLOATS, "(float)(", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(float)(", DOUBLES, ")")); + // There is no cast from boolean. + + ops.add(Expression.make(FLOATS, "(", BOOLEANS, "?", FLOATS, ":", FLOATS, ")")); + + // Arithmetic operators + ops.add(Expression.make(FLOATS, "(-(", FLOATS, "))")); + ops.add(Expression.make(FLOATS, "(", FLOATS, " + ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(", FLOATS, " - ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(", FLOATS, " * ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(", FLOATS, " / ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(", FLOATS, " % ", FLOATS, ")")); + + // ------------ Float ------------- + ops.add(Expression.make(INTS, "Float.compare(", FLOATS, ", ", FLOATS, ")")); + ops.add(Expression.make(INTS, "Float.floatToIntBits(", FLOATS, ")")); + ops.add(Expression.make(INTS, "Float.floatToRawIntBits(", FLOATS, ")", withNondeterministicResult)); + // Note: there are multiple NaN values with different bit representations. + ops.add(Expression.make(FLOATS, "Float.float16ToFloat(", SHORTS, ")")); + ops.add(Expression.make(FLOATS, "Float.intBitsToFloat(", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "Float.isFinite(", FLOATS, ")")); + ops.add(Expression.make(BOOLEANS, "Float.isInfinite(", FLOATS, ")")); + ops.add(Expression.make(BOOLEANS, "Float.isNaN(", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "Float.max(", FLOATS, ", ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "Float.min(", FLOATS, ", ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "Float.sum(", FLOATS, ", ", FLOATS, ")")); + + // ------------ double ------------- + ops.add(Expression.make(DOUBLES, "(double)(", BYTES, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", SHORTS, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", CHARS, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", INTS, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", LONGS, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", FLOATS, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", DOUBLES, ")")); + // There is no cast from boolean. + + ops.add(Expression.make(DOUBLES, "(", BOOLEANS, "?", DOUBLES, ":", DOUBLES, ")")); + + // Arithmetic operators + ops.add(Expression.make(DOUBLES, "(-(", DOUBLES, "))")); + ops.add(Expression.make(DOUBLES, "(", DOUBLES, " + ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "(", DOUBLES, " - ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "(", DOUBLES, " * ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "(", DOUBLES, " / ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "(", DOUBLES, " % ", DOUBLES, ")")); + + // ------------ Double ------------- + ops.add(Expression.make(INTS, "Double.compare(", DOUBLES, ", ", DOUBLES, ")")); + ops.add(Expression.make(LONGS, "Double.doubleToLongBits(", DOUBLES, ")")); + ops.add(Expression.make(LONGS, "Double.doubleToRawLongBits(", DOUBLES, ")", withNondeterministicResult)); + // Note: there are multiple NaN values with different bit representations. + ops.add(Expression.make(DOUBLES, "Double.longBitsToDouble(", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "Double.isFinite(", DOUBLES, ")")); + ops.add(Expression.make(BOOLEANS, "Double.isInfinite(", DOUBLES, ")")); + ops.add(Expression.make(BOOLEANS, "Double.isNaN(", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "Double.max(", DOUBLES, ", ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "Double.min(", DOUBLES, ", ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "Double.sum(", DOUBLES, ", ", DOUBLES, ")")); + + // TODO: Boolean. return ops; } From 213a3447ba6b783049c42037d8ca035d15b99ada Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 27 Aug 2025 08:37:34 +0200 Subject: [PATCH 05/33] more operators --- .../library/Operations.java | 63 +++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 7dc40857ba01a..b13bb96e1e1dd 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -122,12 +122,14 @@ private static List generatePrimitiveOperations() { // Arithmetic operators ops.add(Expression.make(INTS, "(-(", INTS, "))")); - ops.add(Expression.make(INTS, "(~(", INTS, "))")); ops.add(Expression.make(INTS, "(", INTS, " + ", INTS, ")")); ops.add(Expression.make(INTS, "(", INTS, " - ", INTS, ")")); ops.add(Expression.make(INTS, "(", INTS, " * ", INTS, ")")); ops.add(Expression.make(INTS, "(", INTS, " / ", INTS, ")", withArithmeticException)); ops.add(Expression.make(INTS, "(", INTS, " % ", INTS, ")", withArithmeticException)); + + // Bitwise Operators (non short-circuit) + ops.add(Expression.make(INTS, "(~(", INTS, "))")); ops.add(Expression.make(INTS, "(", INTS, " & ", INTS, ")")); ops.add(Expression.make(INTS, "(", INTS, " | ", INTS, ")")); ops.add(Expression.make(INTS, "(", INTS, " ^ ", INTS, ")")); @@ -135,6 +137,14 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(INTS, "(", INTS, " >> ", INTS, ")")); ops.add(Expression.make(INTS, "(", INTS, " >>> ", INTS, ")")); + // Relational / Comparison Operators + ops.add(Expression.make(BOOLEANS, "(", INTS, " == ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " != ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " > ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " < ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " >= ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " <= ", INTS, ")")); + // ------------ Integer ------------- ops.add(Expression.make(INTS, "Integer.bitCount(", INTS, ")")); ops.add(Expression.make(INTS, "Integer.compare(", INTS, ", ", INTS, ")")); @@ -171,12 +181,14 @@ private static List generatePrimitiveOperations() { // Arithmetic operators ops.add(Expression.make(LONGS, "(-(", LONGS, "))")); - ops.add(Expression.make(LONGS, "(~(", LONGS, "))")); ops.add(Expression.make(LONGS, "(", LONGS, " + ", LONGS, ")")); ops.add(Expression.make(LONGS, "(", LONGS, " - ", LONGS, ")")); ops.add(Expression.make(LONGS, "(", LONGS, " * ", LONGS, ")")); ops.add(Expression.make(LONGS, "(", LONGS, " / ", LONGS, ")", withArithmeticException)); ops.add(Expression.make(LONGS, "(", LONGS, " % ", LONGS, ")", withArithmeticException)); + + // Bitwise Operators (non short-circuit) + ops.add(Expression.make(LONGS, "(~(", LONGS, "))")); ops.add(Expression.make(LONGS, "(", LONGS, " & ", LONGS, ")")); ops.add(Expression.make(LONGS, "(", LONGS, " | ", LONGS, ")")); ops.add(Expression.make(LONGS, "(", LONGS, " ^ ", LONGS, ")")); @@ -184,6 +196,14 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(LONGS, "(", LONGS, " >> ", LONGS, ")")); ops.add(Expression.make(LONGS, "(", LONGS, " >>> ", LONGS, ")")); + // Relational / Comparison Operators + ops.add(Expression.make(BOOLEANS, "(", LONGS, " == ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " != ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " > ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " < ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " >= ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " <= ", LONGS, ")")); + // ------------ Long ------------- ops.add(Expression.make(INTS, "Long.bitCount(", LONGS, ")")); ops.add(Expression.make(INTS, "Long.compare(", LONGS, ", ", LONGS, ")")); @@ -225,6 +245,15 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(FLOATS, "(", FLOATS, " / ", FLOATS, ")")); ops.add(Expression.make(FLOATS, "(", FLOATS, " % ", FLOATS, ")")); + + // Relational / Comparison Operators + ops.add(Expression.make(BOOLEANS, "(", FLOATS, " == ", FLOATS, ")")); + ops.add(Expression.make(BOOLEANS, "(", FLOATS, " != ", FLOATS, ")")); + ops.add(Expression.make(BOOLEANS, "(", FLOATS, " > ", FLOATS, ")")); + ops.add(Expression.make(BOOLEANS, "(", FLOATS, " < ", FLOATS, ")")); + ops.add(Expression.make(BOOLEANS, "(", FLOATS, " >= ", FLOATS, ")")); + ops.add(Expression.make(BOOLEANS, "(", FLOATS, " <= ", FLOATS, ")")); + // ------------ Float ------------- ops.add(Expression.make(INTS, "Float.compare(", FLOATS, ", ", FLOATS, ")")); ops.add(Expression.make(INTS, "Float.floatToIntBits(", FLOATS, ")")); @@ -259,6 +288,14 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(DOUBLES, "(", DOUBLES, " / ", DOUBLES, ")")); ops.add(Expression.make(DOUBLES, "(", DOUBLES, " % ", DOUBLES, ")")); + // Relational / Comparison Operators + ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " == ", DOUBLES, ")")); + ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " != ", DOUBLES, ")")); + ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " > ", DOUBLES, ")")); + ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " < ", DOUBLES, ")")); + ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " >= ", DOUBLES, ")")); + ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " <= ", DOUBLES, ")")); + // ------------ Double ------------- ops.add(Expression.make(INTS, "Double.compare(", DOUBLES, ", ", DOUBLES, ")")); ops.add(Expression.make(LONGS, "Double.doubleToLongBits(", DOUBLES, ")")); @@ -272,8 +309,26 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(DOUBLES, "Double.min(", DOUBLES, ", ", DOUBLES, ")")); ops.add(Expression.make(DOUBLES, "Double.sum(", DOUBLES, ", ", DOUBLES, ")")); - // TODO: Boolean. + // ------------ boolean ------------- + // There is no cast to boolean. + + ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, "?", BOOLEANS, ":", BOOLEANS, ")")); + + // There are no boolean arithmetic operators + + // Logical operators + ops.add(Expression.make(BOOLEANS, "(!(", BOOLEANS, "))")); + ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " || ", BOOLEANS, ")")); + ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " && ", BOOLEANS, ")")); + ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " ^ ", BOOLEANS, ")")); + + // ------------ Boolean ------------- + ops.add(Expression.make(INTS, "Boolean.compare(", BOOLEANS, ", ", BOOLEANS, ")")); + ops.add(Expression.make(BOOLEANS, "Boolean.logicalAnd(", BOOLEANS, ", ", BOOLEANS, ")")); + ops.add(Expression.make(BOOLEANS, "Boolean.logicalOr(", BOOLEANS, ", ", BOOLEANS, ")")); + ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")")); - return ops; + // Make sure the list is not modifiable. + return List.copyOf(ops); } } From a7385d782c960fd0d4b1c6993ab47e029795c633 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 27 Aug 2025 10:48:13 +0200 Subject: [PATCH 06/33] more testing and impl --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 116 ++++++++++++++++++ .../library/Expression.java | 81 +++++++++++- .../library/Operations.java | 2 + .../examples/TestExpressions.java | 10 +- .../tests/TestExpression.java | 112 +++++++++++++++++ 5 files changed, 310 insertions(+), 11 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java create mode 100644 test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java new file mode 100644 index 0000000000000..adc9b102acd95 --- /dev/null +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -0,0 +1,116 @@ +/* + * 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 8359412 + * @summary Use the template framework library to generate random expressions. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @compile ../lib/verify/Verify.java + * @run main compiler.igvn.ExpressionFuzzer + */ + +package compiler.igvn; + +import java.util.List; +import java.util.ArrayList; +import java.util.Set; + +import compiler.lib.compile_framework.*; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.body; +import static compiler.lib.template_framework.Template.let; +import compiler.lib.template_framework.library.Expression; +import compiler.lib.template_framework.library.Operations; +import compiler.lib.template_framework.library.TestFrameworkClass; + +public class ExpressionFuzzer { + public static void main(String[] args) { + // Create a new CompileFramework instance. + CompileFramework comp = new CompileFramework(); + + // Add a java source file. + comp.addJavaSourceCode("compiler.igvn.templated.ExpressionFuzzerInnerTest", generate(comp)); + + // Compile the source file. + comp.compile(); + + // compiler.igvn.templated.InnterTest.main(new String[] {}); + comp.invoke("compiler.igvn.templated.ExpressionFuzzerInnerTest", "main", new Object[] {new String[] {}}); + } + + // Generate a Java source file as String + public static String generate(CompileFramework comp) { + // Generate a list of test methods. + List tests = new ArrayList<>(); + + // Create a test method that executes the expression, with constant arguments. + var withConstantsTemplate = Template.make("expression", (Expression expression) -> { + // Create a token: fill the expression with a fixed set of constants. + // We then use the same token with the same constants, once compiled and once not compiled. + TemplateToken expressionToken = expression.asToken(expression.argumentTypes.stream().map(t -> t.con()).toList()); + return body( + let("returnType", expression.returnType), + """ + @Test + public static void $primitiveConTest() { + #returnType v0 = ${primitiveConTest}_compiled(); + #returnType v1 = ${primitiveConTest}_reference(); + Verify.checkEQ(v0, v1); + } + + @DontInline + public static #returnType ${primitiveConTest}_compiled() { + """, + "return ", expressionToken, ";\n", + """ + } + + @DontCompile + public static #returnType ${primitiveConTest}_reference() { + """, + "return ", expressionToken, ";\n", + """ + } + """ + ); + }); + + for (Expression operation : Operations.PRIMITIVE_OPERATIONS) { + tests.add(withConstantsTemplate.asToken(operation)); + } + + // Create the test class, which runs all tests. + return TestFrameworkClass.render( + // package and class name. + "compiler.igvn.templated", "ExpressionFuzzerInnerTest", + // Set of imports. + Set.of("compiler.lib.verify.*"), + // classpath, so the Test VM has access to the compiled class files. + comp.getEscapedClassPathOfCompiledClasses(), + // The list of tests. + tests); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 89a84dbbd2a7b..04707d4f81d4f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -23,22 +23,27 @@ package compiler.lib.template_framework.library; -import compiler.lib.template_framework.Template; -import compiler.lib.template_framework.TemplateToken; -import static compiler.lib.template_framework.Template.body; - import java.util.List; import java.util.ArrayList; import java.util.Set; +import java.util.Random; +import jdk.test.lib.Utils; +import java.util.stream.Stream; +import java.util.stream.Collectors; + +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.body; /** * TODO: desc */ public class Expression { + private static final Random RANDOM = Utils.getRandomInstance(); public CodeGenerationDataNameType returnType; public List argumentTypes; - private List strings; + List strings; public Info info; private Expression(CodeGenerationDataNameType returnType, @@ -69,9 +74,13 @@ private Info(Info info) { this.isResultDeterministic = info.isResultDeterministic; } + /** + * TODO: desc union of exceptions + */ public Info withExceptions(Set exceptions) { Info info = new Info(this); - info.exceptions = Set.copyOf(exceptions); + info.exceptions = Stream.concat(this.exceptions.stream(), exceptions.stream()) + .collect(Collectors.toSet()); return info; } @@ -80,6 +89,14 @@ public Info withNondeterministicResult() { info.isResultDeterministic = false; return info; } + + Info combineWith(Info other) { + Info info = this.withExceptions(other.exceptions); + if (!other.isResultDeterministic) { + info = info.withNondeterministicResult(); + } + return info; + } } /** @@ -281,4 +298,56 @@ public TemplateToken asToken(List arguments) { )); return template.asToken(); } + + /** + * Nests a random expression from {@code nestingExpressions} into a random argument of + * {@code this} expression, ensuring compatibility of argument and return type. + */ + public Expression nestRandomly(List nestingExpressions) { + int slot = RANDOM.nextInt(this.argumentTypes.size()); + CodeGenerationDataNameType slotType = this.argumentTypes.get(slot); + List filtered = nestingExpressions.stream().filter(e -> e.returnType.isSubtypeOf(slotType)).toList(); + int r = RANDOM.nextInt(filtered.size()); + Expression expression = filtered.get(r); + + return this.nest(slot, expression); + } + + /** + * Nests the {@code nestingExpression} into the specified {@code slot} of + * {@code this} expression. + */ + public Expression nest(int slot, Expression nestingExpression) { + if (!nestingExpression.returnType.isSubtypeOf(this.argumentTypes.get(slot))) { + throw new IllegalArgumentException("Cannot nest expressions because of mismatched types."); + } + + List newArgumentTypes = new ArrayList<>(); + List newStrings = new ArrayList<>(); + // s0 t0 s1 [S0 T0 S1 T1 S2] s2 t2 s3 + for (int i = 0; i < slot; i++) { + newStrings.add(this.strings.get(i)); + newArgumentTypes.add(this.argumentTypes.get(i)); + } + newStrings.add(this.strings.get(slot) + + nestingExpression.strings.get(0)); // concat s1 and S0 + newArgumentTypes.add(nestingExpression.argumentTypes.get(0)); + for (int i = 1; i < nestingExpression.argumentTypes.size(); i++) { + newStrings.add(nestingExpression.strings.get(i)); + newArgumentTypes.add(nestingExpression.argumentTypes.get(i)); + } + newStrings.add(nestingExpression.strings.get(nestingExpression.strings.size() - 1) + + this.strings.get(slot + 1)); // concat S2 and s2 + for (int i = slot; i < this.argumentTypes.size(); i++) { + newArgumentTypes.add(this.argumentTypes.get(i)); + newStrings.add(this.strings.get(i+1)); + } + + return new Expression(this.returnType, newArgumentTypes, newStrings, this.info.combineWith(nestingExpression.info)); + } + + //public static Expression nestRandomly(CodeGenerationDataNameType returnType, + // List expressions, + // int numberOfUsedExpression) { + //} } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index b13bb96e1e1dd..46a08f947e689 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -328,6 +328,8 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(BOOLEANS, "Boolean.logicalOr(", BOOLEANS, ", ", BOOLEANS, ")")); ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")")); + // TODO: Math and other classes. + // Make sure the list is not modifiable. return List.copyOf(ops); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java index 7dec3fc902ea7..9d7be693c192c 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java @@ -75,21 +75,21 @@ public static String generate(CompileFramework comp) { let("returnType", expression.returnType), """ @Test - public static void $test() { - #returnType v0 = ${test}_compiled(); - #returnType v1 = ${test}_reference(); + public static void $primitiveConTest() { + #returnType v0 = ${primitiveConTest}_compiled(); + #returnType v1 = ${primitiveConTest}_reference(); Verify.checkEQ(v0, v1); } @DontInline - public static #returnType ${test}_compiled() { + public static #returnType ${primitiveConTest}_compiled() { """, "return ", expressionToken, ";\n", """ } @DontCompile - public static #returnType ${test}_reference() { + public static #returnType ${primitiveConTest}_reference() { """, "return ", expressionToken, ";\n", """ diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java new file mode 100644 index 0000000000000..581c8d4986e8d --- /dev/null +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java @@ -0,0 +1,112 @@ +/* + * 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 8359412 + * @summary Test template generation with Expressions. + * @modules java.base/jdk.internal.misc + * @library /test/lib / + * @run main template_framework.tests.TestExpression + */ + +package template_framework.tests; + +import java.util.List; + +import compiler.lib.template_framework.DataName; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.body; +import compiler.lib.template_framework.library.CodeGenerationDataNameType; +import compiler.lib.template_framework.library.Expression; + +/** + * This tests the use of the {@link Expression} from the template library. This is + * not a tutorial about how to use Expressions, rather we produce deterministic + * output to be able to compare the generated strings to expected strings. + * + * If you are interested in how to use {@link Expression}s, see {@code examples/TestExpressions.java}. + */ +public class TestExpression { + + // We define our own types, so that we can check if subtyping works right. + public record MyType(String name) implements CodeGenerationDataNameType { + @Override + public Object con() { + return "<" + name() + ">"; + } + + @Override + public boolean isSubtypeOf(DataName.Type other) { + return other instanceof MyType(String n) && name().startsWith(n); + } + + @Override + public String toString() { return name(); } + } + private static final MyType myTypeA = new MyType("MyTypeA"); + private static final MyType myTypeA1 = new MyType("MyTypeA1"); + private static final MyType myTypeB = new MyType("MyTypeB"); + + public static void main(String[] args) { + // The following tests all pass, i.e. have no errors during rendering. + testAsToken(); + // TODO: add some + + // The following tests should all fail, with an expected exception and message. + // TODO: add some + } + + public static void testAsToken() { + Expression e1 = Expression.make(myTypeA, "[", myTypeA, "]"); + Expression e2 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeA, "]"); + Expression e3 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeA, ",", myTypeA, "]"); + Expression e4 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeA, ",", myTypeA, ",", myTypeA, "]"); + + var template = Template.make(() -> body( + "xx", e1.asToken(List.of("a")), "yy\n", + "xx", e2.asToken(List.of("a", "b")), "yy\n", + "xx", e3.asToken(List.of("a", "b", "c")), "yy\n", + "xx", e4.asToken(List.of("a", "b", "c", "d")), "yy\n" + )); + + String expected = + """ + xx[a]yy + xx[a,b]yy + xx[a,b,c]yy + xx[a,b,c,d]yy + """; + String code = template.render(); + checkEQ(code, expected); + } + + public static void checkEQ(String code, String expected) { + if (!code.equals(expected)) { + System.out.println("\"" + code + "\""); + System.out.println("\"" + expected + "\""); + throw new RuntimeException("Template rendering mismatch!"); + } + } +} From bf8f7a7bca2702ff4aa3e1a4b1e198c4c3508eba Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 27 Aug 2025 12:14:14 +0200 Subject: [PATCH 07/33] wip --- .../library/Expression.java | 24 ++++- .../tests/TestExpression.java | 92 ++++++++++++++++++- 2 files changed, 110 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 04707d4f81d4f..2da958d5e8960 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -282,7 +282,8 @@ public TemplateToken asToken(List arguments) { if (arguments.size() != argumentTypes.size()) { throw new IllegalArgumentException("Wrong number of arguments:" + " expected: " + argumentTypes.size() + - " but got: " + arguments.size()); + " but got: " + arguments.size() + + " for " + this.toString()); } // List of tokens: interleave strings and arguments. @@ -299,6 +300,25 @@ public TemplateToken asToken(List arguments) { return template.asToken(); } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append("Expression["); + + for (int i = 0; i < this.argumentTypes.size(); i++) { + sb.append("\""); + sb.append(this.strings.get(i)); + sb.append("\", "); + sb.append(this.argumentTypes.get(i).toString()); + sb.append(", "); + } + sb.append("\""); + sb.append(this.strings.get(this.strings.size()-1)); + sb.append("\"]"); + return sb.toString(); + } + /** * Nests a random expression from {@code nestingExpressions} into a random argument of * {@code this} expression, ensuring compatibility of argument and return type. @@ -338,7 +358,7 @@ public Expression nest(int slot, Expression nestingExpression) { } newStrings.add(nestingExpression.strings.get(nestingExpression.strings.size() - 1) + this.strings.get(slot + 1)); // concat S2 and s2 - for (int i = slot; i < this.argumentTypes.size(); i++) { + for (int i = slot+1; i < this.argumentTypes.size(); i++) { newArgumentTypes.add(this.argumentTypes.get(i)); newStrings.add(this.strings.get(i+1)); } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java index 581c8d4986e8d..e206681545fbd 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java @@ -49,6 +49,10 @@ * If you are interested in how to use {@link Expression}s, see {@code examples/TestExpressions.java}. */ public class TestExpression { + // Interface for failing tests. + interface FailingTest { + void run(); + } // We define our own types, so that we can check if subtyping works right. public record MyType(String name) implements CodeGenerationDataNameType { @@ -72,19 +76,27 @@ public boolean isSubtypeOf(DataName.Type other) { public static void main(String[] args) { // The following tests all pass, i.e. have no errors during rendering. testAsToken(); - // TODO: add some + testNest(); + // TODO: add some: info // The following tests should all fail, with an expected exception and message. + expectIllegalArgumentException(() -> testFailingAsToken1(), "Wrong number of arguments: expected: 2 but got: 1"); + expectIllegalArgumentException(() -> testFailingAsToken2(), "Wrong number of arguments: expected: 2 but got: 3"); + expectIllegalArgumentException(() -> testFailingNest1(), "Cannot nest expressions because of mismatched types."); // TODO: add some } public static void testAsToken() { Expression e1 = Expression.make(myTypeA, "[", myTypeA, "]"); - Expression e2 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeA, "]"); - Expression e3 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeA, ",", myTypeA, "]"); - Expression e4 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeA, ",", myTypeA, ",", myTypeA, "]"); + Expression e2 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeB, "]"); + Expression e3 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeB, ",", myTypeA1, "]"); + Expression e4 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeB, ",", myTypeA1, ",", myTypeA, "]"); var template = Template.make(() -> body( + "xx", e1.toString(), "yy\n", + "xx", e2.toString(), "yy\n", + "xx", e3.toString(), "yy\n", + "xx", e4.toString(), "yy\n", "xx", e1.asToken(List.of("a")), "yy\n", "xx", e2.asToken(List.of("a", "b")), "yy\n", "xx", e3.asToken(List.of("a", "b", "c")), "yy\n", @@ -93,6 +105,10 @@ public static void testAsToken() { String expected = """ + xxExpression["[", MyTypeA, "]"]yy + xxExpression["[", MyTypeA, ",", MyTypeB, "]"]yy + xxExpression["[", MyTypeA, ",", MyTypeB, ",", MyTypeA1, "]"]yy + xxExpression["[", MyTypeA, ",", MyTypeB, ",", MyTypeA1, ",", MyTypeA, "]"]yy xx[a]yy xx[a,b]yy xx[a,b,c]yy @@ -102,6 +118,60 @@ public static void testAsToken() { checkEQ(code, expected); } + public static void testFailingAsToken1() { + Expression e1 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeB, "]"); + e1.asToken(List.of("a")); + } + + public static void testFailingAsToken2() { + Expression e1 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeB, "]"); + e1.asToken(List.of("a", "b", "c")); + } + + public static void testNest() { + Expression e1 = Expression.make(myTypeA, "[", myTypeA, "]"); + Expression e2 = Expression.make(myTypeA, "[", myTypeA, ",", myTypeB, "]"); + Expression e3 = Expression.make(myTypeA1, "[", myTypeA, "]"); + Expression e4 = Expression.make(myTypeA, "[", myTypeA, "x", myTypeA, "y", myTypeA, "z", myTypeA, "]"); + Expression e5 = Expression.make(myTypeA, "[", myTypeA, "u", myTypeA, "v", myTypeA, "w", myTypeA, "]"); + + Expression e1e1 = e1.nest(0, e1); + Expression e2e1 = e2.nest(0, e1); + Expression e3e1 = e3.nest(0, e1); + Expression e4e5 = e4.nest(1, e5); + + var template = Template.make(() -> body( + "xx", e1e1.toString(), "yy\n", + "xx", e2e1.toString(), "yy\n", + "xx", e3e1.toString(), "yy\n", + "xx", e4e5.toString(), "yy\n", + "xx", e1e1.asToken(List.of("a")), "yy\n", + "xx", e2e1.asToken(List.of("a", "b")), "yy\n", + "xx", e3e1.asToken(List.of("a")), "yy\n", + "xx", e4e5.asToken(List.of("a", "b", "c", "d", "e", "f", "g")), "yy\n" + )); + + String expected = + """ + xxExpression["[[", MyTypeA, "]]"]yy + xxExpression["[[", MyTypeA, "],", MyTypeB, "]"]yy + xxExpression["[[", MyTypeA, "]]"]yy + xxExpression["[", MyTypeA, "x[", MyTypeA, "u", MyTypeA, "v", MyTypeA, "w", MyTypeA, "]y", MyTypeA, "z", MyTypeA, "]"]yy + xx[[a]]yy + xx[[a],b]yy + xx[[a]]yy + xx[ax[bucvdwe]yfzg]yy + """; + String code = template.render(); + checkEQ(code, expected); + } + + public static void testFailingNest1() { + Expression e1 = Expression.make(myTypeA, "[", myTypeA, "]"); + Expression e2 = Expression.make(myTypeB, "[", myTypeA, "]"); + Expression e1e2 = e1.nest(0, e2); + } + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); @@ -109,4 +179,18 @@ public static void checkEQ(String code, String expected) { throw new RuntimeException("Template rendering mismatch!"); } } + + public static void expectIllegalArgumentException(FailingTest test, String errorPrefix) { + try { + test.run(); + System.out.println("Should have thrown IllegalArgumentException with prefix: " + errorPrefix); + throw new RuntimeException("Should have thrown!"); + } catch(IllegalArgumentException e) { + if (!e.getMessage().startsWith(errorPrefix)) { + System.out.println("Should have thrown with prefix: " + errorPrefix); + System.out.println("got: " + e.getMessage()); + throw new RuntimeException("Prefix mismatch", e); + } + } + } } From 3e5d0ccc565c5537c76c1d936a1516ffe2735a4d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 27 Aug 2025 14:46:32 +0200 Subject: [PATCH 08/33] more nested testing --- .../library/Expression.java | 33 ++++++++++-- .../tests/TestExpression.java | 52 +++++++++++++++++++ 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 2da958d5e8960..4ef90c6369d76 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -319,6 +319,28 @@ public String toString() { return sb.toString(); } + /** + * Create a nested expression with a specified {@code returnType} from a + * set of {@code expressions}. + */ + public static Expression nestRandomly(CodeGenerationDataNameType returnType, + List expressions, + int maxNumberOfUsedExpression) { + List filtered = expressions.stream().filter(e -> e.returnType.isSubtypeOf(returnType)).toList(); + + if (filtered.size() == 0) { + throw new IllegalArgumentException("Found no exception with the specified returnType."); + } + + int r = RANDOM.nextInt(filtered.size()); + Expression expression = filtered.get(r); + + for (int i = 1; i < maxNumberOfUsedExpression; i++) { + expression = expression.nestRandomly(expressions); + } + return expression; + } + /** * Nests a random expression from {@code nestingExpressions} into a random argument of * {@code this} expression, ensuring compatibility of argument and return type. @@ -327,6 +349,12 @@ public Expression nestRandomly(List nestingExpressions) { int slot = RANDOM.nextInt(this.argumentTypes.size()); CodeGenerationDataNameType slotType = this.argumentTypes.get(slot); List filtered = nestingExpressions.stream().filter(e -> e.returnType.isSubtypeOf(slotType)).toList(); + + if (filtered.size() == 0) { + // Found no expression that has a matching returnType. + return this; + } + int r = RANDOM.nextInt(filtered.size()); Expression expression = filtered.get(r); @@ -365,9 +393,4 @@ public Expression nest(int slot, Expression nestingExpression) { return new Expression(this.returnType, newArgumentTypes, newStrings, this.info.combineWith(nestingExpression.info)); } - - //public static Expression nestRandomly(CodeGenerationDataNameType returnType, - // List expressions, - // int numberOfUsedExpression) { - //} } diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java index e206681545fbd..c9b5cd08bd59a 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java @@ -77,6 +77,7 @@ public static void main(String[] args) { // The following tests all pass, i.e. have no errors during rendering. testAsToken(); testNest(); + testNestRandomly(); // TODO: add some: info // The following tests should all fail, with an expected exception and message. @@ -166,6 +167,57 @@ public static void testNest() { checkEQ(code, expected); } + public static void testNestRandomly() { + Expression e1 = Expression.make(myTypeA, "[", myTypeA, "]"); + Expression e2 = Expression.make(myTypeA, "(", myTypeA, ")"); + Expression e3 = Expression.make(myTypeB, "{", myTypeA, "}"); + Expression e4 = Expression.make(myTypeA1, "<", myTypeA, ">"); + Expression e5 = Expression.make(myTypeA, "[", myTypeB, "]"); + + Expression e1e2 = e1.nestRandomly(List.of(e2)); + Expression e1ex = e1.nestRandomly(List.of(e3, e2, e3)); + Expression e1e4 = e1.nestRandomly(List.of(e3, e4, e3)); + Expression e1ey = e1.nestRandomly(List.of(e3, e3)); + + // 5-deep nesting of e1 + Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e2), 5); + // Alternating pattern + Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5); + + var template = Template.make(() -> body( + "xx", e1e2.toString(), "yy\n", + "xx", e1ex.toString(), "yy\n", + "xx", e1e4.toString(), "yy\n", + "xx", e1ey.toString(), "yy\n", + "xx", deep1.toString(), "yy\n", + "xx", deep2.toString(), "yy\n", + "xx", e1e2.asToken(List.of("a")), "yy\n", + "xx", e1ex.asToken(List.of("a")), "yy\n", + "xx", e1e4.asToken(List.of("a")), "yy\n", + "xx", e1ey.asToken(List.of("a")), "yy\n", + "xx", deep1.asToken(List.of("a")), "yy\n", + "xx", deep2.asToken(List.of("a")), "yy\n" + )); + + String expected = + """ + xxExpression["[(", MyTypeA, ")]"]yy + xxExpression["[(", MyTypeA, ")]"]yy + xxExpression["[<", MyTypeA, ">]"]yy + xxExpression["[", MyTypeA, "]"]yy + xxExpression["[[(([", MyTypeA, "]))]]"]yy + xxExpression["[{[{[", MyTypeB, "]}]}]"]yy + xx[(a)]yy + xx[(a)]yy + xx[]yy + xx[a]yy + xx[[(([a]))]]yy + xx[{[{[a]}]}]yy + """; + String code = template.render(); + checkEQ(code, expected); + } + public static void testFailingNest1() { Expression e1 = Expression.make(myTypeA, "[", myTypeA, "]"); Expression e2 = Expression.make(myTypeB, "[", myTypeA, "]"); From cc6914771c6d084054c4543fddc8a4633be3bb9a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 27 Aug 2025 15:14:30 +0200 Subject: [PATCH 09/33] fixes and info tests --- .../tests/TestExpression.java | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java index c9b5cd08bd59a..2dac740dd936a 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/tests/TestExpression.java @@ -33,6 +33,7 @@ package template_framework.tests; import java.util.List; +import java.util.Set; import compiler.lib.template_framework.DataName; import compiler.lib.template_framework.Template; @@ -78,13 +79,12 @@ public static void main(String[] args) { testAsToken(); testNest(); testNestRandomly(); - // TODO: add some: info + testInfo(); // The following tests should all fail, with an expected exception and message. expectIllegalArgumentException(() -> testFailingAsToken1(), "Wrong number of arguments: expected: 2 but got: 1"); expectIllegalArgumentException(() -> testFailingAsToken2(), "Wrong number of arguments: expected: 2 but got: 3"); expectIllegalArgumentException(() -> testFailingNest1(), "Cannot nest expressions because of mismatched types."); - // TODO: add some } public static void testAsToken() { @@ -180,7 +180,7 @@ public static void testNestRandomly() { Expression e1ey = e1.nestRandomly(List.of(e3, e3)); // 5-deep nesting of e1 - Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e2), 5); + Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5); // Alternating pattern Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5); @@ -205,13 +205,13 @@ public static void testNestRandomly() { xxExpression["[(", MyTypeA, ")]"]yy xxExpression["[<", MyTypeA, ">]"]yy xxExpression["[", MyTypeA, "]"]yy - xxExpression["[[(([", MyTypeA, "]))]]"]yy + xxExpression["[[[[[", MyTypeA, "]]]]]"]yy xxExpression["[{[{[", MyTypeB, "]}]}]"]yy xx[(a)]yy xx[(a)]yy xx[]yy xx[a]yy - xx[[(([a]))]]yy + xx[[[[[a]]]]]yy xx[{[{[a]}]}]yy """; String code = template.render(); @@ -224,6 +224,40 @@ public static void testFailingNest1() { Expression e1e2 = e1.nest(0, e2); } + public static void testInfo() { + Expression e1 = Expression.make(myTypeA, "[", myTypeA, "]"); + Expression e2 = Expression.make(myTypeA, "(", myTypeA, ")"); + Expression e3 = Expression.make(myTypeA, "<", myTypeA, ">", new Expression.Info().withExceptions(Set.of("E1"))); + Expression e4 = Expression.make(myTypeA, "+", myTypeA, "-", new Expression.Info().withExceptions(Set.of("E2"))); + Expression e5 = Expression.make(myTypeA, "x", myTypeA, "y", new Expression.Info().withNondeterministicResult()); + Expression e6 = Expression.make(myTypeA, "u", myTypeA, "v", new Expression.Info().withNondeterministicResult()); + checkInfo(e1, Set.of(), true); + checkInfo(e2, Set.of(), true); + checkInfo(e3, Set.of("E1"), true); + checkInfo(e4, Set.of("E2"), true); + checkInfo(e1.nest(0, e2), Set.of(), true); + checkInfo(e2.nest(0, e1), Set.of(), true); + checkInfo(e1.nest(0, e3), Set.of("E1"), true); + checkInfo(e3.nest(0, e1), Set.of("E1"), true); + checkInfo(e3.nest(0, e4), Set.of("E1", "E2"), true); + checkInfo(e4.nest(0, e3), Set.of("E1", "E2"), true); + checkInfo(e5, Set.of(), false); + checkInfo(e6, Set.of(), false); + checkInfo(e1.nest(0, e5), Set.of(), false); + checkInfo(e5.nest(0, e1), Set.of(), false); + checkInfo(e5.nest(0, e6), Set.of(), false); + checkInfo(e4.nest(0, e3).nest(0, e5), Set.of("E1", "E2"), false); + checkInfo(e5.nest(0, e4).nest(0, e3), Set.of("E1", "E2"), false); + checkInfo(e3.nest(0, e5).nest(0, e4), Set.of("E1", "E2"), false); + } + + public static void checkInfo(Expression e, Set exceptions, boolean isResultDeterministic) { + if (!e.info.exceptions.equals(exceptions) || + e.info.isResultDeterministic != isResultDeterministic) { + throw new RuntimeException("Info not as expected."); + } + } + public static void checkEQ(String code, String expected) { if (!code.equals(expected)) { System.out.println("\"" + code + "\""); From c0ef0ef9332b0561041d70128c084a8c68a916a9 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 27 Aug 2025 15:47:33 +0200 Subject: [PATCH 10/33] wip expression fuzzer --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index adc9b102acd95..6e8ff389f151f 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -44,7 +44,9 @@ import static compiler.lib.template_framework.Template.let; import compiler.lib.template_framework.library.Expression; import compiler.lib.template_framework.library.Operations; +import compiler.lib.template_framework.library.PrimitiveType; import compiler.lib.template_framework.library.TestFrameworkClass; +import static compiler.lib.template_framework.library.CodeGenerationDataNameType.PRIMITIVE_TYPES; public class ExpressionFuzzer { public static void main(String[] args) { @@ -66,40 +68,56 @@ public static String generate(CompileFramework comp) { // Generate a list of test methods. List tests = new ArrayList<>(); - // Create a test method that executes the expression, with constant arguments. - var withConstantsTemplate = Template.make("expression", (Expression expression) -> { - // Create a token: fill the expression with a fixed set of constants. - // We then use the same token with the same constants, once compiled and once not compiled. - TemplateToken expressionToken = expression.asToken(expression.argumentTypes.stream().map(t -> t.con()).toList()); + + var bodyTemplate = Template.make("expression", "arguments", (Expression expression, List arguments) -> body( + """ + try { + """, + "return ", expression.asToken(arguments), ";\n", + expression.info.exceptions.stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), + """ + } finally { + // Just so that javac is happy if there are no exceptions to catch. + } + """ + )); + + var testTemplate = Template.make("expression", (Expression expression) -> { + // Fix the arguments for both the compiled and reference method. + List arguments = expression.argumentTypes.stream().map(t -> t.con()).toList(); return body( - let("returnType", expression.returnType), """ @Test public static void $primitiveConTest() { - #returnType v0 = ${primitiveConTest}_compiled(); - #returnType v1 = ${primitiveConTest}_reference(); + Object v0 = ${primitiveConTest}_compiled(); + Object v1 = ${primitiveConTest}_reference(); Verify.checkEQ(v0, v1); } @DontInline - public static #returnType ${primitiveConTest}_compiled() { + public static Object ${primitiveConTest}_compiled() { """, - "return ", expressionToken, ";\n", + bodyTemplate.asToken(expression, arguments), """ } @DontCompile - public static #returnType ${primitiveConTest}_reference() { + public static Object ${primitiveConTest}_reference() { """, - "return ", expressionToken, ";\n", + bodyTemplate.asToken(expression, arguments), """ } """ ); }); - for (Expression operation : Operations.PRIMITIVE_OPERATIONS) { - tests.add(withConstantsTemplate.asToken(operation)); + for (PrimitiveType type : PRIMITIVE_TYPES) { + for (int i = 0; i < 10; i++) { + Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, 10); + tests.add(testTemplate.asToken(expression)); + } } // Create the test class, which runs all tests. From e24bd843d4c06c7bc5f139f8566584ba6c8b1ea9 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 27 Aug 2025 16:10:08 +0200 Subject: [PATCH 11/33] some todos --- test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 6e8ff389f151f..2ff4b3353b00c 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -68,7 +68,6 @@ public static String generate(CompileFramework comp) { // Generate a list of test methods. List tests = new ArrayList<>(); - var bodyTemplate = Template.make("expression", "arguments", (Expression expression, List arguments) -> body( """ try { @@ -87,6 +86,14 @@ public static String generate(CompileFramework comp) { var testTemplate = Template.make("expression", (Expression expression) -> { // Fix the arguments for both the compiled and reference method. List arguments = expression.argumentTypes.stream().map(t -> t.con()).toList(); + // TODO: fix with Values + // We need to have a way to get values with type constraints + // And we need to "load" them once in compiled and once in reference + // Could do mix of method args, fields and constants. + // And even computations in the method? Not sure + // Args and fields can then be "constrained" in the method before use. + // And the result should be returned, but also some "checksum" to + // detect range and bits. return body( """ @Test @@ -115,6 +122,7 @@ public static String generate(CompileFramework comp) { for (PrimitiveType type : PRIMITIVE_TYPES) { for (int i = 0; i < 10; i++) { + // TODO: have a range of depths? Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, 10); tests.add(testTemplate.asToken(expression)); } From f62bd45f184e1f7970567a452624b61c85b7638f Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 28 Aug 2025 08:36:45 +0200 Subject: [PATCH 12/33] route via MethodArgument --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 2ff4b3353b00c..62e09897e5165 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -36,12 +36,17 @@ import java.util.List; import java.util.ArrayList; import java.util.Set; +import java.util.Random; +import jdk.test.lib.Utils; +import java.util.stream.Collectors; import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; import compiler.lib.template_framework.TemplateToken; import static compiler.lib.template_framework.Template.body; import static compiler.lib.template_framework.Template.let; +import static compiler.lib.template_framework.Template.$; +import compiler.lib.template_framework.library.CodeGenerationDataNameType; import compiler.lib.template_framework.library.Expression; import compiler.lib.template_framework.library.Operations; import compiler.lib.template_framework.library.PrimitiveType; @@ -49,6 +54,10 @@ import static compiler.lib.template_framework.library.CodeGenerationDataNameType.PRIMITIVE_TYPES; public class ExpressionFuzzer { + private static final Random RANDOM = Utils.getRandomInstance(); + + public static record MethodArgument(String name, CodeGenerationDataNameType type) {} + public static void main(String[] args) { // Create a new CompileFramework instance. CompileFramework comp = new CompileFramework(); @@ -83,9 +92,27 @@ public static String generate(CompileFramework comp) { """ )); + var valueTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> body( + "#type #name = ", type.con(), ";\n" + // TODO: randomize! + )); + var testTemplate = Template.make("expression", (Expression expression) -> { // Fix the arguments for both the compiled and reference method. - List arguments = expression.argumentTypes.stream().map(t -> t.con()).toList(); + List methodArguments = new ArrayList<>(); + List expressionArguments = new ArrayList<>(); + for (CodeGenerationDataNameType type : expression.argumentTypes) { + switch (RANDOM.nextInt(2)) { + case 0 -> { + String name = $("arg" + methodArguments.size()); + methodArguments.add(new MethodArgument(name, type)); + expressionArguments.add(name); + } + default -> { + expressionArguments.add(type.con()); + } + } + } // TODO: fix with Values // We need to have a way to get values with type constraints // And we need to "load" them once in compiled and once in reference @@ -95,25 +122,32 @@ public static String generate(CompileFramework comp) { // And the result should be returned, but also some "checksum" to // detect range and bits. return body( + let("methodArguments", + methodArguments.stream().map(ma -> ma.name).collect(Collectors.joining(", "))), + let("methodArgumentsWithTypes", + methodArguments.stream().map(ma -> ma.type + " " + ma.name).collect(Collectors.joining(", "))), """ @Test public static void $primitiveConTest() { - Object v0 = ${primitiveConTest}_compiled(); - Object v1 = ${primitiveConTest}_reference(); + """, + methodArguments.stream().map(ma -> valueTemplate.asToken(ma.name, ma.type)).toList(), + """ + Object v0 = ${primitiveConTest}_compiled(#methodArguments); + Object v1 = ${primitiveConTest}_reference(#methodArguments); Verify.checkEQ(v0, v1); } @DontInline - public static Object ${primitiveConTest}_compiled() { + public static Object ${primitiveConTest}_compiled(#methodArgumentsWithTypes) { """, - bodyTemplate.asToken(expression, arguments), + bodyTemplate.asToken(expression, expressionArguments), """ } @DontCompile - public static Object ${primitiveConTest}_reference() { + public static Object ${primitiveConTest}_reference(#methodArgumentsWithTypes) { """, - bodyTemplate.asToken(expression, arguments), + bodyTemplate.asToken(expression, expressionArguments), """ } """ From c0482d6d0bbca826360f7e9079fef1f1a78999cf Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 28 Aug 2025 16:53:28 +0200 Subject: [PATCH 13/33] checksum --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 62e09897e5165..3e9e54959ec6a 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -39,6 +39,7 @@ import java.util.Random; import jdk.test.lib.Utils; import java.util.stream.Collectors; +import java.util.stream.IntStream; import compiler.lib.compile_framework.*; import compiler.lib.template_framework.Template; @@ -77,11 +78,12 @@ public static String generate(CompileFramework comp) { // Generate a list of test methods. List tests = new ArrayList<>(); - var bodyTemplate = Template.make("expression", "arguments", (Expression expression, List arguments) -> body( + var bodyTemplate = Template.make("expression", "arguments", "checksum", (Expression expression, List arguments, String checksum) -> body( """ try { """, - "return ", expression.asToken(arguments), ";\n", + "var val = ", expression.asToken(arguments), ";\n", + "return #checksum(val);\n", expression.info.exceptions.stream().map(exception -> "} catch (" + exception + " e) { return e;\n" ).toList(), @@ -122,6 +124,7 @@ public static String generate(CompileFramework comp) { // And the result should be returned, but also some "checksum" to // detect range and bits. return body( + let("returnType", expression.returnType), let("methodArguments", methodArguments.stream().map(ma -> ma.name).collect(Collectors.joining(", "))), let("methodArgumentsWithTypes", @@ -140,14 +143,40 @@ public static String generate(CompileFramework comp) { @DontInline public static Object ${primitiveConTest}_compiled(#methodArgumentsWithTypes) { """, - bodyTemplate.asToken(expression, expressionArguments), + bodyTemplate.asToken(expression, expressionArguments, $("checksum")), """ } @DontCompile public static Object ${primitiveConTest}_reference(#methodArgumentsWithTypes) { """, - bodyTemplate.asToken(expression, expressionArguments), + bodyTemplate.asToken(expression, expressionArguments, $("checksum")), + """ + } + + @ForceInline + public static Object $checksum(#returnType val) { + """, + "return new Object[] {", + switch(expression.returnType.name()) { + // The integral values have signed/unsigned ranges and known bits. + // Return val, but also some range and bits tests to see if those + // ranges and bits are correct. + case "byte", "short", "char", "int", "long" -> + List.of("val", + IntStream.range(0, 20).mapToObj(i -> + // Generate a test like: + // val < 5 + // val & 16 + List.of(", val", List.of("<", ">", "<=", ">=", "&").get(i % 5), expression.returnType.con()) + ).toList()); + // Float/Double have no range, just return the value: + case "float", "double" -> "val"; + // Check if the boolean constant folded: + case "boolean" -> "val, val == true, val == false"; + default -> throw new RuntimeException("should only be primitive types"); + } + , "};\n", """ } """ @@ -156,8 +185,8 @@ public static String generate(CompileFramework comp) { for (PrimitiveType type : PRIMITIVE_TYPES) { for (int i = 0; i < 10; i++) { - // TODO: have a range of depths? - Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, 10); + int depth = RANDOM.nextInt(1, 20); + Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth); tests.add(testTemplate.asToken(expression)); } } From fb3c93ae930fa8fd576dddbaeca7e6afce333f10 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 4 Sep 2025 09:03:48 +0200 Subject: [PATCH 14/33] wip rng --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 4 ++++ .../library/PrimitiveType.java | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 3e9e54959ec6a..8a205a09d527f 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -78,6 +78,9 @@ public static String generate(CompileFramework comp) { // Generate a list of test methods. List tests = new ArrayList<>(); + // We are going to use some random numbers in our tests, so import some good methods for that. + tests.add(PrimitiveType.generateRandomNumberGeneratorMethods()); + var bodyTemplate = Template.make("expression", "arguments", "checksum", (Expression expression, List arguments, String checksum) -> body( """ try { @@ -134,6 +137,7 @@ public static String generate(CompileFramework comp) { public static void $primitiveConTest() { """, methodArguments.stream().map(ma -> valueTemplate.asToken(ma.name, ma.type)).toList(), + // TODO: there should be a failure with indeterministic results with checkEQ """ Object v0 = ${primitiveConTest}_compiled(#methodArguments); Object v1 = ${primitiveConTest}_reference(#methodArguments); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index 3bf6c7f62886e..addd50dec741a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -31,6 +31,9 @@ import compiler.lib.generators.RestrictableGenerator; import compiler.lib.template_framework.DataName; +import compiler.lib.template_framework.Template; +import compiler.lib.template_framework.TemplateToken; +import static compiler.lib.template_framework.Template.body; /** * The {@link PrimitiveType} models Java's primitive types, and provides a set @@ -148,4 +151,21 @@ public boolean isFloating() { case FLOAT, DOUBLE -> true; }; } + + /** + * Generates a set of random number generator methods, for single values + * and array filling. + * + * TODO: required imports + * + * @return a TemplateToken that holds all the generated methods. + */ + public static TemplateToken generateRandomNumberGeneratorMethods() { + var template = Template.make(() -> body( + """ + // TODO: methods + """ + )); + return template.asToken(); + }; } From 1d583584a45f18423967c7fdca19a9c29e81df6d Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 5 Sep 2025 17:14:45 +0200 Subject: [PATCH 15/33] LibraryRNG --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 14 +++- .../library/PrimitiveType.java | 82 +++++++++++++++++-- 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 8a205a09d527f..f715905de3fc1 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -79,7 +79,7 @@ public static String generate(CompileFramework comp) { List tests = new ArrayList<>(); // We are going to use some random numbers in our tests, so import some good methods for that. - tests.add(PrimitiveType.generateRandomNumberGeneratorMethods()); + tests.add(PrimitiveType.generateLibraryRNG()); var bodyTemplate = Template.make("expression", "arguments", "checksum", (Expression expression, List arguments, String checksum) -> body( """ @@ -98,8 +98,10 @@ public static String generate(CompileFramework comp) { )); var valueTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> body( - "#type #name = ", type.con(), ";\n" - // TODO: randomize! + //"#type #name = ", type.con(), ";\n" + "#type #name = ", + (type instanceof PrimitiveType pt) ? pt.callLibraryRNG() : type.con(), + ";\n" )); var testTemplate = Template.make("expression", (Expression expression) -> { @@ -135,6 +137,7 @@ public static String generate(CompileFramework comp) { """ @Test public static void $primitiveConTest() { + // In each iteration, generate new random values for the method arguments. """, methodArguments.stream().map(ma -> valueTemplate.asToken(ma.name, ma.type)).toList(), // TODO: there should be a failure with indeterministic results with checkEQ @@ -200,7 +203,10 @@ public static String generate(CompileFramework comp) { // package and class name. "compiler.igvn.templated", "ExpressionFuzzerInnerTest", // Set of imports. - Set.of("compiler.lib.verify.*"), + Set.of("compiler.lib.verify.*", + "java.util.Random", + "jdk.test.lib.Utils", + "compiler.lib.generators.*"), // classpath, so the Test VM has access to the compiled class files. comp.getEscapedClassPathOfCompiledClasses(), // The list of tests. diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java index addd50dec741a..46a9d5bbabe67 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/PrimitiveType.java @@ -153,17 +153,87 @@ public boolean isFloating() { } /** - * Generates a set of random number generator methods, for single values - * and array filling. + * Calls the corresponding pseudo random number generator from + * {@link #generateLibraryRNG}, for the given type. Accordingly, + * one must generate {@link #generateLibraryRNG} into the same + * test if one wants to use this method. * - * TODO: required imports + * Note: if you simply need a compile time constant, then please + * use {@link #con} instead. * - * @return a TemplateToken that holds all the generated methods. + * @return the token representing the method call to obtain a + * random value for the given type at runtime. */ - public static TemplateToken generateRandomNumberGeneratorMethods() { + public Object callLibraryRNG() { + return switch (kind) { + case BYTE -> "LibraryRNG.nextByte()"; + case SHORT -> "LibraryRNG.nextShort()"; + case CHAR -> "LibraryRNG.nextChar()"; + case INT -> "LibraryRNG.nextInt()"; + case LONG -> "LibraryRNG.nextLong()"; + case FLOAT -> "LibraryRNG.nextFloat()"; + case DOUBLE -> "LibraryRNG.nextDouble()"; + case BOOLEAN -> "LibraryRNG.nextBoolean()"; + }; + } + + /** + * Generates the {@code LibraryRNG} class, which makes a set of pseudo + * random number generators available, wrapping {@link Generators}. This + * is supposed to be used in tandem with {@link #callLibraryRNG}. + * + * Note: you must ensure that all required imports are performed: + * {@code java.util.Random} + * {@code jdk.test.lib.Utils} + * {@code compiler.lib.generators.*} + * + * @return a TemplateToken that holds all the {@code LibraryRNG} class. + */ + public static TemplateToken generateLibraryRNG() { var template = Template.make(() -> body( """ - // TODO: methods + public static class LibraryRNG { + private static final Random RANDOM = Utils.getRandomInstance(); + private static final RestrictableGenerator GEN_BYTE = Generators.G.safeRestrict(Generators.G.ints(), Byte.MIN_VALUE, Byte.MAX_VALUE); + private static final RestrictableGenerator GEN_CHAR = Generators.G.safeRestrict(Generators.G.ints(), Character.MIN_VALUE, Character.MAX_VALUE); + private static final RestrictableGenerator GEN_SHORT = Generators.G.safeRestrict(Generators.G.ints(), Short.MIN_VALUE, Short.MAX_VALUE); + private static final RestrictableGenerator GEN_INT = Generators.G.ints(); + private static final RestrictableGenerator GEN_LONG = Generators.G.longs(); + private static final Generator GEN_DOUBLE = Generators.G.doubles(); + private static final Generator GEN_FLOAT = Generators.G.floats(); + + public static byte nextByte() { + return GEN_BYTE.next().byteValue(); + } + + public static short nextShort() { + return GEN_SHORT.next().shortValue(); + } + + public static char nextChar() { + return (char)GEN_CHAR.next().intValue(); + } + + public static int nextInt() { + return GEN_INT.next(); + } + + public static long nextLong() { + return GEN_LONG.next(); + } + + public static float nextFloat() { + return GEN_FLOAT.next(); + } + + public static double nextDouble() { + return GEN_DOUBLE.next(); + } + + public static boolean nextBoolean() { + return RANDOM.nextBoolean(); + } + } """ )); return template.asToken(); From 3356f9aeb092a67d24647fef116c75dbf8537b1e Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 5 Sep 2025 17:24:23 +0200 Subject: [PATCH 16/33] handle non-deterministic results --- test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index f715905de3fc1..dd7472e448df1 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -140,11 +140,12 @@ public static String generate(CompileFramework comp) { // In each iteration, generate new random values for the method arguments. """, methodArguments.stream().map(ma -> valueTemplate.asToken(ma.name, ma.type)).toList(), - // TODO: there should be a failure with indeterministic results with checkEQ """ Object v0 = ${primitiveConTest}_compiled(#methodArguments); Object v1 = ${primitiveConTest}_reference(#methodArguments); - Verify.checkEQ(v0, v1); + """, + expression.info.isResultDeterministic ? "Verify.checkEQ(v0, v1);\n" : "// could fail - don't verify.\n", + """ } @DontInline From d6148619c56027c650645278d0a0cc5a939330be Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Fri, 5 Sep 2025 17:40:15 +0200 Subject: [PATCH 17/33] test refactoring --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 88 +++++++++++-------- 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index dd7472e448df1..b2bfcf74a8045 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -81,20 +81,55 @@ public static String generate(CompileFramework comp) { // We are going to use some random numbers in our tests, so import some good methods for that. tests.add(PrimitiveType.generateLibraryRNG()); + // Create the body for the test. We use it twice: compiled and reference. var bodyTemplate = Template.make("expression", "arguments", "checksum", (Expression expression, List arguments, String checksum) -> body( - """ - try { - """, - "var val = ", expression.asToken(arguments), ";\n", - "return #checksum(val);\n", - expression.info.exceptions.stream().map(exception -> - "} catch (" + exception + " e) { return e;\n" - ).toList(), - """ - } finally { - // Just so that javac is happy if there are no exceptions to catch. - } - """ + """ + try { + """, + "var val = ", expression.asToken(arguments), ";\n", + "return #checksum(val);\n", + expression.info.exceptions.stream().map(exception -> + "} catch (" + exception + " e) { return e;\n" + ).toList(), + """ + } finally { + // Just so that javac is happy if there are no exceptions to catch. + } + """ + )); + + // Checksum method: returns not just the value, but also does some range / bit checks. + // This gives us enhanced verification on the range / bits of the result type. + var checksumTemplate = Template.make("expression", "checksum", (Expression expression, String checksum) -> body( + let("returnType", expression.returnType), + """ + @ForceInline + public static Object #checksum(#returnType val) { + """, + "return new Object[] {", + switch(expression.returnType.name()) { + // The integral values have signed/unsigned ranges and known bits. + // Return val, but also some range and bits tests to see if those + // ranges and bits are correct. + case "byte", "short", "char", "int", "long" -> + List.of("val", + IntStream.range(0, 20).mapToObj(i -> + // Generate a test like: + // val < 5 + // val & 16 + List.of(", val", List.of("<", ">", "<=", ">=", "&").get(i % 5), expression.returnType.con()) + // TODO: unsigned! + ).toList()); + // Float/Double have no range, just return the value: + case "float", "double" -> "val"; + // Check if the boolean constant folded: + case "boolean" -> "val, val == true, val == false"; + default -> throw new RuntimeException("should only be primitive types"); + } + , "};\n", + """ + } + """ )); var valueTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> body( @@ -129,7 +164,6 @@ public static String generate(CompileFramework comp) { // And the result should be returned, but also some "checksum" to // detect range and bits. return body( - let("returnType", expression.returnType), let("methodArguments", methodArguments.stream().map(ma -> ma.name).collect(Collectors.joining(", "))), let("methodArgumentsWithTypes", @@ -162,32 +196,8 @@ public static String generate(CompileFramework comp) { """ } - @ForceInline - public static Object $checksum(#returnType val) { """, - "return new Object[] {", - switch(expression.returnType.name()) { - // The integral values have signed/unsigned ranges and known bits. - // Return val, but also some range and bits tests to see if those - // ranges and bits are correct. - case "byte", "short", "char", "int", "long" -> - List.of("val", - IntStream.range(0, 20).mapToObj(i -> - // Generate a test like: - // val < 5 - // val & 16 - List.of(", val", List.of("<", ">", "<=", ">=", "&").get(i % 5), expression.returnType.con()) - ).toList()); - // Float/Double have no range, just return the value: - case "float", "double" -> "val"; - // Check if the boolean constant folded: - case "boolean" -> "val, val == true, val == false"; - default -> throw new RuntimeException("should only be primitive types"); - } - , "};\n", - """ - } - """ + checksumTemplate.asToken(expression, $("checksum")) ); }); From 37d4ae72b4342270b9dc682eef5d40b3a89640b6 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Tue, 9 Sep 2025 19:43:14 +0200 Subject: [PATCH 18/33] wip test cmp --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index b2bfcf74a8045..810ecb41b6df7 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.Set; import java.util.Random; +import java.util.Collections; import jdk.test.lib.Utils; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -58,6 +59,7 @@ public class ExpressionFuzzer { private static final Random RANDOM = Utils.getRandomInstance(); public static record MethodArgument(String name, CodeGenerationDataNameType type) {} + public static record StringPair(String s0, String s1) {} public static void main(String[] args) { // Create a new CompileFramework instance. @@ -98,6 +100,57 @@ public static String generate(CompileFramework comp) { """ )); + List unsignedCmp = List.of( + new StringPair("(val < ", ")"), + new StringPair("(val > ", ")"), + new StringPair("(val >= ", ")"), + new StringPair("(val <= ", ")"), + new StringPair("(val != ", ")"), + new StringPair("(val == ", ")"), + new StringPair("(val & ", ")") // Extract bits + ); + + List intCmp = List.of( + new StringPair("(val < ", ")"), + new StringPair("(val > ", ")"), + new StringPair("(val >= ", ")"), + new StringPair("(val <= ", ")"), + new StringPair("(val != ", ")"), + new StringPair("(val == ", ")"), + new StringPair("(val & ", ")"), // Extract bits + new StringPair("(Integer.compareUnsigned(val, ", ") > 0)"), + new StringPair("(Integer.compareUnsigned(val, ", ") < 0)"), + new StringPair("(Integer.compareUnsigned(val, ", ") >= 0)"), + new StringPair("(Integer.compareUnsigned(val, ", ") <= 0)") + ); + + List longCmp = List.of( + new StringPair("(val < ", ")"), + new StringPair("(val > ", ")"), + new StringPair("(val >= ", ")"), + new StringPair("(val <= ", ")"), + new StringPair("(val != ", ")"), + new StringPair("(val == ", ")"), + new StringPair("(val & ", ")"), // Extract bits + new StringPair("(Long.compareUnsigned(val, ", ") > 0)"), + new StringPair("(Long.compareUnsigned(val, ", ") < 0)"), + new StringPair("(Long.compareUnsigned(val, ", ") >= 0)"), + new StringPair("(Long.compareUnsigned(val, ", ") <= 0)") + ); + + var integralCmpTemplate = Template.make("type", (CodeGenerationDataNameType type) -> { + List cmps = switch(type.name()) { + case "char" -> unsignedCmp; + case "byte", "short", "int" -> intCmp; + case "long" -> longCmp; + default -> throw new RuntimeException("not handled: " + type.name()); + }; + StringPair cmp = cmps.get(RANDOM.nextInt(cmps.size())); + return body( + ", ", cmp.s0(), type.con(), cmp.s1() + ); + }); + // Checksum method: returns not just the value, but also does some range / bit checks. // This gives us enhanced verification on the range / bits of the result type. var checksumTemplate = Template.make("expression", "checksum", (Expression expression, String checksum) -> body( @@ -112,14 +165,7 @@ public static String generate(CompileFramework comp) { // Return val, but also some range and bits tests to see if those // ranges and bits are correct. case "byte", "short", "char", "int", "long" -> - List.of("val", - IntStream.range(0, 20).mapToObj(i -> - // Generate a test like: - // val < 5 - // val & 16 - List.of(", val", List.of("<", ">", "<=", ">=", "&").get(i % 5), expression.returnType.con()) - // TODO: unsigned! - ).toList()); + List.of("val", Collections.nCopies(20, integralCmpTemplate.asToken(expression.returnType))); // Float/Double have no range, just return the value: case "float", "double" -> "val"; // Check if the boolean constant folded: From aacfc37258d5955c138319ef21b13856bb4be874 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 10 Sep 2025 09:04:37 +0200 Subject: [PATCH 19/33] add more comments --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 810ecb41b6df7..adda298258f3c 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -55,6 +55,14 @@ import compiler.lib.template_framework.library.TestFrameworkClass; import static compiler.lib.template_framework.library.CodeGenerationDataNameType.PRIMITIVE_TYPES; +// We generate random Expressions from primitive type operators. +// +// The goal is to generate random inputs with constrained TypeInt / TypeLong ranges / KnownBits, +// and then verify the output value, ranges and bits. +// +// Should this test fail and make a lot of noise in the CI, you have two choices: +// - Problem-list this test: but other tests may also use the same broken operators. +// - Temporarily remove the operator from {@code Operations.PRIMITIVE_OPERATIONS}. public class ExpressionFuzzer { private static final Random RANDOM = Utils.getRandomInstance(); @@ -84,6 +92,7 @@ public static String generate(CompileFramework comp) { tests.add(PrimitiveType.generateLibraryRNG()); // Create the body for the test. We use it twice: compiled and reference. + // Execute the expression and catch expected Exceptions. var bodyTemplate = Template.make("expression", "arguments", "checksum", (Expression expression, List arguments, String checksum) -> body( """ try { @@ -100,6 +109,12 @@ public static String generate(CompileFramework comp) { """ )); + // Machinery for the "checksum" method. + // + // We want to do output verification. We don't just want to check if the output value is correct, + // but also if the signed/unsigned/KnownBits are correct of the TypeInt and TypeLong. For this, + // we add some comparisons. If we get the ranges/bits wrong (too tight), then the comparisons + // can wrongly constant fold, and we can detect that in the output array. List unsignedCmp = List.of( new StringPair("(val < ", ")"), new StringPair("(val > ", ")"), @@ -178,6 +193,8 @@ public static String generate(CompileFramework comp) { """ )); + // We need to prepare some random values to pass into the test method. We generate the values + // once, and pass the same values into both the compiled and reference method. var valueTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> body( //"#type #name = ", type.con(), ";\n" "#type #name = ", @@ -185,6 +202,13 @@ public static String generate(CompileFramework comp) { ";\n" )); + // The template that generates the whole test machinery needed for testing a given expression. + // Generates: + // - @Test method: generate arguments and call compiled and reference test with it. + // result verification (only if the result is known to be deterministic). + // + // - instantiate compiled and reference test methods. + // - instantiate checksum method. var testTemplate = Template.make("expression", (Expression expression) -> { // Fix the arguments for both the compiled and reference method. List methodArguments = new ArrayList<>(); @@ -247,8 +271,10 @@ public static String generate(CompileFramework comp) { ); }); + // Generate expressions with the primitive types. for (PrimitiveType type : PRIMITIVE_TYPES) { for (int i = 0; i < 10; i++) { + // The depth determines roughly how many operations are going to be used in the expression. int depth = RANDOM.nextInt(1, 20); Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth); tests.add(testTemplate.asToken(expression)); From 8ec054dcd0c96e180b9464bff3022a94904d5389 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 10 Sep 2025 09:47:29 +0200 Subject: [PATCH 20/33] wip constraints --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index adda298258f3c..7275ac24de5fa 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -202,15 +202,50 @@ public static String generate(CompileFramework comp) { ";\n" )); + // At the beginning of the compiled and reference test methods we receive the arguments, + // which have their full bottom_type (e.g. TypeInt: int). We now constrain the ranges and + // bits, for the types that allow it. + // + // To ensure that both the compiled and reference method use the same constraint, we put + // the computation in a ForceInline method. + var constrainValueMethodTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> body( + """ + @ForceInline + public static #type constrain_#name(#type v) { + """, + switch(type.name()) { + // These currently have no type ranges / bits. + // Booleans do have an int-range, but restricting it would just make it constant, which + // is not very useful: we would like to keep it variable here. We already mix in variable + // arguments and constants in the testTemplate. + case "boolean", "float", "double" -> "return v;\n"; + case "byte", "short", "char", "int", "long" -> "return v;\n"; + // TODO: constraintIntegralValueTemplate.asToken(name, type))); + default -> throw new RuntimeException("should only be primitive types"); + }, + """ + } + """ + )); + + var constrainValueTemplate = Template.make("name", (String name) -> body( + """ + #name = constrain_#name(#name); + """ + )); + // The template that generates the whole test machinery needed for testing a given expression. // Generates: // - @Test method: generate arguments and call compiled and reference test with it. // result verification (only if the result is known to be deterministic). // // - instantiate compiled and reference test methods. - // - instantiate checksum method. + // - instantiate value constraint methods (constrains test method arguments types). + // - instantiate checksum method (summarizes value and bounds/bit checks). var testTemplate = Template.make("expression", (Expression expression) -> { // Fix the arguments for both the compiled and reference method. + // We have a mix of variable and constant inputs to the expression. + // The variable inputs are passed as method arguments to the test methods. List methodArguments = new ArrayList<>(); List expressionArguments = new ArrayList<>(); for (CodeGenerationDataNameType type : expression.argumentTypes) { @@ -255,6 +290,7 @@ public static String generate(CompileFramework comp) { @DontInline public static Object ${primitiveConTest}_compiled(#methodArgumentsWithTypes) { """, + methodArguments.stream().map(ma -> constrainValueTemplate.asToken(ma.name)).toList(), bodyTemplate.asToken(expression, expressionArguments, $("checksum")), """ } @@ -262,11 +298,13 @@ public static String generate(CompileFramework comp) { @DontCompile public static Object ${primitiveConTest}_reference(#methodArgumentsWithTypes) { """, + methodArguments.stream().map(ma -> constrainValueTemplate.asToken(ma.name)).toList(), bodyTemplate.asToken(expression, expressionArguments, $("checksum")), """ } """, + methodArguments.stream().map(ma -> constrainValueMethodTemplate.asToken(ma.name, ma.type)).toList(), checksumTemplate.asToken(expression, $("checksum")) ); }); From 2e954ac6959761515db6fdc0e10c54af1da50b76 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 10 Sep 2025 10:10:22 +0200 Subject: [PATCH 21/33] improve expression fuzzer --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 7275ac24de5fa..1c135f79ee9d8 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -63,6 +63,11 @@ // Should this test fail and make a lot of noise in the CI, you have two choices: // - Problem-list this test: but other tests may also use the same broken operators. // - Temporarily remove the operator from {@code Operations.PRIMITIVE_OPERATIONS}. +// +// Future Work [FUTURE]: +// - Constrain also the unsigned bounds +// - Some basic IR tests to ensure that the constraints / checksum mechanics work. +// We may even have to add some IGVN optimizations to be able to better observe things right. public class ExpressionFuzzer { private static final Random RANDOM = Utils.getRandomInstance(); @@ -208,7 +213,7 @@ public static String generate(CompileFramework comp) { // // To ensure that both the compiled and reference method use the same constraint, we put // the computation in a ForceInline method. - var constrainValueMethodTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> body( + var constrainArgumentMethodTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> body( """ @ForceInline public static #type constrain_#name(#type v) { @@ -219,8 +224,22 @@ public static String generate(CompileFramework comp) { // is not very useful: we would like to keep it variable here. We already mix in variable // arguments and constants in the testTemplate. case "boolean", "float", "double" -> "return v;\n"; - case "byte", "short", "char", "int", "long" -> "return v;\n"; - // TODO: constraintIntegralValueTemplate.asToken(name, type))); + case "byte", "short", "char", "int", "long" -> List.of( + // Sometimes constrain the signed range + // v = min(max(v, CON1), CON2) + (RANDOM.nextInt(2) == 0) + ? List.of("v = (#type)Math.min(Math.max(v, ", type.con(),"), ", type.con() ,");\n") + : List.of(), + // Sometimes constrain the bits: + // v = (v & CON1) | CON2 + // Note: + // and (&): forces some bits to zero + // or (|): forces some bits to one + (RANDOM.nextInt(2) == 0) + ? List.of("v = (#type)((v & ", type.con(),") | ", type.con() ,");\n") + : List.of(), + // FUTURE: we could also constrain the unsigned bounds. + "return v;\n"); default -> throw new RuntimeException("should only be primitive types"); }, """ @@ -228,7 +247,7 @@ public static String generate(CompileFramework comp) { """ )); - var constrainValueTemplate = Template.make("name", (String name) -> body( + var constrainArgumentTemplate = Template.make("name", (String name) -> body( """ #name = constrain_#name(#name); """ @@ -240,7 +259,7 @@ public static String generate(CompileFramework comp) { // result verification (only if the result is known to be deterministic). // // - instantiate compiled and reference test methods. - // - instantiate value constraint methods (constrains test method arguments types). + // - instantiate argument constraint methods (constrains test method arguments types). // - instantiate checksum method (summarizes value and bounds/bit checks). var testTemplate = Template.make("expression", (Expression expression) -> { // Fix the arguments for both the compiled and reference method. @@ -260,14 +279,6 @@ public static String generate(CompileFramework comp) { } } } - // TODO: fix with Values - // We need to have a way to get values with type constraints - // And we need to "load" them once in compiled and once in reference - // Could do mix of method args, fields and constants. - // And even computations in the method? Not sure - // Args and fields can then be "constrained" in the method before use. - // And the result should be returned, but also some "checksum" to - // detect range and bits. return body( let("methodArguments", methodArguments.stream().map(ma -> ma.name).collect(Collectors.joining(", "))), @@ -290,7 +301,9 @@ public static String generate(CompileFramework comp) { @DontInline public static Object ${primitiveConTest}_compiled(#methodArgumentsWithTypes) { """, - methodArguments.stream().map(ma -> constrainValueTemplate.asToken(ma.name)).toList(), + // The arguments now have the bottom_type. Constrain the ranges and bits. + methodArguments.stream().map(ma -> constrainArgumentTemplate.asToken(ma.name)).toList(), + // Generate the body with the expression, and calling the checksum. bodyTemplate.asToken(expression, expressionArguments, $("checksum")), """ } @@ -298,13 +311,13 @@ public static String generate(CompileFramework comp) { @DontCompile public static Object ${primitiveConTest}_reference(#methodArgumentsWithTypes) { """, - methodArguments.stream().map(ma -> constrainValueTemplate.asToken(ma.name)).toList(), + methodArguments.stream().map(ma -> constrainArgumentTemplate.asToken(ma.name)).toList(), bodyTemplate.asToken(expression, expressionArguments, $("checksum")), """ } """, - methodArguments.stream().map(ma -> constrainValueMethodTemplate.asToken(ma.name, ma.type)).toList(), + methodArguments.stream().map(ma -> constrainArgumentMethodTemplate.asToken(ma.name, ma.type)).toList(), checksumTemplate.asToken(expression, $("checksum")) ); }); From 4bf692124a1db9ce47597d49047ffee17db04442 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 10 Sep 2025 15:45:51 +0200 Subject: [PATCH 22/33] documentation --- .../library/Expression.java | 198 ++++++++++++------ .../library/Operations.java | 10 +- 2 files changed, 147 insertions(+), 61 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 4ef90c6369d76..8b93f327da256 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -36,15 +36,38 @@ import static compiler.lib.template_framework.Template.body; /** - * TODO: desc + * {@link Expression}s model Java expressions, that have a list of arguments with specified + * argument types, and an result with a specified result type. Once can {@link #make} a new + * {@link Expression} or use existing ones from {@link Operations}. + * + *

+ * The {@link Expression}s are composable, they can be explicitly {@link nest}ed, or randomly + * combined using {@link #nestRandomly}. + * + *

+ * Finally, they can be used in a {@link Template} as a {@link TemplateToken} by calling + * {@link #asToken} with the required arguments. */ public class Expression { private static final Random RANDOM = Utils.getRandomInstance(); - public CodeGenerationDataNameType returnType; - public List argumentTypes; - List strings; - public Info info; + /** + * Specifies the return type of the {@link Expression}. + */ + public final CodeGenerationDataNameType returnType; + + + /** + * Specifies the types of the arguments. + */ + public final List argumentTypes; + + final List strings; + + /** + * Provides additional information about the {@link Expression}. + */ + public final Info info; private Expression(CodeGenerationDataNameType returnType, List argumentTypes, @@ -54,40 +77,66 @@ private Expression(CodeGenerationDataNameType returnType, throw new RuntimeException("Must have one more string than argument."); } this.returnType = returnType; - this.argumentTypes = argumentTypes; - this.strings = strings; + this.argumentTypes = List.copyOf(argumentTypes); + this.strings = List.copyOf(strings); this.info = info; } /** - * TODO: desc: used for all sorts of optional info. + * Specifies additional information for an {@link Expression}. */ public static class Info { - public Set exceptions = Set.of(); - public boolean isResultDeterministic = true; + /** + * Set of exceptions the {@link Exception} could throw when executed. + * By default, we assume that an {@link Expression} throws no exceptions. + */ + public final Set exceptions; - public Info() {} + /** + * Specifies if the result of the {@link Expression} is guaranteed to + * be deterministic. This allows exact result verification, for example + * by comparing compiler and interpreter results. However, there are some + * operations that do not always return the same exact result, which can + * for example happen with {@code Float.floatToRawIntBits} in combination + * with more than one {@code NaN} bit representations. + * By default, we assume that an {@link Expression} is deterministic. + */ + public final boolean isResultDeterministic; - private Info(Info info) { - this.exceptions = Set.copyOf(info.exceptions); - this.isResultDeterministic = info.isResultDeterministic; + /** + * Create a default {@link Info}. + */ + public Info() { + this.exceptions = Set.of(); + this.isResultDeterministic = false; + } + + private Info(Set exceptions, boolean isResultDeterministic) { + this.exceptions = Set.copyOf(exceptions); + this.isResultDeterministic = isResultDeterministic; } /** - * TODO: desc union of exceptions + * Creates a new {@link Info} with additional exceptions that the {@link Expression} could throw. + * + * @param exceptions the exceptions to be added. + * @return a new {@link Info} instance with the added exceptions. */ public Info withExceptions(Set exceptions) { - Info info = new Info(this); - info.exceptions = Stream.concat(this.exceptions.stream(), exceptions.stream()) - .collect(Collectors.toSet()); - return info; + exceptions = Stream.concat(this.exceptions.stream(), exceptions.stream()) + .collect(Collectors.toSet()); + return new Info(exceptions, this.isResultDeterministic); } + /** + * Creates a new {@link Info} that specifies that the {@link Exception} may return + * indeterministic results, which prevents exact result verification. + * + * @return a new {@link Info} instance that specifies indeterministic results. + */ public Info withNondeterministicResult() { - Info info = new Info(this); - info.isResultDeterministic = false; - return info; + return new Info(this.exceptions, false); } Info combineWith(Info other) { @@ -102,10 +151,11 @@ Info combineWith(Info other) { /** * Creates a new Espression with 1 arguments. * - * @param returnType The return type of the expression. + * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. * @param t0 The type of the first argument. - * @param s1 The last string, finishing the expression. + * @param s1 The last string, finishing the {@link Expression}. + * @return the new {@link Expression}. */ public static Expression make(CodeGenerationDataNameType returnType, String s0, @@ -117,11 +167,12 @@ public static Expression make(CodeGenerationDataNameType returnType, /** * Creates a new Espression with 1 argument. * - * @param returnType The return type of the expression. + * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. * @param t0 The type of the first argument. - * @param s1 The last string, finishing the expression. - * @param info Additional information about the Expression. + * @param s1 The last string, finishing the {@link Expression}. + * @param info Additional information about the {@link Expression}. + * @return the new {@link Expression}. */ public static Expression make(CodeGenerationDataNameType returnType, String s0, @@ -134,12 +185,13 @@ public static Expression make(CodeGenerationDataNameType returnType, /** * Creates a new Espression with 2 arguments. * - * @param returnType The return type of the expression. + * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. * @param t0 The type of the first argument. * @param s1 The second string, to be placed before {@code t1}. * @param t1 The type of the second argument. - * @param s2 The last string, finishing the expression. + * @param s2 The last string, finishing the {@link Expression}. + * @return the new {@link Expression}. */ public static Expression make(CodeGenerationDataNameType returnType, String s0, @@ -153,13 +205,14 @@ public static Expression make(CodeGenerationDataNameType returnType, /** * Creates a new Espression with 2 arguments. * - * @param returnType The return type of the expression. + * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. * @param t0 The type of the first argument. * @param s1 The second string, to be placed before {@code t1}. * @param t1 The type of the second argument. - * @param s2 The last string, finishing the expression. - * @param info Additional information about the Expression. + * @param s2 The last string, finishing the {@link Expression}. + * @param info Additional information about the {@link Expression}. + * @return the new {@link Expression}. */ public static Expression make(CodeGenerationDataNameType returnType, String s0, @@ -174,14 +227,15 @@ public static Expression make(CodeGenerationDataNameType returnType, /** * Creates a new Espression with 3 arguments. * - * @param returnType The return type of the expression. + * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. * @param t0 The type of the first argument. * @param s1 The second string, to be placed before {@code t1}. * @param t1 The type of the second argument. * @param s2 The third string, to be placed before {@code t2}. * @param t2 The type of the third argument. - * @param s3 The last string, finishing the expression. + * @param s3 The last string, finishing the {@link Expression}. + * @return the new {@link Expression}. */ public static Expression make(CodeGenerationDataNameType returnType, String s0, @@ -197,15 +251,16 @@ public static Expression make(CodeGenerationDataNameType returnType, /** * Creates a new Espression with 3 arguments. * - * @param returnType The return type of the expression. + * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. * @param t0 The type of the first argument. * @param s1 The second string, to be placed before {@code t1}. * @param t1 The type of the second argument. * @param s2 The third string, to be placed before {@code t2}. * @param t2 The type of the third argument. - * @param s3 The last string, finishing the expression. - * @param info Additional information about the Expression. + * @param s3 The last string, finishing the {@link Expression}. + * @param info Additional information about the {@link Expression}. + * @return the new {@link Expression}. */ public static Expression make(CodeGenerationDataNameType returnType, String s0, @@ -222,7 +277,7 @@ public static Expression make(CodeGenerationDataNameType returnType, /** * Creates a new Espression with 4 arguments. * - * @param returnType The return type of the expression. + * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. * @param t0 The type of the first argument. * @param s1 The second string, to be placed before {@code t1}. @@ -231,7 +286,8 @@ public static Expression make(CodeGenerationDataNameType returnType, * @param t2 The type of the third argument. * @param s3 The fourth string, to be placed before {@code t3}. * @param t3 The type of the fourth argument. - * @param s4 The last string, finishing the expression. + * @param s4 The last string, finishing the {@link Expression}. + * @return the new {@link Expression}. */ public static Expression make(CodeGenerationDataNameType returnType, String s0, @@ -249,7 +305,7 @@ public static Expression make(CodeGenerationDataNameType returnType, /** * Creates a new Espression with 4 arguments. * - * @param returnType The return type of the expression. + * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. * @param t0 The type of the first argument. * @param s1 The second string, to be placed before {@code t1}. @@ -258,8 +314,9 @@ public static Expression make(CodeGenerationDataNameType returnType, * @param t2 The type of the third argument. * @param s3 The fourth string, to be placed before {@code t3}. * @param t3 The type of the fourth argument. - * @param s4 The last string, finishing the expression. - * @param info Additional information about the Expression. + * @param s4 The last string, finishing the {@link Expression}. + * @param info Additional information about the {@link Expression}. + * @return the new {@link Expression}. */ public static Expression make(CodeGenerationDataNameType returnType, String s0, @@ -276,7 +333,13 @@ public static Expression make(CodeGenerationDataNameType returnType, } /** - * TODO: desc + * Creates a {@link TemplateToken} for the use in a {@link Template} by applying the + * {@code arguments} to the {@link Expression}. It is the users responsibility to + * ensure that the argument tokens match the required {@link #argumentTypes}. + * + * @param arguments the tokens to be passed as arguments into the {@link Expression}. + * @return a {@link TemplateToken} representing the {@link Expression} with applied arguments, + * for the use in a {@link Template}. */ public TemplateToken asToken(List arguments) { if (arguments.size() != argumentTypes.size()) { @@ -320,12 +383,19 @@ public String toString() { } /** - * Create a nested expression with a specified {@code returnType} from a + * Create a nested {@link Expression} with a specified {@code returnType} from a * set of {@code expressions}. + * + * @param returnType the type of the return value. + * @param expressions the list of {@link Expression}s from which we sample to create + * the nested {@link Expression}. + * @param maxNumberOfUsedExpressions the maximal number of {@link Expression}s from the + * {@code expressions} are nested. + * @return a new randomly nested {@link Expression}. */ public static Expression nestRandomly(CodeGenerationDataNameType returnType, List expressions, - int maxNumberOfUsedExpression) { + int maxNumberOfUsedExpressions) { List filtered = expressions.stream().filter(e -> e.returnType.isSubtypeOf(returnType)).toList(); if (filtered.size() == 0) { @@ -335,20 +405,23 @@ public static Expression nestRandomly(CodeGenerationDataNameType returnType, int r = RANDOM.nextInt(filtered.size()); Expression expression = filtered.get(r); - for (int i = 1; i < maxNumberOfUsedExpression; i++) { + for (int i = 1; i < maxNumberOfUsedExpressions; i++) { expression = expression.nestRandomly(expressions); } return expression; } /** - * Nests a random expression from {@code nestingExpressions} into a random argument of - * {@code this} expression, ensuring compatibility of argument and return type. + * Nests a random {@link Expression} from {@code nestingExpressions} into a random argument of + * {@code this} {@link Expression}, ensuring compatibility of argument and return type. + * + * @param nestingExpressions list of expressions we sample from for the inner {@link Expression}. + * @return a new nested {@link Expression}. */ public Expression nestRandomly(List nestingExpressions) { - int slot = RANDOM.nextInt(this.argumentTypes.size()); - CodeGenerationDataNameType slotType = this.argumentTypes.get(slot); - List filtered = nestingExpressions.stream().filter(e -> e.returnType.isSubtypeOf(slotType)).toList(); + int argumentIndex = RANDOM.nextInt(this.argumentTypes.size()); + CodeGenerationDataNameType argumentType = this.argumentTypes.get(argumentIndex); + List filtered = nestingExpressions.stream().filter(e -> e.returnType.isSubtypeOf(argumentType)).toList(); if (filtered.size() == 0) { // Found no expression that has a matching returnType. @@ -358,26 +431,31 @@ public Expression nestRandomly(List nestingExpressions) { int r = RANDOM.nextInt(filtered.size()); Expression expression = filtered.get(r); - return this.nest(slot, expression); + return this.nest(argumentIndex, expression); } /** - * Nests the {@code nestingExpression} into the specified {@code slot} of - * {@code this} expression. + * Nests the {@code nestingExpression} into the specified {@code argumentIndex} of + * {@code this} {@link Expression}. + * + * @param argumentIndex the index specifying at which argument of {@code this} + * {@link Expression} we inser the {@code nestingExpression}. + * @param nestingExpression the inner {@link Expression}. + * @return a new nested {@link Expression}. */ - public Expression nest(int slot, Expression nestingExpression) { - if (!nestingExpression.returnType.isSubtypeOf(this.argumentTypes.get(slot))) { + public Expression nest(int argumentIndex, Expression nestingExpression) { + if (!nestingExpression.returnType.isSubtypeOf(this.argumentTypes.get(argumentIndex))) { throw new IllegalArgumentException("Cannot nest expressions because of mismatched types."); } List newArgumentTypes = new ArrayList<>(); List newStrings = new ArrayList<>(); // s0 t0 s1 [S0 T0 S1 T1 S2] s2 t2 s3 - for (int i = 0; i < slot; i++) { + for (int i = 0; i < argumentIndex; i++) { newStrings.add(this.strings.get(i)); newArgumentTypes.add(this.argumentTypes.get(i)); } - newStrings.add(this.strings.get(slot) + + newStrings.add(this.strings.get(argumentIndex) + nestingExpression.strings.get(0)); // concat s1 and S0 newArgumentTypes.add(nestingExpression.argumentTypes.get(0)); for (int i = 1; i < nestingExpression.argumentTypes.size(); i++) { @@ -385,8 +463,8 @@ public Expression nest(int slot, Expression nestingExpression) { newArgumentTypes.add(nestingExpression.argumentTypes.get(i)); } newStrings.add(nestingExpression.strings.get(nestingExpression.strings.size() - 1) + - this.strings.get(slot + 1)); // concat S2 and s2 - for (int i = slot+1; i < this.argumentTypes.size(); i++) { + this.strings.get(argumentIndex + 1)); // concat S2 and s2 + for (int i = argumentIndex+1; i < this.argumentTypes.size(); i++) { newArgumentTypes.add(this.argumentTypes.get(i)); newStrings.add(this.strings.get(i+1)); } diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 46a08f947e689..e70a89cfe3a1b 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -37,10 +37,18 @@ import static compiler.lib.template_framework.library.PrimitiveType.BOOLEANS; /** - * TODO: desc + * This class provides various lists of {@link Expression}s, that represent Java operators or library + * methods. For example, we represent arithmetic operations on primitive types. */ public final class Operations { + // private constructor to avoid instantiation. + private Operations() {} + + /** + * Provides a lits of operations on {@link PrimitiveType}s, such as arithmetic, logical, + * and cast operations. + */ public static final List PRIMITIVE_OPERATIONS = generatePrimitiveOperations(); private static List generatePrimitiveOperations() { From 360e7f2b7935629ae5165cf741e15052fe4f0298 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 10 Sep 2025 15:53:19 +0200 Subject: [PATCH 23/33] fix bug --- .../compiler/lib/template_framework/library/Expression.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 8b93f327da256..87091d137d66f 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -109,7 +109,7 @@ public static class Info { */ public Info() { this.exceptions = Set.of(); - this.isResultDeterministic = false; + this.isResultDeterministic = true; } private Info(Set exceptions, boolean isResultDeterministic) { From b58ce8a6df00f14b111006e2caebe3289e1fdded Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Wed, 10 Sep 2025 16:34:39 +0200 Subject: [PATCH 24/33] LibraryRNG example --- .../examples/TestPrimitiveTypes.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java index 5cd3f3c2a226b..a04a5771cb49a 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestPrimitiveTypes.java @@ -169,6 +169,48 @@ public static void test_names() { tests.put("test_names", namesTemplate.asToken()); + // Test runtime random value generation with LibraryRNG + // Runtime random number generation of a given primitive type can be very helpful + // when writing tests that require random inputs. + var libraryRNGWithTypeTemplate = Template.make("type", (PrimitiveType type) -> body( + """ + { + // Fill an array with 1_000 random values. Every type has at least 2 values, + // so the chance that all values are the same is 2^-1_000 < 10^-300. This should + // never happen, even with a relatively weak PRNG. + #type[] a = new #type[1_000]; + for (int i = 0; i < a.length; i++) { + """, + " a[i] = ", type.callLibraryRNG(), ";\n", + """ + } + boolean allSame = true; + for (int i = 0; i < a.length; i++) { + if (a[i] != a[0]) { + allSame = false; + break; + } + } + if (allSame) { throw new RuntimeException("all values were the same for #type"); } + } + """ + )); + + var libraryRNGTemplate = Template.make(() -> body( + // Make sure we instantiate the LibraryRNG class. + PrimitiveType.generateLibraryRNG(), + // Now we can use it inside the test. + """ + public static void test_LibraryRNG() { + """, + CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(libraryRNGWithTypeTemplate::asToken).toList(), + """ + } + """ + )); + + tests.put("test_LibraryRNG", libraryRNGTemplate.asToken()); + // Finally, put all the tests together in a class, and invoke all // tests from the main method. var template = Template.make(() -> body( @@ -178,6 +220,11 @@ public static void test_names() { import compiler.lib.verify.*; import java.lang.foreign.MemorySegment; + // Imports for LibraryRNG + import java.util.Random; + import jdk.test.lib.Utils; + import compiler.lib.generators.*; + public class InnerTest { public static void main() { """, From 0709731aba2ba6ee56e6fbafbcfe0f1293d814eb Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 11 Sep 2025 07:17:48 +0200 Subject: [PATCH 25/33] fix whitespaces --- .../compiler/lib/template_framework/library/Operations.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index e70a89cfe3a1b..67a3789242ad2 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -266,7 +266,7 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(INTS, "Float.compare(", FLOATS, ", ", FLOATS, ")")); ops.add(Expression.make(INTS, "Float.floatToIntBits(", FLOATS, ")")); ops.add(Expression.make(INTS, "Float.floatToRawIntBits(", FLOATS, ")", withNondeterministicResult)); - // Note: there are multiple NaN values with different bit representations. + // Note: there are multiple NaN values with different bit representations. ops.add(Expression.make(FLOATS, "Float.float16ToFloat(", SHORTS, ")")); ops.add(Expression.make(FLOATS, "Float.intBitsToFloat(", INTS, ")")); ops.add(Expression.make(BOOLEANS, "Float.isFinite(", FLOATS, ")")); @@ -308,7 +308,7 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(INTS, "Double.compare(", DOUBLES, ", ", DOUBLES, ")")); ops.add(Expression.make(LONGS, "Double.doubleToLongBits(", DOUBLES, ")")); ops.add(Expression.make(LONGS, "Double.doubleToRawLongBits(", DOUBLES, ")", withNondeterministicResult)); - // Note: there are multiple NaN values with different bit representations. + // Note: there are multiple NaN values with different bit representations. ops.add(Expression.make(DOUBLES, "Double.longBitsToDouble(", LONGS, ")")); ops.add(Expression.make(BOOLEANS, "Double.isFinite(", DOUBLES, ")")); ops.add(Expression.make(BOOLEANS, "Double.isInfinite(", DOUBLES, ")")); From d66aa9855b8f65e558d4d2afd4c543068950dd7a Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 18 Sep 2025 08:49:34 +0200 Subject: [PATCH 26/33] Apply Manuel's suggestions part 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel Hässig --- .../jtreg/compiler/igvn/ExpressionFuzzer.java | 1 - .../library/Expression.java | 8 +- .../library/Operations.java | 256 +++++++++--------- 3 files changed, 132 insertions(+), 133 deletions(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 1c135f79ee9d8..706e63c9b0927 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -201,7 +201,6 @@ public static String generate(CompileFramework comp) { // We need to prepare some random values to pass into the test method. We generate the values // once, and pass the same values into both the compiled and reference method. var valueTemplate = Template.make("name", "type", (String name, CodeGenerationDataNameType type) -> body( - //"#type #name = ", type.con(), ";\n" "#type #name = ", (type instanceof PrimitiveType pt) ? pt.callLibraryRNG() : type.con(), ";\n" diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 87091d137d66f..d43037b20b14e 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -37,7 +37,7 @@ /** * {@link Expression}s model Java expressions, that have a list of arguments with specified - * argument types, and an result with a specified result type. Once can {@link #make} a new + * argument types, and a result with a specified result type. Once can {@link #make} a new * {@link Expression} or use existing ones from {@link Operations}. * *

@@ -355,7 +355,7 @@ public TemplateToken asToken(List arguments) { tokens.add(strings.get(i)); tokens.add(arguments.get(i)); } - tokens.add(strings.get(strings.size()-1)); + tokens.add(strings.getLast()); var template = Template.make(() -> body( tokens @@ -377,7 +377,7 @@ public String toString() { sb.append(", "); } sb.append("\""); - sb.append(this.strings.get(this.strings.size()-1)); + sb.append(this.strings.getLast()); sb.append("\"]"); return sb.toString(); } @@ -462,7 +462,7 @@ public Expression nest(int argumentIndex, Expression nestingExpression) { newStrings.add(nestingExpression.strings.get(i)); newArgumentTypes.add(nestingExpression.argumentTypes.get(i)); } - newStrings.add(nestingExpression.strings.get(nestingExpression.strings.size() - 1) + + newStrings.add(nestingExpression.strings.getLast() + this.strings.get(argumentIndex + 1)); // concat S2 and s2 for (int i = argumentIndex+1; i < this.argumentTypes.size(); i++) { newArgumentTypes.add(this.argumentTypes.get(i)); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 67a3789242ad2..6642e3476d655 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -58,13 +58,13 @@ private static List generatePrimitiveOperations() { Expression.Info withNondeterministicResult = new Expression.Info().withNondeterministicResult(); // ------------ byte ------------- - ops.add(Expression.make(BYTES, "(byte)(", BYTES, ")")); - ops.add(Expression.make(BYTES, "(byte)(", SHORTS, ")")); - ops.add(Expression.make(BYTES, "(byte)(", CHARS, ")")); - ops.add(Expression.make(BYTES, "(byte)(", INTS, ")")); - ops.add(Expression.make(BYTES, "(byte)(", LONGS, ")")); - ops.add(Expression.make(BYTES, "(byte)(", FLOATS, ")")); - ops.add(Expression.make(BYTES, "(byte)(", DOUBLES, ")")); + ops.add(Expression.make(BYTES, "(byte)(", BYTES, ")")); + ops.add(Expression.make(BYTES, "(byte)(", SHORTS, ")")); + ops.add(Expression.make(BYTES, "(byte)(", CHARS, ")")); + ops.add(Expression.make(BYTES, "(byte)(", INTS, ")")); + ops.add(Expression.make(BYTES, "(byte)(", LONGS, ")")); + ops.add(Expression.make(BYTES, "(byte)(", FLOATS, ")")); + ops.add(Expression.make(BYTES, "(byte)(", DOUBLES, ")")); // There is no cast from boolean. ops.add(Expression.make(BYTES, "(", BOOLEANS, "?", BYTES, ":", BYTES, ")")); @@ -72,19 +72,19 @@ private static List generatePrimitiveOperations() { // Arithmetic operations are not performned in byte, but rather promoted to int. // ------------ Byte ------------- - ops.add(Expression.make(INTS, "Byte.compare(", BYTES, ", ", BYTES, ")")); - ops.add(Expression.make(INTS, "Byte.compareUnsigned(", BYTES, ", ", BYTES, ")")); - ops.add(Expression.make(INTS, "Byte.toUnsignedInt(", BYTES, ")")); - ops.add(Expression.make(LONGS, "Byte.toUnsignedLong(", BYTES, ")")); + ops.add(Expression.make(INTS, "Byte.compare(", BYTES, ", ", BYTES, ")")); + ops.add(Expression.make(INTS, "Byte.compareUnsigned(", BYTES, ", ", BYTES, ")")); + ops.add(Expression.make(INTS, "Byte.toUnsignedInt(", BYTES, ")")); + ops.add(Expression.make(LONGS, "Byte.toUnsignedLong(", BYTES, ")")); // ------------ char ------------- - ops.add(Expression.make(CHARS, "(char)(", BYTES, ")")); - ops.add(Expression.make(CHARS, "(char)(", SHORTS, ")")); - ops.add(Expression.make(CHARS, "(char)(", CHARS, ")")); - ops.add(Expression.make(CHARS, "(char)(", INTS, ")")); - ops.add(Expression.make(CHARS, "(char)(", LONGS, ")")); - ops.add(Expression.make(CHARS, "(char)(", FLOATS, ")")); - ops.add(Expression.make(CHARS, "(char)(", DOUBLES, ")")); + ops.add(Expression.make(CHARS, "(char)(", BYTES, ")")); + ops.add(Expression.make(CHARS, "(char)(", SHORTS, ")")); + ops.add(Expression.make(CHARS, "(char)(", CHARS, ")")); + ops.add(Expression.make(CHARS, "(char)(", INTS, ")")); + ops.add(Expression.make(CHARS, "(char)(", LONGS, ")")); + ops.add(Expression.make(CHARS, "(char)(", FLOATS, ")")); + ops.add(Expression.make(CHARS, "(char)(", DOUBLES, ")")); // There is no cast from boolean. ops.add(Expression.make(CHARS, "(", BOOLEANS, "?", CHARS, ":", CHARS, ")")); @@ -92,17 +92,17 @@ private static List generatePrimitiveOperations() { // Arithmetic operations are not performned in char, but rather promoted to int. // ------------ Character ------------- - ops.add(Expression.make(INTS, "Character.compare(", CHARS, ", ", CHARS, ")")); + ops.add(Expression.make(INTS, "Character.compare(", CHARS, ", ", CHARS, ")")); ops.add(Expression.make(CHARS, "Character.reverseBytes(", CHARS, ")")); // ------------ short ------------- - ops.add(Expression.make(SHORTS, "(short)(", BYTES, ")")); - ops.add(Expression.make(SHORTS, "(short)(", SHORTS, ")")); - ops.add(Expression.make(SHORTS, "(short)(", CHARS, ")")); - ops.add(Expression.make(SHORTS, "(short)(", INTS, ")")); - ops.add(Expression.make(SHORTS, "(short)(", LONGS, ")")); - ops.add(Expression.make(SHORTS, "(short)(", FLOATS, ")")); - ops.add(Expression.make(SHORTS, "(short)(", DOUBLES, ")")); + ops.add(Expression.make(SHORTS, "(short)(", BYTES, ")")); + ops.add(Expression.make(SHORTS, "(short)(", SHORTS, ")")); + ops.add(Expression.make(SHORTS, "(short)(", CHARS, ")")); + ops.add(Expression.make(SHORTS, "(short)(", INTS, ")")); + ops.add(Expression.make(SHORTS, "(short)(", LONGS, ")")); + ops.add(Expression.make(SHORTS, "(short)(", FLOATS, ")")); + ops.add(Expression.make(SHORTS, "(short)(", DOUBLES, ")")); // There is no cast from boolean. ops.add(Expression.make(SHORTS, "(", BOOLEANS, "?", SHORTS, ":", SHORTS, ")")); @@ -110,31 +110,31 @@ private static List generatePrimitiveOperations() { // Arithmetic operations are not performned in short, but rather promoted to int. // ------------ Short ------------- - ops.add(Expression.make(INTS, "Short.compare(", SHORTS, ", ", SHORTS, ")")); - ops.add(Expression.make(INTS, "Short.compareUnsigned(", SHORTS, ", ", SHORTS, ")")); - ops.add(Expression.make(SHORTS, "Short.reverseBytes(", SHORTS, ")")); - ops.add(Expression.make(INTS, "Short.toUnsignedInt(", SHORTS, ")")); - ops.add(Expression.make(LONGS, "Short.toUnsignedLong(", SHORTS, ")")); + ops.add(Expression.make(INTS, "Short.compare(", SHORTS, ", ", SHORTS, ")")); + ops.add(Expression.make(INTS, "Short.compareUnsigned(", SHORTS, ", ", SHORTS, ")")); + ops.add(Expression.make(SHORTS, "Short.reverseBytes(", SHORTS, ")")); + ops.add(Expression.make(INTS, "Short.toUnsignedInt(", SHORTS, ")")); + ops.add(Expression.make(LONGS, "Short.toUnsignedLong(", SHORTS, ")")); // ------------ int ------------- - ops.add(Expression.make(INTS, "(int)(", BYTES, ")")); - ops.add(Expression.make(INTS, "(int)(", SHORTS, ")")); - ops.add(Expression.make(INTS, "(int)(", CHARS, ")")); - ops.add(Expression.make(INTS, "(int)(", INTS, ")")); - ops.add(Expression.make(INTS, "(int)(", LONGS, ")")); - ops.add(Expression.make(INTS, "(int)(", FLOATS, ")")); - ops.add(Expression.make(INTS, "(int)(", DOUBLES, ")")); + ops.add(Expression.make(INTS, "(int)(", BYTES, ")")); + ops.add(Expression.make(INTS, "(int)(", SHORTS, ")")); + ops.add(Expression.make(INTS, "(int)(", CHARS, ")")); + ops.add(Expression.make(INTS, "(int)(", INTS, ")")); + ops.add(Expression.make(INTS, "(int)(", LONGS, ")")); + ops.add(Expression.make(INTS, "(int)(", FLOATS, ")")); + ops.add(Expression.make(INTS, "(int)(", DOUBLES, ")")); // There is no cast from boolean. ops.add(Expression.make(INTS, "(", BOOLEANS, "?", INTS, ":", INTS, ")")); // Arithmetic operators ops.add(Expression.make(INTS, "(-(", INTS, "))")); - ops.add(Expression.make(INTS, "(", INTS, " + ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " - ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " * ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " / ", INTS, ")", withArithmeticException)); - ops.add(Expression.make(INTS, "(", INTS, " % ", INTS, ")", withArithmeticException)); + ops.add(Expression.make(INTS, "(", INTS, " + ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " - ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " * ", INTS, ")")); + ops.add(Expression.make(INTS, "(", INTS, " / ", INTS, ")", withArithmeticException)); + ops.add(Expression.make(INTS, "(", INTS, " % ", INTS, ")", withArithmeticException)); // Bitwise Operators (non short-circuit) ops.add(Expression.make(INTS, "(~(", INTS, "))")); @@ -146,54 +146,54 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(INTS, "(", INTS, " >>> ", INTS, ")")); // Relational / Comparison Operators - ops.add(Expression.make(BOOLEANS, "(", INTS, " == ", INTS, ")")); - ops.add(Expression.make(BOOLEANS, "(", INTS, " != ", INTS, ")")); - ops.add(Expression.make(BOOLEANS, "(", INTS, " > ", INTS, ")")); - ops.add(Expression.make(BOOLEANS, "(", INTS, " < ", INTS, ")")); - ops.add(Expression.make(BOOLEANS, "(", INTS, " >= ", INTS, ")")); - ops.add(Expression.make(BOOLEANS, "(", INTS, " <= ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " == ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " != ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " > ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " < ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " >= ", INTS, ")")); + ops.add(Expression.make(BOOLEANS, "(", INTS, " <= ", INTS, ")")); // ------------ Integer ------------- - ops.add(Expression.make(INTS, "Integer.bitCount(", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.compare(", INTS, ", ", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.compareUnsigned(", INTS, ", ", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.compress(", INTS, ", ", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.divideUnsigned(", INTS, ", ", INTS, ")", withArithmeticException)); - ops.add(Expression.make(INTS, "Integer.expand(", INTS, ", ", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.highestOneBit(", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.lowestOneBit(", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.max(", INTS, ", ", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.min(", INTS, ", ", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.numberOfLeadingZeros(", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.numberOfTrailingZeros(", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.remainderUnsigned(", INTS, ", ", INTS, ")", withArithmeticException)); - ops.add(Expression.make(INTS, "Integer.reverse(", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.reverseBytes(", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.rotateLeft(", INTS, ", ", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.rotateRight(", INTS, ", ", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.signum(", INTS, ")")); - ops.add(Expression.make(INTS, "Integer.sum(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.bitCount(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.compare(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.compareUnsigned(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.compress(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.divideUnsigned(", INTS, ", ", INTS, ")", withArithmeticException)); + ops.add(Expression.make(INTS, "Integer.expand(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.highestOneBit(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.lowestOneBit(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.max(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.min(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.numberOfLeadingZeros(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.numberOfTrailingZeros(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.remainderUnsigned(", INTS, ", ", INTS, ")", withArithmeticException)); + ops.add(Expression.make(INTS, "Integer.reverse(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.reverseBytes(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.rotateLeft(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.rotateRight(", INTS, ", ", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.signum(", INTS, ")")); + ops.add(Expression.make(INTS, "Integer.sum(", INTS, ", ", INTS, ")")); ops.add(Expression.make(LONGS, "Integer.toUnsignedLong(", INTS, ")")); // ------------ long ------------- - ops.add(Expression.make(LONGS, "(long)(", BYTES, ")")); - ops.add(Expression.make(LONGS, "(long)(", SHORTS, ")")); - ops.add(Expression.make(LONGS, "(long)(", CHARS, ")")); - ops.add(Expression.make(LONGS, "(long)(", INTS, ")")); - ops.add(Expression.make(LONGS, "(long)(", LONGS, ")")); - ops.add(Expression.make(LONGS, "(long)(", FLOATS, ")")); - ops.add(Expression.make(LONGS, "(long)(", DOUBLES, ")")); + ops.add(Expression.make(LONGS, "(long)(", BYTES, ")")); + ops.add(Expression.make(LONGS, "(long)(", SHORTS, ")")); + ops.add(Expression.make(LONGS, "(long)(", CHARS, ")")); + ops.add(Expression.make(LONGS, "(long)(", INTS, ")")); + ops.add(Expression.make(LONGS, "(long)(", LONGS, ")")); + ops.add(Expression.make(LONGS, "(long)(", FLOATS, ")")); + ops.add(Expression.make(LONGS, "(long)(", DOUBLES, ")")); // There is no cast from boolean. ops.add(Expression.make(LONGS, "(", BOOLEANS, "?", LONGS, ":", LONGS, ")")); // Arithmetic operators ops.add(Expression.make(LONGS, "(-(", LONGS, "))")); - ops.add(Expression.make(LONGS, "(", LONGS, " + ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " - ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " * ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " / ", LONGS, ")", withArithmeticException)); - ops.add(Expression.make(LONGS, "(", LONGS, " % ", LONGS, ")", withArithmeticException)); + ops.add(Expression.make(LONGS, "(", LONGS, " + ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " - ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " * ", LONGS, ")")); + ops.add(Expression.make(LONGS, "(", LONGS, " / ", LONGS, ")", withArithmeticException)); + ops.add(Expression.make(LONGS, "(", LONGS, " % ", LONGS, ")", withArithmeticException)); // Bitwise Operators (non short-circuit) ops.add(Expression.make(LONGS, "(~(", LONGS, "))")); @@ -205,17 +205,17 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(LONGS, "(", LONGS, " >>> ", LONGS, ")")); // Relational / Comparison Operators - ops.add(Expression.make(BOOLEANS, "(", LONGS, " == ", LONGS, ")")); - ops.add(Expression.make(BOOLEANS, "(", LONGS, " != ", LONGS, ")")); - ops.add(Expression.make(BOOLEANS, "(", LONGS, " > ", LONGS, ")")); - ops.add(Expression.make(BOOLEANS, "(", LONGS, " < ", LONGS, ")")); - ops.add(Expression.make(BOOLEANS, "(", LONGS, " >= ", LONGS, ")")); - ops.add(Expression.make(BOOLEANS, "(", LONGS, " <= ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " == ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " != ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " > ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " < ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " >= ", LONGS, ")")); + ops.add(Expression.make(BOOLEANS, "(", LONGS, " <= ", LONGS, ")")); // ------------ Long ------------- - ops.add(Expression.make(INTS, "Long.bitCount(", LONGS, ")")); - ops.add(Expression.make(INTS, "Long.compare(", LONGS, ", ", LONGS, ")")); - ops.add(Expression.make(INTS, "Long.compareUnsigned(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(INTS, "Long.bitCount(", LONGS, ")")); + ops.add(Expression.make(INTS, "Long.compare(", LONGS, ", ", LONGS, ")")); + ops.add(Expression.make(INTS, "Long.compareUnsigned(", LONGS, ", ", LONGS, ")")); ops.add(Expression.make(LONGS, "Long.compress(", LONGS, ", ", LONGS, ")")); ops.add(Expression.make(LONGS, "Long.divideUnsigned(", LONGS, ", ", LONGS, ")", withArithmeticException)); ops.add(Expression.make(LONGS, "Long.expand(", LONGS, ", ", LONGS, ")")); @@ -223,35 +223,35 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(LONGS, "Long.lowestOneBit(", LONGS, ")")); ops.add(Expression.make(LONGS, "Long.max(", LONGS, ", ", LONGS, ")")); ops.add(Expression.make(LONGS, "Long.min(", LONGS, ", ", LONGS, ")")); - ops.add(Expression.make(INTS, "Long.numberOfLeadingZeros(", LONGS, ")")); - ops.add(Expression.make(INTS, "Long.numberOfTrailingZeros(", LONGS, ")")); + ops.add(Expression.make(INTS, "Long.numberOfLeadingZeros(", LONGS, ")")); + ops.add(Expression.make(INTS, "Long.numberOfTrailingZeros(", LONGS, ")")); ops.add(Expression.make(LONGS, "Long.remainderUnsigned(", LONGS, ", ", LONGS, ")", withArithmeticException)); ops.add(Expression.make(LONGS, "Long.reverse(", LONGS, ")")); ops.add(Expression.make(LONGS, "Long.reverseBytes(", LONGS, ")")); ops.add(Expression.make(LONGS, "Long.rotateLeft(", LONGS, ", ", INTS, ")")); ops.add(Expression.make(LONGS, "Long.rotateRight(", LONGS, ", ", INTS, ")")); - ops.add(Expression.make(INTS, "Long.signum(", LONGS, ")")); + ops.add(Expression.make(INTS, "Long.signum(", LONGS, ")")); ops.add(Expression.make(LONGS, "Long.sum(", LONGS, ", ", LONGS, ")")); // ------------ float ------------- - ops.add(Expression.make(FLOATS, "(float)(", BYTES, ")")); - ops.add(Expression.make(FLOATS, "(float)(", SHORTS, ")")); - ops.add(Expression.make(FLOATS, "(float)(", CHARS, ")")); - ops.add(Expression.make(FLOATS, "(float)(", INTS, ")")); - ops.add(Expression.make(FLOATS, "(float)(", LONGS, ")")); - ops.add(Expression.make(FLOATS, "(float)(", FLOATS, ")")); - ops.add(Expression.make(FLOATS, "(float)(", DOUBLES, ")")); + ops.add(Expression.make(FLOATS, "(float)(", BYTES, ")")); + ops.add(Expression.make(FLOATS, "(float)(", SHORTS, ")")); + ops.add(Expression.make(FLOATS, "(float)(", CHARS, ")")); + ops.add(Expression.make(FLOATS, "(float)(", INTS, ")")); + ops.add(Expression.make(FLOATS, "(float)(", LONGS, ")")); + ops.add(Expression.make(FLOATS, "(float)(", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(float)(", DOUBLES, ")")); // There is no cast from boolean. ops.add(Expression.make(FLOATS, "(", BOOLEANS, "?", FLOATS, ":", FLOATS, ")")); // Arithmetic operators ops.add(Expression.make(FLOATS, "(-(", FLOATS, "))")); - ops.add(Expression.make(FLOATS, "(", FLOATS, " + ", FLOATS, ")")); - ops.add(Expression.make(FLOATS, "(", FLOATS, " - ", FLOATS, ")")); - ops.add(Expression.make(FLOATS, "(", FLOATS, " * ", FLOATS, ")")); - ops.add(Expression.make(FLOATS, "(", FLOATS, " / ", FLOATS, ")")); - ops.add(Expression.make(FLOATS, "(", FLOATS, " % ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(", FLOATS, " + ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(", FLOATS, " - ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(", FLOATS, " * ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(", FLOATS, " / ", FLOATS, ")")); + ops.add(Expression.make(FLOATS, "(", FLOATS, " % ", FLOATS, ")")); // Relational / Comparison Operators @@ -277,24 +277,24 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(FLOATS, "Float.sum(", FLOATS, ", ", FLOATS, ")")); // ------------ double ------------- - ops.add(Expression.make(DOUBLES, "(double)(", BYTES, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", SHORTS, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", CHARS, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", INTS, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", LONGS, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", FLOATS, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", BYTES, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", SHORTS, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", CHARS, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", INTS, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", LONGS, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", FLOATS, ")")); + ops.add(Expression.make(DOUBLES, "(double)(", DOUBLES, ")")); // There is no cast from boolean. ops.add(Expression.make(DOUBLES, "(", BOOLEANS, "?", DOUBLES, ":", DOUBLES, ")")); // Arithmetic operators ops.add(Expression.make(DOUBLES, "(-(", DOUBLES, "))")); - ops.add(Expression.make(DOUBLES, "(", DOUBLES, " + ", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "(", DOUBLES, " - ", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "(", DOUBLES, " * ", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "(", DOUBLES, " / ", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "(", DOUBLES, " % ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "(", DOUBLES, " + ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "(", DOUBLES, " - ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "(", DOUBLES, " * ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "(", DOUBLES, " / ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "(", DOUBLES, " % ", DOUBLES, ")")); // Relational / Comparison Operators ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " == ", DOUBLES, ")")); @@ -305,17 +305,17 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " <= ", DOUBLES, ")")); // ------------ Double ------------- - ops.add(Expression.make(INTS, "Double.compare(", DOUBLES, ", ", DOUBLES, ")")); - ops.add(Expression.make(LONGS, "Double.doubleToLongBits(", DOUBLES, ")")); - ops.add(Expression.make(LONGS, "Double.doubleToRawLongBits(", DOUBLES, ")", withNondeterministicResult)); + ops.add(Expression.make(INTS, "Double.compare(", DOUBLES, ", ", DOUBLES, ")")); + ops.add(Expression.make(LONGS, "Double.doubleToLongBits(", DOUBLES, ")")); // Note: there are multiple NaN values with different bit representations. - ops.add(Expression.make(DOUBLES, "Double.longBitsToDouble(", LONGS, ")")); + ops.add(Expression.make(LONGS, "Double.doubleToRawLongBits(", DOUBLES, ")", withNondeterministicResult)); + ops.add(Expression.make(DOUBLES, "Double.longBitsToDouble(", LONGS, ")")); ops.add(Expression.make(BOOLEANS, "Double.isFinite(", DOUBLES, ")")); ops.add(Expression.make(BOOLEANS, "Double.isInfinite(", DOUBLES, ")")); ops.add(Expression.make(BOOLEANS, "Double.isNaN(", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "Double.max(", DOUBLES, ", ", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "Double.min(", DOUBLES, ", ", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "Double.sum(", DOUBLES, ", ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "Double.max(", DOUBLES, ", ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "Double.min(", DOUBLES, ", ", DOUBLES, ")")); + ops.add(Expression.make(DOUBLES, "Double.sum(", DOUBLES, ", ", DOUBLES, ")")); // ------------ boolean ------------- // There is no cast to boolean. @@ -326,14 +326,14 @@ private static List generatePrimitiveOperations() { // Logical operators ops.add(Expression.make(BOOLEANS, "(!(", BOOLEANS, "))")); - ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " || ", BOOLEANS, ")")); - ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " && ", BOOLEANS, ")")); - ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " ^ ", BOOLEANS, ")")); + ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " || ", BOOLEANS, ")")); + ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " && ", BOOLEANS, ")")); + ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, " ^ ", BOOLEANS, ")")); // ------------ Boolean ------------- - ops.add(Expression.make(INTS, "Boolean.compare(", BOOLEANS, ", ", BOOLEANS, ")")); + ops.add(Expression.make(INTS, "Boolean.compare(", BOOLEANS, ", ", BOOLEANS, ")")); ops.add(Expression.make(BOOLEANS, "Boolean.logicalAnd(", BOOLEANS, ", ", BOOLEANS, ")")); - ops.add(Expression.make(BOOLEANS, "Boolean.logicalOr(", BOOLEANS, ", ", BOOLEANS, ")")); + ops.add(Expression.make(BOOLEANS, "Boolean.logicalOr(", BOOLEANS, ", ", BOOLEANS, ")")); ops.add(Expression.make(BOOLEANS, "Boolean.logicalXor(", BOOLEANS, ", ", BOOLEANS, ")")); // TODO: Math and other classes. From 05fb63c45d0be866c83f3ced2ac870e191e80698 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 18 Sep 2025 09:14:09 +0200 Subject: [PATCH 27/33] Apply Manuel's suggestions part 2 --- .../library/Expression.java | 24 +++++++++---------- .../examples/TestExpressions.java | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index d43037b20b14e..9c1b3c22fa63a 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -149,7 +149,7 @@ Info combineWith(Info other) { } /** - * Creates a new Espression with 1 arguments. + * Creates a new Expression with 1 arguments. * * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. @@ -161,11 +161,11 @@ public static Expression make(CodeGenerationDataNameType returnType, String s0, CodeGenerationDataNameType t0, String s1) { - return new Expression(returnType, List.of(t0), List.of(s0, s1), new Info()); + return make(returnType, s0, t0, s1, new Info()); } /** - * Creates a new Espression with 1 argument. + * Creates a new Expression with 1 argument. * * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. @@ -183,7 +183,7 @@ public static Expression make(CodeGenerationDataNameType returnType, } /** - * Creates a new Espression with 2 arguments. + * Creates a new Expression with 2 arguments. * * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. @@ -199,11 +199,11 @@ public static Expression make(CodeGenerationDataNameType returnType, String s1, CodeGenerationDataNameType t1, String s2) { - return new Expression(returnType, List.of(t0, t1), List.of(s0, s1, s2), new Info()); + return make(returnType, s0, t0, s1, t1, s2, new Info()); } /** - * Creates a new Espression with 2 arguments. + * Creates a new Expression with 2 arguments. * * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. @@ -225,7 +225,7 @@ public static Expression make(CodeGenerationDataNameType returnType, } /** - * Creates a new Espression with 3 arguments. + * Creates a new Expression with 3 arguments. * * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. @@ -245,11 +245,11 @@ public static Expression make(CodeGenerationDataNameType returnType, String s2, CodeGenerationDataNameType t2, String s3) { - return new Expression(returnType, List.of(t0, t1, t2), List.of(s0, s1, s2, s3), new Info()); + return make(returnType, s0, t0, s1, t1, s2, t2, s3, new Info()); } /** - * Creates a new Espression with 3 arguments. + * Creates a new Expression with 3 arguments. * * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. @@ -275,7 +275,7 @@ public static Expression make(CodeGenerationDataNameType returnType, } /** - * Creates a new Espression with 4 arguments. + * Creates a new Expression with 4 arguments. * * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. @@ -299,11 +299,11 @@ public static Expression make(CodeGenerationDataNameType returnType, String s3, CodeGenerationDataNameType t3, String s4) { - return new Expression(returnType, List.of(t0, t1, t2, t3), List.of(s0, s1, s2, s3, s4), new Info()); + return make(returnType, s0, t0, s1, t1, s2, t2, s3, t3, s4, new Info()); } /** - * Creates a new Espression with 4 arguments. + * Creates a new Expression with 4 arguments. * * @param returnType The return type of the {@link Expression}. * @param s0 The first string, to be placed before {@code t0}. diff --git a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java index 9d7be693c192c..6e11a7050540d 100644 --- a/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java +++ b/test/hotspot/jtreg/testlibrary_tests/template_framework/examples/TestExpressions.java @@ -24,7 +24,7 @@ /* * @test * @bug 8359412 - * @summary Demonstrate the use of Expressions form the Template Library. + * @summary Demonstrate the use of Expressions from the Template Library. * @modules java.base/jdk.internal.misc * @library /test/lib / * @compile ../../../compiler/lib/verify/Verify.java From 0a269c3becfeb7f844a428b18c679de86f1957aa Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 18 Sep 2025 09:17:34 +0200 Subject: [PATCH 28/33] Apply Manuel's suggestions part 3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel Hässig --- test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 706e63c9b0927..5d72af4411dec 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -28,7 +28,7 @@ * @modules java.base/jdk.internal.misc * @library /test/lib / * @compile ../lib/verify/Verify.java - * @run main compiler.igvn.ExpressionFuzzer + * @run main -XX:+IgnoreUnrecognizedVMOptions -XX:CompileTaskTimeout=10000 compiler.igvn.ExpressionFuzzer */ package compiler.igvn; From d39d2dec1cd1bc22945ec0ef52c7db86a08db31c Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 18 Sep 2025 10:07:27 +0200 Subject: [PATCH 29/33] add othervm to test --- test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index 5d72af4411dec..b437af2664d3c 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -28,7 +28,7 @@ * @modules java.base/jdk.internal.misc * @library /test/lib / * @compile ../lib/verify/Verify.java - * @run main -XX:+IgnoreUnrecognizedVMOptions -XX:CompileTaskTimeout=10000 compiler.igvn.ExpressionFuzzer + * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:CompileTaskTimeout=10000 compiler.igvn.ExpressionFuzzer */ package compiler.igvn; From c04c879c0a7ad55c9c07520ec0e81bd3aa1f4d4b Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 18 Sep 2025 10:08:53 +0200 Subject: [PATCH 30/33] more comments --- test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java index b437af2664d3c..60b11e8ffbc44 100644 --- a/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java +++ b/test/hotspot/jtreg/compiler/igvn/ExpressionFuzzer.java @@ -68,6 +68,7 @@ // - Constrain also the unsigned bounds // - Some basic IR tests to ensure that the constraints / checksum mechanics work. // We may even have to add some IGVN optimizations to be able to better observe things right. +// - Lower the CompileTaskTimeout, if possible. It is chosen conservatively (rather high) for now. public class ExpressionFuzzer { private static final Random RANDOM = Utils.getRandomInstance(); From 199e06a389b3198ddf79142f98acd31255a21d23 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 13 Oct 2025 10:30:53 +0200 Subject: [PATCH 31/33] Apply suggestions from code review Co-authored-by: Christian Hagedorn --- .../lib/template_framework/library/Expression.java | 12 ++++++------ .../lib/template_framework/library/Operations.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java index 9c1b3c22fa63a..360937c8f7fc8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Expression.java @@ -41,7 +41,7 @@ * {@link Expression} or use existing ones from {@link Operations}. * *

- * The {@link Expression}s are composable, they can be explicitly {@link nest}ed, or randomly + * The {@link Expression}s are composable, they can be explicitly {@link #nest}ed, or randomly * combined using {@link #nestRandomly}. * *

@@ -346,7 +346,7 @@ public TemplateToken asToken(List arguments) { throw new IllegalArgumentException("Wrong number of arguments:" + " expected: " + argumentTypes.size() + " but got: " + arguments.size() + - " for " + this.toString()); + " for " + this); } // List of tokens: interleave strings and arguments. @@ -398,7 +398,7 @@ public static Expression nestRandomly(CodeGenerationDataNameType returnType, int maxNumberOfUsedExpressions) { List filtered = expressions.stream().filter(e -> e.returnType.isSubtypeOf(returnType)).toList(); - if (filtered.size() == 0) { + if (filtered.isEmpty()) { throw new IllegalArgumentException("Found no exception with the specified returnType."); } @@ -423,7 +423,7 @@ public Expression nestRandomly(List nestingExpressions) { CodeGenerationDataNameType argumentType = this.argumentTypes.get(argumentIndex); List filtered = nestingExpressions.stream().filter(e -> e.returnType.isSubtypeOf(argumentType)).toList(); - if (filtered.size() == 0) { + if (filtered.isEmpty()) { // Found no expression that has a matching returnType. return this; } @@ -456,8 +456,8 @@ public Expression nest(int argumentIndex, Expression nestingExpression) { newArgumentTypes.add(this.argumentTypes.get(i)); } newStrings.add(this.strings.get(argumentIndex) + - nestingExpression.strings.get(0)); // concat s1 and S0 - newArgumentTypes.add(nestingExpression.argumentTypes.get(0)); + nestingExpression.strings.getFirst()); // concat s1 and S0 + newArgumentTypes.add(nestingExpression.argumentTypes.getFirst()); for (int i = 1; i < nestingExpression.argumentTypes.size(); i++) { newStrings.add(nestingExpression.strings.get(i)); newArgumentTypes.add(nestingExpression.argumentTypes.get(i)); diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 6642e3476d655..33b20f518d888 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -69,7 +69,7 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(BYTES, "(", BOOLEANS, "?", BYTES, ":", BYTES, ")")); - // Arithmetic operations are not performned in byte, but rather promoted to int. + // Arithmetic operations are not performed in byte, but rather promoted to int. // ------------ Byte ------------- ops.add(Expression.make(INTS, "Byte.compare(", BYTES, ", ", BYTES, ")")); From c6787e411d702f7a67875c654bf64f132dc17678 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 13 Oct 2025 11:57:02 +0200 Subject: [PATCH 32/33] refactor for Christian --- .../library/Operations.java | 229 ++++++------------ 1 file changed, 69 insertions(+), 160 deletions(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index 33b20f518d888..bfc4720316ed8 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -57,18 +57,65 @@ private static List generatePrimitiveOperations() { Expression.Info withArithmeticException = new Expression.Info().withExceptions(Set.of("ArithmeticException")); Expression.Info withNondeterministicResult = new Expression.Info().withNondeterministicResult(); - // ------------ byte ------------- - ops.add(Expression.make(BYTES, "(byte)(", BYTES, ")")); - ops.add(Expression.make(BYTES, "(byte)(", SHORTS, ")")); - ops.add(Expression.make(BYTES, "(byte)(", CHARS, ")")); - ops.add(Expression.make(BYTES, "(byte)(", INTS, ")")); - ops.add(Expression.make(BYTES, "(byte)(", LONGS, ")")); - ops.add(Expression.make(BYTES, "(byte)(", FLOATS, ")")); - ops.add(Expression.make(BYTES, "(byte)(", DOUBLES, ")")); - // There is no cast from boolean. - - ops.add(Expression.make(BYTES, "(", BOOLEANS, "?", BYTES, ":", BYTES, ")")); + // Cast between all primitive types. Escept for Boolean, we cannot cast from and to. + CodeGenerationDataNameType.INTEGRAL_AND_FLOATING_TYPES.stream().forEach(src -> { + CodeGenerationDataNameType.INTEGRAL_AND_FLOATING_TYPES.stream().forEach(dst -> { + ops.add(Expression.make(dst, "(" + dst.name() + ")(", src, ")")); + }); + }); + + // Ternary operator. + CodeGenerationDataNameType.INTEGRAL_AND_FLOATING_TYPES.stream().forEach(type -> { + ops.add(Expression.make(type, "(", BOOLEANS, "?", type, ":", type, ")")); + }); + + List.of(INTS, LONGS).stream().forEach(type -> { + // Arithmetic operators + ops.add(Expression.make(type, "(-(", type, "))")); + ops.add(Expression.make(type, "(", type, " + ", type, ")")); + ops.add(Expression.make(type, "(", type, " - ", type, ")")); + ops.add(Expression.make(type, "(", type, " * ", type, ")")); + ops.add(Expression.make(type, "(", type, " / ", type, ")", withArithmeticException)); + ops.add(Expression.make(type, "(", type, " % ", type, ")", withArithmeticException)); + + // Bitwise Operators (non short-circuit) + ops.add(Expression.make(type, "(~(", type, "))")); + ops.add(Expression.make(type, "(", type, " & ", type, ")")); + ops.add(Expression.make(type, "(", type, " | ", type, ")")); + ops.add(Expression.make(type, "(", type, " ^ ", type, ")")); + ops.add(Expression.make(type, "(", type, " << ", type, ")")); + ops.add(Expression.make(type, "(", type, " >> ", type, ")")); + ops.add(Expression.make(type, "(", type, " >>> ", type, ")")); + + // Relational / Comparison Operators + ops.add(Expression.make(BOOLEANS, "(", type, " == ", type, ")")); + ops.add(Expression.make(BOOLEANS, "(", type, " != ", type, ")")); + ops.add(Expression.make(BOOLEANS, "(", type, " > ", type, ")")); + ops.add(Expression.make(BOOLEANS, "(", type, " < ", type, ")")); + ops.add(Expression.make(BOOLEANS, "(", type, " >= ", type, ")")); + ops.add(Expression.make(BOOLEANS, "(", type, " <= ", type, ")")); + }); + + CodeGenerationDataNameType.FLOATING_TYPES.stream().forEach(type -> { + // Arithmetic operators + ops.add(Expression.make(type, "(-(", type, "))")); + ops.add(Expression.make(type, "(", type, " + ", type, ")")); + ops.add(Expression.make(type, "(", type, " - ", type, ")")); + ops.add(Expression.make(type, "(", type, " * ", type, ")")); + ops.add(Expression.make(type, "(", type, " / ", type, ")")); + ops.add(Expression.make(type, "(", type, " % ", type, ")")); + + // Relational / Comparison Operators + ops.add(Expression.make(BOOLEANS, "(", type, " == ", type, ")")); + ops.add(Expression.make(BOOLEANS, "(", type, " != ", type, ")")); + ops.add(Expression.make(BOOLEANS, "(", type, " > ", type, ")")); + ops.add(Expression.make(BOOLEANS, "(", type, " < ", type, ")")); + ops.add(Expression.make(BOOLEANS, "(", type, " >= ", type, ")")); + ops.add(Expression.make(BOOLEANS, "(", type, " <= ", type, ")")); + }); + // ------------ byte ------------- + // Cast and ternary operator handled above. // Arithmetic operations are not performed in byte, but rather promoted to int. // ------------ Byte ------------- @@ -78,17 +125,7 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(LONGS, "Byte.toUnsignedLong(", BYTES, ")")); // ------------ char ------------- - ops.add(Expression.make(CHARS, "(char)(", BYTES, ")")); - ops.add(Expression.make(CHARS, "(char)(", SHORTS, ")")); - ops.add(Expression.make(CHARS, "(char)(", CHARS, ")")); - ops.add(Expression.make(CHARS, "(char)(", INTS, ")")); - ops.add(Expression.make(CHARS, "(char)(", LONGS, ")")); - ops.add(Expression.make(CHARS, "(char)(", FLOATS, ")")); - ops.add(Expression.make(CHARS, "(char)(", DOUBLES, ")")); - // There is no cast from boolean. - - ops.add(Expression.make(CHARS, "(", BOOLEANS, "?", CHARS, ":", CHARS, ")")); - + // Cast and ternary operator handled above. // Arithmetic operations are not performned in char, but rather promoted to int. // ------------ Character ------------- @@ -96,17 +133,7 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(CHARS, "Character.reverseBytes(", CHARS, ")")); // ------------ short ------------- - ops.add(Expression.make(SHORTS, "(short)(", BYTES, ")")); - ops.add(Expression.make(SHORTS, "(short)(", SHORTS, ")")); - ops.add(Expression.make(SHORTS, "(short)(", CHARS, ")")); - ops.add(Expression.make(SHORTS, "(short)(", INTS, ")")); - ops.add(Expression.make(SHORTS, "(short)(", LONGS, ")")); - ops.add(Expression.make(SHORTS, "(short)(", FLOATS, ")")); - ops.add(Expression.make(SHORTS, "(short)(", DOUBLES, ")")); - // There is no cast from boolean. - - ops.add(Expression.make(SHORTS, "(", BOOLEANS, "?", SHORTS, ":", SHORTS, ")")); - + // Cast and ternary operator handled above. // Arithmetic operations are not performned in short, but rather promoted to int. // ------------ Short ------------- @@ -117,41 +144,8 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(LONGS, "Short.toUnsignedLong(", SHORTS, ")")); // ------------ int ------------- - ops.add(Expression.make(INTS, "(int)(", BYTES, ")")); - ops.add(Expression.make(INTS, "(int)(", SHORTS, ")")); - ops.add(Expression.make(INTS, "(int)(", CHARS, ")")); - ops.add(Expression.make(INTS, "(int)(", INTS, ")")); - ops.add(Expression.make(INTS, "(int)(", LONGS, ")")); - ops.add(Expression.make(INTS, "(int)(", FLOATS, ")")); - ops.add(Expression.make(INTS, "(int)(", DOUBLES, ")")); - // There is no cast from boolean. - - ops.add(Expression.make(INTS, "(", BOOLEANS, "?", INTS, ":", INTS, ")")); - - // Arithmetic operators - ops.add(Expression.make(INTS, "(-(", INTS, "))")); - ops.add(Expression.make(INTS, "(", INTS, " + ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " - ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " * ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " / ", INTS, ")", withArithmeticException)); - ops.add(Expression.make(INTS, "(", INTS, " % ", INTS, ")", withArithmeticException)); - - // Bitwise Operators (non short-circuit) - ops.add(Expression.make(INTS, "(~(", INTS, "))")); - ops.add(Expression.make(INTS, "(", INTS, " & ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " | ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " ^ ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " << ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " >> ", INTS, ")")); - ops.add(Expression.make(INTS, "(", INTS, " >>> ", INTS, ")")); - - // Relational / Comparison Operators - ops.add(Expression.make(BOOLEANS, "(", INTS, " == ", INTS, ")")); - ops.add(Expression.make(BOOLEANS, "(", INTS, " != ", INTS, ")")); - ops.add(Expression.make(BOOLEANS, "(", INTS, " > ", INTS, ")")); - ops.add(Expression.make(BOOLEANS, "(", INTS, " < ", INTS, ")")); - ops.add(Expression.make(BOOLEANS, "(", INTS, " >= ", INTS, ")")); - ops.add(Expression.make(BOOLEANS, "(", INTS, " <= ", INTS, ")")); + // Cast and ternary operator handled above. + // Arithmetic, Bitwise, Relational / Comparison handled above. // ------------ Integer ------------- ops.add(Expression.make(INTS, "Integer.bitCount(", INTS, ")")); @@ -176,41 +170,8 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(LONGS, "Integer.toUnsignedLong(", INTS, ")")); // ------------ long ------------- - ops.add(Expression.make(LONGS, "(long)(", BYTES, ")")); - ops.add(Expression.make(LONGS, "(long)(", SHORTS, ")")); - ops.add(Expression.make(LONGS, "(long)(", CHARS, ")")); - ops.add(Expression.make(LONGS, "(long)(", INTS, ")")); - ops.add(Expression.make(LONGS, "(long)(", LONGS, ")")); - ops.add(Expression.make(LONGS, "(long)(", FLOATS, ")")); - ops.add(Expression.make(LONGS, "(long)(", DOUBLES, ")")); - // There is no cast from boolean. - - ops.add(Expression.make(LONGS, "(", BOOLEANS, "?", LONGS, ":", LONGS, ")")); - - // Arithmetic operators - ops.add(Expression.make(LONGS, "(-(", LONGS, "))")); - ops.add(Expression.make(LONGS, "(", LONGS, " + ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " - ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " * ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " / ", LONGS, ")", withArithmeticException)); - ops.add(Expression.make(LONGS, "(", LONGS, " % ", LONGS, ")", withArithmeticException)); - - // Bitwise Operators (non short-circuit) - ops.add(Expression.make(LONGS, "(~(", LONGS, "))")); - ops.add(Expression.make(LONGS, "(", LONGS, " & ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " | ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " ^ ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " << ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " >> ", LONGS, ")")); - ops.add(Expression.make(LONGS, "(", LONGS, " >>> ", LONGS, ")")); - - // Relational / Comparison Operators - ops.add(Expression.make(BOOLEANS, "(", LONGS, " == ", LONGS, ")")); - ops.add(Expression.make(BOOLEANS, "(", LONGS, " != ", LONGS, ")")); - ops.add(Expression.make(BOOLEANS, "(", LONGS, " > ", LONGS, ")")); - ops.add(Expression.make(BOOLEANS, "(", LONGS, " < ", LONGS, ")")); - ops.add(Expression.make(BOOLEANS, "(", LONGS, " >= ", LONGS, ")")); - ops.add(Expression.make(BOOLEANS, "(", LONGS, " <= ", LONGS, ")")); + // Cast and ternary operator handled above. + // Arithmetic, Bitwise, Relational / Comparison handled above. // ------------ Long ------------- ops.add(Expression.make(INTS, "Long.bitCount(", LONGS, ")")); @@ -234,33 +195,8 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(LONGS, "Long.sum(", LONGS, ", ", LONGS, ")")); // ------------ float ------------- - ops.add(Expression.make(FLOATS, "(float)(", BYTES, ")")); - ops.add(Expression.make(FLOATS, "(float)(", SHORTS, ")")); - ops.add(Expression.make(FLOATS, "(float)(", CHARS, ")")); - ops.add(Expression.make(FLOATS, "(float)(", INTS, ")")); - ops.add(Expression.make(FLOATS, "(float)(", LONGS, ")")); - ops.add(Expression.make(FLOATS, "(float)(", FLOATS, ")")); - ops.add(Expression.make(FLOATS, "(float)(", DOUBLES, ")")); - // There is no cast from boolean. - - ops.add(Expression.make(FLOATS, "(", BOOLEANS, "?", FLOATS, ":", FLOATS, ")")); - - // Arithmetic operators - ops.add(Expression.make(FLOATS, "(-(", FLOATS, "))")); - ops.add(Expression.make(FLOATS, "(", FLOATS, " + ", FLOATS, ")")); - ops.add(Expression.make(FLOATS, "(", FLOATS, " - ", FLOATS, ")")); - ops.add(Expression.make(FLOATS, "(", FLOATS, " * ", FLOATS, ")")); - ops.add(Expression.make(FLOATS, "(", FLOATS, " / ", FLOATS, ")")); - ops.add(Expression.make(FLOATS, "(", FLOATS, " % ", FLOATS, ")")); - - - // Relational / Comparison Operators - ops.add(Expression.make(BOOLEANS, "(", FLOATS, " == ", FLOATS, ")")); - ops.add(Expression.make(BOOLEANS, "(", FLOATS, " != ", FLOATS, ")")); - ops.add(Expression.make(BOOLEANS, "(", FLOATS, " > ", FLOATS, ")")); - ops.add(Expression.make(BOOLEANS, "(", FLOATS, " < ", FLOATS, ")")); - ops.add(Expression.make(BOOLEANS, "(", FLOATS, " >= ", FLOATS, ")")); - ops.add(Expression.make(BOOLEANS, "(", FLOATS, " <= ", FLOATS, ")")); + // Cast and ternary operator handled above. + // Arithmetic and Relational / Comparison handled above. // ------------ Float ------------- ops.add(Expression.make(INTS, "Float.compare(", FLOATS, ", ", FLOATS, ")")); @@ -277,32 +213,8 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(FLOATS, "Float.sum(", FLOATS, ", ", FLOATS, ")")); // ------------ double ------------- - ops.add(Expression.make(DOUBLES, "(double)(", BYTES, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", SHORTS, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", CHARS, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", INTS, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", LONGS, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", FLOATS, ")")); - ops.add(Expression.make(DOUBLES, "(double)(", DOUBLES, ")")); - // There is no cast from boolean. - - ops.add(Expression.make(DOUBLES, "(", BOOLEANS, "?", DOUBLES, ":", DOUBLES, ")")); - - // Arithmetic operators - ops.add(Expression.make(DOUBLES, "(-(", DOUBLES, "))")); - ops.add(Expression.make(DOUBLES, "(", DOUBLES, " + ", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "(", DOUBLES, " - ", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "(", DOUBLES, " * ", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "(", DOUBLES, " / ", DOUBLES, ")")); - ops.add(Expression.make(DOUBLES, "(", DOUBLES, " % ", DOUBLES, ")")); - - // Relational / Comparison Operators - ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " == ", DOUBLES, ")")); - ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " != ", DOUBLES, ")")); - ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " > ", DOUBLES, ")")); - ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " < ", DOUBLES, ")")); - ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " >= ", DOUBLES, ")")); - ops.add(Expression.make(BOOLEANS, "(", DOUBLES, " <= ", DOUBLES, ")")); + // Cast and ternary operator handled above. + // Arithmetic and Relational / Comparison handled above. // ------------ Double ------------- ops.add(Expression.make(INTS, "Double.compare(", DOUBLES, ", ", DOUBLES, ")")); @@ -318,10 +230,7 @@ private static List generatePrimitiveOperations() { ops.add(Expression.make(DOUBLES, "Double.sum(", DOUBLES, ", ", DOUBLES, ")")); // ------------ boolean ------------- - // There is no cast to boolean. - - ops.add(Expression.make(BOOLEANS, "(", BOOLEANS, "?", BOOLEANS, ":", BOOLEANS, ")")); - + // Cast and ternary operator handled above. // There are no boolean arithmetic operators // Logical operators From 3e4a1b766320ebf8fa4f4338493ecdb2beba45e4 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Mon, 13 Oct 2025 12:37:10 +0200 Subject: [PATCH 33/33] Update test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java Co-authored-by: Christian Hagedorn --- .../compiler/lib/template_framework/library/Operations.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java index bfc4720316ed8..53acf943b20d6 100644 --- a/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java +++ b/test/hotspot/jtreg/compiler/lib/template_framework/library/Operations.java @@ -57,7 +57,7 @@ private static List generatePrimitiveOperations() { Expression.Info withArithmeticException = new Expression.Info().withExceptions(Set.of("ArithmeticException")); Expression.Info withNondeterministicResult = new Expression.Info().withNondeterministicResult(); - // Cast between all primitive types. Escept for Boolean, we cannot cast from and to. + // Cast between all primitive types. Except for Boolean, we cannot cast from and to. CodeGenerationDataNameType.INTEGRAL_AND_FLOATING_TYPES.stream().forEach(src -> { CodeGenerationDataNameType.INTEGRAL_AND_FLOATING_TYPES.stream().forEach(dst -> { ops.add(Expression.make(dst, "(" + dst.name() + ")(", src, ")"));